S: Amsterdam
S: The Netherlands
+N: Zach Brown
+E: zab@zabbo.net
+D: maestro pci sound
+
N: Ray Burr
E: ryb@nightmare.com
D: Original author of Amiga FFS filesystem
The module will be called mouse.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
-USB HP scanner support
-CONFIG_USB_HP_SCANNER
- Say Y here if you want to connect a USB HP scanner to your
- computer's USB port. Please read drivers/usb/README.hp_scanner
- for more information.
+USB scanner support
+CONFIG_USB_SCANNER
+ Say Y here if you want to connect a USB scanner to your
+ computer's USB port. Please read drivers/usb/README.scanner
+ and drivers/usb/README.scanner_hp_sane for more information.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
See Documentation/sound/NM256 for further information.
+ESS Maestro sound chipsets
+CONFIG_SOUND_MAESTRO
+ Say Y or M if you have a sound system driven by ESS's Maestro line
+ of PCI sound chips. These include the Maestro 1, Maestro 2, and
+ Maestro 2E. See Documentation/sound/Maestro for more details.
+
Are you using a crosscompiler
CONFIG_CROSSCOMPILE
Say Y here if you are compiling the kernel on a different
--- /dev/null
+
+ Cramfs - cram a filesystem onto a small ROM
+
+cramfs is designed to be simple and small, and to compress things well.
+
+It uses the zlib routines to compress a file one page at a time, and
+allows random page access. The meta-data is not compressed, but is
+expressed in a very terse representation to make it use much less
+diskspace than traditional filesystems.
+
+You can't write to a cramfs filesystem (making it compressible and
+compact also makes it _very_ hard to update on-the-fly), so you have to
+create the disk image with the "mkcramfs" utility in scripts/cramfs.
--- /dev/null
+ An OSS/Lite Driver for the ESS Maestro family of sound cards
+
+ Zach Brown, December 1999
+
+Driver Status and Availability
+------------------------------
+
+The most recent version of this driver will hopefully always be available at
+ http://people.redhat.com/zab/maestro/
+
+I will try and maintain the most recent stable version of the driver
+in both the stable and development kernel lines.
+
+ESS Maestro Chip Family
+-----------------------
+
+There are 3 main variants of the ESS Maestro PCI sound chip. The first
+is the Maestro 1. It was originally produced by Platform Tech as the
+'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with
+0x0100 as the device ID. It was put on some sound boards and a few laptops.
+ESS bought the design and cleaned it up as the Maestro 2. This starts
+their marking with the ESS vendor ID 0x125D and the 'year' device IDs.
+The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978.
+
+The various families of Maestro are mostly identical as far as this
+driver is concerned. It doesn't touch the DSP parts that differ (though
+it could for FM synthesis)
+
+Driver OSS Behavior
+--------------------
+
+This OSS driver exports /dev/mixer and /dev/dsp to applications, which
+mostly adhere to the OSS spec. This driver doesn't register itself
+with /dev/sndstat, so don't expect information to appear there.
+
+The /dev/dsp device exported behaves almost as expected. Playback is
+supported in all the various lovely formats. 8/16bit stereo/mono from
+8khz to 48khz, and mmap()ing for playback behaves. Capture/recording
+is limited due to oddities with the Maestro hardware. One can only
+record in 16bit stereo. For recording the maestro uses non interleaved
+stereo buffers so that mmap()ing the incoming data does not result in
+a ring buffer of LRLR data. mmap()ing of the read buffers is therefore
+disallowed until this can be cleaned up.
+
+/dev/mixer is an interface to the AC'97 codec on the Maestro. It is
+worth noting that there are a variety of AC'97s that can be wired to
+the Maestro. Which is used is entirely up to the hardware implementor.
+This should only be visible to the user by the presence, or lack, of
+'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them.
+
+The driver doesn't support MIDI or FM playback at the moment. Typically
+the Maestro is wired to an MPU MIDI chip, but some hardware implementations
+don't. We need to assemble a white list of hardware implementations that
+have MIDI wired properly before we can claim to support it safely.
+
+Compiling and Installing
+------------------------
+
+With the drivers inclusion into the kernel, compiling and installing
+is the same as most OSS/Lite modular sound drivers. Compilation
+of the driver is enabled through the CONFIG_SOUND_MAESTRO variable
+in the config system.
+
+It may be modular or statically linked. If it is modular it should be
+installed with the rest of the modules for the kernel on the system.
+Typically this will be in /lib/modules/ somewhere. 'alias sound maestro'
+should also be added to your module configs (typically /etc/conf.modules)
+if you're using modular OSS/Lite sound and want to default to using a
+maestro chip.
+
+As this is a PCI device, the module does not need to be informed of
+any IO or IRQ resources it should use, it devines these from the
+system. Somtimes, on sucky PCs, the BIOS fails to allocated resources
+for the maestro. This will result in a message like:
+ maestro: PCI subsystem reports IRQ 0, this might not be correct.
+from the kernel. Should this happen the sound chip most likely will
+not operate correctly. To solve this one has to dig through their BIOS
+(typically entered by hitting a hot key at boot time) and figure out
+what magic needs to happen so that the BIOS will reward the maestro with
+an IRQ. This operation is incredibly system specific, so you're on your
+own. Sometimes the magic lies in 'PNP Capable Operating System' settings.
+
+There are very few options to the driver. One is 'debug' which will
+tell the driver to print minimal debugging information as it runs. This
+can be collected with 'dmesg' or through the klogd daemon.
+
+The other, more interesting option, is 'dsps_order'. Typically at
+install time the driver will only register one available /dev/dsp device
+for its use. The 'dsps_order' module parameter allows for more devices
+to be allocated, as a power of two. Up to 4 devices can be registered
+( dsps_order=2 ). These devices act as fully distinct units and use
+separate channels in the maestro.
+
+.. more details ..
+-----------------
+
+drivers/sound/maestro.c contains comments that hopefully explain
+the maestro implementation.
L: linuxppc-dev@lists.linuxppc.org
S: Maintained
+MAESTRO PCI SOUND DRIVER
+P: Zach Brown
+M: zab@redhat.com
+W: http://people.redhat.com/zab/maestro/
+S: Supported
+
M68K
P: Jes Sorensen
M: Jes.Sorensen@cern.ch
#
CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o
-FILESYSTEMS =fs/filesystems.a
NETWORKS =net/network.a
DRIVERS =drivers/block/block.a \
drivers/char/char.o \
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
--start-group \
$(CORE_FILES) \
- $(FILESYSTEMS) \
$(NETWORKS) \
$(DRIVERS) \
$(LIBS) \
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
+# CONFIG_CRAMFS is not set
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_MINIX_FS is not set
if [ "$CONFIG_APUS" = "y" ]; then
define_bool CONFIG_PCI n
-else if [ "$CONFIG_OAK" = "y" ]; then
+fi
+if [ "$CONFIG_OAK" = "y" ]; then
define_bool CONFIG_PCI n
-else if [ "$CONFIG_8xx" = "y" ]; then
+fi
+if [ "$CONFIG_8xx" = "y" ]; then
bool 'QSpan PCI' CONFIG_PCI
else
+ if [ "$CONFIG_APUS" != "y" ]; then
define_bool CONFIG_PCI y
+ fi
fi
bool 'Networking support' CONFIG_NET
else
#endif
ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */
-
+sprintf(cmd_line, "console=ttyS0,9600 console=tty0");
printk("Boot arguments: %s\n", cmd_line);
request_region(0x20,0x20,"pic1");
* openpic irq. So we just check to make sure the controller
* is an openpic and if it is then eoi
*
- * We do it this way since our irq_desc[irq].ctl can change
+ * We do it this way since our irq_desc[irq].handler can change
* with RTL and no longer be open_pic -- Cort
*/
if ( irq >= open_pic.irq_offset)
}
open_pic.irq_offset = 16;
for ( i = 16 ; i < NR_IRQS ; i++ )
- irq_desc[i].ctl = &open_pic;
+ irq_desc[i].handler = &open_pic;
openpic_init(1);
for ( i = 0 ; i < 16 ; i++ )
- irq_desc[i].ctl = &i8259_pic;
+ irq_desc[i].handler = &i8259_pic;
i8259_init();
#ifdef CONFIG_XMON
request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI),
/* gemini has no 8259 */
open_pic.irq_offset = 0;
for( i=0; i < NR_IRQS; i++ )
- irq_desc[i].ctl = &open_pic;
+ irq_desc[i].handler = &open_pic;
openpic_init(1);
#ifdef __SMP__
request_irq(OPENPIC_VEC_IPI, openpic_ipi_action,
* openpic irq. So we just check to make sure the controller
* is an openpic and if it is then eoi
*
- * We do it this way since our irq_desc[irq].ctl can change
+ * We do it this way since our irq_desc[irq].handler can change
* with RTL and no longer be open_pic -- Cort
*/
if ( irq >= open_pic.irq_offset)
#include <linux/openpic.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/irq.h>
#include <asm/bitops.h>
#include <asm/hydra.h>
#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
-struct irqdesc irq_desc[NR_IRQS] = {{0, 0}, };
+irq_desc_t irq_desc[NR_IRQS];
int ppc_spurious_interrupts = 0;
unsigned int ppc_local_bh_count[NR_CPUS];
unsigned int ppc_local_irq_count[NR_CPUS];
#else
len += sprintf(buf+len, "%10u ", kstat_irqs(i));
#endif /* __SMP__ */
- if ( irq_desc[i].ctl )
- len += sprintf(buf+len, " %s ", irq_desc[i].ctl->typename );
+ if ( irq_desc[i].handler )
+ len += sprintf(buf+len, " %s ", irq_desc[i].handler->typename );
else
len += sprintf(buf+len, " None ");
len += sprintf(buf+len, " %s",action->name);
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
-#include <asm/irq_control.h>
+#include <linux/irq.h>
void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq);
ppc8xx_pic.irq_offset = 0;
for ( i = 0 ; i < NR_SIU_INTS ; i++ )
- irq_desc[i].ctl = &ppc8xx_pic;
+ irq_desc[i].handler = &ppc8xx_pic;
/* We could probably incorporate the CPM into the multilevel
* interrupt structure.
#if defined(CONFIG_PCI)
for ( i = NR_SIU_INTS ; i < (NR_SIU_INTS + NR_8259_INTS) ; i++ )
- irq_desc[i].ctl = &i8259_pic;
+ irq_desc[i].handler = &i8259_pic;
i8259_pic.irq_offset = NR_SIU_INTS;
i8259_init();
request_8xxirq(ISA_BRIDGE_INT, mbx_i8259_action, 0, "8259 cascade", NULL);
+++ /dev/null
-/*
- * MBX pci routines.
- * The MBX uses the QSpan PCI bridge. The config address register
- * is located 0x500 from the base of the bridge control/status registers.
- * The data register is located at 0x504.
- * This is a two step operation. First, the address register is written,
- * then the data register is read/written as required.
- * I don't know what to do about interrupts (yet).
- */
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/bios32.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-#include <asm/mbx.h>
-
-
-/*
- * This blows......The MBX uses the Tundra QSpan PCI bridge. When
- * reading the configuration space, if something does not respond
- * the bus times out and we get a machine check interrupt. So, the
- * good ol' exception tables come to mind to trap it and return some
- * value.
- *
- * On an error we just return a -1, since that is what the caller wants
- * returned if nothing is present. I copied this from __get_user_asm,
- * with the only difference of returning -1 instead of EFAULT.
- * There is an associated hack in the machine check trap code.
- *
- * The QSPAN is also a big endian device, that is it makes the PCI
- * look big endian to us. This presents a problem for the Linux PCI
- * functions, which assume little endian. For example, we see the
- * first 32-bit word like this:
- * ------------------------
- * | Device ID | Vendor ID |
- * ------------------------
- * If we read/write as a double word, that's OK. But in our world,
- * when read as a word, device ID is at location 0, not location 2 as
- * the little endian PCI would believe. We have to switch bits in
- * the PCI addresses given to us to get the data to/from the correct
- * byte lanes.
- *
- * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5.
- * It always forces the MS bit to zero. Therefore, dev_fn values
- * greater than 128 are returned as "no device found" errors.
- *
- * The QSPAN can only perform long word (32-bit) configuration cycles.
- * The "offset" must have the two LS bits set to zero. Read operations
- * require we read the entire word and then sort out what should be
- * returned. Write operations other than long word require that we
- * read the long word, update the proper word or byte, then write the
- * entire long word back.
- *
- * PCI Bridge hack. We assume (correctly) that bus 0 is the primary
- * PCI bus from the QSPAN. If we are called with a bus number other
- * than zero, we create a Type 1 configuration access that a downstream
- * PCI bridge will interpret.
- */
-
-#define __get_mbx_pci_config(x, addr, op) \
- __asm__ __volatile__( \
- "1: "op" %0,0(%1)\n" \
- " eieio\n" \
- "2:\n" \
- ".section .fixup,\"ax\"\n" \
- "3: li %0,-1\n" \
- " b 2b\n" \
- ".section __ex_table,\"a\"\n" \
- " .align 2\n" \
- " .long 1b,3b\n" \
- ".text" \
- : "=r"(x) : "r"(addr))
-
-#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500))
-#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504))
-
-#define mk_config_addr(bus, dev, offset) \
- (((bus)<<16) | ((dev)<<8) | (offset & 0xfc))
-
-#define mk_config_type1(bus, dev, offset) \
- mk_config_addr(bus, dev, offset) | 1;
-
-int mbx_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned char *val)
-{
- uint temp;
- u_char *cp;
-
- if ((bus > 7) || (dev_fn > 127)) {
- *val = 0xff;
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
-
- if (bus == 0)
- *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
- else
- *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
- __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
-
- offset ^= 0x03;
- cp = ((u_char *)&temp) + (offset & 0x03);
- *val = *cp;
- return PCIBIOS_SUCCESSFUL;
-}
-
-int mbx_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned short *val)
-{
- uint temp;
- ushort *sp;
-
- if ((bus > 7) || (dev_fn > 127)) {
- *val = 0xffff;
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
-
- if (bus == 0)
- *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
- else
- *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
- __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
- offset ^= 0x02;
-
- sp = ((ushort *)&temp) + ((offset >> 1) & 1);
- *val = *sp;
- return PCIBIOS_SUCCESSFUL;
-}
-
-int mbx_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned int *val)
-{
- if ((bus > 7) || (dev_fn > 127)) {
- *val = 0xffffffff;
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- if (bus == 0)
- *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
- else
- *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
- __get_mbx_pci_config(*val, QS_CONFIG_DATA, "lwz");
- return PCIBIOS_SUCCESSFUL;
-}
-
-int mbx_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned char val)
-{
- uint temp;
- u_char *cp;
-
- if ((bus > 7) || (dev_fn > 127))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
-
- offset ^= 0x03;
- cp = ((u_char *)&temp) + (offset & 0x03);
- *cp = val;
-
- if (bus == 0)
- *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
- else
- *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
- *QS_CONFIG_DATA = temp;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-int mbx_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned short val)
-{
- uint temp;
- ushort *sp;
-
- if ((bus > 7) || (dev_fn > 127))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
-
- offset ^= 0x02;
- sp = ((ushort *)&temp) + ((offset >> 1) & 1);
- *sp = val;
-
- if (bus == 0)
- *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
- else
- *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
- *QS_CONFIG_DATA = temp;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-int mbx_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned int val)
-{
- if ((bus > 7) || (dev_fn > 127))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- if (bus == 0)
- *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
- else
- *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
- *(unsigned int *)QS_CONFIG_DATA = val;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-int mbx_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
- unsigned short index, unsigned char *bus_ptr,
- unsigned char *dev_fn_ptr)
-{
- int num, devfn;
- unsigned int x, vendev;
-
- if (vendor == 0xffff)
- return PCIBIOS_BAD_VENDOR_ID;
- vendev = (dev_id << 16) + vendor;
- num = 0;
- for (devfn = 0; devfn < 32; devfn++) {
- mbx_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
- if (x == vendev) {
- if (index == num) {
- *bus_ptr = 0;
- *dev_fn_ptr = devfn<<3;
- return PCIBIOS_SUCCESSFUL;
- }
- ++num;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-int mbx_pcibios_find_class(unsigned int class_code, unsigned short index,
- unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
-{
- int devnr, x, num;
-
- num = 0;
- for (devnr = 0; devnr < 32; devnr++) {
- mbx_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
- if ((x>>8) == class_code) {
- if (index == num) {
- *bus_ptr = 0;
- *dev_fn_ptr = devnr<<3;
- return PCIBIOS_SUCCESSFUL;
- }
- ++num;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-void __init
-mbx_pcibios_fixup(void)
-{
- /* Nothing to do here? */
-}
-
-void __init
-mbx_setup_pci_ptrs(void)
-{
- set_config_access_method(mbx);
-
- ppc_md.pcibios_fixup = mbx_pcibios_fixup;
-}
-
+++ /dev/null
-/*
- * $Id: mbx_setup.c,v 1.12 1999/08/31 06:53:56 davem Exp $
- *
- * linux/arch/ppc/kernel/setup.c
- *
- * Copyright (C) 1995 Linus Torvalds
- * Adapted from 'alpha' version by Gary Thomas
- * Modified by Cort Dougan (cort@cs.nmt.edu)
- * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
- */
-
-/*
- * bootup setup stuff..
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/ptrace.h>
-#include <linux/malloc.h>
-#include <linux/user.h>
-#include <linux/a.out.h>
-#include <linux/tty.h>
-#include <linux/major.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/blk.h>
-#include <linux/ide.h>
-#include <linux/ioport.h>
-
-#include <asm/mmu.h>
-#include <asm/processor.h>
-#include <asm/residual.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/mbx.h>
-#include <asm/machdep.h>
-
-#include "time.h"
-#include "local_irq.h"
-
-static int mbx_set_rtc_time(unsigned long time);
-unsigned long mbx_get_rtc_time(void);
-void mbx_calibrate_decr(void);
-
-extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode);
-extern int mackbd_getkeycode(unsigned int scancode);
-extern int mackbd_translate(unsigned char scancode, unsigned char *keycode,
- char raw_mode);
-extern char mackbd_unexpected_up(unsigned char keycode);
-extern void mackbd_leds(unsigned char leds);
-extern void mackbd_init_hw(void);
-
-extern unsigned long loops_per_sec;
-
-unsigned long empty_zero_page[1024];
-
-#ifdef CONFIG_BLK_DEV_RAM
-extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
-extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
-extern int rd_image_start; /* starting block # of image */
-#endif
-
-extern char saved_command_line[256];
-
-extern unsigned long find_available_memory(void);
-extern void m8xx_cpm_reset(uint);
-
-void __init adbdev_init(void)
-{
-}
-
-void __init
-mbx_setup_arch(void)
-{
- int cpm_page;
- extern char cmd_line[];
-
- cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
-
- sprintf(cmd_line,
-"%s root=/dev/nfs nfsroot=/sys/mbxroot",
- cmd_line);
- printk("Boot arguments: %s\n", cmd_line);
-
- /* Reset the Communication Processor Module.
- */
- m8xx_cpm_reset(cpm_page);
-
-#ifdef notdef
- ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
-#endif
-
-#ifdef CONFIG_BLK_DEV_INITRD
-#if 0
- ROOT_DEV = to_kdev_t(0x0200); /* floppy */
- rd_prompt = 1;
- rd_doload = 1;
- rd_image_start = 0;
-#endif
-#if 0 /* XXX this may need to be updated for the new bootmem stuff,
- or possibly just deleted (see set_phys_avail() in init.c).
- - paulus. */
- /* initrd_start and size are setup by boot/head.S and kernel/head.S */
- if ( initrd_start )
- {
- if (initrd_end > *memory_end_p)
- {
- printk("initrd extends beyond end of memory "
- "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
- initrd_end,*memory_end_p);
- initrd_start = 0;
- }
- }
-#endif
-#endif
-
-#ifdef notdef
- request_region(0x20,0x20,"pic1");
- request_region(0xa0,0x20,"pic2");
- request_region(0x00,0x20,"dma1");
- request_region(0x40,0x20,"timer");
- request_region(0x80,0x10,"dma page reg");
- request_region(0xc0,0x20,"dma2");
-#endif
-}
-
-void
-abort(void)
-{
-#ifdef CONFIG_XMON
- extern void xmon(void *);
- xmon(0);
-#endif
- machine_restart(NULL);
-}
-
-/* The decrementer counts at the system (internal) clock frequency divided by
- * sixteen, or external oscillator divided by four. Currently, we only
- * support the MBX, which is system clock divided by sixteen.
- */
-void __init mbx_calibrate_decr(void)
-{
- bd_t *binfo = (bd_t *)&res;
- int freq, fp, divisor;
-
- if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0)
- printk("WARNING: Wrong decrementer source clock.\n");
-
- /* The manual says the frequency is in Hz, but it is really
- * as MHz. The value 'fp' is the number of decrementer ticks
- * per second.
- */
- fp = (binfo->bi_intfreq * 1000000) / 16;
- freq = fp*60; /* try to make freq/1e6 an integer */
- divisor = 60;
- printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
- decrementer_count = freq / HZ / divisor;
- count_period_num = divisor;
- count_period_den = freq / 1000000;
-}
-
-/* A place holder for time base interrupts, if they are ever enabled.
-*/
-void timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
-{
- printk("timebase_interrupt()\n");
-}
-
-/* The RTC on the MPC8xx is an internal register.
- * We want to protect this during power down, so we need to unlock,
- * modify, and re-lock.
- */
-static int
-mbx_set_rtc_time(unsigned long time)
-{
- ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY;
- ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time;
- ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY;
- return(0);
-}
-
-unsigned long __init mbx_get_rtc_time(void)
-{
- /* First, unlock all of the registers we are going to modify.
- * To protect them from corruption during power down, registers
- * that are maintained by keep alive power are "locked". To
- * modify these registers we have to write the key value to
- * the key location associated with the register.
- */
- ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY;
- ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY;
-
-
- /* Disable the RTC one second and alarm interrupts.
- */
- ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &=
- ~(RTCSC_SIE | RTCSC_ALE);
-
- /* Enabling the decrementer also enables the timebase interrupts
- * (or from the other point of view, to get decrementer interrupts
- * we have to enable the timebase). The decrementer interrupt
- * is wired into the vector table, nothing to do here for that.
- */
- ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr =
- ((mk_int_int_mask(DEC_INTERRUPT) << 8) |
- (TBSCR_TBF | TBSCR_TBE));
- if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0)
- panic("Could not allocate timer IRQ!");
-
- /* Get time from the RTC.
- */
- return ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc;
-}
-
-void
-mbx_restart(char *cmd)
-{
- extern void MBX_gorom(void);
-
- MBX_gorom();
-}
-
-void
-mbx_power_off(void)
-{
- mbx_restart(NULL);
-}
-
-void
-mbx_halt(void)
-{
- mbx_restart(NULL)
-}
-
-
-int mbx_setup_residual(char *buffer)
-{
- int len = 0;
- bd_t *bp;
- extern RESIDUAL *res;
-
- bp = (bd_t *)res;
-
- len += sprintf(len+buffer,"clock\t\t: %dMHz\n"
- "bus clock\t: %dMHz\n",
- bp->bi_intfreq /*/ 1000000*/,
- bp->bi_busfreq /*/ 1000000*/);
-
- return len;
-}
-
-void
-mbx_do_IRQ(struct pt_regs *regs,
- int cpu,
- int isfake)
-{
- int irq;
- unsigned long bits = 0;
-
- /* For MPC8xx, read the SIVEC register and shift the bits down
- * to get the irq number. */
- bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec;
- irq = bits >> 26;
- irq += ppc8xx_pic.irq_offset;
- bits = 1UL << irq;
-
- if (irq < 0) {
- printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
- irq, regs->nip);
- spurious_interrupts++;
- }
- else {
- ppc_irq_dispatch_handler( regs, irq );
- }
-
-}
-
-static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs)
-{
- int bits, irq;
-
- /* A bug in the QSpan chip causes it to give us 0xff always
- * when doing a character read. So read 32 bits and shift.
- * This doesn't seem to return useful values anyway, but
- * read it to make sure things are acked.
- * -- Cort
- */
- irq = (inl(0x508) >> 24)&0xff;
- if ( irq != 0xff ) printk("iack %d\n", irq);
-
- outb(0x0C, 0x20);
- irq = inb(0x20) & 7;
- if (irq == 2)
- {
- outb(0x0C, 0xA0);
- irq = inb(0xA0);
- irq = (irq&7) + 8;
- }
- bits = 1UL << irq;
- irq += i8259_pic.irq_offset;
- ppc_irq_dispatch_handler( regs, irq );
-}
-
-
-/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External
- * interrupts can be either edge or level triggered, but there is no
- * reason for us to change the EPPC-bug values (it would not work if we did).
- */
-void __init
-mbx_init_IRQ(void)
-{
- int i;
-
- ppc8xx_pic.irq_offset = 16;
- for ( i = 16 ; i < 32 ; i++ )
- irq_desc[i].ctl = &ppc8xx_pic;
- unmask_irq(CPM_INTERRUPT);
-
- for ( i = 0 ; i < 16 ; i++ )
- irq_desc[i].ctl = &i8259_pic;
- i8259_init();
- request_irq(ISA_BRIDGE_INT, mbx_i8259_action, 0, "8259 cascade", NULL);
- enable_irq(ISA_BRIDGE_INT);
-}
-
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-/*
- * IDE stuff.
- */
-void
-mbx_ide_insw(ide_ioreg_t port, void *buf, int ns)
-{
- ide_insw(port+_IO_BASE), buf, ns);
-}
-
-void
-mbx_ide_outsw(ide_ioreg_t port, void *buf, int ns)
-{
- ide_outsw(port+_IO_BASE, buf, ns);
-}
-
-int
-mbx_ide_default_irq(ide_ioreg_t base)
-{
- return 14;
-}
-
-ide_ioreg_t
-mbx_ide_default_io_base(int index)
-{
- return index;
-}
-
-int
-mbx_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
- return 0
-}
-
-void
-mbx_ide_request_region(ide_ioreg_t from,
- unsigned int extent,
- const char *name)
-{
-}
-
-void
-mbx_ide_release_region(ide_ioreg_t from,
- unsigned int extent)
-{
-}
-
-void
-mbx_ide_fix_driveid(struct hd_driveid *id)
-{
- ppc_generic_ide_fix_driveid(id);
-}
-
-void
-mbx_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
-{
- ide_ioreg_t reg = data_port;
- int i;
-
- *irq = 0;
-
- if (data_port != 0) /* Only map the first ATA flash drive */
- return;
-
-#ifdef ATA_FLASH
-
- reg = (ide_ioreg_t) ioremap(PCMCIA_MEM_ADDR, 0x200);
-
- for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
- hw->io_ports[i] = reg;
- reg += 1;
- }
-
- /* Does not matter */
-
- if (ctrl_port) {
- hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
- } else {
- hw->io_ports[IDE_CONTROL_OFFSET] = reg;
- }
- if (irq)
- hw->irq = 13;
-#endif
-}
-#endif
-
-void __init
-mbx_init(unsigned long r3, unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7)
-{
-
- if ( r3 )
- memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
-
-#ifdef CONFIG_PCI
- mbx_setup_pci_ptrs();
-#endif
-
-#ifdef CONFIG_BLK_DEV_INITRD
- /* take care of initrd if we have one */
- if ( r4 )
- {
- initrd_start = r4 + KERNELBASE;
- initrd_end = r5 + KERNELBASE;
- }
-#endif /* CONFIG_BLK_DEV_INITRD */
- /* take care of cmd line */
- if ( r6 )
- {
-
- *(char *)(r7+KERNELBASE) = 0;
- strcpy(cmd_line, (char *)(r6+KERNELBASE));
- }
-
- ppc_md.setup_arch = mbx_setup_arch;
- ppc_md.setup_residual = mbx_setup_residual;
- ppc_md.get_cpuinfo = NULL;
- ppc_md.irq_cannonicalize = NULL;
- ppc_md.init_IRQ = mbx_init_IRQ;
- ppc_md.do_IRQ = mbx_do_IRQ;
- ppc_md.init = NULL;
-
- ppc_md.restart = mbx_restart;
- ppc_md.power_off = mbx_power_off;
- ppc_md.halt = mbx_halt;
-
- ppc_md.time_init = NULL;
- ppc_md.set_rtc_time = mbx_set_rtc_time;
- ppc_md.get_rtc_time = mbx_get_rtc_time;
- ppc_md.calibrate_decr = mbx_calibrate_decr;
-
- ppc_md.kbd_setkeycode = pckbd_setkeycode;
- ppc_md.kbd_getkeycode = pckbd_getkeycode;
- ppc_md.kbd_translate = pckbd_translate;
- ppc_md.kbd_unexpected_up = pckbd_unexpected_up;
- ppc_md.kbd_leds = pckbd_leds;
- ppc_md.kbd_init_hw = pckbd_init_hw;
-#ifdef CONFIG_MAGIC_SYSRQ
- ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate;
-#endif
-
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
- ppc_ide_md.insw = mbx_ide_insw;
- ppc_ide_md.outsw = mbx_ide_outsw;
- ppc_ide_md.default_irq = mbx_ide_default_irq;
- ppc_ide_md.default_io_base = mbx_ide_default_io_base;
- ppc_ide_md.ide_check_region = mbx_ide_check_region;
- ppc_ide_md.ide_request_region = mbx_ide_request_region;
- ppc_ide_md.ide_release_region = mbx_ide_release_region;
- ppc_ide_md.fix_driveid = mbx_ide_fix_driveid;
- ppc_ide_md.ide_init_hwif = mbx_ide_init_hwif_ports;
-
- ppc_ide_md.io_base = _IO_BASE;
-#endif
-}
void openpic_enable_irq(u_int irq)
{
check_arg_irq(irq);
- openpic_clearfield(&OpenPIC->Source[irq-irq_desc[irq].ctl->irq_offset].Vector_Priority, OPENPIC_MASK);
+ openpic_clearfield(&OpenPIC->Source[irq-irq_desc[irq].handler->irq_offset].Vector_Priority, OPENPIC_MASK);
}
void openpic_disable_irq(u_int irq)
{
check_arg_irq(irq);
- openpic_setfield(&OpenPIC->Source[irq-irq_desc[irq].ctl->irq_offset].Vector_Priority, OPENPIC_MASK);
+ openpic_setfield(&OpenPIC->Source[irq-irq_desc[irq].handler->irq_offset].Vector_Priority, OPENPIC_MASK);
}
/*
* don't. Put this here to check for it.
* -- Cort
*/
- if ( irq_desc[irq].ctl != &gatwick_pic )
+ if ( irq_desc[irq].handler != &gatwick_pic )
printk("gatwick irq not from gatwick pic\n");
else
ppc_irq_dispatch_handler( regs, irq );
max_irqs = 64;
}
for ( i = 0; i < max_real_irqs ; i++ )
- irq_desc[i].ctl = &pmac_pic;
+ irq_desc[i].handler = &pmac_pic;
/* get addresses of first controller */
if (irqctrler) {
if (device_is_compatible(irqctrler, "gatwick"))
pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs);
for ( i = max_real_irqs ; i < max_irqs ; i++ )
- irq_desc[i].ctl = &gatwick_pic;
+ irq_desc[i].handler = &gatwick_pic;
request_irq( second_irq, gatwick_action, SA_INTERRUPT,
"gatwick cascade", 0 );
}
#include <linux/nvram.h>
#include <linux/spinlock.h>
#include <linux/console.h>
+#include <linux/irq.h>
#include <asm/page.h>
#include <asm/semaphore.h>
if (OpenPIC != NULL) {
for ( i = 16 ; i < 36 ; i++ )
- irq_desc[i].ctl = &open_pic;
+ irq_desc[i].handler = &open_pic;
openpic_init(1);
}
for ( i = 0 ; i < 16 ; i++ )
- irq_desc[i].ctl = &i8259_pic;
+ irq_desc[i].handler = &i8259_pic;
i8259_init();
#ifdef __SMP__
request_irq(openpic_to_irq(OPENPIC_VEC_SPURIOUS), openpic_ipi_action,
}
}
-static __init struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
+static struct pci_bus * __init pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
{
struct pci_bus *child;
* A CardBus bridge is basically the same as a regular PCI bridge,
* except we don't scan behind it because it will be changing.
*/
-static __init int pci_scan_cardbus(struct pci_bus *bus, struct pci_dev *dev, int busnr)
+static int __init pci_scan_cardbus(struct pci_bus *bus, struct pci_dev *dev, int busnr)
{
unsigned short cr;
unsigned int buses;
/*
* If it's a bridge, scan the bus behind it.
*/
-static __init int pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max)
+static int __init pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max)
{
unsigned int buses;
unsigned short cr;
* Read interrupt line and base address registers.
* The architecture-dependent code can tweak these, of course.
*/
-static __init void pci_read_irq(struct pci_dev *dev)
+static void __init pci_read_irq(struct pci_dev *dev)
{
unsigned char irq;
1948 Solo?
1968 ES1968 Maestro 2
1969 ES1969 Solo-1 Audiodrive
- 1978 ES1978 Maestro Audiodrive
+ 1978 ES1978 Maestro 2E
2808 ES336H Fax Modem (Later Model)
2898 ES2898 Modem
125e Specialvideo Engineering SRL
e886 IT8330G
1284 Sahara Networks, Inc.
1285 Platform Technologies, Inc.
- 0100 PT-201C-O-P AGOGO-XP sound chip
+ 0100 AGOGO sound chip (aka ESS Maestro 1)
1286 Mazet GmbH
1287 M-Pact, Inc.
001e LS220D DVD Decoder
if (c) {
struct pci_dev **p;
/* Unlink from PCI device chain */
- for (p = &pci_devices; *p; p = &((*p)->next)) {
+ p = &pci_devices;
+ while (*p) {
struct pci_dev * dev = *p;
- if (dev->bus != s->cap.cb_bus)
+ if (dev->bus != s->cap.cb_bus) {
+ p = &dev->next;
continue;
+ }
*p = dev->next;
#ifdef CONFIG_PROC_FS
pci_proc_detach_device(dev);
exca_writeb(socket, I365_ADDRWIN, addr);
}
+ exca_writeb(socket, CB_MEM_PAGE(map), start >> 24);
+
word = (start >> 12) & 0x0fff;
if (mem->flags & MAP_16BIT)
word |= I365_MEM_16BIT;
word |= I365_MEM_REG;
exca_writew(socket, I365_MEM(map) + I365_W_OFF, word);
- exca_writeb(socket, CB_MEM_PAGE(map), start >> 24);
-
if (mem->flags & MAP_ACTIVE)
exca_writeb(socket, I365_ADDRWIN, addr | enable);
return 0;
dep_tristate ' Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
dep_tristate ' Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND
dep_tristate ' ESS Technology Solo1' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- dep_tristate ' ESS Maestro, Maestro2, Maestro2E driver (EXPERIMENTAL)' CONFIG_SOUND_MAESTRO $CONFIG_SOUND
-fi
+dep_tristate ' ESS Maestro, Maestro2, Maestro2E driver' CONFIG_SOUND_MAESTRO $CONFIG_SOUND
dep_tristate ' S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND
if [ "$CONFIG_VISWS" = "y" ]; then
dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND
/*****************************************************************************
*
- * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.2.x
+ * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* proprietors of Hack Central for fine lodging.
*
* Supported devices:
- * /dev/dsp0-7 standard /dev/dsp device, (mostly) OSS compatible
+ * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible
* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible
*
* Hardware Description
*
* A working Maestro setup contains the Maestro chip wired to a
- * codec or 2. In the Maestro we have the APUs, the ASP, and the
+ * codec or 2. In the Maestro we have the APUs, the ASSP, and the
* Wavecache. The APUs can be though of as virtual audio routing
* channels. They can take data from a number of sources and perform
* basic encodings of the data. The wavecache is a storehouse for
* PCM data. Typically it deals with PCI and interracts with the
- * APUs. The ASP is a wacky DSP like device that ESS is loth
+ * APUs. The ASSP is a wacky DSP like device that ESS is loth
* to release docs on. Thankfully it isn't required on the Maestro
* until you start doing insane things like FM emulation and surround
* encoding. The codecs are almost always AC-97 compliant codecs,
* but it appears that early Maestros may have had PT101 (an ESS
* part?) wired to them. The only real difference in the Maestro
* families is external goop like docking capability, memory for
- * the ASP, and initialization differences.
+ * the ASSP, and initialization differences.
*
* Driver Operation
*
* /dev/dsp? device. 2 channels for output, and 4 channels for
* input.
*
- * For output we maintain a ring buffer of data that we are DMAing
- * to the card. In mono operation this is nice and easy. When
- * we receive data we tack it onto the ring buffer and make sure
- * the APU assigned to it is playing over the data. When we fill
- * the ring buffer we put the client to sleep until there is
- * room again. Easy.
+ * Each APU can do a number of things, but we only really use
+ * 3 basic functions. For playback we use them to convert PCM
+ * data fetched over PCI by the wavecahche into analog data that
+ * is handed to the codec. One APU for mono, and a pair for stereo.
+ * When in stereo, the combination of smarts in the APU and Wavecache
+ * decide which wavecache gets the left or right channel.
*
- * However, this starts to stink when we use stereo. The APUs
- * supposedly can decode LRLR packed stereo data, but it
- * doesn't work. So we're forced to use dual mono APUs walking over
- * mono encoded data. This requires us to split the input from
- * the client and complicates the buffer maths tremendously. Ick.
- *
- * This also pollutes the recording paths as well. We have to use
- * 2 L/R incoming APUs that are fixed at 16bit/48khz. We then pipe
- * these through 2 rate converion apus that mix them down to the
- * requested frequency and write them to memory through the wavecache.
- * We also apparently need a 512byte region thats used as temp space
- * between the incoming APUs and the rate converters.
+ * For record we still use the old overly mono system. For each in
+ * coming channel the data comes in from the codec, through a 'input'
+ * APU, through another rate converter APU, and then into memory via
+ * the wavecache and PCI. If its stereo, we mash it back into LRLR in
+ * software. The pass between the 2 APUs is supposedly what requires us
+ * to have a 512 byte buffer sitting around in wavecache/memory.
*
* The wavecache makes our life even more fun. First off, it can
* only address the first 28 bits of PCI address space, making it
* similar.
*
* History
+ * v0.13 - Nov 18 1999 - Zach Brown <zab@redhat.com>
+ * fix nec Versas? man would that be cool.
+ * v0.12 - Nov 12 1999 - Zach Brown <zab@redhat.com>
+ * brown bag volume max fix..
+ * v0.11 - Nov 11 1999 - Zach Brown <zab@redhat.com>
+ * use proper stereo apu decoding, mmap/write should work.
+ * make volume sliders more useful, tweak rate calculation.
+ * fix lame 8bit format reporting bug. duh. apm apu saving buglet also
+ * fix maestro 1 clock freq "bug", remove pt101 support
* v0.10 - Oct 28 1999 - Zach Brown <zab@redhat.com>
* aha, so, sometimes the WP writes a status word to offset 0
* from one of the PCMBARs. rearrange allocation accordingly..
+ * cheers again to Eric for being a good hacker in investigating this.
* Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :)
* v0.09 - Oct 23 1999 - Zach Brown <zab@redhat.com>
* added APM support.
*
* TODO
* some people get indir reg timeouts?
- * anyone have a pt101 codec?
- * mmap(), but beware stereo encoding nastiness.
- * actually post pci writes
* fix bob frequency
+ * endianness
* do smart things with ac97 2.0 bits.
- * ugh.. non aligned writes in the middle of a data stream.. ugh
- * sort out 0x34->0x36 crap in init
* docking and dual codecs and 978?
- * pcm_sync?
- * actually use LRLR
+ * leave 54->61 open
+ * resolve 2.3/2.2 stuff
*
* it also would be fun to have a mode that would not use pci dma at all
* but would copy into the wavecache on board memory and use that
#define SILLY_INIT_SEM(SEM) SEM=MUTEX;
#define init_waitqueue_head init_waitqueue
#define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC)
+ #define SILLY_OFFSET(VMA) ((VMA)->vm_offset)
#else
#define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start)
#define SILLY_INIT_SEM(SEM) init_MUTEX(&SEM)
#define SILLY_MAKE_INIT(FUNC) __init FUNC
+ #define SILLY_OFFSET(VMA) ((VMA)->vm_pgoff)
#endif
#endif
/* --------------------------------------------------------------------- */
-#define DRIVER_VERSION "0.10"
+#define DRIVER_VERSION "0.13"
#ifndef PCI_VENDOR_ESS
#define PCI_VENDOR_ESS 0x125D
#define ESS_CHAN_HARD 0x100
+/* NEC Versas ? */
+#define NEC_VERSA_SUBID1 0x80581033
+#define NEC_VERSA_SUBID2 0x803c1033
+
/* changed so that I could actually find all the
references and fix them up. its a little more readable now. */
#define DAC_RUNNING 1
#define ADC_RUNNING 2
-#define MAX_DSP_ORDER 3
-#define MAX_DSPS (1<<3)
+#define MAX_DSP_ORDER 2
+#define MAX_DSPS (1<<MAX_DSP_ORDER)
#define NR_DSPS (1<<dsps_order)
#define NR_IDRS 32
[TYPE_MAESTRO2E] = "ESS Maestro 2E"
};
+static int clock_freq[]={
+ [TYPE_MAESTRO] = (49152000L / 1024L),
+ [TYPE_MAESTRO2] = (50000000L / 1024L),
+ [TYPE_MAESTRO2E] = (50000000L / 1024L)
+};
/* --------------------------------------------------------------------- */
[SOUND_MIXER_BASS] = 0x3232,
[SOUND_MIXER_TREBLE] = 0x3232,
[SOUND_MIXER_SPEAKER] = 0x3232,
- [SOUND_MIXER_MIC] = 0x3232,
+ [SOUND_MIXER_MIC] = 0x8000, /* annoying */
[SOUND_MIXER_LINE] = 0x3232,
[SOUND_MIXER_CD] = 0x3232,
[SOUND_MIXER_VIDEO] = 0x3232,
/* write the OSS encoded volume to the given OSS encoded mixer,
again caller's job to make sure all is well in arg land,
call with spinlock held */
+
+/* linear scale -> log */
+unsigned char lin2log[101] =
+{
+0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 ,
+50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 ,
+63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 ,
+72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 ,
+78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 ,
+83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 ,
+87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 ,
+90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 ,
+93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 ,
+95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 ,
+97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99
+};
+
static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right)
{
u16 val=0;
right = (right * mh->scale) / 100;
left = (left * mh->scale) / 100;
if ((left == 0) && (right == 0))
- val |= 0x8000;
- } else {
- right = ((100 - right) * mh->scale) / 100;
- left = ((100 - left) * mh->scale) / 100;
- if((left == mh->scale) && (right == mh->scale))
val |= 0x8000;
+ } else {
+ /* log conversion for the stereo controls */
+ if((left == 0) && (right == 0))
+ val = 0x8000;
+ right = ((100 - lin2log[right]) * mh->scale) / 100;
+ left = ((100 - lin2log[left]) * mh->scale) / 100;
}
val |= (left << 8) | right;
return 0;
}
+#if 0 /* there has been 1 person on the planet with a pt101 that we
+ know of. If they care, they can put this back in :) */
static u16 maestro_pt101_init(struct ess_card *card,int iobase)
{
printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
maestro_ac97_set(iobase, 0x0E, 0x801F);
return 0;
}
+#endif
/* this is very magic, and very slow.. */
static void
{
u16 save_68;
u16 w;
+ u32 vend;
outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
outw(0x0000, ioaddr+0x36);
save_68 = inw(ioaddr+0x68);
pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */
+ pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend);
if( w & 0x1)
save_68 |= 0x10;
outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */
}
}
#endif
+ if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) {
+ /* turn on external amp? */
+ outw(0xf9ff, ioaddr + 0x64);
+ outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68);
+ outw(0x0209, ioaddr + 0x60);
+ }
}
/*
* Indirect register access. Not all registers are readable so we
/* sets the play formats of these apus, should be passed the already shifted format */
static void set_apu_fmt(struct ess_state *s, int apu, int mode)
{
- if(mode&ESS_FMT_16BIT) {
- s->apu_mode[apu] = 0x10;
- s->apu_mode[apu+1] = 0x10;
- } else {
- s->apu_mode[apu] = 0x30;
- s->apu_mode[apu+1] = 0x30;
- }
+ int apu_fmt = 0x10;
+
+ if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20;
+ if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10;
+ s->apu_mode[apu] = apu_fmt;
+ s->apu_mode[apu+1] = apu_fmt;
}
/* this only fixes the output apu mode to be later set by start_dac and
set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK);
}
-static u16 compute_rate(u32 freq)
+/* this is off by a little bit.. */
+static u32 compute_rate(struct ess_state *s, u32 freq)
{
- if(freq==48000)
- return 0xFFFF;
- freq<<=16;
- freq/=48000;
- return freq;
+ u32 clock = clock_freq[s->card->card_type];
+
+ if (freq == 48000) return 0x10000;
+
+ return ((freq / clock) <<16 )+
+ (((freq % clock) << 16) / clock);
}
-static void set_dac_rate(struct ess_state *s, unsigned rate)
+static void set_dac_rate(struct ess_state *s, unsigned int rate)
{
u32 freq;
+ int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
if (rate > 48000)
rate = 48000;
s->ratedac = rate;
- if(!((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT))
- rate >>= 1; /* who knows */
+ if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO))
+ rate >>= 1;
/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/
- freq = compute_rate(rate);
+ freq = compute_rate(s, rate);
/* Load the frequency, turn on 6dB */
apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)|
{
u32 freq;
- if (rate > 48000)
- rate = 48000;
+ /* Sample Rate conversion APUs don't like 0x10000 for their rate */
+ if (rate > 47999)
+ rate = 47999;
if (rate < 4000)
rate = 4000;
s->rateadc = rate;
- freq = compute_rate(rate);
+ freq = compute_rate(s, rate);
/* Load the frequency, turn on 6dB */
apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)|
/* all maestro sizes are in 16bit words */
size >>=1;
- /* we're given the full size of the buffer, but
- in stereo each channel will only play its half */
if(mode&ESS_FMT_STEREO) {
- size >>=1;
high_apu++;
+ /* only 16/stereo gets size divided */
+ if(mode&ESS_FMT_16BIT)
+ size>>=1;
}
for(channel=0; channel <= high_apu; channel++)
{
- int i;
-
- if(!channel)
- pa = virt_to_bus(buffer);
- else
- /* right channel plays its split half.
- *2 accomodates for rampant shifting earlier */
- pa = virt_to_bus(buffer + size*2);
+ pa = virt_to_bus(buffer);
/* set the wavecache control reg */
tmpval = (pa - 0x10) & 0xFFF8;
- if(!(mode & 2)) tmpval |= 4; /* 8bit */
+ if(!(mode & ESS_FMT_16BIT)) tmpval |= 4;
+ if(mode & ESS_FMT_STEREO) tmpval |= 2;
ess->apu_base[channel]=tmpval;
wave_set_register(ess, ess->apu[channel]<<3, tmpval);
pa>>=1; /* words */
/* base offset of dma calcs when reading the pointer
- on this left one */
+ on the left one */
if(!channel) ess->dma_dac.base = pa&0xFFFF;
pa|=0x00400000; /* System RAM */
-
- /* Begin loading the APU */
- for(i=0;i<15;i++) /* clear all PBRs */
- apu_set_register(ess, channel, i, 0x0000);
+
+ /* XXX the 16bit here might not be needed.. */
+ if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) {
+ if(channel)
+ pa|=0x00800000; /* Stereo */
+ pa>>=1;
+ }
/* XXX think about endianess when writing these registers */
M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa);
/* dma on, no envelopes, filter to all 1s) */
apu_set_register(ess, channel, 0, 0x400F);
- if(mode&ESS_FMT_STEREO)
- /* set panning: left or right */
- apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0));
- else
- apu_set_register(ess, channel, 10, 0x8F08);
-
if(mode&ESS_FMT_16BIT)
ess->apu_mode[channel]=0x10;
else
ess->apu_mode[channel]=0x30;
+
+ if(mode&ESS_FMT_STEREO) {
+ /* set panning: left or right */
+ apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0));
+ ess->apu_mode[channel] += 0x10;
+ } else
+ apu_set_register(ess, channel, 10, 0x8F08);
}
/* clear WP interupts */
clear_advance(struct ess_state *s)
{
unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80;
+
unsigned char *buf = s->dma_dac.rawbuf;
unsigned bsize = s->dma_dac.dmasize;
- /* swptr is always in bytes as read from an apu.. */
unsigned bptr = s->dma_dac.swptr;
unsigned len = s->dma_dac.fragsize;
- int i=1;
-
- if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) {
- i++;
- bsize >>=1;
- }
-
- for ( ;i; i-- , buf += bsize) {
-
- if (bptr + len > bsize) {
- unsigned x = bsize - bptr;
- memset(buf + bptr, c, x);
- /* account for wrapping? */
- bptr = 0;
- len -= x;
- }
- memset(buf + bptr, c, len);
+
+ if (bptr + len > bsize) {
+ unsigned x = bsize - bptr;
+ memset(buf + bptr, c, x);
+ /* account for wrapping? */
+ bptr = 0;
+ len -= x;
}
+ memset(buf + bptr, c, len);
}
/* call with spinlock held! */
}
/* update DAC pointer */
if (s->dma_dac.ready) {
- /* this is so gross. */
- hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize;
+ hwptr = get_dmaa(s) % s->dma_dac.dmasize;
+ /* the apu only reports the length it has seen, not the
+ length of the memory that has been used (the WP
+ knows that */
+ if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT))
+ hwptr<<=1;
+
diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/
s->dma_dac.hwptr = hwptr;
return ret;
}
-/* god this is gross..*/
-/* again, the mode passed is shifted/masked */
-static int
-split_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset,
- int count, int bufsize, int mode)
-{
- /* oh, bother. stereo decoding APU's don't work in 16bit so we
- use dual linear decoders. which means we have to hack up stereo
- buffer's we're given. yuck. */
-
- unsigned char *so,*left,*right;
- int i;
-
- so = tmp_buffer;
- left = real_buffer + offset;
- right = real_buffer + bufsize/2 + offset;
-
- if(mode & ESS_FMT_16BIT) {
- for(i=count/4; i ; i--) {
- *(right++) = (*(so+2));
- *(right++) = (*(so+3));
- *(left++) = (*so);
- *(left++) = (*(so+1));
- so+=4;
- }
- } else {
- for(i=count/2; i ; i--) {
- *(right++) = (*(so+1));
- *(left++) = (*so);
- so+=2;
- }
- }
-
- return 0;
-}
-
static ssize_t
ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
ssize_t ret;
unsigned long flags;
unsigned swptr;
- unsigned char *splitbuf = NULL;
int cnt;
- int mode = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK;
VALIDATE_STATE(s);
if (ppos != &file->f_pos)
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
- /* XXX be more clever than this.. */
- if (!(splitbuf = kmalloc(count,GFP_KERNEL)))
- return -ENOMEM;
ret = 0;
calc_bob_rate(s);
}
swptr = s->dma_dac.swptr;
- if(mode & ESS_FMT_STEREO) {
- /* in stereo we have the 'dual' buffers.. */
- cnt = ((s->dma_dac.dmasize/2)-swptr)*2;
- } else {
- cnt = s->dma_dac.dmasize-swptr;
- }
+ cnt = s->dma_dac.dmasize-swptr;
+
if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
cnt = s->dma_dac.dmasize - s->dma_dac.count;
if (cnt > count)
cnt = count;
- /* our goofball stereo splitter can only deal in mults of 4 */
- if (cnt > 0)
- cnt &= ~3;
-
if (cnt <= 0) {
start_dac(s);
if (file->f_flags & O_NONBLOCK) {
}
continue;
}
- if(mode & ESS_FMT_STEREO) {
- if (copy_from_user(splitbuf, buffer, cnt)) {
- if (!ret) ret = -EFAULT;
- goto return_free;
- }
- split_stereo(s->dma_dac.rawbuf,splitbuf,swptr,cnt,s->dma_dac.dmasize,
- mode);
- } else {
- if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
- if (!ret) ret = -EFAULT;
- goto return_free;
- }
+ if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) {
+ if (!ret) ret = -EFAULT;
+ goto return_free;
}
- if(mode & ESS_FMT_STEREO) {
- /* again with the weird pointer magic */
- swptr = (swptr + (cnt/2)) % (s->dma_dac.dmasize/2);
- } else {
- swptr = (swptr + cnt) % s->dma_dac.dmasize;
- }
+ swptr = (swptr + cnt) % s->dma_dac.dmasize;
+
spin_lock_irqsave(&s->lock, flags);
s->dma_dac.swptr = swptr;
s->dma_dac.count += cnt;
start_dac(s);
}
return_free:
- if (splitbuf) kfree(splitbuf);
return ret;
}
return mask;
}
-/* this needs to be fixed to deal with the dual apus/buffers */
-#if 0
static int ess_mmap(struct file *file, struct vm_area_struct *vma)
{
struct ess_state *s = (struct ess_state *)file->private_data;
if ((ret = prog_dmabuf(s, 1)) != 0)
return ret;
db = &s->dma_dac;
- } else if (vma->vm_flags & VM_READ) {
+ } else
+#if 0
+ /* if we can have the wp/wc do the combining
+ we can turn this back on. */
+ if (vma->vm_flags & VM_READ) {
if ((ret = prog_dmabuf(s, 0)) != 0)
return ret;
db = &s->dma_adc;
- } else
+ } else
+#endif
return -EINVAL;
- if (vma->vm_pgofft != 0)
+ if (SILLY_OFFSET(vma) != 0)
return -EINVAL;
size = vma->vm_end - vma->vm_start;
if (size > (PAGE_SIZE << db->buforder))
db->mapped = 1;
return 0;
}
-#endif
static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER /*| DSP_CAP_MMAP*/, (int *)arg);
+ return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
case SNDCTL_DSP_RESET:
if (file->f_mode & FMODE_WRITE) {
: (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg);
case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S8|AFMT_S16_LE, (int *)arg);
+ return put_user(AFMT_U8|AFMT_S16_LE, (int *)arg);
case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
get_user_ret(val, (int *)arg, -EFAULT);
(ESS_FMT_16BIT << ESS_ADC_SHIFT)
: (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ?
AFMT_S16_LE :
- AFMT_S8,
+ AFMT_U8,
(int *)arg);
case SNDCTL_DSP_POST:
NULL, /* readdir */
&ess_poll,
&ess_ioctl,
- NULL, /* XXX &ess_mmap, */
+ &ess_mmap,
&ess_open,
NULL, /* flush */
&ess_release,
w&=~(1<<7); /* HWV off */
w&=~(1<<6); /* Debounce off */
w&=~(1<<5); /* GPIO 4:5 */
- w|= (1<<4); /* Disconnect from the CHI. Enabling this in made a dell 7500 work. */
+ w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */
w&=~(1<<3); /* IDMA off (undocumented) */
w&=~(1<<2); /* MIDI fix off (undoc) */
w&=~(1<<1); /* reserved, always write 0 */
maestro_config(card);
if(maestro_ac97_get(iobase, 0x00)==0x0080) {
- maestro_pt101_init(card,iobase);
+ printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n"
+ "\tyou should tell someone about this.\n");
} else {
maestro_ac97_init(card,iobase);
}
}
static int
-maestro_apm_suspend(void)
+maestro_suspend(void)
{
struct ess_card *card;
unsigned long flags;
stop_dac(s);
stop_adc(s);
for(j=0;j<6;j++)
- card->apu_map[s->apu[i]][5]=apu_get_register(s,i,5);
+ card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5);
}
return 0;
}
static int
-maestro_apm_resume(void)
+maestro_resume(void)
{
struct ess_card *card;
unsigned long flags;
case APM_SYS_SUSPEND:
case APM_CRITICAL_SUSPEND:
case APM_USER_SUSPEND:
- maestro_apm_suspend();break;
+ maestro_suspend();break;
case APM_NORMAL_RESUME:
case APM_CRITICAL_RESUME:
case APM_STANDBY_RESUME:
- maestro_apm_resume();break;
+ maestro_resume();break;
default: break;
}
dep_tristate ' USB keyboard support' CONFIG_USB_KBD $CONFIG_USB
dep_tristate ' USB mouse support' CONFIG_USB_MOUSE $CONFIG_USB
dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
- dep_tristate ' USB HP scanner support' CONFIG_USB_HP_SCANNER $CONFIG_USB
+ dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB
dep_tristate ' USB Communications Device Class (ACM) support' CONFIG_USB_ACM $CONFIG_USB
dep_tristate ' USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB
endif
ifeq ($(CONFIG_USB_MOUSE),m)
M_OBJS += mouse.o
- MI_OBJS += mouse.o
endif
-ifeq ($(CONFIG_USB_HP_SCANNER),y)
- L_OBJS += hp_scanner.o
+ifeq ($(CONFIG_USB_SCANNER),y)
+ L_OBJS += scanner.o
endif
-ifeq ($(CONFIG_USB_HP_SCANNER),m)
- M_OBJS +=hp_scanner.o
- MI_OBJS +=hp_scanner.o
+ifeq ($(CONFIG_USB_SCANNER),m)
+ M_OBJS +=scanner.o
endif
ifeq ($(CONFIG_USB_ACM),y)
endif
ifeq ($(CONFIG_USB_ACM),m)
M_OBJS += acm.o
- MI_OBJS += acm.o
endif
ifeq ($(CONFIG_USB_PRINTER),y)
ifeq ($(CONFIG_USB_PRINTER),m)
M_OBJS += printer.o
- MI_OBJS += printer.o
endif
ifeq ($(CONFIG_USB_SERIAL),y)
ifeq ($(CONFIG_USB_SERIAL),m)
M_OBJS += usb-serial.o
- MI_OBJS += usb-serial.o
endif
ifneq ($(CONFIG_ADB_KEYBOARD),y)
ifeq ($(CONFIG_USB_AUDIO),m)
M_OBJS += audio.o
- MI_OBJS += audio.o
endif
ifeq ($(CONFIG_USB_CPIA),y)
ifeq ($(CONFIG_USB_CPIA),m)
M_OBJS += cpia.o
- MI_OBJS += cpia.o
endif
ifeq ($(CONFIG_USB_DC2XX),y)
endif
ifeq ($(CONFIG_USB_DC2XX),m)
M_OBJS += dc2xx.o
- MI_OBJS += dc2xx.o
endif
ifeq ($(CONFIG_USB_SCSI),y)
ifeq ($(CONFIG_USB_EZUSB),m)
M_OBJS += ezusb.o
- MI_OBJS += ezusb.o
endif
ifeq ($(CONFIG_USB_USS720),y)
ifeq ($(CONFIG_USB_USS720),m)
M_OBJS += uss720.o
- MI_OBJS += uss720.o
endif
ifeq ($(CONFIG_USB_DABUSB),y)
ifeq ($(CONFIG_USB_DABUSB),m)
M_OBJS += dabusb.o
- MI_OBJS += dabusb.o
endif
include $(TOPDIR)/Rules.make
+++ /dev/null
-August 30, 1999
-
-
-Overview
-
-This README will address issues regarding how to configure the kernel
-to access a USB HP Scanner. The scanner should support the Scanner
-Control Language (SCL) so that applications such as SANE can access it
-properly. Refer to the document README.hp_scanner_sane for guidance
-on how to configure SANE to use the USB HP Scanner.
-
-
-Requirements
-
-A host with a USB port. Ideally, either a UHCI (Intel) or OHCI
-(Compaq and others) hardware port should work. However, I've only
-been able to really use an OHCI controller. I did have access to a
-system with a UHCI controller but some very limited testing did not
-produce satisfactory results.
-
-A Linux kernel with USB support (preferably 2.3.15+).
-
-A Linux kernel with USB HP Scanner support.
-
-
-Configuration
-
-Add both USB controller support and USB HP Scanner support using `make
-menuconfig`. If you decide to use the ohci-hcd driver, don't forget
-to add HUB support. Compile and install the modules. Testing was
-performed only as modules, YMMV.
-
-Add a device for the USB scanner: `mknod /dev/usbscanner c 16 1`
-
-Set appropriate permissions for /dev/usbscanner. Both read and write
-permissions are needed for proper operation.
-
-Load the appropriate modules:
-
- OHCI:
-
- modprobe usb-ohci
- modprobe hp_scanner
-
- OHCI-HCD:
- modprobe usb-ohci-hcd
- modprobe hub
- modprobe hp_scanner
-
-That's it. SANE should now be able to access the device.
-
-There is a small test program (hp_scan.c) that can be used to test the
-scanner device. It's purpose is to test the driver(s) without having
-to retrieve/configure SANE. Hp_scan.c will scan the entire bed and
-put the output into a file called out.dat in the current directory.
-The data in the file is raw data.
-
-David /\/elson
-dnelson@jump.net
+++ /dev/null
-August 30, 1999
-
-NOTE: This is all VERY alpha. Use at your own risk. There is no
-warranty expressed nor implied.
-
-
-Introduction
-
-This document will hopefully provide enough info on how to get SANE
-working with a Hewlett Packard USB capable scanner using the USB
-interface. The majority of HP Scanners support the Scanner Control
-Language (SCL) which is both published by HP and supported by SANE.
-The only HP Scanner that I'm aware of that does not support SCL is the
-4200C. All other HP scanners with USB interfaces should work (4100C,
-5200C, 6200C). Of course as HP releases new scanners this information
-may change.
-
-
-Requirements
-
-In order to get this running you'll need USB support in your kernel in
-addition to USB HP Scanner support. Please refer to README.hp_scanner
-for issues pertaining to Linux USB and USB HP Scanner support.
-
-An installed version of SANE which is available from
-http://www.mostang.com/sane/. Testing has been performed using
-version SANE-1.0.1. For instructions on building and installing SANE,
-refer to the various README files within the distribution.
-
-
-Ok, so what do I do?
-
-NOTE: $INSTALL_DIR is the location where SANE was installed. It may
-be /usr/local, /usr, /opt or somewhere else. If you don't know, ask
-your system administrator.
-
-1) Make sure that you have the libsane-hp.* libraries under the
-$INSTALL_DIR/lib/sane/ directory.
-
-2) Under the directory $INSTALL_DIR/etc/sane.d/ edit the following
-files: dll.conf, hp.conf.
-
- dll.conf: Make sure that the 'hp' entry is present and uncommented.
-
- hp.conf: This should contain two lines:
-
- option connect-device
- /dev/usbscanner
-
-3) You should now be able to use SANE (xscanimage or scanimage).
-
-Don't forget to read any relevant man pages regarding the usage of
-SANE. If you have other entries uncommented in dll.conf, you my have
-to specify the device to (x)scanimage. The xscanimage (1) man page
-has info on how to get 'The Gimp' to work with xscanimage. Note that
-Gimp support must be compiled into SANE for it work. If you are
-dealing with a RedHat system, you'll also need to install the
-gimp-devel rpm package.
-
-NOTE: Most of the time xscanimage will run without incident, then on
-the next invocation it'll core dump at different locations. I don't
-know why yet and I don't have a work around either other than to try
-again. But once you get it started, it'll scan without any problems
-(or at least it does for me).
-
-David /\/elson
-dnelson@jump.net
--- /dev/null
+Oct 19, 1999
+
+CHANGES
+
+- Ammended for linux-2.3.22+
+- Appended hp_scan.c to end of this README
+- Removed most references to HP
+
+
+OVERVIEW
+
+This README will address issues regarding how to configure the kernel
+to access a USB scanner. Although the driver was originally conceived
+for USB HP scanners, it's general enough so that it can be used with
+other scanners. Also, one can now pass the USB Vendor and
+Product ID's using module parameters for unknown scanners. Refer to
+the document README.scanner_hp_sane for guidance on how to configure
+SANE to use a USB HP Scanner.
+
+
+ADDITIONAL INFORMATION
+
+http://www.linux-usb.org/
+http://www.dynamine.net/linux-usb/HOWTO/
+
+
+REQUIREMENTS
+
+A host with a USB port. Ideally, either a UHCI (Intel) or OHCI
+(Compaq and others) hardware port should work. However, I've only
+been able to really use an OHCI controller. I did have access to a
+system with a UHCI controller but some very limited testing did not
+produce satisfactory results. Luke Ordelmans
+<postbus@ordelmans.demon.nl> has reported success using the UHCI host
+controller with kernel 2.3.18 and a ChainTech motherboard. Here
+lately I've been having better success with the ohci-hcd driver. But
+since Linux USB support is still in a state of constant development
+that may change at a later date. I am confident that eventually all
+the host contollers will perform without incident.
+
+A Linux kernel with USB support (preferably linux-2.3.18+)
+
+A Linux kernel with USB Scanner support.
+
+
+CONFIGURATION
+
+Using `make menuconfig` or your prefered method for configuring the
+kernel, select 'Support for USB', 'OHCI/OHCI-HCD/UHCI' depending on
+your hardware, 'USB hub support', and 'USB Scanner support'. Compile
+and install the modules (you may need to execute `depmod -a` to update
+the module dependencies). Testing was performed only as modules,
+YMMV.
+
+Add a device for the USB scanner:
+ linux-2.3.22 and above: `mknod /dev/usbscanner c 180 48`
+ linux-2.3.21 and below: `mknod /dev/usbscanner c 16 1`
+
+Set appropriate permissions for /dev/usbscanner (don't forget about
+group and world permissions). Both read and write permissions are
+required for proper operation.
+
+Load the appropriate modules (if compiled as modules):
+
+ OHCI:
+ modprobe usb-ohci
+ modprobe scanner
+
+ OHCI-HCD:
+ modprobe usb-ohci-hcd
+ modprobe hub
+ modprobe scanner
+
+ UHCI:
+ modprobe usb-uhci
+ modprobe hub (don't know if this is required or not)
+ modprobe scanner
+
+That's it. SANE should now be able to access the device.
+
+There is a small test program (hp_scan.c -- appended below) that can
+be used to test the scanner device if it's an HP scanner that supports
+SCL. Its purpose is to test the driver without having to
+retrieve/configure SANE. Hp_scan.c will scan the entire bed and put
+the output into a file called 'out.dat' in the current directory. The
+data in the file is raw data so it's not very useful for imaging.
+
+
+MODULE PARAMETERS
+
+If you have a device that wish to experiment with or try using this
+driver with, but the Vendor and Product ID's are not coded in, don't
+despair. If the driver was compiled as a module, you can pass options
+to the driver. Simply add 'options scanner vendor=0x####
+product=0x****' to the conf.modules/modules.conf file replacing the
+#'s and the *'s with the correct ID's. The ID's can be retrieved from
+the messages file or using `cat /proc/bus/usb/devices` if USB /proc
+support was selected during kernel configuration.
+
+
+BUGS
+
+If you encounter any problems feel free to drop me an email.
+
+David /\/elson
+dnelson@jump.net
+http://www.jump.net/~dnelson
+
+--------------- snip -- hp_scan.c -- snip ---------------
+/*
+
+This is a really crude attempt at writing a short test program. It's
+mostly only to be used to test connectivity with USB HP scanners that
+understand SCL. Currently, the supported models are 4100C, 5200C,
+6200C, and the 6300C. Note that the 4200C is *NOT* acceptable.
+
+Copyright (C) David E. Nelson <dnelson@jump.net>, 1999
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/*
+ Gray Output produces about a 8945400 byte file.
+ Color Output produces a 26836200 byte file.
+
+ To compile: gcc -o hp_scan hp_scan.c
+*/
+
+// #define COLOR /* Undef to scan GrayScale */
+
+int send_cmd(int, const char *, int);
+int read_cmd(int, char *, int);
+
+int
+main(void) {
+
+ ssize_t cnt = 0, total_cnt = 0;
+
+ FILE *fpout;
+
+ int fp;
+ int data_size = 32768;
+
+ char *data;
+
+ static char reset_cmd[] = {'\x1b','E'};
+
+#ifdef COLOR
+ static char data_type_cmd[] = {'\x1b','*','a','5','T'}; /* Color */
+ static char data_width_cmd[] = {'\x1b','*','a','2','4','G'}; /* 24 Bit Color */
+#else
+ static char data_type_cmd[] = {'\x1b','*','a','4','T'}; /* Gray */
+ static char data_width_cmd[] = {'\x1b','*','a','8','G'}; /* 8 Bit Gray */
+#endif
+
+ static char query_cmd[] = {'\x1b', '*', 's', '2', '5', '7', 'E'};
+ static char start_scan_cmd[] = {'\x1b','*','f','0','S'};
+
+ if(!(data=malloc(data_size))) {
+ perror("malloc failed");
+ exit (1);
+ }
+
+ if((fp=open("/dev/usbscanner", O_RDWR)) < 0) {
+ perror("Unable to open scanner device");
+ exit (1);
+ }
+
+ if((fpout=fopen("out.dat", "w+")) == NULL) {
+ perror("Unable to open ouput file");
+ exit(1);
+ }
+
+ send_cmd(fp, reset_cmd, sizeof(reset_cmd));
+ send_cmd(fp, data_type_cmd, sizeof(data_type_cmd));
+ send_cmd(fp, data_width_cmd, sizeof(data_width_cmd));
+ send_cmd(fp, start_scan_cmd, sizeof(start_scan_cmd));
+
+ while ((cnt = read(fp, data, data_size)) > 0) {
+ printf("Read: %u\n", cnt);
+ if(fwrite(data, sizeof(char), cnt, fpout) < 0) {
+ perror("Write to output file failed");
+ exit (1);
+ }
+ total_cnt += cnt;
+ }
+ if (cnt < 0) {
+ perror("Read from scanner failed");
+ exit (1);
+ }
+
+ printf("\nRead %lu bytes.\n", total_cnt);
+
+ send_cmd(fp, reset_cmd, sizeof(reset_cmd));
+
+ close(fp);
+ fclose(fpout);
+ return (0);
+}
+
+int
+send_cmd(int fp, const char * cmd, int length) {
+
+ int result;
+ int x;
+
+ if((result = write(fp, cmd, length)) != length) {
+ printf ("Write warning: %d bytes requested, %d written\n");
+ } else if (result < 0) {
+ perror ("send_cmd failure");
+ exit (1);
+ }
+ return (result);
+}
+
+int
+read_cmd(int fp, char * response, int length) {
+
+ return read(fp, response, length);
+
+}
--- /dev/null
+Oct. 19, 1999
+
+CHANGES
+
+- Ammended for Linux-2.3.22+
+
+
+INTRODUCTION
+
+This document will hopefully provide enough info on how to get SANE
+working with a Hewlett Packard USB capable scanner using the USB
+interface. The majority of HP Scanners support the Scanner Control
+Language (SCL) which is both published by HP and supported by SANE.
+The only HP Scanner that I'm aware of that does not support SCL is the
+4200C. All other HP scanners with USB interfaces should work (4100C,
+5200C, 6200C, and 6300C). Of course as HP releases new scanners this
+information may change.
+
+
+REQUIREMENTS
+
+In order to get this running you'll need USB support in your kernel in
+addition to USB Scanner support. Please refer to README.scanner
+for issues pertaining to Linux USB and USB Scanner support.
+
+An installed version of SANE which is available from
+http://www.mostang.com/sane/. Testing has been performed using
+version SANE-1.0.1. For instructions on building and installing SANE,
+refer to the various README files within the SANE distribution.
+
+
+OK, I'VE INSTALLED SANE. SO WHAT DO I DO NOW?
+
+NOTE: $INSTALL_DIR is the location where SANE was installed. It may
+be /usr/local, /usr, /opt or somewhere else. If you don't know, ask
+your system administrator.
+
+1) Make sure that you have the libsane-hp.* libraries under the
+$INSTALL_DIR/lib/sane/ directory. If you don't, then the HP backend
+was either not compiled or installed properly.
+
+2) Under the directory $INSTALL_DIR/etc/sane.d/ edit the following
+files: dll.conf, hp.conf.
+
+ dll.conf: Make sure that the 'hp' entry is present and uncommented.
+
+ hp.conf: This should contain two lines:
+
+ /dev/usbscanner
+ option connect-device
+
+3) You should now be able to use SANE (xscanimage or scanimage).
+
+Don't forget to read any relevant man pages regarding the usage of
+SANE. If you have other entries uncommented in dll.conf, you may have
+to specify the device to (x)scanimage. Again, `man` is your friend.
+The xscanimage (1) man page has info on how to get 'The Gimp' to work
+with xscanimage. Note that Gimp support must be compiled into SANE
+for it work. If you are dealing with a RedHat system, this means that
+you'll also need to install the gimp-devel rpm package.
+
+NOTE: The issues regarding core dumping by (x)scanimage have (or seem
+to be thus far) been resolved with version 0.2+ of the USB scanner
+driver which should be available in linux-2.3.23. If you notice
+otherwise, please contact me.
+
+David /\/elson
+dnelson@jump.net
+http://www.jump.net/~dnelson
/*
- * USB Abstract Control Model based on Brad Keryan's USB busmouse driver
+ * acm.c Version 0.10
*
- * (C) Copyright 1999 Armin Fuerst <fuerst@in.tum.de>
- * (C) Copyright 1999 Pavel Machek <pavel@suse.cz>
- * (C) Copyright 1999 Johannes Erdfelt <jerdfelt@valinux.com>
+ * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
+ * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
+ * Copyright (c) 1999 Johannes Erdfelt <jerdfelt@valinux.com>
+ * Copyright (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
*
- * version 0.9: Johanness Erdfelt converted this to urb interface.
+ * USB Abstract Control Model driver for USB modems and ISDN adapters
*
- * version 0.8: Fixed endianity bug, some cleanups. I really hate to have
- * half of driver in form if (...) { info("x"); return y; }
- * Pavel Machek <pavel@suse.cz>
+ * Sponsored by SuSE
*
- * version 0.7: Added usb flow control. Fixed bug in uhci.c (what idiot
- * wrote this code? ...Oops that was me). Fixed module cleanup. Did some
- * testing at 3Com => zmodem uload+download works, pppd had trouble but
- * seems to work now. Changed Menuconfig texts "Communications Device
- * Class (ACM)" might be a bit more intuitive. Ported to 2.3.13-1 prepatch.
- * (2/8/99)
- *
- * version 0.6: Modularized driver, added disconnect code, improved
- * assignment of device to tty minor number.
- * (21/7/99)
- *
- * version 0.5: Driver now generates a tty instead of a simple character
- * device. Moved async bulk transfer to 2.3.10 kernel version. fixed a bug
- * in uhci_td_allocate. Commenetd out getstringtable which causes crash.
- * (13/7/99)
- *
- * version 0.4: Small fixes in the FIFO, cleanup. Updated Bulk transfer in
- * uhci.c. Should have the correct interface now.
- * (6/6/99)
- *
- * version 0.3: Major changes. Changed Bulk transfer to interrupt based
- * transfer. Using FIFO Buffers now. Consistent handling of open/close
- * file state and detected/nondetected device. File operations behave
- * according to this. Driver is able to send+receive now! Heureka!
- * (27/5/99)
- *
- * version 0.2: Improved Bulk transfer. TX led now flashes every time data is
- * sent. Send Encapsulated Data is not needed, nor does it do anything.
- * Why's that ?!? Thanks to Thomas Sailer for his close look at the bulk code.
- * He told me about some importand bugs. (5/21/99)
+ * ChangeLog:
+ * v0.9 Vojtech Pavlik - thorough cleaning, URBification, almost a rewrite
+ * v0.10 Vojtech Pavlik - some more cleanups
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * version 0.1: Bulk transfer for uhci seems to work now, no dangling tds any
- * more. TX led of the ISDN TA flashed the first time. Does this mean it works?
- * The interrupt of the ctrl endpoint crashes the kernel => no read possible
- * (5/19/99)
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * version 0.0: Driver sets up configuration, sets up data pipes, opens misc
- * device. No actual data transfer is done, since we don't have bulk transfer,
- * yet. Purely skeleton for now. (5/8/99)
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/tty_flip.h>
#include <linux/tty.h>
#include <linux/module.h>
+#include <linux/config.h>
#include "usb.h"
-#define NR_PORTS 3
-#define ACM_MAJOR 166 /* Wow, major is now officially allocated */
+#ifdef CONFIG_USB_ACM_DEBUG
+#define acm_debug(fmt,arg...) printk(KERN_DEBUG "acm: " fmt "\n" , ##arg)
+#else
+#define acm_debug(fmt,arg...) do {} while(0)
+#endif
-//#define info(message...) printk(KERN_DEBUG message)
-#define info(message...)
+/*
+ * Major and minor numbers.
+ */
-#define CTRL_STAT_DTR 1
-#define CTRL_STAT_RTS 2
+#define ACM_TTY_MAJOR 166
+#define ACM_TTY_MINORS 8
-static struct usb_driver acm_driver;
+/*
+ * Output control lines.
+ */
-static int acm_refcount;
-
-static struct tty_driver acm_tty_driver;
-static struct tty_struct *acm_tty[NR_PORTS];
-static struct termios *acm_termios[NR_PORTS];
-static struct termios *acm_termios_locked[NR_PORTS];
-static struct acm_state acm_state_table[NR_PORTS];
-
-struct acm_state {
- struct usb_device *dev; //the coresponding usb device
- int cfgnum; //configuration number on this device
- struct tty_struct *tty; //the coresponding tty
- char present; //a device for this struct was detected => this tty is used
- char active; //someone has this acm's device open
- unsigned int ctrlstate; //Status of the serial control lines (handshake,...)
- unsigned int linecoding; //Status of the line coding (Bits, Stop, Parity)
- int writesize, readsize, ctrlsize; //size of the usb buffers
- char *writebuffer, *readbuffer, *ctrlbuffer; //the usb buffers
- char writing, reading; //flag if transfer is running
- unsigned int readendp,writeendp,ctrlendp; //endpoints and
- unsigned int readpipe,writepipe,ctrlpipe; //pipes (are one of these obsolete?)
- urb_t *readurb, *writeurb, *ctrlurb;
- unsigned ctrlinterval; //interval to poll from device
-};
+#define ACM_CTRL_DTR 0x01
+#define ACM_CTRL_RTS 0x02
-#define ACM_READY (acm->present && acm->active)
+/*
+ * Input control lines and line errors.
+ */
-//functions for various ACM requests
+#define ACM_CTRL_DCD 0x01
+#define ACM_CTRL_DSR 0x02
+#define ACM_CTRL_BRK 0x04
+#define ACM_CTRL_RI 0x08
-void Set_Control_Line_Status(unsigned int status, struct acm_state *acm)
-{
- struct usb_device *dev = acm->dev;
- int ret;
+#define ACM_CTRL_FRAMING 0x10
+#define ACM_CTRL_PARITY 0x20
+#define ACM_CTRL_OVERRUN 0x40
- info("Set_control_Line_Status\n");
+/*
+ * Line speed and caracter encoding.
+ */
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x22, 0x22,
- status, 0, NULL, 0, HZ);
- if (ret < 0)
- printk(KERN_ERR "acm: Set_Control_Line_Status failed\n");
+struct acm_coding {
+ __u32 speed;
+ __u8 stopbits;
+ __u8 parity;
+ __u8 databits;
+} __attribute__ ((packed));
- acm->ctrlstate = status;
-}
+/*
+ * Internal driver structures.
+ */
-void Set_Line_Coding(unsigned int coding, struct acm_state *acm)
-{
- struct usb_device *dev = acm->dev;
- int ret;
+struct acm {
+ struct usb_device *dev; /* the coresponding usb device */
+ struct usb_config_descriptor *cfg; /* configuration number on this device */
+ struct tty_struct *tty; /* the coresponding tty */
+ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
+ unsigned int ctrlout; /* output control lines (DTR, RTS) */
+ struct acm_coding linecoding; /* line coding (bits, stop, parity) */
+ unsigned int writesize; /* max packet size for the output bulk endpoint */
+ struct urb ctrlurb, readurb, writeurb; /* urbs */
+ unsigned char present; /* this device is connected to the usb bus */
+ unsigned char used; /* someone has this acm's device open */
+};
- info("Set_Line_Coding\n");
+static struct usb_driver acm_driver;
+static struct acm acm_table[ACM_TTY_MINORS];
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x30, 0x22,
- coding, 0, NULL, 0, HZ);
+#define ACM_READY(acm) (acm->present && acm->used)
- acm->linecoding = coding;
+/*
+ * Functions for ACM control messages.
+ */
+
+static void acm_set_control(unsigned int status, struct acm *acm)
+{
+ if (usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 0x22, 0x22, status, 0, NULL, 0, HZ) < 0)
+ acm_debug("acm_set_control() failed");
+
+ acm_debug("output control lines: dtr%c rts%c",
+ acm->ctrlout & ACM_CTRL_DTR ? '+' : '-', acm->ctrlout & ACM_CTRL_RTS ? '+' : '-');
}
-//Interrupt handler for various usb events
-static void acm_irq(urb_t *urb)
+#if 0
+static void acm_set_coding(struct acm_coding *coding, struct acm *acm)
{
- struct acm_state *acm = (struct acm_state *)urb->context;
- unsigned char *data = urb->transfer_buffer;
- devrequest *dr;
+ if (usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 0x30, 0x22, 0, 0, coding, sizeof(struct acm_coding), HZ) < 0)
+ acm_debug("acm_set_coding() failed");
+}
+
+static void acm_send_break(int ms, struct acm *acm)
+{
+ if (usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 0x30, 0x33, ms, 0, NULL, 0, HZ) < 0)
+ acm_debug("acm_send_break() failed");
+}
+#endif
- info("ACM_USB_IRQ\n");
+/*
+ * Interrupt handler for various ACM control events
+ */
+
+static void acm_ctrl_irq(struct urb *urb)
+{
+ struct acm *acm = urb->context;
+ devrequest *dr = urb->transfer_buffer;
+ unsigned char *data = (unsigned char *)(dr + 1);
if (urb->status < 0) {
- printk(KERN_DEBUG "acm_irq: strange status received: %d\n", urb->status);
+ acm_debug("nonzero ctrl irq status received: %d", urb->status);
return;
}
- if (!acm->present)
- return;
- if (!acm->active)
- return;
+ if (!ACM_READY(acm)) return;
- dr = (devrequest *)data;
- data += sizeof(dr);
-
-#if 0
- printk("reqtype: %02X\n",dr->requesttype);
- printk("request: %02X\n",dr->request);
- printk("wValue: %02X\n",dr->value);
- printk("wIndex: %02X\n",dr->index);
- printk("wLength: %02X\n",dr->length);
-#endif
+ switch (dr->request) {
- switch(dr->request) {
- case 0x00: /* Network connection */
- printk(KERN_DEBUG "Network connection: ");
- if (dr->request==0) printk(KERN_DEBUG "disconnected\n");
- if (dr->request==1) printk(KERN_DEBUG "connected\n");
- break;
-
- case 0x01: /* Response available */
- printk(KERN_DEBUG "Response available\n");
- break;
-
- case 0x20: /* Set serial line state */
- printk(KERN_DEBUG "acm.c: Set serial control line state\n");
- if ((dr->index==1) && (dr->length==2)) {
- acm->ctrlstate = data[0] || (data[1] << 16);
- printk(KERN_DEBUG "Serstate: %02X\n", acm->ctrlstate);
- }
- break;
+ case 0x20: /* Set serial line state */
+
+ if ((dr->index != 1) || (dr->length != 2)) {
+ acm_debug("unknown set serial line request: index %d len %d", dr->index, dr->length);
+ return;
+ }
+
+ acm->ctrlin = data[0] | (((unsigned int) data[1]) << 8);
+
+ acm_debug("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+ acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
+
+ return;
+
+ default:
+ acm_debug("unknown control event received: request %d index %d len %d data0 %d data1 %d",
+ dr->request, dr->index, dr->length, data[0], data[1]);
+ return;
}
return;
}
-static void acm_read_irq(urb_t *urb)
+static void acm_read_bulk(struct urb *urb)
{
- struct acm_state *acm = (struct acm_state *)urb->context;
- struct tty_struct *tty = acm->tty;
- unsigned char *data = acm->readbuffer;
+ struct acm *acm = urb->context;
+ struct tty_struct *tty = acm->tty;
+ unsigned char *data = urb->transfer_buffer;
int i;
- info("ACM_READ_IRQ: state %d, %d bytes\n", urb->status, urb->actual_length);
if (urb->status) {
- printk("acm_read_irq: strange state received: %d\n", urb->status);
+ acm_debug("nonzero read bulk status received: %d", urb->status);
return;
}
-
- if (!ACM_READY)
- return;
- for (i=0;i<urb->actual_length;i++)
- tty_insert_flip_char(tty,data[i],0);
- tty_flip_buffer_push(tty);
+ if (!ACM_READY(acm)) return;
- usb_submit_urb(urb);
+ for (i = 0; i < urb->actual_length; i++)
+ tty_insert_flip_char(tty, data[i], 0);
+
+ tty_flip_buffer_push(tty);
+
+ if (usb_submit_urb(urb))
+ acm_debug("failed resubmitting read urb");
return;
}
-static void acm_write_irq(urb_t *urb)
+static void acm_write_bulk(struct urb *urb)
{
- struct acm_state *acm = (struct acm_state *)urb->context;
- struct tty_struct *tty = acm->tty;
-
- info("ACM_WRITE_IRQ\n");
+ struct acm *acm = (struct acm *)urb->context;
+ struct tty_struct *tty = acm->tty;
- if (!ACM_READY)
+ if (urb->status) {
+ acm_debug("nonzero write bulk status received: %d", urb->status);
return;
+ }
+
+ if (!ACM_READY(acm)) return;
- acm->writing = 0;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
+
wake_up_interruptible(&tty->write_wait);
return;
}
-/*TTY STUFF*/
-static int rs_open(struct tty_struct *tty, struct file *filp)
-{
- struct acm_state *acm;
- int ret;
+/*
+ * TTY handlers
+ */
- info("USB_FILE_OPEN\n");
+static int acm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct acm *acm = &acm_table[MINOR(tty->device)];
- tty->driver_data = acm =
- &acm_state_table[MINOR(tty->device) - tty->driver.minor_start];
+ tty->driver_data = acm;
acm->tty = tty;
-
- if (!acm->present)
- return -EINVAL;
- if (acm->active++)
+ if (!acm->present) return -EINVAL;
+
+ if (acm->used++) {
+ MOD_INC_USE_COUNT;
return 0;
-
+ }
+
MOD_INC_USE_COUNT;
- /* Start reading from the device */
- FILL_INT_URB(acm->ctrlurb, acm->dev, acm->ctrlpipe,
- acm->ctrlbuffer, acm->ctrlsize,
- acm_irq, acm, acm->ctrlinterval);
- ret = usb_submit_urb(acm->ctrlurb);
- if (ret)
- printk(KERN_ERR "acm: usb_submit_urb(INT) failed (%d)\n", ret);
- acm->reading = 1;
-
- FILL_BULK_URB(acm->readurb, acm->dev, acm->readpipe, acm->readbuffer,
- acm->readsize, acm_read_irq, acm);
- ret = usb_submit_urb(acm->readurb);
-
- if (ret)
- printk(KERN_ERR "acm: usb_submit_urb(READ) failed (%d)\n", ret);
- acm->reading = 1;
-
- Set_Control_Line_Status(CTRL_STAT_DTR | CTRL_STAT_RTS, acm);
+ if (usb_submit_urb(&acm->ctrlurb))
+ acm_debug("usb_submit_urb(ctrl irq) failed");
+
+ if (usb_submit_urb(&acm->readurb))
+ acm_debug("usb_submit_urb(read bulk) failed");
+
+ acm_set_control(acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS, acm);
return 0;
}
-static void rs_close(struct tty_struct *tty, struct file *filp)
+static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
- struct acm_state *acm = (struct acm_state *)tty->driver_data;
+ struct acm *acm = tty->driver_data;
- info("rs_close\n");
+ if (!ACM_READY(acm)) return;
- if (!acm->present)
+ if (--acm->used) {
+ MOD_DEC_USE_COUNT;
return;
-
- if (--acm->active)
- goto early;
-
- Set_Control_Line_Status(0, acm);
-
- if (acm->writing) {
- if (acm->writeurb)
- usb_unlink_urb(acm->writeurb);
- acm->writing = 0;
- }
- if (acm->reading) {
- if (acm->readurb)
- usb_unlink_urb(acm->readurb);
- acm->reading = 0;
}
-early:
+ acm_set_control(acm->ctrlout = 0, acm);
+ usb_unlink_urb(&acm->writeurb);
+ usb_unlink_urb(&acm->readurb);
+
MOD_DEC_USE_COUNT;
}
-static int rs_write(struct tty_struct *tty, int from_user,
- const unsigned char *buf, int count)
+static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
{
- struct acm_state *acm = (struct acm_state *)tty->driver_data;
- int written, ret;
+ struct acm *acm = tty->driver_data;
- info("rs_write\n");
+ if (!ACM_READY(acm)) return -EINVAL;
+ if (acm->writeurb.status == -EINPROGRESS) return 0;
- if (!ACM_READY)
- return -EINVAL;
-
- if (acm->writing) {
- info("already writing\n");
- return 0;
- }
-
- written = (count>acm->writesize) ? acm->writesize : count;
+ count = (count > acm->writesize) ? acm->writesize : count;
if (from_user)
- copy_from_user(acm->writebuffer, buf, written);
+ copy_from_user(acm->writeurb.transfer_buffer, buf, count);
else
- memcpy(acm->writebuffer, buf, written);
-
- //start the transfer
- acm->writing = 1;
+ memcpy(acm->writeurb.transfer_buffer, buf, count);
- FILL_BULK_URB(acm->writeurb, acm->dev, acm->writepipe, acm->writebuffer,
- written, acm_write_irq, acm);
- ret = usb_submit_urb(acm->writeurb);
- if (ret)
- printk("acm: usb_submit_urb(WRITE) failed: %d\n", ret);
+ acm->writeurb.transfer_buffer_length = count;
- return written;
-}
+ if (usb_submit_urb(&acm->writeurb))
+ acm_debug("usb_submit_urb(write bulk) failed");
-static void rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
- printk(KERN_DEBUG "acm: rs_put_char: Who called this unsupported routine?\n");
- BUG();
+ return count;
}
-static int rs_write_room(struct tty_struct *tty)
+static int acm_tty_write_room(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *)tty->driver_data;
-
- info("rs_write_room\n");
-
- if (!ACM_READY)
- return -EINVAL;
-
- return acm->writing ? 0 : acm->writesize;
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return -EINVAL;
+ return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize;
}
-static int rs_chars_in_buffer(struct tty_struct *tty)
+static int acm_tty_chars_in_buffer(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *)tty->driver_data;
-
-// info("rs_chars_in_buffer\n");
-
- if (!ACM_READY)
- return -EINVAL;
-
- return acm->writing ? acm->writesize : 0;
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return -EINVAL;
+ return acm->writeurb.status == -EINPROGRESS ? acm->writesize : 0;
}
-static void rs_throttle(struct tty_struct *tty)
+static void acm_tty_throttle(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *)tty->driver_data;
-
- info("rs_throttle\n");
-
- if (!ACM_READY)
- return;
-/*
- if (I_IXOFF(tty))
- rs_send_xchar(tty, STOP_CHAR(tty));
-*/
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
if (tty->termios->c_cflag & CRTSCTS)
- Set_Control_Line_Status(acm->ctrlstate & ~CTRL_STAT_RTS, acm);
+ acm_set_control(acm->ctrlout &= ~ACM_CTRL_RTS, acm);
}
-static void rs_unthrottle(struct tty_struct *tty)
+static void acm_tty_unthrottle(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *) tty->driver_data;
-
- info("rs_unthrottle\n");
-
- if (!ACM_READY)
- return;
-/*
- if (I_IXOFF(tty))
- rs_send_xchar(tty, STOP_CHAR(tty));
-*/
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
if (tty->termios->c_cflag & CRTSCTS)
- Set_Control_Line_Status(acm->ctrlstate | CTRL_STAT_RTS, acm);
+ acm_set_control(acm->ctrlout |= ACM_CTRL_RTS, acm);
}
-static int get_free_acm(void)
+static void acm_tty_set_termios(struct tty_struct *tty, struct termios *old)
{
- int i;
-
- for (i = 0; i < NR_PORTS; i++) {
- if (!acm_state_table[i].present)
- return i;
- }
- return -1;
+ acm_debug("set_termios called, but not there yet");
+ return;
}
-static void * acm_probe(struct usb_device *dev, unsigned int ifnum)
+/*
+ * USB probe and disconnect routines.
+ */
+
+static void *acm_probe(struct usb_device *dev, unsigned int ifnum)
{
- struct acm_state *acm;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *endpoint;
- int cfgnum,acmno;
- int swapped=0;
-
- info("acm_probe\n");
-
- if (0>(acmno=get_free_acm())) {
- info("Too many acm devices connected\n");
+ struct acm *acm;
+ struct usb_interface_descriptor *ifcom, *ifdata;
+ struct usb_endpoint_descriptor *epctrl, *epread, *epwrite;
+ int readsize, ctrlsize, minor, i;
+ unsigned char *buf;
+ char *s = NULL;
+
+ for (minor = 0; minor < ACM_TTY_MINORS &&
+ (acm_table[minor].present || acm_table[minor].used); minor++);
+ if (acm_table[minor].present || acm_table[minor].used) {
+ acm_debug("no more free acm devices");
return NULL;
}
- acm = &acm_state_table[acmno];
+ acm = acm_table + minor;
+ memset(acm, 0, sizeof(struct acm));
- /* Only use CDC */
- if (dev->descriptor.bDeviceClass != 2 ||
- dev->descriptor.bDeviceSubClass != 0 ||
- dev->descriptor.bDeviceProtocol != 0)
- return NULL;
+ acm->dev = dev;
-#define IFCLASS(if) ((if->bInterfaceClass << 24) | (if->bInterfaceSubClass << 16) | (if->bInterfaceProtocol << 8) | (if->bNumEndpoints))
+ if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0
+ || dev->descriptor.bDeviceProtocol != 0) {
+ return NULL;
+ }
- /* FIXME: should the driver really be doing the configuration
- * selecting or should the usbcore? [different configurations
- * can have different bandwidth requirements] -greg */
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
- printk("Acm: found device with right class...\n" );
+ acm_debug("probing config %d", i);
+ acm->cfg = dev->config + i;
- /* Now scan all configs for a ACM configuration */
- for (cfgnum=0;cfgnum<dev->descriptor.bNumConfigurations;cfgnum++) {
- /* The first one should be Communications interface? */
- interface = &dev->config[cfgnum].interface[0].altsetting[0];
- if (IFCLASS(interface) != 0x02020101)
+ ifcom = acm->cfg->interface[0].altsetting + 0;
+ if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 ||
+ ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1)
continue;
- /* Which uses an interrupt input */
- endpoint = &interface->endpoint[0];
- if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
- (endpoint->bmAttributes & 3) != 3)
+ epctrl = ifcom->endpoint + 0;
+ if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3)
continue;
-
- /* The second one should be a Data interface? */
- interface = &dev->config[cfgnum].interface[1].altsetting[0];
- if (interface->bInterfaceClass != 10 ||
- interface->bNumEndpoints != 2)
+
+ ifdata = acm->cfg->interface[1].altsetting + 0;
+ if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2)
continue;
- /* make sure both interfaces are available for our use */
- if (usb_interface_claimed(&dev->config[cfgnum].interface[0]) ||
- usb_interface_claimed(&dev->config[cfgnum].interface[1])) {
- printk("usb-acm: required interface already has a driver\n");
+ if (usb_interface_claimed(acm->cfg->interface + 0) ||
+ usb_interface_claimed(acm->cfg->interface + 1))
continue;
- }
- endpoint = &interface->endpoint[0];
- if ((endpoint->bEndpointAddress & 0x80) != 0x80)
- swapped = 1;
+ epread = ifdata->endpoint + 0;
+ epwrite = ifdata->endpoint + 1;
- /*With a bulk input */
- endpoint = &interface->endpoint[0^swapped];
- if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
- (endpoint->bmAttributes & 3) != 2)
- continue;
-
- /*And a bulk output */
- endpoint = &interface->endpoint[1^swapped];
- if ((endpoint->bEndpointAddress & 0x80) == 0x80 ||
- (endpoint->bmAttributes & 3) != 2)
+ if ((epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 ||
+ ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80)
continue;
- printk("USB ACM %d found on config %d\n", acmno, cfgnum);
- usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
+ if ((epread->bEndpointAddress & 0x80) != 0x80) {
+ epread = ifdata->endpoint + 1;
+ epwrite = ifdata->endpoint + 0;
+ }
- acm->dev=dev;
+ usb_set_configuration(dev, acm->cfg->bConfigurationValue);
- acm->readendp=dev->config[cfgnum].interface[1].altsetting[0].endpoint[0^swapped].bEndpointAddress;
- acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp);
- acm->readbuffer=kmalloc(acm->readsize=dev->config[cfgnum].interface[1].altsetting[0].endpoint[0^swapped].wMaxPacketSize,GFP_KERNEL);
- acm->reading=0;
- if (!acm->readbuffer) {
- printk("ACM: Couldn't allocate readbuffer\n");
- return NULL;
- }
- acm->readurb = usb_alloc_urb(0);
- if (!acm->readurb) {
- printk("acm: couldn't allocate read urb\n");
- return NULL;
- }
+ ctrlsize = epctrl->wMaxPacketSize;
+ readsize = epread->wMaxPacketSize;
+ acm->writesize = epwrite->wMaxPacketSize;
- acm->writeendp=dev->config[cfgnum].interface[1].altsetting[0].endpoint[1^swapped].bEndpointAddress;
- acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp);
- acm->writebuffer=kmalloc(acm->writesize=dev->config[cfgnum].interface[1].altsetting[0].endpoint[1^swapped].wMaxPacketSize, GFP_KERNEL);
- acm->writing=0;
- if (!acm->writebuffer) {
- printk("ACM: Couldn't allocate writebuffer\n");
- kfree(acm->readbuffer);
+ if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL)))
return NULL;
- }
- acm->writeurb = usb_alloc_urb(0);
- if (!acm->writeurb) {
- printk("acm: couldn't allocate write urb\n");
- return NULL;
- }
- acm->ctrlbuffer=kmalloc(acm->ctrlsize=dev->config[cfgnum].interface[0].altsetting[0].endpoint[0].wMaxPacketSize, GFP_KERNEL);
- acm->ctrlendp=dev->config[cfgnum].interface[0].altsetting[0].endpoint[0].bEndpointAddress;
- acm->ctrlpipe=usb_rcvintpipe(acm->dev,acm->ctrlendp);
- acm->ctrlinterval=dev->config[cfgnum].interface[0].altsetting[0].endpoint[0].bInterval;
- if (!acm->ctrlbuffer) {
- printk("acm: couldn't allocate ctrlbuffer\n");
- return NULL;
- }
- acm->ctrlurb = usb_alloc_urb(0);
- if (!acm->ctrlurb) {
- printk("acm: couldn't allocate write urb\n");
- return NULL;
- }
+ FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
+ buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
- acm->cfgnum = cfgnum;
- acm->present=1;
- MOD_INC_USE_COUNT;
+ FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
+ buf += ctrlsize, readsize, acm_read_bulk, acm);
+
+ FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
+ buf += readsize , acm->writesize, acm_write_bulk, acm);
+
+ printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
+
+ usb_driver_claim_interface(&acm_driver, acm->cfg->interface + 0, acm);
+ usb_driver_claim_interface(&acm_driver, acm->cfg->interface + 1, acm);
+
+ acm->present = 1;
- usb_driver_claim_interface(&acm_driver,
- &dev->config[cfgnum].interface[0], acm);
- usb_driver_claim_interface(&acm_driver,
- &dev->config[cfgnum].interface[1], acm);
return acm;
}
+
return NULL;
}
static void acm_disconnect(struct usb_device *dev, void *ptr)
{
- struct acm_state *acm = ptr;
+ struct acm *acm = ptr;
- info("acm_disconnect\n");
-
- if (!acm->present)
+ if (!acm->present) {
+ acm_debug("disconnect on nonexisting interface");
return;
-
- acm->active=0;
- acm->present=0;
- if (acm->writing)
- acm->writing=0;
- if (acm->reading)
- acm->reading=0;
-
- if (acm->ctrlurb) {
- usb_unlink_urb(acm->ctrlurb);
- usb_free_urb(acm->ctrlurb);
- acm->ctrlurb = NULL;
- }
- if (acm->readurb) {
- usb_unlink_urb(acm->readurb);
- usb_free_urb(acm->readurb);
- acm->readurb = NULL;
- }
- if (acm->writeurb) {
- usb_unlink_urb(acm->writeurb);
- usb_free_urb(acm->writeurb);
- acm->writeurb = NULL;
}
- //BUG: What to do if a device is open?? Notify process or not allow cleanup?
- kfree(acm->writebuffer);
- kfree(acm->readbuffer);
- kfree(acm->ctrlbuffer);
- /* release the interfaces so that other drivers can have at them */
- usb_driver_release_interface(&acm_driver,
- &dev->config[acm->cfgnum].interface[0]);
- usb_driver_release_interface(&acm_driver,
- &dev->config[acm->cfgnum].interface[1]);
+ acm->present = 0;
- MOD_DEC_USE_COUNT;
+ usb_unlink_urb(&acm->ctrlurb);
+ usb_unlink_urb(&acm->readurb);
+ usb_unlink_urb(&acm->writeurb);
+
+ kfree(acm->ctrlurb.transfer_buffer);
+
+ usb_driver_release_interface(&acm_driver, acm->cfg->interface + 0);
+ usb_driver_release_interface(&acm_driver, acm->cfg->interface + 1);
}
-/*USB DRIVER STUFF*/
+/*
+ * USB driver structure.
+ */
+
static struct usb_driver acm_driver = {
- "acm",
- acm_probe,
- acm_disconnect,
- { NULL, NULL }
+ name: "acm",
+ probe: acm_probe,
+ disconnect: acm_disconnect
};
-int usb_acm_init(void)
-{
- int cnt;
-
- info("usb_acm_init\n");
-
- //INITIALIZE GLOBAL DATA STRUCTURES
- for (cnt = 0; cnt < NR_PORTS; cnt++)
- memset(&acm_state_table[cnt], 0, sizeof(struct acm_state));
-
- //REGISTER TTY DRIVER
- memset(&acm_tty_driver, 0, sizeof(struct tty_driver));
- acm_tty_driver.magic = TTY_DRIVER_MAGIC;
- acm_tty_driver.driver_name = "usb";
- acm_tty_driver.name = "ttyACM";
- acm_tty_driver.major = ACM_MAJOR;
- acm_tty_driver.minor_start = 0;
- acm_tty_driver.num = NR_PORTS;
- acm_tty_driver.type = TTY_DRIVER_TYPE_SERIAL;
- acm_tty_driver.subtype = SERIAL_TYPE_NORMAL;
- acm_tty_driver.init_termios = tty_std_termios;
- acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- acm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
- acm_tty_driver.refcount = &acm_refcount;
- acm_tty_driver.table = acm_tty;
- acm_tty_driver.termios = acm_termios;
- acm_tty_driver.termios_locked = acm_termios_locked;
-
- acm_tty_driver.open = rs_open;
- acm_tty_driver.close = rs_close;
- acm_tty_driver.write = rs_write;
- acm_tty_driver.put_char = rs_put_char;
- acm_tty_driver.flush_chars = NULL; //rs_flush_chars;
- acm_tty_driver.write_room = rs_write_room;
- acm_tty_driver.ioctl = NULL; //rs_ioctl
- acm_tty_driver.set_termios = NULL; //rs_set_termios;
- acm_tty_driver.set_ldisc = NULL;
- acm_tty_driver.throttle = rs_throttle;
- acm_tty_driver.unthrottle = rs_unthrottle;
- acm_tty_driver.stop = NULL; //rs_stop;
- acm_tty_driver.start = NULL; //rs_start;
- acm_tty_driver.hangup = NULL; //rs_hangup;
- acm_tty_driver.break_ctl = NULL; //rs_break;
- acm_tty_driver.wait_until_sent = NULL; //rs_wait_until_sent;
- acm_tty_driver.send_xchar = NULL; //rs_send_xchar;
- acm_tty_driver.read_proc = NULL; //rs_read_proc;
- acm_tty_driver.chars_in_buffer = rs_chars_in_buffer;
- acm_tty_driver.flush_buffer = NULL; //rs_flush_buffer;
- if (tty_register_driver(&acm_tty_driver)) {
- printk(KERN_ERR "acm: failed to register tty driver\n");
- return -EPERM;
- }
+/*
+ * TTY driver structures.
+ */
- if (usb_register(&acm_driver) < 0) {
- tty_unregister_driver(&acm_tty_driver);
- return -1;
- }
+static int acm_tty_refcount;
+
+static struct tty_struct *acm_tty_table[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS];
+
+static struct tty_driver acm_tty_driver = {
+ magic: TTY_DRIVER_MAGIC,
+ driver_name: "usb",
+ name: "ttyACM",
+ major: ACM_TTY_MAJOR,
+ minor_start: 0,
+ num: ACM_TTY_MINORS,
+ type: TTY_DRIVER_TYPE_SERIAL,
+ subtype: SERIAL_TYPE_NORMAL,
+ flags: TTY_DRIVER_REAL_RAW,
+
+ refcount: &acm_tty_refcount,
+
+ table: acm_tty_table,
+ termios: acm_tty_termios,
+ termios_locked: acm_tty_termios_locked,
+
+ open: acm_tty_open,
+ close: acm_tty_close,
+ write: acm_tty_write,
+ write_room: acm_tty_write_room,
+ set_termios: acm_tty_set_termios,
+ throttle: acm_tty_throttle,
+ unthrottle: acm_tty_unthrottle,
+ chars_in_buffer: acm_tty_chars_in_buffer
+};
- printk(KERN_INFO "USB ACM registered.\n");
- return 0;
-}
+/*
+ * Init / cleanup.
+ */
-void usb_acm_cleanup(void)
+#ifdef MODULE
+void cleanup_module(void)
{
- int i;
- struct acm_state *acm;
-
- info("usb_acm_cleanup\n");
-
- for (i=0;i<NR_PORTS;i++) {
- acm=&acm_state_table[i];
- if (acm->present) {
- printk("disconnecting %d\n",i);
- acm_disconnect(acm->dev, acm);
- }
- }
- tty_unregister_driver(&acm_tty_driver);
-
usb_deregister(&acm_driver);
-
+ tty_unregister_driver(&acm_tty_driver);
}
-#ifdef MODULE
int init_module(void)
+#else
+int usb_acm_init(void)
+#endif
{
- return usb_acm_init();
-}
+ memset(acm_table, 0, sizeof(struct acm) * ACM_TTY_MINORS);
-void cleanup_module(void)
-{
- usb_acm_cleanup();
+ acm_tty_driver.init_termios = tty_std_termios;
+ acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+
+ if (tty_register_driver(&acm_tty_driver))
+ return -1;
+
+ if (usb_register(&acm_driver) < 0) {
+ tty_unregister_driver(&acm_tty_driver);
+ return -1;
+ }
+
+ return 0;
}
-#endif
+
* that means they won't play short sounds. Should probably maintain
* the ISO datastream even if there's nothing to play.
* Fix counting the total_bytes counter, RealPlayer G2 depends on it.
+ * 1999-12-20: Fix bad bug in conversion to per interface probing.
+ * disconnect was called multiple times for the audio device,
+ * leading to a premature freeing of the audio structures
*
*/
*/
/*****************************************************************************/
+
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/io.h>
-//#include <linux/spinlock.h>
#include "usb.h"
#include "audio.h"
usbin_stop(as);
}
-static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples)
+static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt)
{
- union {
- __s16 s[64];
- unsigned char b[0];
- } tmp;
- unsigned int scnt, maxs, ufmtsh, dfmtsh, cnt, i;
+ unsigned int cnt, i;
__s16 *sp, *sp2, s;
unsigned char *bp;
- ufmtsh = AFMT_BYTESSHIFT(u->format);
- dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
- maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
- while (samples > 0) {
- scnt = samples;
- if (scnt > maxs)
- scnt = maxs;
- cnt = scnt;
- if (AFMT_ISSTEREO(u->format))
- cnt <<= 1;
- sp = tmp.s + cnt;
- switch (u->format & ~AFMT_STEREO) {
- case AFMT_U8:
- for (bp = buffer+cnt, i = 0; i < cnt; i++) {
- bp--;
- sp--;
- *sp = (*bp ^ 0x80) << 8;
- }
- break;
+ cnt = scnt;
+ if (AFMT_ISSTEREO(ifmt))
+ cnt <<= 1;
+ sp = ((__s16 *)tmp) + cnt;
+ switch (ifmt & ~AFMT_STEREO) {
+ case AFMT_U8:
+ for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
+ bp--;
+ sp--;
+ *sp = (*bp ^ 0x80) << 8;
+ }
+ break;
+
+ case AFMT_S8:
+ for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
+ bp--;
+ sp--;
+ *sp = *bp << 8;
+ }
+ break;
+
+ case AFMT_U16_LE:
+ for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+ bp -= 2;
+ sp--;
+ *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000;
+ }
+ break;
- case AFMT_S8:
- for (bp = buffer+cnt, i = 0; i < cnt; i++) {
- bp--;
- sp--;
- *sp = *bp << 8;
- }
- break;
+ case AFMT_U16_BE:
+ for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+ bp -= 2;
+ sp--;
+ *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000;
+ }
+ break;
- case AFMT_U16_LE:
- for (bp = buffer+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000;
- }
- break;
+ case AFMT_S16_LE:
+ for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+ bp -= 2;
+ sp--;
+ *sp = bp[0] | (bp[1] << 8);
+ }
+ break;
- case AFMT_U16_BE:
- for (bp = buffer+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000;
- }
- break;
+ case AFMT_S16_BE:
+ for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
+ bp -= 2;
+ sp--;
+ *sp = bp[1] | (bp[0] << 8);
+ }
+ break;
+ }
+ if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) {
+ /* expand from mono to stereo */
+ for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) {
+ sp--;
+ sp2 -= 2;
+ sp2[0] = sp2[1] = sp[0];
+ }
+ }
+ if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) {
+ /* contract from stereo to mono */
+ for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2)
+ sp[0] = (sp2[0] + sp2[1]) >> 1;
+ }
+ cnt = scnt;
+ if (AFMT_ISSTEREO(ofmt))
+ cnt <<= 1;
+ sp = ((__s16 *)tmp);
+ bp = ((unsigned char *)obuf);
+ switch (ofmt & ~AFMT_STEREO) {
+ case AFMT_U8:
+ for (i = 0; i < cnt; i++, sp++, bp++)
+ *bp = (*sp >> 8) ^ 0x80;
+ break;
- case AFMT_S16_LE:
- for (bp = buffer+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = bp[0] | (bp[1] << 8);
- }
- break;
+ case AFMT_S8:
+ for (i = 0; i < cnt; i++, sp++, bp++)
+ *bp = *sp >> 8;
+ break;
- case AFMT_S16_BE:
- for (bp = buffer+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = bp[1] | (bp[0] << 8);
- }
- break;
+ case AFMT_U16_LE:
+ for (i = 0; i < cnt; i++, sp++, bp += 2) {
+ s = *sp;
+ bp[0] = s;
+ bp[1] = (s >> 8) ^ 0x80;
}
- if (!AFMT_ISSTEREO(u->format) && AFMT_ISSTEREO(u->dma.format)) {
- /* expand from mono to stereo */
- for (sp = tmp.s+scnt, sp2 = tmp.s+2*scnt, i = 0; i < scnt; i++) {
- sp--;
- sp2 -= 2;
- sp2[0] = sp2[1] = sp[0];
- }
- }
- if (AFMT_ISSTEREO(u->format) && !AFMT_ISSTEREO(u->dma.format)) {
- /* contract from stereo to mono */
- for (sp = sp2 = tmp.s, i = 0; i < scnt; i++, sp++, sp2 += 2)
- sp[0] = (sp2[0] + sp2[1]) >> 1;
- }
- cnt = scnt;
- if (AFMT_ISSTEREO(u->dma.format))
- cnt <<= 1;
- sp = tmp.s;
- bp = tmp.b;
- switch (u->dma.format & ~AFMT_STEREO) {
- case AFMT_U8:
- for (i = 0; i < cnt; i++, sp++, bp++)
- *bp = (*sp >> 8) ^ 0x80;
- break;
+ break;
- case AFMT_S8:
- for (i = 0; i < cnt; i++, sp++, bp++)
- *bp = *sp >> 8;
- break;
+ case AFMT_U16_BE:
+ for (i = 0; i < cnt; i++, sp++, bp += 2) {
+ s = *sp;
+ bp[1] = s;
+ bp[0] = (s >> 8) ^ 0x80;
+ }
+ break;
- case AFMT_U16_LE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[0] = s;
- bp[1] = (s >> 8) ^ 0x80;
- }
- break;
+ case AFMT_S16_LE:
+ for (i = 0; i < cnt; i++, sp++, bp += 2) {
+ s = *sp;
+ bp[0] = s;
+ bp[1] = s >> 8;
+ }
+ break;
- case AFMT_U16_BE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[1] = s;
- bp[0] = (s >> 8) ^ 0x80;
- }
- break;
+ case AFMT_S16_BE:
+ for (i = 0; i < cnt; i++, sp++, bp += 2) {
+ s = *sp;
+ bp[1] = s;
+ bp[0] = s >> 8;
+ }
+ break;
+ }
+
+}
- case AFMT_S16_LE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[0] = s;
- bp[1] = s >> 8;
- }
- break;
+static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples)
+{
+ union {
+ __s16 s[64];
+ unsigned char b[0];
+ } tmp;
+ unsigned int scnt, maxs, ufmtsh, dfmtsh;
- case AFMT_S16_BE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[1] = s;
- bp[0] = s >> 8;
- }
- break;
- }
+ ufmtsh = AFMT_BYTESSHIFT(u->format);
+ dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+ maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
+ while (samples > 0) {
+ scnt = samples;
+ if (scnt > maxs)
+ scnt = maxs;
+ conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt);
dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh);
buffer += scnt << ufmtsh;
samples -= scnt;
i = u->flags;
spin_unlock_irqrestore(&as->lock, flags);
while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
- set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
spin_lock_irqsave(&as->lock, flags);
i = u->flags;
static void usbout_disc(struct usb_audiodev *as)
{
struct usbout *u = &as->usbout;
-
unsigned long flags;
spin_lock_irqsave(&as->lock, flags);
__s16 s[64];
unsigned char b[0];
} tmp;
- unsigned int scnt, maxs, ufmtsh, dfmtsh, cnt, i;
- __s16 *sp, *sp2, s;
- unsigned char *bp;
+ unsigned int scnt, maxs, ufmtsh, dfmtsh;
ufmtsh = AFMT_BYTESSHIFT(u->format);
dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
scnt = samples;
if (scnt > maxs)
scnt = maxs;
- cnt = scnt;
- if (AFMT_ISSTEREO(u->dma.format))
- cnt <<= 1;
dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh);
- sp = tmp.s + cnt;
- switch (u->dma.format & ~AFMT_STEREO) {
- case AFMT_U8:
- for (bp = tmp.b+cnt, i = 0; i < cnt; i++) {
- bp--;
- sp--;
- *sp = (*bp ^ 0x80) << 8;
- }
- break;
-
- case AFMT_S8:
- for (bp = tmp.b+cnt, i = 0; i < cnt; i++) {
- bp--;
- sp--;
- *sp = *bp << 8;
- }
- break;
-
- case AFMT_U16_LE:
- for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000;
- }
- break;
-
- case AFMT_U16_BE:
- for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000;
- }
- break;
-
- case AFMT_S16_LE:
- for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = bp[0] | (bp[1] << 8);
- }
- break;
-
- case AFMT_S16_BE:
- for (bp = tmp.b+2*cnt, i = 0; i < cnt; i++) {
- bp -= 2;
- sp--;
- *sp = bp[1] | (bp[0] << 8);
- }
- break;
- }
- if (!AFMT_ISSTEREO(u->dma.format) && AFMT_ISSTEREO(u->format)) {
- /* expand from mono to stereo */
- for (sp = tmp.s+scnt, sp2 = tmp.s+2*scnt, i = 0; i < scnt; i++) {
- sp--;
- sp2 -= 2;
- sp2[0] = sp2[1] = sp[0];
- }
- }
- if (AFMT_ISSTEREO(u->dma.format) && !AFMT_ISSTEREO(u->format)) {
- /* contract from stereo to mono */
- for (sp = sp2 = tmp.s, i = 0; i < scnt; i++, sp++, sp2 += 2)
- sp[0] = (sp2[0] + sp2[1]) >> 1;
- }
- cnt = scnt;
- if (AFMT_ISSTEREO(u->format))
- cnt <<= 1;
- sp = tmp.s;
- bp = buffer;
- switch (u->format & ~AFMT_STEREO) {
- case AFMT_U8:
- for (i = 0; i < cnt; i++, sp++, bp++)
- *bp = (*sp >> 8) ^ 0x80;
- break;
-
- case AFMT_S8:
- for (i = 0; i < cnt; i++, sp++, bp++)
- *bp = *sp >> 8;
- break;
-
- case AFMT_U16_LE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[0] = s;
- bp[1] = (s >> 8) ^ 0x80;
- }
- break;
-
- case AFMT_U16_BE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[1] = s;
- bp[0] = (s >> 8) ^ 0x80;
- }
- break;
-
- case AFMT_S16_LE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[0] = s;
- bp[1] = s >> 8;
- }
- break;
-
- case AFMT_S16_BE:
- for (i = 0; i < cnt; i++, sp++, bp += 2) {
- s = *sp;
- bp[1] = s;
- bp[0] = s >> 8;
- }
- break;
- }
+ conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt);
buffer += scnt << ufmtsh;
samples -= scnt;
}
struct usb_device *dev = as->state->usbdev;
struct usb_config_descriptor *config = dev->actconfig;
struct usb_interface_descriptor *alts;
- struct usb_interface *iface;
+ struct usb_interface *iface;
struct usbin *u = &as->usbin;
struct dmabuf *d = &u->dma;
struct audioformat *fmt;
(ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0)
goto err;
return 0;
-
+
case BASS_CONTROL:
case MID_CONTROL:
case TREBLE_CONTROL:
(ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, HZ) < 0)
goto err;
return 0;
-
+
default:
return -1;
}
err:
printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n",
- dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector);
+ dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector);
return -1;
}
while (!list_empty(&s->audiolist)) {
as = list_entry(s->audiolist.next, struct usb_audiodev, list);
list_del(&as->list);
-
usbin_release(as);
usbout_release(as);
dmabuf_release(&as->usbin.dma);
}
while (!list_empty(&s->mixerlist)) {
ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list);
-
list_del(&ms->list);
kfree(ms);
}
case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
/* don't know how to handle this yet */
return put_user(0, (int *)arg);
-
+
case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
for (val = i = 0; i < ms->numch; i++)
val |= 1 << ms->ch[i].osschannel;
case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
/* don't know how to handle this yet */
return put_user(0, (int *)arg);
-
+
case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
for (val = i = 0; i < ms->numch; i++)
if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))
static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr)
{
-// struct usb_device *dev = state->s->usbdev;
+ struct usb_device *dev = state->s->usbdev;
struct mixerchannel *ch;
unsigned short chftr, mchftr;
-// unsigned char data[1];
+ unsigned char data[1];
usb_audio_recurseunit(state, ftr[4]);
if (state->nrchannels == 0) {
if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) {
if (numifin < USB_MAXINTERFACES) {
ifin[numifin++] = j;
- usb_driver_claim_interface(&usb_audio_driver, iface, s);
+ usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
}
} else {
if (numifout < USB_MAXINTERFACES) {
ifout[numifout++] = j;
- usb_driver_claim_interface(&usb_audio_driver, iface, s);
+ usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
}
}
}
down(&open_sem);
list_add_tail(&s->audiodev, &audiodevs);
up(&open_sem);
+ printk(KERN_DEBUG "usb_audio_parsecontrol: usb_audio_state at %p\n", s);
return s;
}
if (!(buffer = kmalloc(buflen, GFP_KERNEL)))
return NULL;
ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen);
- if (ret<0) {
+ if (ret < 0) {
kfree(buffer);
printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum);
return NULL;
struct list_head *list;
struct usb_audiodev *as;
struct usb_mixerdev *ms;
-
+
+ /* we get called with -1 for every audiostreaming interface registered */
+ if (s == (struct usb_audio_state *)-1) {
+ printk(KERN_DEBUG "usb_audio_disconnect: called with -1\n");
+ return;
+ }
+ if (!s->usbdev) {
+ printk(KERN_DEBUG "usb_audio_disconnect: already called for %p!\n", s);
+ return;
+ }
down(&open_sem);
list_del(&s->audiodev);
INIT_LIST_HEAD(&s->audiodev);
+++ /dev/null
-/* -*- linux-c -*- */
-
-/*
- * Driver for USB HP Scanners
- *
- * David E. Nelson (dnelson@jump.net)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
- *
- * History
- * 0.1 8/31/1999
- *
- * Developed/tested using linux-2.3.15 with minor ohci.c changes to
- * support short packes during bulk xfer mode. Some testing was
- * done with ohci-hcd but the performace was low. Very limited
- * testing was performed with uhci but I was unable to get it to
- * work. Initial relase to the linux-usb development effort.
- *
- * */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/malloc.h>
-#include <linux/spinlock.h>
-
-#include "usb.h"
-
-/* stall/wait timeout for scanner */
-#define NAK_TIMEOUT (HZ)
-
-/* For some reason, an IBUF_SIZE of 8192 causes REALLY big problems
- * with linux-2.3.15. Anything more than 4k seems to not have an
- * effect on increasing performance. Anything smaller than 4k hurts
- * it. */
-#define IBUF_SIZE 4096
-
-/* This is a scanner, so not much data is sent to it. The largest
- * stuff may be some kind of maps and stuff but that's kinda rare. */
-#define OBUF_SIZE 128
-
-struct hpscan_usb_data {
- struct usb_device *hpscan_dev; /* init: probe_scanner */
- __u8 isopen; /* nz if open */
-
- __u8 present; /* Device is present on the bus */
- char *obuf; /* transfer buffers */
- char *ibuf;
- wait_queue_head_t wait_q; /* for timeouts */
-};
-
-static struct hpscan_usb_data hpscan;
-
-static int
-open_scanner(struct inode * inode, struct file * file)
-{
- struct hpscan_usb_data *hps = &hpscan;
-
- if (hps->isopen) {
- return -EBUSY;
- }
- hps->isopen = 1;
-
- init_waitqueue_head(&hps->wait_q);
-
- MOD_INC_USE_COUNT;
-
- return 0;
-}
-
-static int
-close_scanner(struct inode * inode, struct file * file)
-{
- struct hpscan_usb_data *hps = &hpscan;
-
- hps->isopen = 0;
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-}
-
-static ssize_t
-write_scanner(struct file * file, const char * buffer,
- size_t count, loff_t *ppos)
-{
- struct hpscan_usb_data *hps = &hpscan;
-
- unsigned long copy_size;
- unsigned long bytes_written = 0;
- unsigned long partial;
-
- int result = 0;
- int maxretry;
-
- do {
- unsigned long thistime;
- char *obuf = hps->obuf;
-
- thistime = copy_size = (count > OBUF_SIZE) ? OBUF_SIZE : count;
- if (copy_from_user(hps->obuf, buffer, copy_size))
- return -EFAULT;
- maxretry = 5;
- while (thistime) {
- if (!hps->hpscan_dev)
- return -ENODEV;
- if (signal_pending(current)) {
- return bytes_written ? bytes_written : -EINTR;
- }
-
- result = usb_bulk_msg(hps->hpscan_dev,usb_sndbulkpipe(hps->hpscan_dev, 2), obuf, thistime, &partial, HZ*10);
-
- //printk(KERN_DEBUG "write stats: result:%d thistime:%lu partial:%lu\n", result, thistime, partial);
-
- if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
- if(!maxretry--) {
- return -ETIME;
- }
- interruptible_sleep_on_timeout(&hps->wait_q, NAK_TIMEOUT);
- continue;
- } else if (!result & partial) {
- obuf += partial;
- thistime -= partial;
- } else
- break;
- };
- if (result) {
- printk("Write Whoops - %x\n", result);
- return -EIO;
- }
- bytes_written += copy_size;
- count -= copy_size;
- buffer += copy_size;
- } while ( count > 0 );
-
- return bytes_written ? bytes_written : -EIO;
-}
-
-static ssize_t
-read_scanner(struct file * file, char * buffer,
- size_t count, loff_t *ppos)
-{
- struct hpscan_usb_data *hps = &hpscan;
-
- ssize_t read_count;
-
- unsigned long partial;
-
- int this_read;
- int result;
-
-/* Wait for the scanner to get it's act together. This may involve
- * resetting the head, warming up the lamp, etc. maxretry is number
- * of seconds. */
- int maxretry = 30;
-
- char *ibuf = hps->ibuf;
-
- read_count = 0;
-
- while (count) {
- if (signal_pending(current)) {
- return read_count ? read_count : -EINTR;
- }
- if (!hps->hpscan_dev)
- return -ENODEV;
- this_read = (count > IBUF_SIZE) ? IBUF_SIZE : count;
-
- result = usb_bulk_msg(hps->hpscan_dev, usb_rcvbulkpipe(hps->hpscan_dev, 1), ibuf, this_read, &partial, HZ*10);
-
- printk(KERN_DEBUG "read stats: result:%d this_read:%u partial:%lu\n", result, this_read, partial);
-
- if (partial) {
- count = this_read = partial;
- } else if (result == USB_ST_TIMEOUT || result == 15) { /* FIXME: 15 ??? */
- if(!maxretry--) {
- printk(KERN_DEBUG "read_scanner: maxretry timeout\n");
- return -ETIME;
- }
- interruptible_sleep_on_timeout(&hps->wait_q, NAK_TIMEOUT);
- continue;
- } else if (result != USB_ST_DATAUNDERRUN) {
- printk("Read Whoops - result:%u partial:%lu this_read:%u\n", result, partial, this_read);
- return -EIO;
- } else {
- return (0);
- }
-
- if (this_read) {
- if (copy_to_user(buffer, ibuf, this_read))
- return -EFAULT;
- count -= this_read;
- read_count += this_read;
- buffer += this_read;
- }
- }
- return read_count;
-}
-
-static void *
-probe_scanner(struct usb_device *dev, unsigned int ifnum)
-{
- struct hpscan_usb_data *hps = &hpscan;
-
- /*
- * Don't bother using an HP 4200C since it does NOT understand
- * SCL and HP isn't going to be releasing the specs any time
- * soon. */
- if (dev->descriptor.idVendor != 0x3f0 ) {
- printk(KERN_INFO "Scanner is not an HP Scanner.\n");
- return NULL;
- }
-
- if (dev->descriptor.idProduct != 0x101 && /* HP 4100C */
- dev->descriptor.idProduct != 0x202 && /* HP 5100C */
- dev->descriptor.idProduct != 0x601) { /* HP 6300C */
- printk(KERN_INFO "Scanner model not supported/tested.\n");
- return NULL;
- }
-
- printk(KERN_DEBUG "USB Scanner found at address %d\n", dev->devnum);
-
- hps->present = 1;
- hps->hpscan_dev = dev;
-
- if (!(hps->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
- return NULL;
- }
-
- if (!(hps->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
- return NULL;
- }
-
- return hps;
-}
-
-static void
-disconnect_scanner(struct usb_device *dev, void *ptr)
-{
- struct hpscan_usb_data *hps = (struct hpscan_usb_data *) ptr;
-
- if (hps->isopen) {
- /* better let it finish - the release will do whats needed */
- hps->hpscan_dev = NULL;
- return;
- }
- kfree(hps->ibuf);
- kfree(hps->obuf);
-
- hps->present = 0;
-}
-
-static struct
-file_operations usb_scanner_fops = {
- NULL, /* seek */
- read_scanner,
- write_scanner,
- NULL, /* readdir */
- NULL, /* poll */
- NULL, /* ioctl */
- NULL, /* mmap */
- open_scanner,
- NULL, /* flush */
- close_scanner,
- NULL,
- NULL, /* fasync */
-};
-
-static struct
-usb_driver scanner_driver = {
- "usbscanner",
- probe_scanner,
- disconnect_scanner,
- { NULL, NULL },
- &usb_scanner_fops,
- 48
-};
-
-int
-usb_hp_scanner_init(void)
-{
- if (usb_register(&scanner_driver) < 0)
- return -1;
-
- printk(KERN_DEBUG "USB Scanner support registered.\n");
- return 0;
-}
-
-
-void
-usb_hp_scanner_cleanup(void)
-{
- struct hpscan_usb_data *hps = &hpscan;
-
- hps->present = 0;
- usb_deregister(&scanner_driver);
-}
-
-#ifdef MODULE
-
-int
-init_module(void)
-{
- return usb_hp_scanner_init();
-}
-
-void
-cleanup_module(void)
-{
- usb_hp_scanner_cleanup();
-}
-#endif
-
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Gregory P. Smith
+ *
+ * $Id: hub.c,v 1.15 1999/12/27 15:17:45 acher Exp $
*/
#include <linux/kernel.h>
USB_DT_HUB << 8, 0, data, size, HZ);
}
+static int usb_clear_hub_feature(struct usb_device *dev, int feature)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0 , NULL, 0, HZ);
+}
+
static int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
{
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
printk(KERN_INFO "hub: enabling power on all ports\n");
for (i = 0; i < hub->nports; i++)
usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
-
return 0;
}
struct usb_device *usb;
struct usb_port_status portsts;
unsigned short portstatus, portchange;
+ int tries;
+ wait_ms(100);
/* Check status */
if (usb_get_port_status(hub, port + 1, &portsts)<0) {
printk(KERN_ERR "get_port_status failed\n");
portstatus = le16_to_cpu(portsts.wPortStatus);
portchange = le16_to_cpu(portsts.wPortChange);
- printk("hub.c: portstatus %x, change %x\n",portstatus,portchange);
+ printk("hub.c: portstatus %x, change %x, %s\n",portstatus,portchange,
+ (portstatus&(1<<USB_PORT_FEAT_LOWSPEED)?"Low Speed":"High Speed"));
/* If it's not in CONNECT and ENABLE state, we're done */
if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
(!(portstatus & USB_PORT_STAT_ENABLE))) {
return;
}
wait_ms(400);
+
/* Reset the port */
- usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
- wait_ms(100);
+
+#define MAX_TRIES 5
+
+ for(tries=0;tries<MAX_TRIES;tries++) {
+
+ usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+ wait_ms(200);
+
+ if (usb_get_port_status(hub, port + 1, &portsts)<0) {
+ printk(KERN_ERR "get_port_status failed\n");
+ return;
+ }
+ portstatus = le16_to_cpu(portsts.wPortStatus);
+ portchange = le16_to_cpu(portsts.wPortChange);
+ printk("hub.c: portstatus %x, change %x, %s\n",portstatus,portchange,
+ (portstatus&(1<<USB_PORT_FEAT_LOWSPEED)?"Low Speed":"High Speed"));
+
+ if ((portstatus&(1<<USB_PORT_FEAT_ENABLE)))
+ break;
+
+ wait_ms(200);
+ }
+
+ if (tries==MAX_TRIES) {
+ printk("hub.c: Can not enable port %i after %i retries, disabling port\n",port+1,MAX_TRIES);
+ return;
+ }
/* Allocate a new device struct for it */
+
usb = usb_alloc_dev(hub, hub->bus);
if (!usb) {
printk(KERN_ERR "couldn't allocate usb_device\n");
void usb_major_cleanup(void);
int usb_mouse_init(void);
void usb_mouse_cleanup(void);
-int usb_hp_scanner_init(void);
-void usb_hp_scanner_cleanup(void);
+int usb_scanner_init(void);
+void usb_scanner_cleanup(void);
int usb_printer_init(void);
int usb_scsi_init(void);
int usb_serial_init(void);
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a,
- 0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34,
+ 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34,
0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46,
0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e,
0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
- 0x48, 0x49, 0x52, 0x53, 0x00, 0x6d, 0x00, 0x00,
+ 0x48, 0x49, 0x52, 0x53, 0x56, 0x6d, 0x00, 0x00,
0xbd, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*
* Look like a PS/2 mouse, please..
- *
+ * In XFree86 (3.3.5 tested) you must select Protocol "NetMousePS/2",
+ * then use your wheel as Button 4 and 5 via ZAxisMapping 4 5.
* The PS/2 protocol is fairly strange, but
* oh, well, it's at least common..
*/
static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos)
{
+ DECLARE_WAITQUEUE(wait, current);
int retval = 0;
static int state = 0;
struct mouse_state *mouse = &static_mouse_state;
if (!mouse->present)
return 0;
- /*
- * FIXME - Other mouse drivers handle blocking and nonblocking reads
- * differently here...
- */
+
+ if (!mouse->ready) {
+ if (file->f_flags & O_NONBLOCK) return -EAGAIN;
+
+ add_wait_queue(&mouse->wait, &wait);
+repeat:
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!mouse->ready && !signal_pending(current)) {
+ schedule();
+ goto repeat;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&mouse->wait, &wait);
+ }
+ if (signal_pending(current)) return -ERESTARTSYS;
+
+ if (!mouse->present)
+ return 0;
if (count) {
mouse->ready = 0;
switch (state) {
{
printk(KERN_DEBUG "%s(%d): mouse resume\n", __FILE__, __LINE__);
/* restart the usb controller's polling of the mouse */
-
+
pipe = usb_rcvintpipe(mouse->dev, mouse->bEndpointAddress);
FILL_INT_URB(mouse->urb,mouse->dev,pipe,
mouse->buffer,
static void mouse_disconnect(struct usb_device *dev, void *priv)
{
struct mouse_state *mouse = priv;
-
+
/* stop the usb interrupt transfer */
if (mouse->present) {
- usb_unlink_urb(mouse->urb);
+ usb_unlink_urb(mouse->urb);
+ wake_up(&mouse->wait);
}
/* this might need work */
-
/* Driver for USB Printers
*
* Copyright 1999 Michael Gee (michael@linuxspecific.com)
#include "usb.h"
+/* Define IEEE_DEVICE_ID if you want to see the IEEE-1284 Device ID string.
+ * This may include the printer's serial number.
+ * An example from an HP 970C DeskJet printer is (this is one long string,
+ * with the serial number changed):
+MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ: ;
+ */
+#define IEEE_DEVICE_ID
+
#define NAK_TIMEOUT (HZ) /* stall wait for printer */
#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */
static unsigned char printer_read_status(struct pp_usb_data *p)
{
__u8 status;
+ int err;
struct usb_device *dev = p->pusb_dev;
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
+ err = usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
USB_PRINTER_REQ_GET_PORT_STATUS,
USB_TYPE_CLASS | USB_RT_INTERFACE | USB_DIR_IN,
- 0, 0, &status, sizeof(status), HZ)) {
+ 0, 0, &status, sizeof(status), HZ);
+ if (err < 0) {
+ printk(KERN_ERR "usblp%d: read_status control_msg error = %d\n",
+ p->minor, err);
return 0;
}
return status;
static void printer_reset(struct pp_usb_data *p)
{
struct usb_device *dev = p->pusb_dev;
+ int err;
- usb_control_msg(dev, usb_sndctrlpipe(dev,0),
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev,0),
USB_PRINTER_REQ_SOFT_RESET,
USB_TYPE_CLASS | USB_RECIP_OTHER,
0, 0, NULL, 0, HZ);
+ if (err < 0)
+ printk(KERN_ERR "usblp%d: reset control_msg error = %d\n",
+ p->minor, err);
}
static int open_printer(struct inode *inode, struct file *file)
p->minor = MINOR(inode->i_rdev);
if (p->isopen++) {
+ printk(KERN_ERR "usblp%d: printer is already open\n",
+ p->minor);
return -EBUSY;
}
if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) {
p->isopen = 0;
+ printk(KERN_ERR "usblp%d: cannot allocate memory\n",
+ p->minor);
return -ENOMEM;
}
if (copy_from_user(p->obuf, buffer, copy_size))
return -EFAULT;
maxretry = MAX_RETRY_COUNT;
+
while (thistime) {
if (!p->pusb_dev)
return -ENODEV;
result = usb_bulk_msg(p->pusb_dev,
usb_rcvbulkpipe(p->pusb_dev, p->bulk_in_ep),
buf, this_read, &partial, HZ*20);
+ if (result < 0)
+ printk(KERN_ERR "usblp%d read_printer bulk_msg error = %d\n",
+ p->minor, result);
/* unlike writes, we don't retry a NAK, just stop now */
if (!result & partial)
buffer += this_read;
}
}
+
return read_count;
}
struct usb_interface_descriptor *interface;
struct pp_usb_data *pp;
int i;
+ __u8 status;
/*
* FIXME - this will not cope with combined printer/scanners
break;
}
if (i >= MAX_PRINTERS) {
- printk(KERN_ERR "No minor table space available for USB Printer\n");
+ printk(KERN_ERR "No minor table space available for new USB printer\n");
return NULL;
}
- printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum);
+ printk(KERN_INFO "USB printer found at address %d\n", dev->devnum);
if (!(pp = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) {
- printk(KERN_DEBUG "usb_printer: no memory!\n");
+ printk(KERN_DEBUG "USB printer: no memory!\n");
return NULL;
}
memset(pp, 0, sizeof(struct pp_usb_data));
minor_data[i] = PPDATA(pp);
+
pp->minor = i;
pp->pusb_dev = dev;
pp->maxout = (BIG_BUF_SIZE > PAGE_SIZE) ? PAGE_SIZE : BIG_BUF_SIZE;
pp->bulk_out_index,
pp->bulk_out_ep);
-#if 1
+#ifdef IEEE_DEVICE_ID
{
- __u8 status;
- __u8 ieee_id[64]; /* first 2 bytes are (big-endian) length */
- int length = be16_to_cpup((__u16 *)ieee_id);
+ __u8 ieee_id[64]; /* first 2 bytes are (big-endian) length */
+ /* This string space may be too short. */
+ int length = (ieee_id[0] << 8) + ieee_id[1]; /* high-low */
+ /* This calc. or be16_to_cpu() both get
+ * some weird results for <length>. */
+ int err;
/* Let's get the device id if possible. */
- if (usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
+ err = usb_control_msg(dev, usb_rcvctrlpipe(dev,0),
USB_PRINTER_REQ_GET_DEVICE_ID,
USB_TYPE_CLASS | USB_RT_INTERFACE | USB_DIR_IN,
0, 0, ieee_id,
- sizeof(ieee_id)-1, HZ) == 0) {
+ sizeof(ieee_id)-1, HZ);
+ if (err >= 0) {
if (ieee_id[1] < sizeof(ieee_id) - 1)
ieee_id[ieee_id[1]+2] = '\0';
else
ieee_id[sizeof(ieee_id)-1] = '\0';
- printk(KERN_INFO " usblp%d Device ID length=%d, string=%s\n",
- pp->minor, length, &ieee_id[2]);
+ printk(KERN_INFO "usblp%d Device ID length=%d [%x:%x]\n",
+ pp->minor, length, ieee_id[0], ieee_id[1]);
+ printk(KERN_INFO "usblp%d Device ID=%s\n",
+ pp->minor, &ieee_id[2]);
}
else
- printk(KERN_INFO " usblp%d: error reading IEEE-1284 Device ID\n",
- pp->minor);
-
- status = printer_read_status(PPDATA(pp));
- printk(KERN_INFO " usblp%d Probe Status is %x: %s,%s,%s\n",
- pp->minor, status,
- (status & LP_PSELECD) ? "Selected" : "Not Selected",
- (status & LP_POUTPA) ? "No Paper" : "Paper",
- (status & LP_PERRORP) ? "No Error" : "Error");
+ printk(KERN_INFO "usblp%d: error = %d reading IEEE-1284 Device ID\n",
+ pp->minor, err);
}
#endif
+
+ status = printer_read_status(PPDATA(pp));
+ printk(KERN_INFO "usblp%d probe status is %x: %s,%s,%s\n",
+ pp->minor, status,
+ (status & LP_PSELECD) ? "Selected" : "Not Selected",
+ (status & LP_POUTPA) ? "No Paper" : "Paper",
+ (status & LP_PERRORP) ? "No Error" : "Error");
+
return pp;
}
if (usb_register(&printer_driver))
return -1;
- printk(KERN_INFO "USB Printer support registered.\n");
+ printk(KERN_INFO "USB Printer driver registered.\n");
return 0;
}
--- /dev/null
+/* -*- linux-c -*- */
+
+/*
+ * Driver for USB Scanners (linux-2.3.33)
+ *
+ * David E. Nelson (dnelson@jump.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
+ *
+ * History
+ * 0.1 8/31/1999
+ *
+ * Developed/tested using linux-2.3.15 with minor ohci.c changes to
+ * support short packes during bulk xfer mode. Some testing was
+ * done with ohci-hcd but the performace was low. Very limited
+ * testing was performed with uhci but I was unable to get it to
+ * work. Initial relase to the linux-usb development effort.
+ *
+ * 0.2 10/16/1999
+ *
+ * FIXED:
+ * - Device can't be opened unless a scanner is plugged into the USB.
+ * - Finally settled on a reasonable value for the I/O buffer's.
+ * - Cleaned up write_scanner()
+ * - Disabled read/write stats
+ * - A little more code cleanup
+ *
+ * 0.3 10/18/1999
+ *
+ * FIXED:
+ * - Device registration changed to reflect new device
+ * allocation/registration for linux-2.3.22+.
+ * - Adopted David Brownell's <david-b@pacbell.net> technique for
+ * assigning bulk endpoints.
+ * - Removed unnessesary #include's
+ * - Scanner model now reported via syslog INFO after being detected
+ * *and* configured.
+ * - Added user specified verdor:product USB ID's which can be passed
+ * as module parameters.
+ *
+ * 0.3.1
+ * FIXED:
+ * - Applied patches for linux-2.3.25.
+ * - Error number reporting changed to reflect negative return codes.
+ *
+ * 0.3.2
+ * FIXED:
+ * - Applied patches for linux-2.3.26 to scanner_init().
+ * - Debug read/write stats now report values as signed decimal.
+ *
+ *
+ * 0.3.3
+ * FIXED:
+ * - Updated the bulk_msg() calls to usb usb_bulk_msg().
+ * - Added a small delay in the write_scanner() method to aid in
+ * avoiding NULL data reads on HP scanners. We'll see how this works.
+ * - Return values from usb_bulk_msg() now ignore positive values for
+ * use with the ohci driver.
+ * - Added conditional debugging instead of commenting/uncommenting
+ * all over the place.
+ * - kfree()'d the pointer after using usb_string() as documented in
+ * linux-usb-api.txt.
+ * - Added usb_set_configuration(). It got lost in version 0.3 -- ack!
+ * - Added the HP 5200C USB Vendor/Product ID's
+ *
+ * TODO
+ * - Simultaneous multiple device attachment
+ * - ioctl()'s ?
+ *
+ * Thanks to:
+ * - All the folks on the linux-usb list who put up with me. :) This
+ * has been a great learning experience for me.
+ * - To Linus Torvalds for this great OS.
+ * - The GNU folks.
+ * - The folks that forwarded Vendor:Product ID's to me.
+ * - And anybody else who chimed in with reports and suggestions.
+ *
+ * Performance:
+ * System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner
+ * 300 dpi scan of the entire bed
+ * 24 Bit Color ~ 70 secs - 3.6 Mbit/sec
+ * 8 Bit Gray ~ 17 secs - 4.2 Mbit/sec
+ * */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include "usb.h"
+
+// #define SCN_DBG /* Enable to print results of read/write_scanner() calls */
+// #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
+// #define WR_DATA_DUMP
+
+#ifdef SCN_DBG
+#define SCN_DEBUG(X) X
+#else
+#define SCN_DEBUG(X)
+#endif
+
+#define IBUF_SIZE 32768
+#define OBUF_SIZE 4096
+
+struct hpscan_usb_data {
+ struct usb_device *hpscan_dev;
+ int isopen; /* Not zero if the device is open */
+ int present; /* Device is present on the bus */
+ char *obuf, *ibuf; /* transfer buffers */
+ char iep, oep; /* I/O Endpoints */
+};
+
+static struct hpscan_usb_data hpscan;
+
+MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson");
+MODULE_DESCRIPTION("USB Scanner Driver");
+
+static __u16 vendor=0, product=0;
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+static int
+open_scanner(struct inode * inode, struct file * file)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ if (!hps->present) {
+ return -ENODEV;
+ }
+
+ if (!hps->hpscan_dev) {
+ return -ENODEV;
+ }
+
+ if (hps->isopen) {
+ return -EBUSY;
+ }
+
+ hps->isopen = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+close_scanner(struct inode * inode, struct file * file)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ hps->isopen = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static ssize_t
+write_scanner(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned long partial;
+
+ ssize_t ret = 0;
+
+ int result = 0;
+
+ char *obuf = hps->obuf;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (count > 0) {
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ copy_size = (count > OBUF_SIZE) ? OBUF_SIZE : count;
+
+ if (copy_from_user(hps->obuf, buffer, copy_size)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ result = usb_bulk_msg(hps->hpscan_dev,usb_sndbulkpipe(hps->hpscan_dev, hps->oep), obuf, copy_size, &partial, 30*HZ);
+ SCN_DEBUG(printk(KERN_DEBUG "write stats: result:%d copy_size:%lu partial:%lu\n", (int)result, copy_size, partial);)
+
+ if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
+ printk(KERN_WARNING "write_scanner: NAK recieved.\n");
+ ret = -ETIME;
+ break;
+ } else if (result < 0) { /* We should not get any I/O errors */
+ printk(KERN_WARNING "write_scanner: funky result: %d. Please notify the maintainer.\n", result);
+ ret = -EIO;
+ break;
+ }
+
+#ifdef WR_DATA_DUMP
+ if (partial) {
+ unsigned char cnt, cnt_max;
+ cnt_max = (partial > 24) ? 24 : partial;
+ printk(KERN_DEBUG "dump: ");
+ for (cnt=0; cnt < cnt_max; cnt++) {
+ printk("%X ", obuf[cnt]);
+ }
+ printk("\n");
+ }
+#endif
+ if (partial != copy_size) { /* Unable to write complete amount */
+ ret = -EIO;
+ break;
+ }
+
+ if (partial) { /* Data written */
+ obuf += partial;
+ count -= partial;
+ bytes_written += partial;
+ } else { /* No data written */
+ ret = 0;
+ bytes_written = 0;
+ break;
+ }
+ }
+// mdelay(5);
+ set_current_state(TASK_RUNNING);
+ return ret ? ret : bytes_written;
+}
+
+static ssize_t
+read_scanner(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ ssize_t read_count, ret = 0;
+
+ unsigned long partial;
+
+ int this_read;
+ int result;
+
+ char *ibuf = hps->ibuf;
+
+ read_count = 0;
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (count) {
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ this_read = (count > IBUF_SIZE) ? IBUF_SIZE : count;
+
+ result = usb_bulk_msg(hps->hpscan_dev, usb_rcvbulkpipe(hps->hpscan_dev, hps->iep), ibuf, this_read, &partial, 60*HZ);
+ SCN_DEBUG(printk(KERN_DEBUG "read stats: result:%d this_read:%u partial:%lu\n", (int)result, this_read, partial);)
+
+ if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
+ printk(KERN_WARNING "read_scanner: NAK received\n");
+ ret = -ETIME;
+ break;
+ } else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) {
+ printk(KERN_WARNING "read_scanner: funky result: %d. Please notify the maintainer.\n", (int)result);
+ ret = -EIO;
+ break;
+ }
+
+#ifdef RD_DATA_DUMP
+ if (partial) {
+ unsigned char cnt, cnt_max;
+ cnt_max = (partial > 24) ? 24 : partial;
+ printk(KERN_DEBUG "dump: ");
+ for (cnt=0; cnt < cnt_max; cnt++) {
+ printk("%X ", ibuf[cnt]);
+ }
+ printk("\n");
+ }
+#endif
+
+ if (partial) { /* Data returned */
+ count = this_read = partial;
+ } else {
+ ret = 0;
+ read_count = 0;
+ break;
+ }
+
+ if (this_read) {
+ if (copy_to_user(buffer, ibuf, this_read)) {
+ ret = -EFAULT;
+ break;
+ }
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ set_current_state(TASK_RUNNING);
+ return ret ? ret : read_count;
+}
+
+static void *
+probe_scanner(struct usb_device *dev, unsigned int ifnum)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+ struct usb_endpoint_descriptor *endpoint;
+
+ char *ident;
+
+ hps->present = 0;
+
+ if (vendor != 0 || product != 0) {
+ printk(KERN_INFO "USB Scanner Vendor:Product - %x:%x\n", vendor, product);
+ }
+
+/* There doesn't seem to be an imaging class defined in the USB
+ * Spec. (yet). If there is, HP isn't following it and it doesn't
+ * look like anybody else is either. Therefore, we have to test the
+ * Vendor and Product ID's to see what we have. This makes this
+ * driver a high maintenance driver since it has to be updated with
+ * each release of a product. Also, other scanners may be able to use
+ * this driver but again, their Vendor and Product ID's must be added.
+ *
+ * NOTE: Just because a product is supported here does not mean that
+ * applications exist that support the product. It's in the hopes
+ * that this will allow developers a means to produce applications
+ * that will support USB products.
+ *
+ * Until we detect a device which is pleasing, we silently punt.
+ * */
+
+ if (dev->descriptor.idVendor != 0x03f0 && /* Hewlett Packard */
+ dev->descriptor.idVendor != 0x06bd && /* AGFA */
+ dev->descriptor.idVendor != 0x1606 && /* UMAX */
+ dev->descriptor.idVendor != vendor ) { /* User specified */
+ return NULL;
+ }
+
+ if (dev->descriptor.idProduct != 0x0101 && /* HP 4100C */
+ dev->descriptor.idProduct != 0x0102 && /* HP 4200C & PhotoSmart S20? */
+ dev->descriptor.idProduct != 0x0202 && /* HP 5100C */
+ dev->descriptor.idProduct != 0x0401 && /* HP 5200C */
+ dev->descriptor.idProduct != 0x0201 && /* HP 6200C */
+ dev->descriptor.idProduct != 0x0601 && /* HP 6300C */
+ dev->descriptor.idProduct != 0x0001 && /* AGFA SnapScan 1212U */
+ dev->descriptor.idProduct != 0x0030 && /* Umax 2000U */
+ dev->descriptor.idProduct != product) { /* User specified */
+ return NULL;
+ }
+
+/* After this point we can be a little noisy about what we are trying to
+ * configure. */
+
+ if (dev->descriptor.bNumConfigurations != 1 ||
+ dev->config[0].bNumInterfaces != 1) {
+ printk(KERN_INFO "probe_scanner: only simple configurations supported\n");
+ return NULL;
+ }
+
+ endpoint = dev->config[0].interface[0].altsetting[0].endpoint;
+
+ if (endpoint[0].bmAttributes != USB_ENDPOINT_XFER_BULK
+ || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK) {
+ printk(KERN_INFO "probe_scanner: invalid bulk endpoints\n");
+ return NULL;
+ }
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk (KERN_INFO "probe_scanner: failed usb_set_configuration\n");
+ hps->hpscan_dev = NULL;
+ return NULL;
+ }
+
+/* By the time we get here, we should be dealing with a fairly simple
+ * device that supports at least two bulk endpoints on endpoints 1 and
+ * 2.
+ *
+ * We determine the bulk endpoints so that the read_*() and write_*()
+ * procedures can recv/send data to the correct endpoint.
+ * */
+
+ hps->iep = hps->oep = 0;
+
+ if ((endpoint[0].bEndpointAddress & 0x80) == 0x80) {
+ hps->iep = endpoint[0].bEndpointAddress & 0x7f;
+ } else {
+ hps->oep = endpoint[0].bEndpointAddress;
+ }
+
+ if ((endpoint[1].bEndpointAddress & 0x80) == 0x80) {
+ hps->iep = endpoint[1].bEndpointAddress & 0x7f;
+ } else {
+ hps->oep = endpoint[1].bEndpointAddress;
+ }
+
+ ident = usb_string(dev, dev->descriptor.iProduct); /* usb_string allocates memory using kmalloc() so kfree() needs to be called afterwards when the pointer is no longer needed. */
+ printk(KERN_INFO "USB Scanner (%s) found at address %d\n", ident, dev->devnum);
+ kfree(ident);
+
+ SCN_DEBUG(printk(KERN_DEBUG "probe_scanner: using bulk endpoints - In: %x Out: %x\n", hps->iep, hps->oep);)
+
+ hps->present = 1;
+ hps->hpscan_dev = dev;
+
+ if (!(hps->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+ return NULL;
+ }
+
+ if (!(hps->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+ return NULL;
+ }
+
+ return hps;
+}
+
+static void
+disconnect_scanner(struct usb_device *dev, void *ptr)
+{
+ struct hpscan_usb_data *hps = (struct hpscan_usb_data *) ptr;
+
+ if (hps->isopen) {
+ /* better let it finish - the release will do whats needed */
+ hps->hpscan_dev = NULL;
+ return;
+ }
+ kfree(hps->ibuf);
+ kfree(hps->obuf);
+
+ hps->present = 0;
+}
+
+static struct
+file_operations usb_scanner_fops = {
+ NULL, /* seek */
+ read_scanner,
+ write_scanner,
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_scanner,
+ NULL, /* flush */
+ close_scanner,
+ NULL,
+ NULL, /* fasync */
+};
+
+static struct
+usb_driver scanner_driver = {
+ "usbscanner",
+ probe_scanner,
+ disconnect_scanner,
+ { NULL, NULL },
+ &usb_scanner_fops,
+ 48
+};
+
+int
+usb_scanner_init(void)
+{
+ if (usb_register(&scanner_driver) < 0)
+ return -1;
+
+ printk(KERN_INFO "USB Scanner support registered.\n");
+ return 0;
+}
+
+
+void
+usb_scanner_cleanup(void)
+{
+ struct hpscan_usb_data *hps = &hpscan;
+
+ hps->present = 0;
+ usb_deregister(&scanner_driver);
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ return usb_scanner_init();
+}
+
+void
+cleanup_module(void)
+{
+ usb_scanner_cleanup();
+}
+#endif
+
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: uhci.c,v 1.139 1999/12/17 17:50:59 fliegl Exp $
+ * $Id: uhci.c,v 1.149 1999/12/26 20:57:14 acher Exp $
*/
/* Build the TDs for the bulk request */
len = purb->transfer_buffer_length;
data = purb->transfer_buffer;
- dbg (KERN_DEBUG MODSTR "uhci_submit_bulk_urb: len %d\n", len);
+ dbg (KERN_DEBUG MODSTR "uhci_submit_bulk_urb: pipe %x, len %d\n", pipe, len);
while (len > 0) {
int pktsze = len;
//dbg("insert td %p, len %i\n",td,pktsze);
insert_td (s, qh, td, UHCI_PTR_DEPTH);
-
+
/* Alternate Data0/1 (start with Data0) */
usb_dotoggle (purb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
}
unsigned long flags=0;
struct list_head *p;
- if (!purb) // you never know...
-
+ if (!purb) // you never know...
return -1;
s = (puhci_t) purb->dev->bus->hcpriv; // get pointer to uhci struct
// URB probably still in work
purb_priv = purb->hcpriv;
dequeue_urb (s, &purb->urb_list,1);
+ purb->status = USB_ST_URB_KILLED; // mark urb as killed
+
if(!in_interrupt()) {
spin_unlock_irqrestore (&s->unlink_urb_lock, flags); // allow interrupts from here
}
delete_qh (s, qh); // remove it physically
}
- purb->status = USB_ST_URB_KILLED; // mark urb as killed
-
+
#ifdef _UHCI_SLAB
kmem_cache_free(urb_priv_kmem, purb->hcpriv);
#else
now, purb->start_frame, purb->number_of_packets, purb->pipe);
{
puhci_t s = (puhci_t) purb->dev->bus->hcpriv;
- struct list_head *p = s->urb_list.next;
+ struct list_head *p;
purb_t u;
int a = -1, b = -1;
unsigned long flags;
+
spin_lock_irqsave (&s->urb_list_lock, flags);
+ p=s->urb_list.next;
for (; p != &s->urb_list; p = p->next) {
u = list_entry (p, urb_t, urb_list);
// we can accept this urb if it is not queued at this time
// or if non-iso transfer requests should be scheduled for the same device and pipe
if ((usb_pipetype (purb->pipe) != PIPE_ISOCHRONOUS &&
- tmp->dev == purb->dev && tmp->pipe == purb->pipe) || (purb == tmp))
+ tmp->dev == purb->dev && tmp->pipe == purb->pipe) || (purb == tmp)) {
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
return 1; // found another urb already queued for processing
-
+ }
}
spin_unlock_irqrestore (&s->urb_list_lock, flags);
if (search_dev_ep (s, purb)) {
usb_dec_dev_use (purb->dev);
- return -ENXIO; // no such address
+ return -ENXIO; // urb already queued
}
for (i = 0; i < 8; i++)
uhci->rh.c_p_r[i] = 0;
- dbg (KERN_DEBUG MODSTR "Root-Hub: adr: %2x cmd(%1x): %04 %04 %04 %04\n",
+ dbg(KERN_DEBUG MODSTR "Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x\n",
uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
switch (bmRType_bReq) {
}
- dbg (KERN_DEBUG MODSTR "Root-Hub stat port1: %x port2: %x \n",
+ printk (KERN_DEBUG MODSTR "Root-Hub stat port1: %x port2: %x \n",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
purb->actual_length = len;
uhci_show_td (desc); // show first TD of each transfer
#endif
- // go into error condition to keep data_toggle unchanged if short packet occurs
- if ((purb->transfer_flags & USB_DISABLE_SPD) && (actual_length < maxlength)) {
- ret = USB_ST_SHORT_PACKET;
- printk (KERN_DEBUG MODSTR "process_transfer: SPD!!\n");
- break; // exit after this TD because SP was detected
+ // got less data than requested
+ if ( (actual_length < maxlength)) {
+ if (purb->transfer_flags & USB_DISABLE_SPD) {
+ ret = USB_ST_SHORT_PACKET; // treat as real error
+ printk (KERN_DEBUG MODSTR "process_transfer: SPD!!\n");
+ break; // exit after this TD because SP was detected
+ }
+ // short read during control-IN: re-start status stage
+ if ((usb_pipetype (purb->pipe) == PIPE_CONTROL)) {
+ if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) {
+ uhci_show_td (last_desc);
+ qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
+ printk("uhci: short packet during control transfer, retrigger status stage @ %p\n",last_desc);
+ purb_priv->short_control_packet=1;
+ return 0;
+ }
+ }
+ // all other cases: short read is OK
+ data_toggle = uhci_toggle (desc->hw.td.info);
+ break;
}
data_toggle = uhci_toggle (desc->hw.td.info);
//printk(KERN_DEBUG MODSTR"process_transfer: len:%d status:%x mapped:%x toggle:%d\n", actual_length, desc->hw.td.status,status, data_toggle);
-#if 1
- if ((usb_pipetype (purb->pipe) == PIPE_CONTROL) && (actual_length < maxlength)) {
- if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) {
- uhci_show_td (last_desc);
- qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage
- printk("uhci: short packet during control transfer, retrigger status stage\n");
- purb_priv->short_control_packet=1;
- return 0;
- }
- }
-#endif
+
}
usb_settoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe), !data_toggle);
transfer_finished:
status & TD_CTRL_NAK )
{
ret=0;
- purb->status=0;
+ status=0;
}
unlink_qh (s, qh);
delete_qh (s, qh);
purb->status = status;
-
-
+
dbg(KERN_DEBUG MODSTR"process_transfer: urb %p, wanted len %d, len %d status %x err %d\n",
purb,purb->transfer_buffer_length,purb->actual_length, purb->status, purb->error_count);
//dbg(KERN_DEBUG MODSTR"process_transfer: exit\n");
{
desc = list_entry (p, uhci_desc_t, desc_list);
- if (desc->hw.td.status & TD_CTRL_NAK) {
- // NAKed transfer
- //printk("TD NAK Status @%p %08x\n",desc,desc->hw.td.status);
- goto err;
- }
-
if (desc->hw.td.status & TD_CTRL_ACTIVE) {
// do not process active TDs
//printk("TD ACT Status @%p %08x\n",desc,desc->hw.td.status);
- goto err;
+ break;
}
if (!desc->hw.td.status & TD_CTRL_IOC) {
- // do not process one-shot TDs
- goto err;
+ // do not process one-shot TDs, no recycling
+ break;
}
// extract transfer parameters from TD
// if any error occured: ignore this td, and continue
if (status != USB_ST_NOERROR) {
purb->error_count++;
- goto err;
+ goto recycle;
}
else
purb->actual_length = actual_length;
purb->complete ((struct urb *) purb);
purb->status = USB_ST_URB_PENDING;
}
-
+ recycle:
// Recycle INT-TD if interval!=0, else mark TD as one-shot
if (purb->interval) {
desc->hw.td.status |= TD_CTRL_ACTIVE;
usb_dotoggle (purb->dev, usb_pipeendpoint (purb->pipe), usb_pipeout (purb->pipe));
}
else {
- desc->hw.td.status &= ~TD_CTRL_IOC;
+ desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD
}
-
- err:
+ err:
}
return ret;
_static int process_urb (puhci_t s, struct list_head *p)
{
int ret = USB_ST_NOERROR;
- purb_t purb = list_entry (p, urb_t, urb_list);
- spin_lock(&s->urb_list_lock);
+ purb_t purb;
+ spin_lock(&s->urb_list_lock);
+ purb=list_entry (p, urb_t, urb_list);
dbg ( /*KERN_DEBUG */ MODSTR "found queued urb: %p\n", purb);
+
switch (usb_pipetype (purb->pipe)) {
case PIPE_CONTROL:
case PIPE_BULK:
if (purb->complete && (!proceed || (purb->transfer_flags & USB_URB_EARLY_COMPLETE))) {
dbg (KERN_DEBUG MODSTR "process_transfer: calling early completion\n");
purb->complete ((struct urb *) purb);
- if (!proceed && is_ring)
+ if (!proceed && is_ring && (purb->status != USB_ST_URB_KILLED))
uhci_submit_urb (purb);
}
tmp = purb->next; // pointer to first urb
do {
- if ((tmp->status != USB_ST_URB_PENDING) && uhci_submit_urb (tmp) != USB_ST_NOERROR)
+ if ((tmp->status != USB_ST_URB_PENDING) && (tmp->status != USB_ST_URB_KILLED) && uhci_submit_urb (tmp) != USB_ST_NOERROR)
break;
tmp = tmp->next;
}
start_hc (s);
if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) {
+ printk(MODSTR KERN_ERR"request_irq %d failed!\n",irq);
usb_free_bus (bus);
reset_hc (s);
release_region (s->io_addr, s->io_size);
/* Is it already in use? */
if (check_region (io_addr, io_size))
break;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8)
- pci_enable_device (dev);
-#endif
/* disable legacy emulation */
pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
if (type != 0)
continue;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,8)
+ pci_enable_device (dev);
+#endif
+ if(!dev->irq)
+ {
+ printk(KERN_ERR MODSTR"Found UHCI device with no IRQ assigned. Check BIOS settings!\n");
+ continue;
+ }
+
/* Ok set it up */
retval = start_uhci (dev);
+
if (!retval)
i++;
+
}
#ifdef CONFIG_APM
# ifdef CONFIG_USB_MOUSE
usb_mouse_init();
# endif
-# ifdef CONFIG_USB_HP_SCANNER
- usb_hp_scanner_init();
+# ifdef CONFIG_USB_SCANNER
+ usb_scanner_init();
# endif
# ifdef CONFIG_USB_KBD
usb_kbd_init();
# ifdef CONFIG_USB_MOUSE
usb_mouse_cleanup();
# endif
-# ifdef CONFIG_USB_HP_SCANNER
- usb_hp_scanner_cleanup();
+# ifdef CONFIG_USB_SCANNER
+ usb_scanner_cleanup();
# endif
# ifdef CONFIG_USB_DABUSB
dabusb_cleanup();
* It should be considered a slave, with no callbacks. Callbacks
* are evil.
*
- * $Id: usb.c,v 1.37 1999/12/17 10:48:08 fliegl Exp $
+ * $Id: usb.c,v 1.39 1999/12/27 15:17:47 acher Exp $
*/
#ifndef EXPORT_SYMTAB
#define MODSTR "usbcore: "
#ifdef USB_DEBUG
- #define dbg printk
+ #define dbg(format, arg...) printk(format, ## arg)
#else
- #define dbg nix
-static void nix(const char *format, ...)
-{
-}
+ #define dbg(format, arg...)
#endif
/*
#define USB_MAJOR 180
+extern int usb_hub_init(void);
+extern int usb_kbd_init(void);
+extern int usb_cpia_init(void);
+extern int usb_dc2xx_init(void);
+extern int usb_mouse_init(void);
+extern int usb_printer_init(void);
+
+extern void usb_hub_cleanup(void);
+extern void usb_mouse_cleanup(void);
+
/* for 2.2-kernels */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
static __inline__ void wait_ms(unsigned int ms)
{
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(1 + ms * HZ / 1000);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1 + ms * HZ / 1000);
}
typedef struct {
int num_altsetting; /* number of alternate settings */
int max_altsetting; /* total memory allocated */
- struct usb_driver *driver; /* driver */
+ struct usb_driver *driver; /* driver */
void *private_data;
};
#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \
do {\
- a->dev=aa;\
- a->pipe=b;\
- a->setup_packet=c;\
- a->transfer_buffer=d;\
- a->transfer_buffer_length=e;\
- a->complete=f;\
- a->context=g;\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->setup_packet=c;\
+ (a)->transfer_buffer=d;\
+ (a)->transfer_buffer_length=e;\
+ (a)->complete=f;\
+ (a)->context=g;\
} while (0)
#define FILL_BULK_URB(a,aa,b,c,d,e,f) \
do {\
- a->dev=aa;\
- a->pipe=b;\
- a->transfer_buffer=c;\
- a->transfer_buffer_length=d;\
- a->complete=e;\
- a->context=f;\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->transfer_buffer=c;\
+ (a)->transfer_buffer_length=d;\
+ (a)->complete=e;\
+ (a)->context=f;\
} while (0)
#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \
do {\
- a->dev=aa;\
- a->pipe=b;\
- a->transfer_buffer=c;\
- a->transfer_buffer_length=d;\
- a->complete=e;\
- a->context=f;\
- a->interval=g;\
- a->start_frame=-1;\
+ (a)->dev=aa;\
+ (a)->pipe=b;\
+ (a)->transfer_buffer=c;\
+ (a)->transfer_buffer_length=d;\
+ (a)->complete=e;\
+ (a)->context=f;\
+ (a)->interval=g;\
+ (a)->start_frame=-1;\
} while (0)
purb_t usb_alloc_urb(int iso_packets);
int bandwidth_isoc_reqs; /* number of Isoc. requesters */
/* procfs entry */
- int proc_busnum;
struct proc_dir_entry *proc_entry;
};
tristate 'EFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS
fi
+tristate 'Compressed ROM filessytem support' CONFIG_CRAMFS
tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS
if [ "$CONFIG_ISO9660_FS" != "n" ]; then
bool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET
#
# Note 2! The CFLAGS definitions are now in the main makefile.
-L_TARGET := filesystems.a
-L_OBJS = $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+FILESYSTEMS = $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
O_TARGET := fs.o
O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
- dcache.o inode.o attr.o bad_inode.o file.o iobuf.o $(BINFMTS)
+ dcache.o inode.o attr.o bad_inode.o file.o iobuf.o \
+ $(BINFMTS) $(FILESYSTEMS)
MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \
- nfsd nls devpts adfs partitions qnx4 udf bfs openpromfs
+ nfsd nls devpts adfs partitions qnx4 udf bfs cramfs openpromfs
SUB_DIRS := partitions
O_OBJS += noquot.o
endif
+ifeq ($(CONFIG_EXT2_FS),y)
+SUB_DIRS += ext2
+else
+ ifeq ($(CONFIG_EXT2_FS),m)
+ MOD_SUB_DIRS += ext2
+ endif
+endif
+
+ifeq ($(CONFIG_CRAMFS),y)
+SUB_DIRS += cramfs
+else
+ ifeq ($(CONFIG_CRAMFS),m)
+ MOD_SUB_DIRS += cramfs
+ endif
+endif
+
ifeq ($(CONFIG_CODA_FS),y)
SUB_DIRS += coda
else
endif
endif
-ifeq ($(CONFIG_EXT2_FS),y)
-SUB_DIRS += ext2
-else
- ifeq ($(CONFIG_EXT2_FS),m)
- MOD_SUB_DIRS += ext2
- endif
-endif
-
ifeq ($(CONFIG_FAT_FS),y)
SUB_DIRS += fat
else
--- /dev/null
+#
+# Makefile for the linux cramfs routines.
+#
+
+O_TARGET := cramfs.o
+
+O_OBJS := inode.o uncompress.o inflate/zlib.o
+
+include $(TOPDIR)/Rules.make
+
+inflate/zlib.o:
+ make -C inflate
--- /dev/null
+#ifndef __CRAMFS_H
+#define __CRAMFS_H
+
+#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */
+#define CRAMFS_SIGNATURE "Compressed ROMFS"
+
+/*
+ * Reasonably terse representation of the inode
+ * data.. When the mode of the inode indicates
+ * a special device node, the "offset" bits will
+ * encode i_rdev. In other cases, "offset" points
+ * to the ROM image for the actual file data
+ * (whether that data be directory or compressed
+ * file data depends on the inode type again)
+ */
+struct cramfs_inode {
+ u32 mode:16, uid:16;
+ u32 size:24, gid:8;
+ u32 namelen:6, offset:26;
+};
+
+/*
+ * Superblock information at the beginning of the FS.
+ */
+struct cramfs_super {
+ u32 magic; /* 0x28cd3d45 - random number */
+ u32 size; /* > offset, < 2**26 */
+ u32 flags; /* 0 */
+ u32 future; /* 0 */
+ u8 signature[16]; /* "Compressed ROMFS" */
+ u8 fsid[16]; /* random number */
+ u8 name[16]; /* user-defined name */
+ struct cramfs_inode root; /* Root inode data */
+};
+
+/* Uncompression interfaces to the underlying zlib */
+int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen);
+int cramfs_uncompress_init(void);
+int cramfs_uncompress_exit(void);
+
+#endif
--- /dev/null
+#
+# inflate/Makefile
+#
+#
+# NOTE NOTE NOTE!
+#
+# This code is basically a bastardized version of the zlib
+# library uncompression. It avoids dynamic allocations at
+# all cost, and is cut down in other ways too, to make it
+# simpler and more specialized. If you want to get the real
+# thing, don't look here.
+#
+# The simplifications mean that this version of the library
+# (unlike the real lib) is completely single-threaded, and
+# you cannot do multiple uncompressions at a time. You can
+# ONLY use it to uncompress a single block, with both the
+# source and the destination completely in memory. In SMP
+# environments, the uncompression has to be protected by
+# some lock to guarantee single-threaded access to the static
+# data structures used for inflation.
+#
+# You have been warned.
+#
+# (The upsides of the simplification is that you can't get in
+# any nasty situations wrt memory management, and that the
+# uncompression can be done without blocking on allocation).
+#
+
+OBJECTFILES=adler32.o infblock.o infcodes.o inffast.o inflate.o inftrees.o infutil.o uncompr.o
+
+zlib.o: $(OBJECTFILES)
+ ld -r -o zlib.o $(OBJECTFILES)
+
+include $(TOPDIR)/Rules.make
--- /dev/null
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zlib.h"
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
--- /dev/null
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local const uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+ if (c != Z_NULL)
+ *c = s->check;
+ if (s->mode == CODES)
+ inflate_codes_free(s->sub.decode.codes, z);
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0);
+ Tracev((stderr, "inflate: blocks reset\n"));
+}
+
+
+inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_streamp z;
+check_func c;
+uInt w;
+{
+ inflate_blocks_statef *s;
+ static struct inflate_blocks_state working_blocks_state;
+ static inflate_huft working_hufts[MANY];
+ static unsigned char working_window[1 << MAX_WBITS];
+
+ s = &working_blocks_state;
+ s->hufts = working_hufts;
+ s->window = working_window;
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Tracev((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, Z_NULL);
+ return s;
+}
+
+
+int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Tracev((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, &tl, &td, z);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BAD;
+ z->msg = (char*)"invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
+ {
+ s->mode = BAD;
+ z->msg = (char*)"invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ memcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BAD;
+ z->msg = (char*)"too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ {
+ static unsigned int working_blens [258 + 0x1f + 0x1f];
+ s->sub.trees.blens = working_blens;
+ }
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, s->hufts, z);
+ if (t != Z_OK)
+ {
+ r = t;
+ if (r == Z_DATA_ERROR)
+ s->mode = BAD;
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->bits;
+ c = h->base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ s->mode = BAD;
+ z->msg = (char*)"invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td,
+ s->hufts, z);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ s->mode = BAD;
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok\n"));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.codes = c;
+ }
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONE;
+ case DONE:
+ r = Z_STREAM_END;
+ LEAVE
+ case BAD:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+int inflate_blocks_free(s, z)
+inflate_blocks_statef *s;
+z_streamp z;
+{
+ inflate_blocks_reset(s, z, Z_NULL);
+ Tracev((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+
+void inflate_set_dictionary(s, d, n)
+inflate_blocks_statef *s;
+const Bytef *d;
+uInt n;
+{
+ memcpy(s->window, d, n);
+ s->read = s->write = s->window + n;
+}
+
+
+/* Returns true if inflate is currently at the end of a block generated
+ * by Z_SYNC_FLUSH or Z_FULL_FLUSH.
+ * IN assertion: s != Z_NULL
+ */
+int inflate_blocks_sync_point(s)
+inflate_blocks_statef *s;
+{
+ return s->mode == LENS;
+}
--- /dev/null
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+extern inflate_blocks_statef * inflate_blocks_new OF((
+ z_streamp z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+extern int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int)); /* initial return code */
+
+extern void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+extern int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_streamp));
+
+extern void inflate_set_dictionary OF((
+ inflate_blocks_statef *s,
+ const Bytef *d, /* dictionary */
+ uInt n)); /* dictionary length */
+
+extern int inflate_blocks_sync_point OF((
+ inflate_blocks_statef *s));
--- /dev/null
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#include "inffast.h"
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+inflate_codes_mode;
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ inflate_codes_mode mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+z_streamp z;
+{
+ inflate_codes_statef *c;
+ static inflate_codes_statef memory_allocation;
+
+ c = &memory_allocation;
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t + t->base;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t + t->base;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+ f = (uInt)(q - s->window) < c->sub.copy.dist ?
+ s->end - (c->sub.copy.dist - (q - s->window)) :
+ q - c->sub.copy.dist;
+#else
+ f = q - c->sub.copy.dist;
+ if ((uInt)(q - s->window) < c->sub.copy.dist)
+ f = s->end - (c->sub.copy.dist - (uInt)(q - s->window));
+#endif
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+#ifdef NEED_DUMMY_RETURN
+ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
+#endif
+}
+
+
+void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_streamp z;
+{
+ Tracev((stderr, "inflate: codes free\n"));
+}
--- /dev/null
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+extern inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_streamp ));
+
+extern int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+extern void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_streamp ));
+
--- /dev/null
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#include "inffast.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;}
+
+/* Called with number of bytes left to write in window at least 258
+ (the maximum string length) and number of input bytes available
+ at least ten. The ten bytes are six bytes for the longest length/
+ distance pair plus four bytes for overloading the bit buffer. */
+
+int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+inflate_blocks_statef *s;
+z_streamp z;
+{
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ uInt ml; /* mask for literal/length tree */
+ uInt md; /* mask for distance tree */
+ uInt c; /* bytes to copy */
+ uInt d; /* distance back to copy from */
+ Bytef *r; /* copy source pointer */
+
+ /* load input, output, bit values */
+ LOAD
+
+ /* initialize masks */
+ ml = inflate_mask[bl];
+ md = inflate_mask[bd];
+
+ /* do until not enough input or output space for fast loop */
+ do { /* assume called with m >= 258 && n >= 10 */
+ /* get literal/length code */
+ GRABBITS(20) /* max bits for literal/length code */
+ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ continue;
+ }
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits for length */
+ e &= 15;
+ c = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * length %u\n", c));
+
+ /* decode distance base of block to copy */
+ GRABBITS(15); /* max bits for distance code */
+ e = (t = td + ((uInt)b & md))->exop;
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits to add to distance base */
+ e &= 15;
+ GRABBITS(e) /* get extra bits (up to 13) */
+ d = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * distance %u\n", d));
+
+ /* do the copy */
+ m -= c;
+ if ((uInt)(q - s->window) >= d) /* offset before dest */
+ { /* just copy */
+ r = q - d;
+ *q++ = *r++; c--; /* minimum count is three, */
+ *q++ = *r++; c--; /* so unroll loop a little */
+ }
+ else /* else offset after destination */
+ {
+ e = d - (uInt)(q - s->window); /* bytes from offset to end */
+ r = s->end - e; /* pointer to offset */
+ if (c > e) /* if source crosses, */
+ {
+ c -= e; /* copy to end of window */
+ do {
+ *q++ = *r++;
+ } while (--e);
+ r = s->window; /* copy rest from start of window */
+ }
+ }
+ do { /* copy all or what's left */
+ *q++ = *r++;
+ } while (--c);
+ break;
+ }
+ else if ((e & 64) == 0)
+ {
+ t += t->base;
+ e = (t += ((uInt)b & inflate_mask[e]))->exop;
+ }
+ else
+ {
+ z->msg = (char*)"invalid distance code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ break;
+ }
+ if ((e & 64) == 0)
+ {
+ t += t->base;
+ if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ break;
+ }
+ }
+ else if (e & 32)
+ {
+ Tracevv((stderr, "inflate: * end of block\n"));
+ UNGRAB
+ UPDATE
+ return Z_STREAM_END;
+ }
+ else
+ {
+ z->msg = (char*)"invalid literal/length code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ } while (m >= 258 && n >= 10);
+
+ /* not enough input or output--restore pointers and return */
+ UNGRAB
+ UPDATE
+ return Z_OK;
+}
--- /dev/null
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+extern int inflate_fast OF((
+ uInt,
+ uInt,
+ inflate_huft *,
+ inflate_huft *,
+ inflate_blocks_statef *,
+ z_streamp ));
--- /dev/null
+/* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by the maketree.c program
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+local uInt fixed_bl = 9;
+local uInt fixed_bd = 5;
+local inflate_huft fixed_tl[] = {
+ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
+ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
+ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
+ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
+ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
+ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
+ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
+ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
+ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
+ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
+ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
+ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
+ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
+ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
+ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
+ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
+ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
+ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
+ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
+ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
+ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
+ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
+ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
+ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
+ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
+ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
+ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
+ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
+ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
+ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
+ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
+ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
+ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
+ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
+ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
+ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
+ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
+ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
+ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
+ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
+ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
+ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
+ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
+ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
+ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
+ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
+ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
+ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
+ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
+ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
+ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
+ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
+ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
+ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
+ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
+ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
+ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
+ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
+ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
+ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
+ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
+ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
+ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
+ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
+ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
+ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
+ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
+ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
+ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
+ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
+ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
+ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
+ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
+ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
+ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
+ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
+ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
+ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
+ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
+ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
+ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
+ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
+ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
+ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
+ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
+ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
+ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
+ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
+ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
+ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
+ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
+ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
+ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
+ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
+ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
+ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
+ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
+ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
+ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
+ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
+ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
+ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
+ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
+ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
+ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
+ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
+ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
+ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
+ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
+ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
+ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
+ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
+ };
+local inflate_huft fixed_td[] = {
+ {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
+ {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
+ {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
+ {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
+ {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
+ {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
+ {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
+ {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
+ };
--- /dev/null
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+
+struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
+
+typedef enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ DICT4, /* four dictionary check bytes to go */
+ DICT3, /* three dictionary check bytes to go */
+ DICT2, /* two dictionary check bytes to go */
+ DICT1, /* one dictionary check byte to go */
+ DICT0, /* waiting for inflateSetDictionary */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+inflate_mode;
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ inflate_mode mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+int ZEXPORT inflateReset(z)
+z_streamp z;
+{
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, Z_NULL);
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateEnd(z)
+z_streamp z;
+{
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z);
+ z->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateInit2_(z, w, version, stream_size)
+z_streamp z;
+int w;
+const char *version;
+int stream_size;
+{
+ static struct internal_state internal_state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != sizeof(z_stream))
+ return Z_VERSION_ERROR;
+
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->msg = Z_NULL;
+ z->state = &internal_state;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Tracev((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+int ZEXPORT inflateInit_(z, version, stream_size)
+z_streamp z;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(z, DEF_WBITS, version, stream_size);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)return r;r=f;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int ZEXPORT inflate(z, f)
+z_streamp z;
+int f;
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL)
+ return Z_STREAM_ERROR;
+ f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ b = NEXTBYTE;
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ if (!(b & PRESET_DICT))
+ {
+ z->state->mode = BLOCKS;
+ break;
+ }
+ z->state->mode = DICT4;
+ case DICT4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = DICT3;
+ case DICT3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = DICT2;
+ case DICT2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = DICT1;
+ case DICT1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+ z->adler = z->state->sub.check.need;
+ z->state->mode = DICT0;
+ return Z_NEED_DICT;
+ case DICT0:
+ z->state->mode = BAD;
+ z->msg = (char*)"need dictionary";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_STREAM_ERROR;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r == Z_OK)
+ r = f;
+ if (r != Z_STREAM_END)
+ return r;
+ r = f;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Tracev((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+#ifdef NEED_DUMMY_RETURN
+ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
+#endif
+}
+
+
+int ZEXPORT inflateSync(z)
+z_streamp z;
+{
+ uInt n; /* number of bytes to look at */
+ Bytef *p; /* pointer to bytes */
+ uInt m; /* number of marker bytes found in a row */
+ uLong r, w; /* temporaries to save total_in and total_out */
+
+ /* set up */
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->mode != BAD)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0;
+ }
+ if ((n = z->avail_in) == 0)
+ return Z_BUF_ERROR;
+ p = z->next_in;
+ m = z->state->sub.marker;
+
+ /* search */
+ while (n && m < 4)
+ {
+ static const Byte mark[4] = {0, 0, 0xff, 0xff};
+ if (*p == mark[m])
+ m++;
+ else if (*p)
+ m = 0;
+ else
+ m = 4 - m;
+ p++, n--;
+ }
+
+ /* restore */
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ z->avail_in = n;
+ z->state->sub.marker = m;
+
+ /* return no joy or set up to restart on a new block */
+ if (m != 4)
+ return Z_DATA_ERROR;
+ r = z->total_in; w = z->total_out;
+ inflateReset(z);
+ z->total_in = r; z->total_out = w;
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+
+/* Returns true if inflate is currently at the end of a block generated
+ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
+ * but removes the length bytes of the resulting empty stored block. When
+ * decompressing, PPP checks that at the end of input packet, inflate is
+ * waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(z)
+z_streamp z;
+{
+ if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL)
+ return Z_STREAM_ERROR;
+ return inflate_blocks_sync_point(z->state->blocks);
+}
--- /dev/null
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+const char inflate_copyright[] =
+ " inflate 1.1.3 Copyright 1995-1998 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ const uIntf *, /* list of base values for non-simple codes */
+ const uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ inflate_huft *, /* space for trees */
+ uInt *, /* hufts used in space */
+ uIntf * )); /* space for values */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+
+local int huft_build(b, n, s, d, e, t, m, hp, hn, v)
+uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
+uInt n; /* number of codes (assumed <= 288) */
+uInt s; /* number of simple-valued codes (0..s-1) */
+const uIntf *d; /* list of base values for non-simple codes */
+const uIntf *e; /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t; /* result: starting table */
+uIntf *m; /* maximum lookup bits, returns actual */
+inflate_huft *hp; /* space for trees */
+uInt *hn; /* hufts used in space */
+uIntf *v; /* working area: values in order of bit length */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
+ lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+ n = x[g]; /* set n to length of v */
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = g - w;
+ z = z > (uInt)l ? l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate new table */
+ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */
+ return Z_MEM_ERROR; /* not enough memory */
+ u[h] = q = hp + *hn;
+ *hn += z;
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ j = i >> (w - l);
+ r.base = (uInt)(q - u[h-1] - j); /* offset to this table */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ else
+ *t = q; /* first table is returned result */
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ mask = (1 << w) - 1; /* needed on HP, cc -O bug */
+ while ((i & mask) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ mask = (1 << w) - 1;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+int inflate_trees_bits(c, bb, tb, hp, z)
+uIntf *c; /* 19 code lengths */
+uIntf *bb; /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+inflate_huft *hp; /* space for trees */
+z_streamp z; /* for messages */
+{
+ int r;
+ uInt hn = 0; /* hufts used in space */
+ uIntf *v; /* work area for huft_build */
+ static unsigned int work_area[19];
+
+ v = work_area;
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL,
+ tb, bb, hp, &hn, v);
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR || *bb == 0)
+ {
+ z->msg = (char*)"incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+}
+
+int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z)
+uInt nl; /* number of literal/length codes */
+uInt nd; /* number of distance codes */
+uIntf *c; /* that many (total) code lengths */
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+inflate_huft *hp; /* space for trees */
+z_streamp z; /* for messages */
+{
+ int r;
+ uInt hn = 0; /* hufts used in space */
+ uIntf *v; /* work area for huft_build */
+ static unsigned int work_area[288];
+
+ /* allocate work area */
+ v = work_area;
+
+ /* build literal/length tree */
+ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
+ if (r != Z_OK || *bl == 0)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed literal/length tree";
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+ }
+
+ /* build distance tree */
+ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
+ if (r != Z_OK || (*bd == 0 && nl > 257))
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed distance tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ z->msg = (char*)"incomplete distance tree";
+ r = Z_DATA_ERROR;
+ }
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"empty distance tree with lengths";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+#endif
+ }
+
+ /* done */
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+#include "inffixed.h"
+
+
+int inflate_trees_fixed(bl, bd, tl, td, z)
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_streamp z; /* for memory allocation */
+{
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
--- /dev/null
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ uInt pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit int's) */
+ uInt base; /* literal, length base, distance base,
+ or table offset */
+};
+
+/* Maximum size of dynamic tree. The maximum found in a long but non-
+ exhaustive search was 1004 huft structures (850 for length/literals
+ and 154 for distances, the latter actually the result of an
+ exhaustive search). The actual maximum is not known, but the
+ value below is more than safe. */
+#define MANY 1440
+
+extern int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ inflate_huft *, /* space for trees */
+ z_streamp)); /* for messages */
+
+extern int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ inflate_huft *, /* space for trees */
+ z_streamp)); /* for messages */
+
+extern int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ z_streamp)); /* for memory allocation */
--- /dev/null
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+
+/* And'ing with mask[n] masks the lower n bits */
+uInt inflate_mask[17] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt n;
+ Bytef *p;
+ Bytef *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ memcpy(p, q, n);
+ p += n;
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ memcpy(p, q, n);
+ p += n;
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
--- /dev/null
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFUTIL_H
+#define _INFUTIL_H
+
+typedef enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONE, /* finished last block, done */
+ BAD} /* got a data error--stuck here */
+inflate_block_mode;
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ inflate_block_mode mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ inflate_huft *hufts; /* single malloc for tree space */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
+extern uInt inflate_mask[17];
+
+/* copy as much as possible from the sliding window to the output area */
+extern int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+struct internal_state {int dummy;}; /* for buggy compilers */
+
+#endif
--- /dev/null
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-1998 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zlib.h"
+
+/* ===========================================================================
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
--- /dev/null
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-1998 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+#if defined(__GNUC__) || defined(__386__) || defined(i386)
+# ifndef __32BIT__
+# define __32BIT__
+# endif
+#endif
+
+#if defined(__STDC__) || defined(__cplusplus)
+# ifndef STDC
+# define STDC
+# endif
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# define MAX_MEM_LEVEL 9
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef FAR
+# define FAR
+#endif
+
+typedef unsigned char Byte; /* 8 bits */
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+typedef Byte FAR Bytef;
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+typedef void FAR *voidpf;
+typedef void *voidp;
+
+#include <sys/types.h> /* for off_t */
+#include <unistd.h> /* for SEEK_* and off_t */
+#define z_off_t off_t
+
+#endif /* _ZCONF_H */
--- /dev/null
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.1.3, July 9th, 1998
+
+ Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.1.3"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func nozalloc; /* used to allocate the internal state */
+ free_func nozfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ the compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out).
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero).
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may some
+ introduce some output latency (reading input without producing any output)
+ except when forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
+ output as possible to the output buffer. The flushing behavior of inflate is
+ not specified for values of the flush parameter other than Z_SYNC_FLUSH
+ and Z_FINISH, but the current implementation actually flushes as much output
+ as possible anyway.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ If a preset dictionary is needed at this point (see inflateSetDictionary
+ below), inflate sets strm-adler to the adler32 checksum of the
+ dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
+ it sets strm->adler to the adler32 checksum of all output produced
+ so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
+ an error code as described below. At the end of the stream, inflate()
+ checks that its computed adler32 checksum is equal to that saved by the
+ compressor and returns Z_STREAM_END only if the checksum is correct.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect
+ adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
+ (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if no progress is possible or if there was not
+ enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
+ case, the application may then call inflateSync to look for a good
+ compression block.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match). Filtered data consists mostly of small values with a
+ somewhat random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of Z_FILTERED is to force more
+ Huffman coding and less string matching; it is somewhat intermediate
+ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+ the compression ratio but not the correctness of the compressed output even
+ if it is not set appropriately.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front.
+
+ Upon return of this function, strm->adler is set to the Adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The Adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.)
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. If a compressed stream with a larger window size is given as
+ input, inflate() will return with the error code Z_DATA_ERROR instead of
+ trying to allocate a larger window.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+ memLevel). msg is set to null if there is no error message. inflateInit2
+ does not perform any decompression apart from reading the zlib header if
+ present: this will be done by inflate(). (So next_in and avail_in may be
+ modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate
+ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the Adler32 value returned by this call of
+ inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect Adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h". (See the description
+ of deflateInit2 for more information about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ const voidp buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int err));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
--- /dev/null
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-1998 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+# include <stddef.h>
+# include <stdlib.h>
+
+#ifdef __KERNEL__
+# include <linux/string.h>
+# include <linux/errno.h>
+#else
+# include <string.h>
+# include <errno.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+ /* Common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int z_verbose;
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf,
+ uInt len));
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) ({ \
+ printf("alloc %d %d at %s:%d\n", items, size, __FILE__, __LINE__); \
+ calloc((items), (size)); })
+#define ZFREE(strm, addr) free(addr)
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* _Z_UTIL_H */
--- /dev/null
+/*
+ * Compressed rom filesystem for Linux.
+ *
+ * Copyright (C) 1999 Linus Torvalds.
+ *
+ * This file is released under the GPL.
+ */
+
+/*
+ * These are the VFS interfaces to the compressed rom filesystem.
+ * The actual compression is based on zlib, see the other files.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#include <asm/uaccess.h>
+
+#include "cramfs.h"
+
+static struct super_operations cramfs_ops;
+static struct inode_operations cramfs_file_inode_operations;
+static struct inode_operations cramfs_dir_inode_operations;
+static struct inode_operations cramfs_symlink_inode_operations;
+
+#define CRAMINO(x) ((x)->offset?(x)->offset<<2:1)
+
+static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode)
+{
+ struct inode * inode = get_empty_inode();
+
+ if (inode) {
+ inode->i_mode = cramfs_inode->mode;
+ inode->i_uid = cramfs_inode->uid;
+ inode->i_size = cramfs_inode->size;
+ inode->i_gid = cramfs_inode->gid;
+ inode->i_ino = CRAMINO(cramfs_inode);
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ insert_inode_hash(inode);
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &cramfs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &cramfs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &cramfs_symlink_inode_operations;
+ else {
+ inode->i_size = 0;
+ init_special_inode(inode, inode->i_mode, cramfs_inode->size);
+ }
+ }
+ return inode;
+}
+
+/*
+ * We have our own block cache: don't fill up the buffer cache
+ * with the rom-image, because the way the filesystem is set
+ * up the accesses should be fairly regular and cached in the
+ * page cache and dentry tree anyway..
+ *
+ * This also acts as a way to guarantee contiguous areas of
+ * up to 2*PAGE_CACHE_SIZE, so that the caller doesn't need
+ * to worry about end-of-buffer issues even when decompressing
+ * a full page cache.
+ */
+#define READ_BUFFERS (2)
+static unsigned char read_buffers[READ_BUFFERS][PAGE_CACHE_SIZE*4];
+static int buffer_blocknr[READ_BUFFERS];
+static int last_buffer = 0;
+
+static void *cramfs_read(struct super_block *sb, unsigned int offset)
+{
+ struct buffer_head * bh_array[4];
+ int i, blocknr, buffer;
+
+ blocknr = offset >> PAGE_CACHE_SHIFT;
+ offset &= PAGE_CACHE_SIZE-1;
+ for (i = 0; i < READ_BUFFERS; i++) {
+ if (blocknr == buffer_blocknr[i])
+ return read_buffers[i] + offset;
+ }
+
+ /* Ok, read in four buffers completely first */
+ for (i = 0; i < 4; i++)
+ bh_array[i] = bread(sb->s_dev, blocknr + i, PAGE_CACHE_SIZE);
+
+ /* Ok, copy them to the staging area without sleeping.. */
+ buffer = last_buffer;
+ last_buffer = buffer ^ 1;
+ buffer_blocknr[buffer] = blocknr;
+ for (i = 0; i < 4; i++) {
+ struct buffer_head * bh = bh_array[i];
+ if (bh) {
+ memcpy(read_buffers[buffer] + i*PAGE_CACHE_SIZE, bh->b_data, PAGE_CACHE_SIZE);
+ bforget(bh);
+ }
+ blocknr++;
+ }
+ return read_buffers[buffer] + offset;
+}
+
+
+static struct super_block * cramfs_read_super(struct super_block *sb, void *data, int silent)
+{
+ int i;
+ struct cramfs_super super;
+ unsigned long root_offset;
+ struct super_block * retval = NULL;
+
+ lock_super(sb);
+ set_blocksize(sb->s_dev, PAGE_CACHE_SIZE);
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+
+ /* Invalidate the read buffers on mount: think disk change.. */
+ for (i = 0; i < READ_BUFFERS; i++)
+ buffer_blocknr[i] = -1;
+
+ /* Read the first block and get the superblock from it */
+ memcpy(&super, cramfs_read(sb, 0), sizeof(super));
+
+ /* Do sanity checks on the superblock */
+ if (super.magic != CRAMFS_MAGIC) {
+ printk("wrong magic\n");
+ goto out;
+ }
+ if (memcmp(super.signature, CRAMFS_SIGNATURE, sizeof(super.signature))) {
+ printk("wrong signature\n");
+ goto out;
+ }
+
+ /* Check that the root inode is in a sane state */
+ root_offset = super.root.offset << 2;
+ if (root_offset < sizeof(struct cramfs_super)) {
+ printk("root offset too small\n");
+ goto out;
+ }
+ if (root_offset >= super.size) {
+ printk("root offset too large (%lu %u)\n", root_offset, super.size);
+ goto out;
+ }
+ if (!S_ISDIR(super.root.mode)) {
+ printk("root is not a directory\n");
+ goto out;
+ }
+
+ /* Set it all up.. */
+ sb->s_op = &cramfs_ops;
+ sb->s_root = d_alloc_root(get_cramfs_inode(sb, &super.root));
+ retval = sb;
+
+out:
+ unlock_super(sb);
+ return retval;
+}
+
+/* Nothing to do.. */
+static void cramfs_put_super(struct super_block *sb)
+{
+ return;
+}
+
+static int cramfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
+{
+ struct statfs tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.f_type = CRAMFS_MAGIC;
+ tmp.f_bsize = PAGE_CACHE_SIZE;
+ tmp.f_blocks = 0;
+ tmp.f_namelen = 255;
+ return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
+}
+
+/*
+ * Read a cramfs directory entry..
+ *
+ * Remember: the inode number is the byte offset of the start
+ * of the directory..
+ */
+static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned int offset;
+ int copied;
+
+ /* Offset within the thing.. */
+ offset = filp->f_pos;
+ if (offset >= inode->i_size)
+ return 0;
+ /* Directory entries are always 4-byte aligned */
+ if (offset & 3)
+ return -EINVAL;
+
+ copied = 0;
+ while (offset < inode->i_size) {
+ struct cramfs_inode *de;
+ unsigned long nextoffset;
+ char *name;
+ int namelen, error;
+
+ de = cramfs_read(sb, offset + inode->i_ino);
+ name = (char *)(de+1);
+
+ /*
+ * Namelengths on disk are shifted by two
+ * and the name padded out to 4-byte boundaries
+ * with zeroes.
+ */
+ namelen = de->namelen << 2;
+ nextoffset = offset + sizeof(*de) + namelen;
+ for (;;) {
+ if (!namelen)
+ return -EIO;
+ if (name[namelen-1])
+ break;
+ namelen--;
+ }
+ error = filldir(dirent, name, namelen, offset, CRAMINO(de));
+ if (error)
+ break;
+
+ offset = nextoffset;
+ filp->f_pos = offset;
+ copied++;
+ }
+ return 0;
+}
+
+/*
+ * Lookup and fill in the inode data..
+ */
+static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ unsigned int offset = 0;
+
+ while (offset < dir->i_size) {
+ struct cramfs_inode *de;
+ char *name;
+ int namelen;
+
+ de = cramfs_read(dir->i_sb, offset + dir->i_ino);
+ name = (char *)(de+1);
+ namelen = de->namelen << 2;
+ offset += sizeof(*de) + namelen;
+
+ /* Quick check that the name is roughly the right length */
+ if (((dentry->d_name.len + 3) & ~3) != namelen)
+ continue;
+
+ for (;;) {
+ if (!namelen)
+ return ERR_PTR(-EIO);
+ if (name[namelen-1])
+ break;
+ namelen--;
+ }
+ if (namelen != dentry->d_name.len)
+ continue;
+ if (memcmp(dentry->d_name.name, name, namelen))
+ continue;
+ d_add(dentry, get_cramfs_inode(dir->i_sb, de));
+ return NULL;
+ }
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+static int cramfs_readpage(struct dentry *dentry, struct page * page)
+{
+ struct inode *inode = dentry->d_inode;
+ unsigned long maxblock, bytes;
+
+ maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ bytes = 0;
+ if (page->index < maxblock) {
+ struct super_block *sb = inode->i_sb;
+ unsigned long block_offset = inode->i_ino + page->index*4;
+ unsigned long start_offset = inode->i_ino + maxblock*4;
+ unsigned long end_offset;
+
+ end_offset = *(u32 *) cramfs_read(sb, block_offset);
+ if (page->index)
+ start_offset = *(u32 *) cramfs_read(sb, block_offset-4);
+
+ bytes = inode->i_size & (PAGE_CACHE_SIZE - 1);
+ if (page->index < maxblock)
+ bytes = PAGE_CACHE_SIZE;
+
+ cramfs_uncompress_block((void *) page_address(page), PAGE_CACHE_SIZE, cramfs_read(sb, start_offset), end_offset - start_offset);
+ }
+ memset((void *) (page_address(page) + bytes), 0, PAGE_CACHE_SIZE - bytes);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ return 0;
+}
+
+static struct page *get_symlink_page(struct dentry *dentry)
+{
+ return read_cache_page(&dentry->d_inode->i_data, 0, (filler_t *)cramfs_readpage, dentry);
+}
+
+static int cramfs_readlink(struct dentry *dentry, char *buffer, int len)
+{
+ struct inode *inode = dentry->d_inode;
+ int retval;
+
+ if (!inode || !S_ISLNK(inode->i_mode))
+ return -EBADF;
+
+ retval = inode->i_size;
+ if (retval) {
+ int len;
+ struct page *page = get_symlink_page(dentry);
+
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+ wait_on_page(page);
+ len = retval;
+ retval = -EIO;
+ if (Page_Uptodate(page)) {
+ retval = -EFAULT;
+ if (!copy_to_user(buffer, (void *) page_address(page), len))
+ retval = len;
+ }
+ page_cache_release(page);
+ }
+ return retval;
+}
+
+static struct dentry *cramfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
+{
+ struct page *page = get_symlink_page(dentry);
+ struct dentry *result;
+
+ if (IS_ERR(page)) {
+ dput(base);
+ return ERR_PTR(PTR_ERR(page));
+ }
+
+ result = lookup_dentry((void *) page_address(page), base, follow);
+ page_cache_release(page);
+ return result;
+}
+
+/*
+ * Our operations:
+ *
+ * A regular file can be read and mmap'ed.
+ */
+static struct file_operations cramfs_file_operations = {
+ NULL, /* lseek - default */
+ generic_file_read, /* read */
+ NULL, /* write - bad */
+ NULL, /* readdir */
+ NULL, /* poll - default */
+ NULL, /* ioctl */
+ generic_file_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* flush */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL /* revalidate */
+};
+
+/*
+ * A directory can only readdir
+ */
+static struct file_operations cramfs_directory_operations = {
+ NULL, /* lseek - default */
+ NULL, /* read */
+ NULL, /* write - bad */
+ cramfs_readdir, /* readdir */
+ NULL, /* poll - default */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* flush */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL /* revalidate */
+};
+
+static struct inode_operations cramfs_file_inode_operations = {
+ &cramfs_file_operations,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* get_block */
+ cramfs_readpage, /* readpage */
+ NULL, /* writepage */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* revalidate */
+};
+
+static struct inode_operations cramfs_dir_inode_operations = {
+ &cramfs_directory_operations,
+ NULL, /* create */
+ cramfs_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* get_block */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* revalidate */
+};
+
+static struct inode_operations cramfs_symlink_inode_operations = {
+ NULL, /* symlinks do not have files */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ cramfs_readlink, /* readlink */
+ cramfs_follow_link, /* follow_link */
+ NULL, /* get_block */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* revalidate */
+};
+
+static struct super_operations cramfs_ops = {
+ NULL, /* read inode */
+ NULL, /* write inode */
+ NULL, /* put inode */
+ NULL, /* delete inode */
+ NULL, /* notify change */
+ cramfs_put_super, /* put super */
+ NULL, /* write super */
+ cramfs_statfs, /* statfs */
+ NULL /* remount */
+};
+
+static struct file_system_type cramfs_fs_type = {
+ "cramfs",
+ FS_REQUIRES_DEV,
+ cramfs_read_super,
+ NULL
+};
+
+static int __init init_cramfs_fs(void)
+{
+ cramfs_uncompress_init();
+ return register_filesystem(&cramfs_fs_type);
+}
+
+static void __exit exit_cramfs_fs(void)
+{
+ cramfs_uncompress_exit();
+ unregister_filesystem(&cramfs_fs_type);
+}
+
+module_init(init_cramfs_fs)
+module_exit(exit_cramfs_fs)
--- /dev/null
+/*
+ * uncompress.c
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ *
+ * cramfs interfaces to the uncompression library. There's really just
+ * three entrypoints:
+ *
+ * - cramfs_uncompress_init() - called to initialize the thing.
+ * - cramfs_uncompress_exit() - tell me when you're done
+ * - cramfs_uncompress_block() - uncompress a block.
+ *
+ * NOTE NOTE NOTE! The uncompression is entirely single-threaded. We
+ * only have one stream, and we'll initialize it only once even if it
+ * then is used by multiple filesystems.
+ */
+
+#include <linux/kernel.h>
+
+#include "inflate/zlib.h"
+
+static z_stream stream;
+static int initialized = 0;
+
+int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen)
+{
+ int err;
+
+ stream.next_in = src;
+ stream.avail_in = srclen;
+
+ stream.next_out = dst;
+ stream.avail_out = dstlen;
+
+ inflateReset(&stream);
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ printk("Error %d while decompressing!\n", err);
+ printk("%p(%d)->%p(%d)\n", src, srclen, dst, dstlen);
+ }
+ return stream.total_out;
+}
+
+int cramfs_uncompress_init(void)
+{
+ if (!initialized++) {
+ stream.next_in = NULL;
+ stream.avail_in = 0;
+ inflateInit(&stream);
+ }
+ return 0;
+}
+
+int cramfs_uncompress_exit(void)
+{
+ if (!--initialized)
+ inflateEnd(&stream);
+ return 0;
+}
return 0;
}
-static struct file_system_type ext2_fs_type = {
- "ext2",
- FS_REQUIRES_DEV /* | FS_IBASKET */, /* ibaskets have unresolved bugs */
- ext2_read_super,
- NULL
-};
-
-int __init init_ext2_fs(void)
-{
- return register_filesystem(&ext2_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
- return init_ext2_fs();
-}
-
-void cleanup_module(void)
-{
- unregister_filesystem(&ext2_fs_type);
-}
-
-#endif
-
int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
{
unsigned long overhead;
tmp.f_namelen = EXT2_NAME_LEN;
return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
+
+static struct file_system_type ext2_fs_type = {
+ "ext2",
+ FS_REQUIRES_DEV /* | FS_IBASKET */, /* ibaskets have unresolved bugs */
+ ext2_read_super,
+ NULL
+};
+
+static int __init init_ext2_fs(void)
+{
+ return register_filesystem(&ext2_fs_type);
+}
+
+static int __exit exit_ext2_fs(void)
+{
+ unregister_filesystem(&ext2_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ext2_fs)
+module_exit(exit_ext2_fs)
void __init filesystem_setup(void)
{
-#ifdef CONFIG_EXT2_FS
- init_ext2_fs();
-#endif
-
#ifdef CONFIG_MINIX_FS
init_minix_fs();
#endif
--- /dev/null
+/*
+ * $Id: irq_control.h,v 1.8 1999/09/15 23:58:48 cort Exp $
+ *
+ * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
+ */
+#ifndef _PPC_HW_IRQ_H
+#define _PPC_HW_IRQ_H
+
+#if 0
+/* Structure describing interrupts */
+struct hw_interrupt_type {
+ const char * typename;
+ void (*startup)(unsigned int irq);
+ void (*shutdown)(unsigned int irq);
+ void (*enable)(unsigned int irq);
+ void (*disable)(unsigned int irq);
+ void (*mask_and_ack)(unsigned int irq);
+ int irq_offset;
+};
+
+struct irqdesc {
+ struct irqaction *action;
+ struct hw_interrupt_type *ctl;
+};
+#endif
+
+struct int_control_struct
+{
+ void (*int_cli)(void);
+ void (*int_sti)(void);
+ void (*int_restore_flags)(unsigned long);
+ void (*int_save_flags)(unsigned long *);
+};
+extern struct int_control_struct int_control;
+extern unsigned long timer_interrupt_intercept;
+extern unsigned long do_IRQ_intercept;
+void timer_interrupt(struct pt_regs *);
+
+extern void __no_use_sti(void);
+extern void __no_use_cli(void);
+extern void __no_use_restore_flags(unsigned long);
+extern void __no_use_save_flags(unsigned long *);
+
+#define __cli() int_control.int_cli()
+#define __sti() int_control.int_sti()
+#define __save_flags(flags) int_control.int_save_flags(&flags)
+#define __restore_flags(flags) int_control.int_restore_flags(flags)
+#define __save_and_cli(flags) ({__save_flags(flags);__cli();})
+
+extern void do_lost_interrupts(unsigned long);
+extern atomic_t ppc_n_lost_interrupts;
+
+#define mask_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->disable) irq_desc[irq].handler->disable(irq);})
+#define unmask_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->enable) irq_desc[irq].handler->enable(irq);})
+#define mask_and_ack_irq(irq) ({if (irq_desc[irq].handler && irq_desc[irq].handler->ack) irq_desc[irq].handler->ack(irq);})
+
+#endif /* _PPC_HW_IRQ_H */
+++ /dev/null
-/*
- * $Id: irq_control.h,v 1.8 1999/09/15 23:58:48 cort Exp $
- *
- * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
- */
-#ifndef _PPC_IRQ_CONTROL_H
-#define _PPC_IRQ_CONTROL_H
-
-#include <asm/irq.h>
-#include <asm/atomic.h>
-
-/* Structure describing interrupts */
-struct hw_interrupt_type {
- const char * typename;
- void (*startup)(unsigned int irq);
- void (*shutdown)(unsigned int irq);
- void (*enable)(unsigned int irq);
- void (*disable)(unsigned int irq);
- void (*mask_and_ack)(unsigned int irq);
- int irq_offset;
-};
-
-struct irqdesc {
- struct irqaction *action;
- struct hw_interrupt_type *ctl;
-};
-
-extern struct irqdesc irq_desc[NR_IRQS];
-
-struct int_control_struct
-{
- void (*int_cli)(void);
- void (*int_sti)(void);
- void (*int_restore_flags)(unsigned long);
- void (*int_save_flags)(unsigned long *);
-};
-extern struct int_control_struct int_control;
-extern unsigned long timer_interrupt_intercept;
-extern unsigned long do_IRQ_intercept;
-void timer_interrupt(struct pt_regs *);
-
-extern void __no_use_sti(void);
-extern void __no_use_cli(void);
-extern void __no_use_restore_flags(unsigned long);
-extern void __no_use_save_flags(unsigned long *);
-
-#define __cli() int_control.int_cli()
-#define __sti() int_control.int_sti()
-#define __save_flags(flags) int_control.int_save_flags(&flags)
-#define __restore_flags(flags) int_control.int_restore_flags(flags)
-#define __save_and_cli(flags) ({__save_flags(flags);__cli();})
-
-extern void do_lost_interrupts(unsigned long);
-extern atomic_t ppc_n_lost_interrupts;
-
-#define mask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->disable) irq_desc[irq].ctl->disable(irq);})
-#define unmask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->enable) irq_desc[irq].ctl->enable(irq);})
-#define mask_and_ack_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->mask_and_ack) irq_desc[irq].ctl->mask_and_ack(irq);})
-
-#endif /* _PPC_IRQ_CONTROL_H */
#include <asm/processor.h>
#include <asm/atomic.h>
-#include <asm/irq_control.h>
+#include <asm/hw_irq.h>
/*
* Memory barrier.
#define __NR_putpmsg 188 /* some people actually want streams */
#define __NR_vfork 189
#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define __NR_mmap2 192
+#define __NR_truncate64 193
+#define __NR_ftruncate64 194
+#define __NR_stat64 195
+#define __NR_lstat64 196
+#define __NR_fstat64 197
#define __NR(n) #n
--- /dev/null
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* zlib required.. */
+#include <zlib.h>
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+#include "cramfs.h"
+
+#define PAGE_CACHE_SIZE (4096)
+
+static const char *progname = "mkcramfs";
+
+void usage(void)
+{
+ fprintf(stderr, "Usage: '%s dirname outfile'\n"
+ " where <dirname> is the root of the\n"
+ " filesystem to be compressed.\n", progname);
+ exit(1);
+}
+
+struct entry {
+ /* stats */
+ char *name;
+ unsigned int mode, size, uid, gid;
+
+ /* FS data */
+ void *uncompressed;
+ unsigned int dir_offset; /* Where in the archive is the directory entry? */
+ unsigned int data_offset; /* Where in the archive is the start of the data? */
+
+ /* organization */
+ struct entry *child;
+ struct entry *next;
+};
+
+/*
+ * We should mind about memory leaks and
+ * checking for out-of-memory.
+ *
+ * We don't.
+ */
+static unsigned int parse_directory(const char *name, struct entry **prev)
+{
+ DIR *dir;
+ int count = 0, totalsize = 0;
+ struct dirent *dirent;
+ char *path, *endpath;
+ int len = strlen(name);
+
+ dir = opendir(name);
+ if (!dir) {
+ perror(name);
+ exit(2);
+ }
+ /* Set up the path.. */
+ path = malloc(4096);
+ memcpy(path, name, len);
+ endpath = path + len;
+ *endpath = '/';
+ endpath++;
+
+ while ((dirent = readdir(dir)) != NULL) {
+ struct entry *entry;
+ struct stat st;
+ int fd, size;
+
+ /* Ignore "." and ".." - we won't be adding them to the archive */
+ if (dirent->d_name[0] == '.') {
+ if (dirent->d_name[1] == '\0')
+ continue;
+ if (dirent->d_name[1] == '.') {
+ if (dirent->d_name[2] == '\0')
+ continue;
+ }
+ }
+ strcpy(endpath, dirent->d_name);
+
+ if (lstat(path, &st) < 0) {
+ perror(endpath);
+ continue;
+ }
+ entry = calloc(1, sizeof(struct entry));
+ entry->name = strdup(dirent->d_name);
+ entry->mode = st.st_mode;
+ entry->size = st.st_size;
+ entry->uid = st.st_uid;
+ entry->gid = st.st_gid;
+ size = sizeof(struct cramfs_inode) + (~3 & (strlen(entry->name) + 3));
+ if (S_ISDIR(st.st_mode)) {
+ entry->size = parse_directory(path, &entry->child);
+ } else if (S_ISREG(st.st_mode)) {
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ perror(path);
+ continue;
+ }
+ if (entry->size)
+ entry->uncompressed = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (-1 == (int) (long) entry->uncompressed) {
+ perror("mmap");
+ exit(5);
+ }
+ close(fd);
+ } else if (S_ISLNK(st.st_mode)) {
+ entry->uncompressed = malloc(st.st_size);
+ if (readlink(path, entry->uncompressed, st.st_size) < 0) {
+ perror(path);
+ continue;
+ }
+ } else {
+ entry->size = st.st_rdev;
+ }
+
+ /* Link it into the list */
+ *prev = entry;
+ prev = &entry->next;
+ count++;
+ totalsize += size;
+ }
+ closedir(dir);
+ free(path);
+ return totalsize;
+}
+
+static void set_random(void *area, int size)
+{
+ int fd = open("/dev/random", O_RDONLY);
+
+ if (fd >= 0) {
+ if (read(fd, area, size) == size)
+ return;
+ }
+ memset(area, 0x00, size);
+}
+
+static unsigned int write_superblock(struct entry *root, char *base)
+{
+ struct cramfs_super *super = (struct cramfs_super *) base;
+ unsigned int offset = sizeof(struct cramfs_super);
+
+ super->magic = CRAMFS_MAGIC;
+ super->flags = 0;
+ super->size = 0x10000;
+ memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
+ set_random(super->fsid, sizeof(super->fsid));
+ strncpy(super->name, "Compressed", sizeof(super->name));
+
+ super->root.mode = root->mode;
+ super->root.uid = root->uid;
+ super->root.gid = root->gid;
+ super->root.size = root->size;
+ super->root.offset = offset >> 2;
+
+ return offset;
+}
+
+static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
+{
+ struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
+ inode->offset = (offset >> 2);
+}
+
+
+/*
+ * We do a width-first printout of the directory
+ * entries, using a stack to remember the directories
+ * we've seen.
+ */
+#define MAXENTRIES (100)
+static int stack_entries = 0;
+static struct entry *entry_stack[MAXENTRIES];
+
+static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
+{
+ for (;;) {
+ while (entry) {
+ struct cramfs_inode *inode = (struct cramfs_inode *) (base + offset);
+ int len = strlen(entry->name);
+
+ entry->dir_offset = offset;
+ offset += sizeof(struct cramfs_inode);
+
+ inode->mode = entry->mode;
+ inode->uid = entry->uid;
+ inode->gid = entry->gid;
+ inode->size = entry->size;
+ inode->offset = 0; /* Fill in later */
+
+ memcpy(base + offset, entry->name, len);
+ /* Pad up the name to a 4-byte boundary */
+ while (len & 3) {
+ *(base + offset + len) = '\0';
+ len++;
+ }
+ inode->namelen = len >> 2;
+ offset += len;
+
+ printf(" %s\n", entry->name);
+
+ if (entry->child) {
+ entry_stack[stack_entries] = entry;
+ stack_entries++;
+ }
+ entry = entry->next;
+ }
+ if (!stack_entries)
+ break;
+ stack_entries--;
+ entry = entry_stack[stack_entries];
+
+ set_data_offset(entry, base, offset);
+ printf("'%s':\n", entry->name);
+ entry = entry->child;
+ }
+ return offset;
+}
+
+/*
+ * One 4-byte pointer per block and then the actual blocked
+ * output. The first block does not need an offset pointer,
+ * as it will start immediately after the pointer block.
+ *
+ * Note that size > 0, as a zero-sized file wouldn't ever
+ * have gotten here in the first place.
+ */
+static unsigned int do_compress(char *base, unsigned int offset, char *uncompressed, unsigned int size)
+{
+ unsigned long original_size = size;
+ unsigned long original_offset = offset;
+ unsigned long new_size;
+ unsigned long blocks = (size - 1) / PAGE_CACHE_SIZE + 1;
+ unsigned long curr = offset + 4 * blocks;
+ int change;
+
+ do {
+ unsigned int input = size;
+ unsigned long len = 8192;
+ if (input > PAGE_CACHE_SIZE)
+ input = PAGE_CACHE_SIZE;
+ compress(base + curr, &len, uncompressed, input);
+ uncompressed += input;
+ size -= input;
+ curr += len;
+
+ if (len > PAGE_CACHE_SIZE*2) {
+ printf("AIEEE: block expanded to > 2*blocklength (%d)\n", len);
+ exit(1);
+ }
+
+ *(u32 *) (base + offset) = curr;
+ offset += 4;
+ } while (size);
+
+ new_size = curr - original_offset;
+ change = new_size - original_size;
+ printf("%4.2f %% (%d bytes)\n", (change * 100) / (double) original_size, change);
+
+ return (curr + 3) & ~3;
+}
+
+static unsigned int write_data(struct entry *entry, char *base, unsigned int offset)
+{
+ do {
+ if (entry->uncompressed) {
+ set_data_offset(entry, base, offset);
+ offset = do_compress(base, offset, entry->uncompressed, entry->size);
+ }
+ if (entry->child)
+ offset = write_data(entry->child, base, offset);
+ entry = entry->next;
+ } while (entry);
+ return offset;
+}
+
+/* This is the maximum rom-image you can create */
+#define MAXROM (64*1024*1024)
+
+/*
+ * Usage:
+ *
+ * mkcramfs directory-name
+ *
+ * where "directory-name" is simply the root of the directory
+ * tree that we want to generate a compressed filesystem out
+ * of..
+ */
+int main(int argc, char **argv)
+{
+ struct stat st;
+ struct entry *root_entry;
+ char *rom_image;
+ unsigned int offset, written;
+ int fd;
+
+ if (argc)
+ progname = argv[0];
+ if (argc != 3)
+ usage();
+
+ if (stat(argv[1], &st) < 0) {
+ perror(argv[1]);
+ exit(1);
+ }
+ fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ root_entry = calloc(1, sizeof(struct entry));
+ root_entry->mode = st.st_mode;
+ root_entry->uid = st.st_uid;
+ root_entry->gid = st.st_gid;
+ root_entry->name = "";
+
+ root_entry->size = parse_directory(argv[1], &root_entry->child);
+
+ rom_image = mmap(NULL, MAXROM, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (-1 == (int) (long) rom_image) {
+ perror("ROM image map");
+ exit(1);
+ }
+ offset = write_superblock(root_entry, rom_image);
+ printf("Super block: %d bytes\n", offset);
+
+ offset = write_directory_structure(root_entry->child, rom_image, offset);
+ printf("Directory data: %d bytes\n", offset);
+
+ offset = write_data(root_entry, rom_image, offset);
+ printf("Everything: %d bytes\n", offset);
+
+ written = write(fd, rom_image, offset);
+ if (written < 0) {
+ perror("rom image");
+ exit(1);
+ }
+ if (offset != written) {
+ fprintf(stderr, "ROM image write failed (%d %d)\n", written, offset);
+ exit(1);
+ }
+ return 0;
+}