summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/acm.c293
-rw-r--r--drivers/usb/cpia.c1267
-rw-r--r--drivers/usb/cpia.h139
-rw-r--r--drivers/usb/keymap-mac.c50
-rw-r--r--drivers/usb/maps/mac.map350
-rw-r--r--drivers/usb/mkmap.adb88
-rw-r--r--drivers/usb/printer.c413
-rw-r--r--drivers/usb/usb-core.c96
-rw-r--r--drivers/usb/usb_scsi.c1098
-rw-r--r--drivers/usb/usb_scsi.h145
-rw-r--r--drivers/usb/usb_scsi_debug.c104
-rw-r--r--drivers/usb/usb_scsi_dt.c4
12 files changed, 4047 insertions, 0 deletions
diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c
new file mode 100644
index 000000000..d4796e28c
--- /dev/null
+++ b/drivers/usb/acm.c
@@ -0,0 +1,293 @@
+/*
+ * USB Abstract Control Model based on Brad Keryan's USB busmouse driver
+ *
+ * Armin Fuerst 5/8/1999
+ *
+ * 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)
+ *
+ * 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)
+ *
+ * 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)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define USB_ACM_MINOR 32
+
+struct acm_state {
+ int present; /* this acm is plugged in */
+ int active; /* someone is has this acm's device open */
+ int serstate; /* Status of the serial port (rate, handshakelines,...) */
+ struct usb_device *dev;
+ unsigned ctrlbuffer; /*buffer for control messages*/
+ unsigned int readendp,writeendp,ctrlendp;
+ unsigned int readpipe,writepipe,ctrlpipe;
+ char buffer;
+};
+
+static struct acm_state static_acm_state;
+
+spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED;
+
+static int acm_irq(int state, void *__buffer, void *dev_id)
+{
+// unsigned char *data = __buffer;
+ struct acm_state *acm = &static_acm_state;
+ devrequest *dr;
+
+ dr=__buffer;
+ printk("ACM_USB_IRQ\n");
+ 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);
+
+ switch(dr->request) {
+ //Network connection
+ case 0x00:
+ printk("Network connection: ");
+ if (dr->request==0) printk("disconnected\n");
+ if (dr->request==1) printk("connected\n");
+ break;
+
+ //Response available
+ case 0x01:
+ printk("Response available\n");
+ acm->buffer=1;
+ break;
+
+ //Set serial line state
+ case 0x20:
+ if ((dr->index==1)&&(dr->length==2)) {
+ acm->serstate=acm->ctrlbuffer;
+ printk("Serstate: %02X\n",acm->ctrlbuffer);
+ }
+ break;
+ }
+/*
+ if(!acm->active)
+ return 1;
+*/
+ return 1;
+}
+
+static int release_acm(struct inode * inode, struct file * file)
+{
+ struct acm_state *acm = &static_acm_state;
+ printk("ACM_FILE_RELEASE\n");
+
+// fasync_acm(-1, file, 0);
+ if (--acm->active)
+ return 0;
+ return 0;
+}
+
+static int open_acm(struct inode * inode, struct file * file)
+{
+ struct acm_state *acm = &static_acm_state;
+ printk("USB_FILE_OPEN\n");
+
+ if (!acm->present)
+ return -EINVAL;
+ if (acm->active++)
+ return 0;
+ return 0;
+}
+
+static ssize_t write_acm(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ devrequest dr;
+ struct acm_state *acm = &static_acm_state;
+ unsigned long retval;
+
+ printk("USB_FILE_WRITE\n");
+//Huh, i seem to got that wrong, we don't need this ?!?
+/*
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT;
+ dr.request = 0;
+ dr.value = 0;
+ dr.index = acm->writeendp;
+ dr.length = count;
+ acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0);
+*/
+
+ acm->dev->bus->op->bulk_msg(acm->dev,&acm->writepipe,buffer, count, &retval);
+ return -EINVAL;
+}
+
+
+static ssize_t read_acm(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+ devrequest dr;
+ struct acm_state *acm = &static_acm_state;
+ unsigned long retval;
+ printk("USB_FILE_READ\n");
+// if (!acm->buffer) return -1;
+ acm->buffer=0;
+//We don't need this
+/*
+ printk("writing control msg\n");
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT | 0x80;
+ dr.request = 1;
+ dr.value = 0;
+ dr.index = acm->readendp;
+ dr.length = 0;
+ acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0);
+*/
+ printk("reading:>%s<\n",buffer);
+ acm->dev->bus->op->bulk_msg(acm->dev,&acm->readpipe,buffer, 1,&retval);
+ printk("done:>%s<\n",buffer);
+ return 1;
+}
+
+struct file_operations usb_acm_fops = {
+ NULL, /* acm_seek */
+ read_acm,
+ write_acm,
+ NULL, /* acm_readdir */
+ NULL, /* acm_poll */
+ NULL, /* acm_ioctl */
+ NULL, /* acm_mmap */
+ open_acm,
+ NULL, /* flush */
+ release_acm,
+ NULL,
+ NULL, /*fasync*/
+};
+
+static struct miscdevice usb_acm = {
+ USB_ACM_MINOR, "USB ACM", &usb_acm_fops
+};
+
+static int acm_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct acm_state *acm = &static_acm_state;
+ int cfgnum;
+
+ /* Only use CDC */
+ if (dev->descriptor.bDeviceClass != 2 ||
+ dev->descriptor.bDeviceSubClass != 0 ||
+ dev->descriptor.bDeviceProtocol != 0)
+ return -1;
+
+ /*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].altsetting[0].interface[0];
+ if (interface->bInterfaceClass != 2 ||
+ interface->bInterfaceSubClass != 2 ||
+ interface->bInterfaceProtocol != 1 ||
+ interface->bNumEndpoints != 1)
+ continue;
+
+ /*Which uses an interrupt input */
+ endpoint = &interface->endpoint[0];
+ if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
+ (endpoint->bmAttributes & 3) != 3)
+ continue;
+
+ /* The second one should be a Data interface? */
+ interface = &dev->config[cfgnum].altsetting[0].interface[1];
+ if (interface->bInterfaceClass != 10 ||
+ interface->bInterfaceSubClass != 0 ||
+ interface->bInterfaceProtocol != 0 ||
+ interface->bNumEndpoints != 2)
+ continue;
+
+ /*With a bulk input */
+ endpoint = &interface->endpoint[0];
+ if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
+ (endpoint->bmAttributes & 3) != 2)
+ continue;
+
+ /*And a bulk output */
+ endpoint = &interface->endpoint[1];
+ if ((endpoint->bEndpointAddress & 0x80) == 0x80 ||
+ (endpoint->bmAttributes & 3) != 2)
+ continue;
+
+ printk("USB ACM found\n");
+ usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
+ acm->dev=dev;
+ acm->readendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[0].bEndpointAddress;
+ acm->writeendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[1].bEndpointAddress;
+ acm->ctrlendp=dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bEndpointAddress;
+ acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp);
+ acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp);
+ usb_request_irq(dev,acm->ctrlpipe=usb_rcvctrlpipe(dev,acm->ctrlendp), acm_irq, dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bInterval, &acm->ctrlbuffer);
+ acm->present = 1;
+ acm->buffer=0;
+ return 0;
+ }
+
+ return -1;
+}
+
+static void acm_disconnect(struct usb_device *dev)
+{
+ struct acm_state *acm = &static_acm_state;
+
+ /* this might need work */
+ acm->present = 0;
+}
+
+static struct usb_driver acm_driver = {
+ "acm",
+ acm_probe,
+ acm_disconnect,
+ { NULL, NULL }
+};
+
+int usb_acm_init(void)
+{
+ struct acm_state *acm = &static_acm_state;
+
+ misc_register(&usb_acm);
+
+ acm->present = acm->active = 0;
+
+ usb_register(&acm_driver);
+ printk(KERN_INFO "USB ACM registered.\n");
+ return 0;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ return usb_acm_init();
+}
+
+void cleanup_module(void)
+{
+ /* this, too, probably needs work */
+ usb_deregister(&acm_driver);
+ misc_deregister(&usb_acm);
+}
+
+#endif
diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c
new file mode 100644
index 000000000..87e1e4254
--- /dev/null
+++ b/drivers/usb/cpia.c
@@ -0,0 +1,1267 @@
+/*
+ * USB CPiA Video Camera driver
+ *
+ * Supports CPiA based Video Camera's. Many manufacturers use this chipset.
+ * There's a good chance, if you have a USB video camera, it's a CPiA based
+ * one
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/wrapper.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+
+#include "usb.h"
+#include "uhci.h"
+#include "cpia.h"
+
+#define MAX_FRAME_SIZE (384 * 288 * 3)
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/* convert virtual user memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline unsigned long uvirt_to_phys(unsigned long adr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+
+ pgd = pgd_offset(current->mm, adr);
+ if (pgd_none(*pgd))
+ return 0;
+ pmd = pmd_offset(pgd, adr);
+ if (pmd_none(*pmd))
+ return 0;
+ ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
+ pte = *ptep;
+ if(pte_present(pte))
+ return
+ virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+ return 0;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+ return virt_to_bus(phys_to_virt(uvirt_to_phys(adr)));
+}
+
+/* convert virtual kernel memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline unsigned long kvirt_to_phys(unsigned long adr)
+{
+ return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+ return uvirt_to_bus(VMALLOC_VMADDR(adr));
+}
+
+
+static void * rvmalloc(unsigned long size)
+{
+ void * mem;
+ unsigned long adr, page;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ mem=vmalloc(size);
+ if (mem)
+ {
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(adr);
+ mem_map_reserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size-=PAGE_SIZE;
+ else
+ size=0;
+ }
+ }
+ return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ if (mem)
+ {
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(adr);
+ mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size-=PAGE_SIZE;
+ else
+ size=0;
+ }
+ vfree(mem);
+ }
+}
+
+int usb_cpia_get_version(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80;
+ dr.request = USB_REQ_CPIA_GET_VERSION;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 4);
+}
+
+int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80;
+ dr.request = USB_REQ_CPIA_GET_PNP_ID;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 6;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 6);
+}
+
+int usb_cpia_get_camera_status(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80;
+ dr.request = USB_REQ_CPIA_GET_CAMERA_STATUS;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 8;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 8);
+}
+
+int usb_cpia_goto_hi_power(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_GOTO_HI_POWER;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_get_vp_version(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_GET_VP_VERSION;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, buf, 4);
+}
+
+int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_SENSOR_FPS;
+ dr.value = (sensorclkdivisor << 8) + sensorbaserate;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_GRAB_FRAME;
+ dr.value = streamstartline << 8;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_upload_frame(struct usb_device *dev, int forceupload)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_UPLOAD_FRAME;
+ dr.value = forceupload;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_GRAB_MODE;
+ dr.value = continuousgrab;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_FORMAT;
+ dr.value = (subsample << 8) + size;
+ dr.index = order;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_COMPRESSION;
+ dr.value = (decimation << 8) + compmode;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_INIT_STREAM_CAP;
+ dr.value = (streamstartline << 8) + skipframes;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_finistreamcap(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_FINI_STREAM_CAP;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_startstreamcap(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_START_STREAM_CAP;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_endstreamcap(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_END_STREAM_CAP;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+#define scratch_left(x) (cpia->scratchlen - (int)((char *)x - (char *)cpia->scratch))
+
+static void cpia_parse_data(struct usb_cpia *cpia)
+{
+ unsigned char *data = cpia->scratch;
+ int done;
+
+ done = 0;
+ while (!done && scratch_left(data)) {
+ switch (cpia->state) {
+ case STATE_SCANNING:
+ {
+ unsigned char *begin = data;
+
+ /* We need atleast 2 bytes for the magic value */
+ if (scratch_left(data) < 2) {
+ done = 1;
+ break;
+ }
+
+ printk("header: %X\n", (*data << 8) + *(data + 1));
+ if ((*data == 0x19) && (*(data + 1) == 0x68)) {
+ cpia->state = STATE_HEADER;
+ printk("moving to header\n");
+ break;
+ }
+
+ if (scratch_left(data) < 4) {
+ done = 1;
+ break;
+ }
+
+ printk("Scanning for end of frame\n");
+ while (scratch_left(data) >= 4) {
+ if ((*data == 0xFF) &&
+ (*(data + 1) == 0xFF) &&
+ (*(data + 2) == 0xFF) &&
+ (*(data + 3) == 0xFF)) {
+ data += 4;
+ break;
+ }
+ data++;
+ }
+printk("scan: scanned %d bytes\n", data-begin);
+ break;
+ }
+ case STATE_HEADER:
+ /* We need atleast 64 bytes for the header */
+ if (scratch_left(data) < 64) {
+ done = 1;
+ break;
+ }
+
+printk("header: framerate %d\n", data[41]);
+
+ data += 64;
+
+ cpia->state = STATE_LINES;
+
+ break;
+ case STATE_LINES:
+ {
+ unsigned char *begin = data;
+ int found = 0;
+
+ while (scratch_left(data)) {
+ if (*data == 0xFD) {
+ data++;
+ found = 1;
+ break;
+ } else if ((*data == 0xFF) &&
+ (scratch_left(data) >= 3) &&
+ (*(data + 1) == 0xFF) &&
+ (*(data + 2) == 0xFF) &&
+ (*(data + 3) == 0xFF)) {
+ data+=4;
+ cpia->curline = 144;
+ found = 1;
+ break;
+ }
+
+ data++;
+ }
+#if 0
+printk("line %d: scanned %d bytes\n", cpia->curline, data-begin);
+#endif
+if (data-begin == 355 && cpia->frame[cpia->curframe].width != 64) {
+ int i;
+ char *f = cpia->frame[cpia->curframe].data, *b = begin;
+
+#if 0
+printk("copying\n");
+#endif
+
+ b+=2;
+ f+=(cpia->frame[cpia->curframe].width*3)*cpia->curline;
+ for (i = 0; i < 176; i++)
+ f[(i * 3) + 0] =
+ f[(i * 3) + 1] =
+ f[(i * 3) + 2] =
+ b[(i * 2)];
+}
+ if (found) {
+ cpia->curline++;
+ if (cpia->curline >= 144) {
+ wake_up(&cpia->wq);
+ cpia->state = STATE_SCANNING;
+ cpia->curline = 0;
+ cpia->curframe = -1;
+ done = 1;
+ }
+ } else {
+ data = begin;
+ done = 1;
+ }
+
+ break;
+ }
+ }
+ }
+
+ {
+ int l;
+
+ l = scratch_left(data);
+ memmove(cpia->scratch, data, l);
+ cpia->scratchlen = l;
+ }
+}
+
+static int cpia_isoc_irq(int status, void *__buffer, void *dev_id)
+{
+ struct usb_cpia *cpia = dev_id;
+ struct usb_device *dev = cpia->dev;
+ struct cpia_sbuf *sbuf;
+ int i;
+ char *p;
+
+ if (!cpia->streaming) {
+ printk("oops, not streaming, but interrupt\n");
+ return 0;
+ }
+
+ if (cpia->curframe < 0) {
+ if (cpia->frame[0].state == FRAME_READY) {
+ cpia->curframe = 0;
+ cpia->frame[0].state = FRAME_GRABBING;
+printk("capturing to frame 0\n");
+ } else if (cpia->frame[1].state == FRAME_READY) {
+ cpia->curframe = 1;
+ cpia->frame[1].state = FRAME_GRABBING;
+printk("capturing to frame 1\n");
+ } else
+printk("no frame available\n");
+ }
+
+ sbuf = &cpia->sbuf[cpia->receivesbuf];
+
+ uhci_unsched_isochronous(dev, sbuf->isodesc);
+
+ /* Do something to it now */
+ sbuf->len = uhci_compress_isochronous(dev, sbuf->isodesc);
+
+ if (sbuf->len)
+ printk("%d bytes received\n", sbuf->len);
+
+ if (sbuf->len && cpia->curframe >= 0) {
+ if (sbuf->len > (SCRATCH_BUF_SIZE - cpia->scratchlen)) {
+ printk("overflow!\n");
+ return 0;
+ }
+ memcpy(cpia->scratch + cpia->scratchlen, sbuf->data, sbuf->len);
+ cpia->scratchlen += sbuf->len;
+
+ cpia_parse_data(cpia);
+ }
+
+ /* Reschedule this block of Isochronous desc */
+ uhci_sched_isochronous(dev, sbuf->isodesc, cpia->sbuf[(cpia->receivesbuf + 2) % 3].isodesc);
+
+ /* Move to the next one */
+ cpia->receivesbuf = (cpia->receivesbuf + 1) % 3;
+
+ return 1;
+}
+
+int cpia_init_isoc(struct usb_cpia *cpia)
+{
+ struct usb_device *dev = cpia->dev;
+
+ cpia->receivesbuf = 0;
+
+#if 0
+ cpia->parsesbuf = 0;
+ cpia->parsepos = 0;
+#endif
+ cpia->scratchlen = 0;
+ cpia->curline = 0;
+ cpia->state = STATE_SCANNING;
+
+ /* Allocate all of the memory necessary */
+ cpia->sbuf[0].isodesc = uhci_alloc_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[0].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia);
+ cpia->sbuf[1].isodesc = uhci_alloc_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[1].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia);
+ cpia->sbuf[2].isodesc = uhci_alloc_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[2].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia);
+
+ printk("isodesc[0] @ %p\n", cpia->sbuf[0].isodesc);
+ printk("isodesc[1] @ %p\n", cpia->sbuf[1].isodesc);
+ printk("isodesc[2] @ %p\n", cpia->sbuf[2].isodesc);
+
+ /* Schedule the queues */
+ uhci_sched_isochronous(dev, cpia->sbuf[0].isodesc, NULL);
+ uhci_sched_isochronous(dev, cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc);
+ uhci_sched_isochronous(dev, cpia->sbuf[2].isodesc, cpia->sbuf[1].isodesc);
+
+ if (usb_set_interface(cpia->dev, 1, 3)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+
+ usb_cpia_startstreamcap(cpia->dev);
+
+ cpia->streaming = 1;
+
+ return 0;
+}
+
+void cpia_stop_isoc(struct usb_cpia *cpia)
+{
+ struct usb_device *dev = cpia->dev;
+
+ if (!cpia->streaming)
+ return;
+
+ cpia->streaming = 0;
+
+ /* Stop the streaming */
+ usb_cpia_endstreamcap(cpia->dev);
+
+ /* Set packet size to 0 */
+ if (usb_set_interface(cpia->dev, 1, 0)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+
+ /* Unschedule all of the iso td's */
+ uhci_unsched_isochronous(dev, cpia->sbuf[2].isodesc);
+ uhci_unsched_isochronous(dev, cpia->sbuf[1].isodesc);
+ uhci_unsched_isochronous(dev, cpia->sbuf[0].isodesc);
+
+ /* Delete them all */
+ uhci_delete_isochronous(dev, cpia->sbuf[2].isodesc);
+ uhci_delete_isochronous(dev, cpia->sbuf[1].isodesc);
+ uhci_delete_isochronous(dev, cpia->sbuf[0].isodesc);
+}
+
+/* Video 4 Linux API */
+static int cpia_open(struct video_device *dev, int flags)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+
+printk("cpia_open\n");
+
+ cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
+ if (!cpia->fbuf)
+ return -ENOMEM;
+
+ cpia->frame[0].state = FRAME_DONE;
+ cpia->frame[1].state = FRAME_DONE;
+
+ cpia->frame[0].data = cpia->fbuf;
+ cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE;
+ printk("frame [0] @ %p\n", cpia->frame[0].data);
+ printk("frame [1] @ %p\n", cpia->frame[1].data);
+
+ cpia->sbuf[0].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL);
+ if (!cpia->sbuf[0].data)
+ return -ENOMEM;
+
+ cpia->sbuf[1].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL);
+ if (!cpia->sbuf[1].data)
+ return -ENOMEM;
+
+ cpia->sbuf[2].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL);
+ if (!cpia->sbuf[2].data)
+ return -ENOMEM;
+
+ printk("sbuf[0] @ %p\n", cpia->sbuf[0].data);
+ printk("sbuf[1] @ %p\n", cpia->sbuf[1].data);
+ printk("sbuf[2] @ %p\n", cpia->sbuf[2].data);
+
+ cpia->curframe = -1;
+ cpia->receivesbuf = 0;
+
+ usb_cpia_initstreamcap(cpia->dev, 0, 60);
+
+ cpia_init_isoc(cpia);
+
+ return 0;
+}
+
+static void cpia_close(struct video_device *dev)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+
+printk("cpia_close\n");
+
+ cpia_stop_isoc(cpia);
+
+ usb_cpia_finistreamcap(cpia->dev);
+
+ rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE);
+
+ kfree(cpia->sbuf[2].data);
+ kfree(cpia->sbuf[1].data);
+ kfree(cpia->sbuf[0].data);
+}
+
+static int cpia_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static long cpia_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+ return -EINVAL;
+}
+
+#if 0
+ if (usb_set_interface(dev, 1, 3)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+
+ if (usb_cpia_grab_frame(dev, 0)) {
+ printk("cpia_grab_frame error\n");
+ return -EINVAL;
+ }
+
+ if (usb_cpia_upload_frame(dev, 0)) {
+ printk("cpia_upload_frame error\n");
+ return -EINVAL;
+ }
+
+ buf = cpia->ibuf;
+ uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), buf, 176*144*4);
+
+ {
+ printk("header magic: %X\n", (buf[0] << 8) + buf[1]);
+
+ while ((buf[0] != 0x19) || (buf[1] != 0x68)) {
+ int i;
+
+ printk("resync'ing\n");
+ for (i=0;i<(176*144*4)-4;i++, buf++)
+ if (
+ (buf[0] == 0xFF) &&
+ (buf[1] == 0xFF) &&
+ (buf[2] == 0xFF) &&
+ (buf[3] == 0xFF)) {
+ buf+=4;
+ i+=4;
+ break;
+ }
+
+ memmove(cpia->ibuf, buf, (176*144*4) - i);
+ uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->ibuf + (176*144*4) - i, i);
+ buf = cpia->ibuf;
+
+#if 0
+ printk("header magic: %X\n", (buf[0] << 8) + buf[1]);
+#endif
+ }
+
+ printk("size: %d, sample: %d, order: %d\n", buf[16], buf[17], buf[18]);
+ printk("comp: %d, decimation: %d\n", buf[28], buf[29]);
+ printk("roi: top left: %d, %d bottom right: %d, %d\n",
+ buf[26] * 4, buf[24] * 8,
+ buf[27] * 4, buf[25] * 8);
+
+ printk("vm->frame: %d\n", vm->frame);
+
+ {
+ int i, i1;
+ char *b = buf + 64, *fbuf = &cpia->fbuffer[MAX_FRAME_SIZE * (vm->frame & 1)];
+ for (i=0;i<144;i++) {
+#if 0
+ printk("line len: %d\n", (b[1] << 8) + b[0]);
+#endif
+ b += 2;
+ for (i1=0;i1<176;i1++) {
+ fbuf[(i * vm->width * 3) + (i1 * 3)] =
+ fbuf[(i * vm->width * 3) + (i1 * 3) + 1] =
+ fbuf[(i * vm->width * 3) + (i1 * 3) + 2] =
+ b[i1 * 2];
+#if 0
+ *((short *)&fbuf[(i * vm->width * 2) + (i1 * 2)]) =
+ ((b[i1 * 2] >> 3) << 11) + ((b[i1 * 2] >> 2) << 6) + (b[i1 * 2] >> 3);
+#endif
+ }
+ b += (176 * 2) + 1;
+ }
+ }
+
+ }
+
+ if (usb_set_interface(dev, 1, 0)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+#endif
+
+static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ strcpy(b.name, "CPiA USB Camera");
+ b.type = VID_TYPE_CAPTURE /* | VID_TYPE_SUBCAPTURE */;
+ b.channels = 1;
+ b.audios = 0;
+ b.maxwidth = 176 /* 352 */;
+ b.maxheight = 144 /* 240 */;
+ b.minwidth = 176 /* (Something small?) */;
+ b.minheight = 144 /* " " */;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if (v.channel != 0)
+ return -EINVAL;
+
+ v.flags = 0;
+ v.tuners = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ strcpy(v.name, "Camera");
+
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ int v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v != 0)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v.tuner)
+ return -EINVAL;
+
+ strcpy(v.name, "Format");
+
+ v.rangelow = 0;
+ v.rangehigh = 0;
+ v.flags = 0;
+ v.mode = VIDEO_MODE_AUTO;
+
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v.tuner)
+ return -EINVAL;
+
+ if (v.mode != VIDEO_MODE_AUTO)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p;
+
+ p.colour = 0x8000; /* Damn British people :) */
+ p.hue = 0x8000;
+ p.brightness = 180 << 8; /* XXX */
+ p.contrast = 192 << 8; /* XXX */
+ p.whiteness = 105 << 8; /* XXX */
+#if 0
+ p.depth = 24;
+#endif
+ p.depth = 16;
+ p.palette = VIDEO_PALETTE_YUYV;
+
+ if (copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+
+ if (copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
+
+printk("Attempting to set palette %d, depth %d\n", p.palette, p.depth);
+
+#if 0
+ if (p.palette != VIDEO_PALETTE_YUYV)
+ return -EINVAL;
+ if (p.depth != 16)
+ return -EINVAL;
+#endif
+
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+
+printk("VIDIOCSWIN\n");
+ if (copy_from_user(&vw, arg, sizeof(vw)))
+ return -EFAULT;
+ if (vw.flags)
+ return -EINVAL;
+ if (vw.clipcount)
+ return -EINVAL;
+ if (vw.height != 176)
+ return -EINVAL;
+ if (vw.width != 144)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+
+printk("VIDIOCGWIN\n");
+ vw.x = 0;
+ vw.y = 0;
+ vw.width = 176;
+ vw.height = 144;
+ vw.chromakey = 0;
+ vw.flags = 0;
+
+ if (copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
+
+ memset(&vm, 0, sizeof(vm));
+ vm.size = MAX_FRAME_SIZE * 2;
+ vm.frames = 2;
+ vm.offsets[0] = 0;
+ vm.offsets[1] = MAX_FRAME_SIZE;
+
+ if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+
+ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
+ return -EFAULT;
+
+printk("MCAPTURE\n");
+printk("frame: %d, size: %dx%d, format: %d\n", vm.frame, vm.width, vm.height, vm.format);
+
+ if (vm.format != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+
+ if ((vm.frame != 0) && (vm.frame != 1))
+ return -EINVAL;
+
+ cpia->frame[vm.frame].width = vm.width;
+ cpia->frame[vm.frame].height = vm.height;
+
+ /* Mark it as free */
+ cpia->frame[vm.frame].state = FRAME_READY;
+
+ return 0;
+ }
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, arg, sizeof(int)))
+ return -EFAULT;
+
+ printk("syncing to frame %d\n", frame);
+ switch (cpia->frame[frame].state) {
+ case FRAME_UNUSED:
+ return -EINVAL;
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ interruptible_sleep_on(&cpia->wq);
+ case FRAME_DONE:
+ cpia->frame[frame].state = FRAME_UNUSED;
+ break;
+ }
+ printk("synced to frame %d\n", frame);
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCGFBUF:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCGFREQ:
+ return -EINVAL;
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ return -EINVAL;
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+ int len;
+
+ printk("cpia_read: %d bytes\n", count);
+#if 0
+ len = cpia_capture(cpia, buf, count);
+
+ return len;
+#endif
+ return 0;
+}
+
+static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+ unsigned long start = (unsigned long)adr;
+ unsigned long page, pos;
+
+ printk("mmap: %d (%X) bytes\n", size, size);
+ if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+ return -EINVAL;
+
+#if 0
+ if (!cpia->fbuffer) {
+ if ((cpia->fbuffer = rvmalloc(2 * MAX_FRAME_SIZE)) == NULL)
+ return -EINVAL;
+ }
+#endif
+
+ pos = (unsigned long)cpia->fbuf;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(pos);
+ if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start+=PAGE_SIZE;
+ pos+=PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size-=PAGE_SIZE;
+ else
+ size=0;
+ }
+
+ return 0;
+}
+
+static struct video_device cpia_template = {
+ "CPiA USB Camera",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_CPIA,
+ cpia_open,
+ cpia_close,
+ cpia_read,
+ cpia_write,
+ NULL,
+ cpia_ioctl,
+ cpia_mmap,
+ cpia_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+static void usb_cpia_configure(struct usb_cpia *cpia)
+{
+ struct usb_device *dev = cpia->dev;
+ unsigned char version[4];
+ unsigned char pnpid[6];
+ unsigned char camerastat[8];
+ unsigned char *buf;
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+
+ if (usb_cpia_get_version(dev, version)) {
+ printk("cpia_get_version error\n");
+ return;
+ }
+
+ printk("cpia: Firmware v%d.%d, VC Hardware v%d.%d\n",
+ version[0], version[1], version[2], version[3]);
+
+ if (usb_cpia_get_pnp_id(dev, pnpid)) {
+ printk("cpia_get_pnp_id error\n");
+ return;
+ }
+
+ printk("cpia: PnP Id: Vendor: %X, Product: %X, Revision: %X\n",
+ (pnpid[1] << 8) + pnpid[0], (pnpid[3] << 8) + pnpid[2],
+ (pnpid[5] << 8) + pnpid[4]);
+
+ memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template));
+
+ init_waitqueue_head(&cpia->wq);
+
+ if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) {
+ printk("video_register_device failed\n");
+ return;
+ }
+
+ if (usb_cpia_goto_hi_power(dev)) {
+ printk("cpia_goto_hi_power error\n");
+ return;
+ }
+
+ if (usb_cpia_get_vp_version(dev, version)) {
+ printk("cpia_get_vp_version error\n");
+ return;
+ }
+
+ printk("cpia: VP v%d rev %d\n", version[0], version[1]);
+ printk("cpia: Camera Head ID %04X\n", (version[3] << 8) + version[2]);
+
+ /* Turn off continuous grab */
+ if (usb_cpia_set_grab_mode(dev, 1)) {
+ printk("cpia_set_grab_mode error\n");
+ return;
+ }
+
+ /* Set up the sensor to be 30fps */
+ if (usb_cpia_set_sensor_fps(dev, 1, 0)) {
+ printk("cpia_set_sensor_fps error\n");
+ return;
+ }
+
+ /* Set video into QCIF mode, and order into YUYV mode */
+ if (usb_cpia_set_format(dev, CPIA_QCIF, 1, CPIA_YUYV)) {
+ printk("cpia_set_format error\n");
+ return;
+ }
+
+ /* Turn off compression */
+ if (usb_cpia_set_compression(dev, 0, 0)) {
+ printk("cpia_set_compression error\n");
+ return;
+ }
+
+#if 0
+ if (usb_cpia_grab_frame(dev, 0)) {
+ printk("cpia_grab_frame error\n");
+ return;
+ }
+
+ if (usb_cpia_upload_frame(dev, 1)) {
+ printk("cpia_upload_frame error\n");
+ return;
+ }
+
+ buf = (void *)__get_free_page(GFP_KERNEL);
+
+ {
+ int i;
+ for (i=0;i<448;i++)
+ buf[i]=0;
+ }
+ uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), buf, 448);
+
+ {
+ int i;
+ for (i=0;i<448;i++) {
+ printk("%02X ", buf[i]);
+ if ((i % 16) == 15)
+ printk("\n");
+ }
+ printk("\n");
+ }
+
+ free_page((unsigned long)buf);
+#endif
+}
+
+static int cpia_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_cpia *cpia;
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -1;
+
+#if 0
+ /* We don't handle multi-interface hubs */
+ if (dev->config[0].bNumInterfaces != 1)
+ return -1;
+#endif
+
+ interface = &dev->config[0].altsetting[0].interface[0];
+
+ /* Is it a CPiA? */
+/*
+Apr 24 17:49:04 bjorn kernel: Vendor: 0545
+Apr 24 17:49:04 bjorn kernel: Product: 8080
+*/
+/*
+ if (dev->descriptor.idVendor != 0x0545)
+ return -1;
+ if (dev->descriptor.idProduct != 0x8080)
+ return -1;
+ if (interface->bInterfaceClass != 0xFF)
+ return -1;
+ if (interface->bInterfaceSubClass != 0xFF)
+ return -1;
+*/
+ if (dev->descriptor.idVendor != 0x0553)
+ return -1;
+ if (dev->descriptor.idProduct != 0x0002)
+ return -1;
+ if (interface->bInterfaceClass != 0xFF)
+ return -1;
+ if (interface->bInterfaceSubClass != 0x00)
+ return -1;
+
+#if 0
+ /* Multiple endpoints? What kind of mutant ninja-hub is this? */
+ if (interface->bNumEndpoints != 1)
+ return -1;
+
+ endpoint = &interface->endpoint[0];
+
+ /* Output endpoint? Curiousier and curiousier.. */
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -1;
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & 3) != 3)
+ return -1;
+#endif
+
+ /* We found a CPiA */
+ printk("USB CPiA camera found\n");
+
+ if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) {
+ printk("couldn't kmalloc cpia struct\n");
+ return -1;
+ }
+
+ memset(cpia, 0, sizeof(*cpia));
+
+ dev->private = cpia;
+ cpia->dev = dev;
+
+ usb_cpia_configure(cpia);
+
+#if 0
+ usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), pport_irq, endpoint->bInterval, pport);
+#endif
+
+ return 0;
+}
+
+static void cpia_disconnect(struct usb_device *dev)
+{
+ struct usb_cpia *cpia = dev->private;
+
+ video_unregister_device(&cpia->vdev);
+
+ /* Free the memory */
+ kfree(cpia);
+}
+
+static struct usb_driver cpia_driver = {
+ "cpia",
+ cpia_probe,
+ cpia_disconnect,
+ { NULL, NULL }
+};
+
+/*
+ * This should be a separate module.
+ */
+int usb_cpia_init(void)
+{
+ usb_register(&cpia_driver);
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_cpia_init();
+}
+void cleanup_module(void)
+{
+}
+#endif
+
diff --git a/drivers/usb/cpia.h b/drivers/usb/cpia.h
new file mode 100644
index 000000000..51ff43e79
--- /dev/null
+++ b/drivers/usb/cpia.h
@@ -0,0 +1,139 @@
+#ifndef __LINUX_CPIA_H
+#define __LINUX_CPIA_H
+
+#include <linux/list.h>
+
+#define USB_REQ_CPIA_GET_VERSION 0x01
+#define USB_REQ_CPIA_GET_PNP_ID 0x02
+#define USB_REQ_CPIA_GET_CAMERA_STATUS 0x03
+#define USB_REQ_CPIA_GOTO_HI_POWER 0x04
+#define USB_REQ_CPIA_GOTO_LO_POWER 0x05
+/* No 0x06 */
+#define USB_REQ_CPIA_GOTO_SUSPEND 0x07
+#define USB_REQ_CPIA_GOTO_PASS_THROUGH 0x08
+/* No 0x09 */
+#define USB_REQ_CPIA_MODIFY_CAMERA_STATUS 0x0A
+
+#define USB_REQ_CPIA_READ_VC_REGS 0x21
+#define USB_REQ_CPIA_WRITE_BC_REG 0x22
+#define USB_REQ_CPIA_READ_MC_PORTS 0x23
+#define USB_REQ_CPIA_WRITE_MC_PORT 0x24
+#define USB_REQ_CPIA_SET_BAUD_RATE 0x25
+#define USB_REQ_CPIA_SET_ECP_TIMING 0x26
+#define USB_REQ_CPIA_READ_IDATA 0x27
+#define USB_REQ_CPIA_WRITE_IDATA 0x28
+#define USB_REQ_CPIA_GENERIC_CALL 0x29
+#define USB_REQ_CPIA_I2CSTART 0x2A
+#define USB_REQ_CPIA_I2CSTOP 0x2B
+#define USB_REQ_CPIA_I2CWRITE 0x2C
+#define USB_REQ_CPIA_I2CREAD 0x2D
+
+#define USB_REQ_CPIA_GET_VP_VERSION 0xA1
+#define USB_REQ_CPIA_SET_COLOUR_PARAMS 0xA3
+#define USB_REQ_CPIA_SET_EXPOSURE 0xA4
+/* No 0xA5 */
+#define USB_REQ_CPIA_SET_COLOUR_BALANCE 0xA6
+#define USB_REQ_CPIA_SET_SENSOR_FPS 0xA7
+#define USB_REQ_CPIA_SET_VP_DEFAULTS 0xA8
+#define USB_REQ_CPIA_SET_APCOR 0xA9
+#define USB_REQ_CPIA_SET_FLICKER_CTRL 0xAA
+#define USB_REQ_CPIA_SET_VL_OFFSET 0xAB
+
+#define USB_REQ_CPIA_GET_COLOUR_PARAMETERS 0xB0
+#define USB_REQ_CPIA_GET_COLOUR_BALANCE 0xB1
+#define USB_REQ_CPIA_GET_EXPOSURE 0xB2
+#define USB_REQ_CPIA_SET_SENSOR_MATRIX 0xB3
+
+#define USB_REQ_CPIA_COLOUR_BARS 0xBD
+#define USB_REQ_CPIA_READ_VP_REGS 0xBE
+#define USB_REQ_CPIA_WRITE_VP_REGS 0xBF
+
+#define USB_REQ_CPIA_GRAB_FRAME 0xC1
+#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2
+#define USB_REQ_CPIA_SET_GRAB_MODE 0xC3
+#define USB_REQ_CPIA_INIT_STREAM_CAP 0xC4
+#define USB_REQ_CPIA_FINI_STREAM_CAP 0xC5
+#define USB_REQ_CPIA_START_STREAM_CAP 0xC6
+#define USB_REQ_CPIA_END_STREAM_CAP 0xC7
+#define USB_REQ_CPIA_SET_FORMAT 0xC8
+#define USB_REQ_CPIA_SET_ROI 0xC9
+#define USB_REQ_CPIA_SET_COMPRESSION 0xCA
+#define USB_REQ_CPIA_SET_COMPRESSION_TARGET 0xCB
+#define USB_REQ_CPIA_SET_YUV_THRESH 0xCC
+#define USB_REQ_CPIA_SET_COMPRESSION_PARAMS 0xCD
+#define USB_REQ_CPIA_DISCARD_FRAME 0xCE
+
+#define USB_REQ_CPIA_OUTPUT_RS232 0xE1
+#define USB_REQ_CPIA_ABORT_PROCESS 0xE4
+#define USB_REQ_CPIA_SET_DRAM_PAGE 0xE5
+#define USB_REQ_CPIA_START_DRAM_UPLOAD 0xE6
+#define USB_REQ_CPIA_START_DUMMY_STREAM 0xE8
+#define USB_REQ_CPIA_ABORT_STREAM 0xE9
+#define USB_REQ_CPIA_DOWNLOAD_DRAM 0xEA
+/* #define USB_REQ_CPIA_NULL_CMD 0x?? */
+
+#define CPIA_QCIF 0
+#define CPIA_CIF 1
+
+#define CPIA_YUYV 0
+#define CPIA_UYVY 1
+
+#define STREAM_BUF_SIZE (PAGE_SIZE * 4)
+
+#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2)
+
+enum {
+ STATE_SCANNING, /* Scanning for start */
+ STATE_HEADER, /* Parsing header */
+ STATE_LINES, /* Parsing lines */
+};
+
+struct usb_device;
+
+struct cpia_sbuf {
+ char *data;
+ int len;
+ void *isodesc;
+};
+
+enum {
+ FRAME_READY, /* Ready to grab into */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+};
+
+struct cpia_frame {
+ char *data;
+ int width;
+ int height;
+ int state;
+};
+
+struct usb_cpia {
+ struct video_device vdev;
+
+ /* Device structure */
+ struct usb_device *dev;
+
+ int streaming;
+
+ char *fbuf; /* Videodev buffer area */
+
+ int curframe;
+ struct cpia_frame frame[2]; /* Double buffering */
+
+ int receivesbuf; /* Current receiving sbuf */
+ struct cpia_sbuf sbuf[3]; /* Triple buffering */
+
+ int state; /* Current scanning state */
+ int curline;
+
+ char scratch[SCRATCH_BUF_SIZE];
+ int scratchlen;
+
+ wait_queue_head_t wq;
+};
+
+#endif
+
diff --git a/drivers/usb/keymap-mac.c b/drivers/usb/keymap-mac.c
new file mode 100644
index 000000000..48fc25e9b
--- /dev/null
+++ b/drivers/usb/keymap-mac.c
@@ -0,0 +1,50 @@
+unsigned char usb_kbd_map[256] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x0b, 0x08, 0x02,
+ 0x0e, 0x03, 0x05, 0x04, 0x22, 0x26, 0x28, 0x25,
+
+ 0x2e, 0x2d, 0x1f, 0x23, 0x0c, 0x0f, 0x01, 0x11,
+ 0x20, 0x09, 0x0d, 0x07, 0x10, 0x06, 0x12, 0x13,
+
+ 0x14, 0x15, 0x17, 0x16, 0x1a, 0x1c, 0x19, 0x1d,
+ 0x24, 0x35, 0x33, 0x30, 0x31, 0x1b, 0x18, 0x21,
+
+ 0x1e, 0x2a, 0x00, 0x29, 0x27, 0x32, 0x2b, 0x2f,
+ 0x2c, 0x39, 0x7a, 0x78, 0x63, 0x76, 0x60, 0x61,
+
+ 0x62, 0x64, 0x65, 0x6d, 0x67, 0x6f, 0x69, 0x6b,
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0x77, 0x79, 0x3c,
+
+ 0x3b, 0x3d, 0x3e, 0x47, 0x4b, 0x43, 0x4e, 0x45,
+ 0x4c, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+
+ 0x5b, 0x5c, 0x52, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x38, 0x3a, 0x37, 0x7d, 0x7b, 0x7c, 0x37,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/drivers/usb/maps/mac.map b/drivers/usb/maps/mac.map
new file mode 100644
index 000000000..dd601f76d
--- /dev/null
+++ b/drivers/usb/maps/mac.map
@@ -0,0 +1,350 @@
+# Kernel keymap for Macintoshes. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+#
+# Fixups:
+keycode 0x69 = Print_Screen
+keycode 0x6b = F14
+keycode 0x37 = Window_R
+#
+#keycode 0x00 = a
+# hack!
+keycode 0x80 = a
+ altgr keycode 0x00 = Hex_A
+keycode 0x01 = s
+keycode 0x02 = d
+ altgr keycode 0x02 = Hex_D
+keycode 0x03 = f
+ altgr keycode 0x03 = Hex_F
+keycode 0x04 = h
+keycode 0x05 = g
+keycode 0x06 = z
+keycode 0x07 = x
+keycode 0x08 = c
+ altgr keycode 0x08 = Hex_C
+keycode 0x09 = v
+keycode 0x0a =
+keycode 0x0b = b
+ altgr keycode 0x0b = Hex_B
+keycode 0x0c = q
+keycode 0x0d = w
+keycode 0x0e = e
+ altgr keycode 0x0e = Hex_E
+keycode 0x0f = r
+keycode 0x10 = y
+keycode 0x11 = t
+keycode 0x12 = one exclam
+ alt keycode 0x12 = Meta_one
+keycode 0x13 = two at at
+ control keycode 0x13 = nul
+ shift control keycode 0x13 = nul
+ alt keycode 0x13 = Meta_two
+keycode 0x14 = three numbersign
+ control keycode 0x14 = Escape
+ alt keycode 0x14 = Meta_three
+keycode 0x15 = four dollar dollar
+ control keycode 0x15 = Control_backslash
+ alt keycode 0x15 = Meta_four
+keycode 0x16 = six asciicircum
+ control keycode 0x16 = Control_asciicircum
+ alt keycode 0x16 = Meta_six
+keycode 0x17 = five percent
+ control keycode 0x17 = Control_bracketright
+ alt keycode 0x17 = Meta_five
+keycode 0x18 = equal plus
+ alt keycode 0x18 = Meta_equal
+keycode 0x19 = nine parenleft bracketright
+ alt keycode 0x19 = Meta_nine
+keycode 0x1a = seven ampersand braceleft
+ control keycode 0x1a = Control_underscore
+ alt keycode 0x1a = Meta_seven
+keycode 0x1b = minus underscore backslash
+ control keycode 0x1b = Control_underscore
+ shift control keycode 0x1b = Control_underscore
+ alt keycode 0x1b = Meta_minus
+keycode 0x1c = eight asterisk bracketleft
+ control keycode 0x1c = Delete
+ alt keycode 0x1c = Meta_eight
+keycode 0x1d = zero parenright braceright
+ alt keycode 0x1d = Meta_zero
+keycode 0x1e = bracketright braceright asciitilde
+ control keycode 0x1e = Control_bracketright
+ alt keycode 0x1e = Meta_bracketright
+keycode 0x1f = o
+keycode 0x20 = u
+keycode 0x21 = bracketleft braceleft
+ control keycode 0x21 = Escape
+ alt keycode 0x21 = Meta_bracketleft
+keycode 0x22 = i
+keycode 0x23 = p
+keycode 0x24 = Return
+ alt keycode 0x24 = Meta_Control_m
+keycode 0x25 = l
+keycode 0x26 = j
+keycode 0x27 = apostrophe quotedbl
+ control keycode 0x27 = Control_g
+ alt keycode 0x27 = Meta_apostrophe
+keycode 0x28 = k
+keycode 0x29 = semicolon colon
+ alt keycode 0x29 = Meta_semicolon
+keycode 0x2a = backslash bar
+ control keycode 0x2a = Control_backslash
+ alt keycode 0x2a = Meta_backslash
+keycode 0x2b = comma less
+ alt keycode 0x2b = Meta_comma
+keycode 0x2c = slash question
+ control keycode 0x2c = Delete
+ alt keycode 0x2c = Meta_slash
+keycode 0x2d = n
+keycode 0x2e = m
+keycode 0x2f = period greater
+ control keycode 0x2f = Compose
+ alt keycode 0x2f = Meta_period
+keycode 0x30 = Tab Tab
+ alt keycode 0x30 = Meta_Tab
+keycode 0x31 = space space
+ control keycode 0x31 = nul
+ alt keycode 0x31 = Meta_space
+keycode 0x32 = grave asciitilde
+ control keycode 0x32 = nul
+ alt keycode 0x32 = Meta_grave
+keycode 0x33 = Delete Delete
+ control keycode 0x33 = BackSpace
+ alt keycode 0x33 = Meta_Delete
+keycode 0x34 =
+keycode 0x35 = Escape Escape
+ alt keycode 0x35 = Meta_Escape
+keycode 0x36 = Control
+keycode 0x37 = Window
+keycode 0x38 = Shift
+keycode 0x39 = Caps_Lock
+keycode 0x3a = Alt
+keycode 0x3b = Left
+ alt keycode 0x3b = Decr_Console
+keycode 0x3c = Right
+ alt keycode 0x3c = Incr_Console
+keycode 0x3d = Down
+keycode 0x3e = Up
+keycode 0x3f =
+keycode 0x40 =
+keycode 0x41 = KP_Period
+keycode 0x42 =
+keycode 0x43 = KP_Multiply
+keycode 0x44 =
+keycode 0x45 = KP_Add
+keycode 0x46 =
+keycode 0x47 = Num_Lock
+# shift keycode 0x47 = Bare_Num_Lock
+keycode 0x48 =
+keycode 0x49 =
+keycode 0x4a =
+keycode 0x4b = KP_Divide
+keycode 0x4c = KP_Enter
+keycode 0x4d =
+keycode 0x4e = KP_Subtract
+keycode 0x4f =
+keycode 0x50 =
+keycode 0x51 =
+#keycode 0x51 = KP_Equals
+keycode 0x52 = KP_0
+ alt keycode 0x52 = Ascii_0
+ altgr keycode 0x52 = Hex_0
+keycode 0x53 = KP_1
+ alt keycode 0x53 = Ascii_1
+ altgr keycode 0x53 = Hex_1
+keycode 0x54 = KP_2
+ alt keycode 0x54 = Ascii_2
+ altgr keycode 0x54 = Hex_2
+keycode 0x55 = KP_3
+ alt keycode 0x55 = Ascii_3
+ altgr keycode 0x55 = Hex_3
+keycode 0x56 = KP_4
+ alt keycode 0x56 = Ascii_4
+ altgr keycode 0x56 = Hex_4
+keycode 0x57 = KP_5
+ alt keycode 0x57 = Ascii_5
+ altgr keycode 0x57 = Hex_5
+keycode 0x58 = KP_6
+ alt keycode 0x58 = Ascii_6
+ altgr keycode 0x58 = Hex_6
+keycode 0x59 = KP_7
+ alt keycode 0x59 = Ascii_7
+ altgr keycode 0x59 = Hex_7
+keycode 0x5b = KP_8
+ alt keycode 0x5b = Ascii_8
+ altgr keycode 0x5b = Hex_8
+keycode 0x5c = KP_9
+ alt keycode 0x5c = Ascii_9
+ altgr keycode 0x5c = Hex_9
+keycode 0x5d =
+keycode 0x5e =
+keycode 0x5f =
+keycode 0x60 = F5 F15 Console_17
+ control keycode 0x60 = F5
+ alt keycode 0x60 = Console_5
+ control alt keycode 0x60 = Console_5
+keycode 0x61 = F6 F16 Console_18
+ control keycode 0x61 = F6
+ alt keycode 0x61 = Console_6
+ control alt keycode 0x61 = Console_6
+keycode 0x62 = F7 F17 Console_19
+ control keycode 0x62 = F7
+ alt keycode 0x62 = Console_7
+ control alt keycode 0x62 = Console_7
+keycode 0x63 = F3 F13 Console_15
+ control keycode 0x63 = F3
+ alt keycode 0x63 = Console_3
+ control alt keycode 0x63 = Console_3
+keycode 0x64 = F8 F18 Console_20
+ control keycode 0x64 = F8
+ alt keycode 0x64 = Console_8
+ control alt keycode 0x64 = Console_8
+keycode 0x65 = F9 F19 Console_21
+ control keycode 0x65 = F9
+ alt keycode 0x65 = Console_9
+ control alt keycode 0x65 = Console_9
+keycode 0x66 =
+keycode 0x67 = F11 F11 Console_23
+ control keycode 0x67 = F11
+ alt keycode 0x67 = Console_11
+ control alt keycode 0x67 = Console_11
+keycode 0x68 =
+keycode 0x69 = F13
+keycode 0x6a =
+keycode 0x6b = Scroll_Lock Show_Memory Show_Registers
+ control keycode 0x6b = Show_State
+ alt keycode 0x6b = Scroll_Lock
+keycode 0x6c =
+keycode 0x6d = F10 F20 Console_22
+ control keycode 0x6d = F10
+ alt keycode 0x6d = Console_10
+ control alt keycode 0x6d = Console_10
+keycode 0x6e =
+keycode 0x6f = F12 F12 Console_24
+ control keycode 0x6f = F12
+ alt keycode 0x6f = Console_12
+ control alt keycode 0x6f = Console_12
+keycode 0x70 =
+keycode 0x71 = Pause
+keycode 0x72 = Insert
+keycode 0x73 = Home
+keycode 0x74 = Prior
+ shift keycode 0x74 = Scroll_Backward
+keycode 0x75 = Remove
+keycode 0x76 = F4 F14 Console_16
+ control keycode 0x76 = F4
+ alt keycode 0x76 = Console_4
+ control alt keycode 0x76 = Console_4
+keycode 0x77 = End
+keycode 0x78 = F2 F12 Console_14
+ control keycode 0x78 = F2
+ alt keycode 0x78 = Console_2
+ control alt keycode 0x78 = Console_2
+keycode 0x79 = Next
+ shift keycode 0x79 = Scroll_Forward
+keycode 0x7a = F1 F11 Console_13
+ control keycode 0x7a = F1
+ alt keycode 0x7a = Console_1
+ control alt keycode 0x7a = Console_1
+keycode 0x7b = Shift_R
+keycode 0x7c = Alt_R
+keycode 0x7d = Control_R
+keycode 0x7e =
+keycode 0x7f =
+#keycode 0x7f = Power
+ control shift keycode 0x7f = Boot
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
diff --git a/drivers/usb/mkmap.adb b/drivers/usb/mkmap.adb
new file mode 100644
index 000000000..45020d5cc
--- /dev/null
+++ b/drivers/usb/mkmap.adb
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+
+($ME = $0) =~ s|.*/||;
+
+$file = "maps/mac.map";
+$line = 1;
+open(PC, $file) || die("$!");
+while(<PC>)
+{
+ if(/^\s*keycode\s+(\d+|0x[0-9a-fA-F]+)\s*=\s*(\S+)/)
+ {
+ my($idx) = $1;
+ my($sym) = $2;
+ if ($idx =~ "0x.*") {
+ $idx = hex($idx);
+ } else {
+ $idx = int($idx);
+ }
+ if(defined($map{uc($sym)}))
+ {
+ # print STDERR "$file:$line: warning: `$sym' redefined\n";
+ }
+ $map{uc($sym)} = $idx;
+ }
+ $line++;
+}
+close(PC);
+
+# $file = "maps/fixup.map";
+# $line = 1;
+# open(FIXUP, $file) || die("$!");
+# while(<FIXUP>)
+# {
+# if(/^\s*keycode\s+(\d+)\s*=\s*/)
+# {
+# my($idx) = int($1);
+# for $sym (split(/\s+/, $'))
+# {
+# $map{uc($sym)} = $idx;
+# }
+# }
+# $line++;
+# }
+# close(FIXUP);
+
+$file = "maps/usb.map";
+$line = 1;
+open(USB, $file) || die("$!");
+while(<USB>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*/)
+ {
+ my($idx) = int($1);
+ for $sym (split(/\s+/, $'))
+ {
+ my($val) = $map{uc($sym)};
+ $map[$idx] = $val;
+ if(!defined($val))
+ {
+ print STDERR "$file:$line: warning: `$sym' undefined\n";
+ }
+ else
+ {
+ last;
+ }
+ }
+ }
+ $line++;
+}
+close(USB);
+
+print "unsigned char usb_kbd_map[256] = \n{\n";
+for($x = 0; $x < 32; $x++)
+{
+ if($x && !($x % 2))
+ {
+ print "\n";
+ }
+ print " ";
+ for($y = 0; $y < 8; $y++)
+ {
+ my($idx) = $x * 8 + $y;
+ print sprintf(" 0x%02x,",
+ int(defined($map[$idx]) ? $map[$idx]:0));
+ }
+ print "\n";
+}
+print "};\n";
diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c
new file mode 100644
index 000000000..88723c6ff
--- /dev/null
+++ b/drivers/usb/printer.c
@@ -0,0 +1,413 @@
+
+/* Driver for USB Printers
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#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/lp.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define NAK_TIMEOUT (HZ) /* stall wait for printer */
+#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */
+
+#ifndef USB_PRINTER_MAJOR
+#define USB_PRINTER_MAJOR 0
+#endif
+
+static int mymajor = USB_PRINTER_MAJOR;
+
+#define MAX_PRINTERS 8
+
+struct pp_usb_data {
+ struct usb_device *pusb_dev;
+ __u8 isopen; /* nz if open */
+ __u8 noinput; /* nz if no input stream */
+ __u8 minor; /* minor number of device */
+ __u8 status; /* last status from device */
+ int maxin, maxout; /* max transfer size in and out */
+ char *obuf; /* transfer buffer (out only) */
+ wait_queue_head_t wait_q; /* for timeouts */
+ unsigned int last_error; /* save for checking */
+};
+
+static struct pp_usb_data *minor_data[MAX_PRINTERS];
+
+#define PPDATA(x) ((struct pp_usb_data *)(x))
+
+unsigned char printer_read_status(struct pp_usb_data *p)
+{
+ __u8 status;
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 1;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) {
+ return 0;
+ }
+ return status;
+}
+
+static int printer_check_status(struct pp_usb_data *p)
+{
+ unsigned int last = p->last_error;
+ unsigned char status = printer_read_status(p);
+
+ if (status & LP_PERRORP)
+ /* No error. */
+ last = 0;
+ else if ((status & LP_POUTPA)) {
+ if (last != LP_POUTPA) {
+ last = LP_POUTPA;
+ printk(KERN_INFO "usblp%d out of paper\n", p->minor);
+ }
+ } else if (!(status & LP_PSELECD)) {
+ if (last != LP_PSELECD) {
+ last = LP_PSELECD;
+ printk(KERN_INFO "usblp%d off-line\n", p->minor);
+ }
+ } else {
+ if (last != LP_PERRORP) {
+ last = LP_PERRORP;
+ printk(KERN_INFO "usblp%d on fire\n", p->minor);
+ }
+ }
+
+ p->last_error = last;
+
+ return status;
+}
+
+void printer_reset(struct pp_usb_data *p)
+{
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER;
+ dr.request = 2;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+ dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+static int open_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p;
+
+ if(MINOR(inode->i_rdev) >= MAX_PRINTERS ||
+ !minor_data[MINOR(inode->i_rdev)]) {
+ return -ENODEV;
+ }
+
+ p = minor_data[MINOR(inode->i_rdev)];
+ p->minor = MINOR(inode->i_rdev);
+
+ if (p->isopen++) {
+ return -EBUSY;
+ }
+ if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ printer_check_status(p);
+
+
+ file->private_data = p;
+// printer_reset(p);
+ init_waitqueue_head(&p->wait_q);
+ return 0;
+}
+
+static int close_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p = file->private_data;
+
+ free_page((unsigned long)p->obuf);
+ p->isopen = 0;
+ file->private_data = NULL;
+ if(!p->pusb_dev) {
+ minor_data[p->minor] = NULL;
+ kfree(p);
+
+ MOD_DEC_USE_COUNT;
+
+ }
+ return 0;
+}
+
+static ssize_t write_printer(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned long partial;
+ int result;
+ int maxretry;
+
+ do {
+ char *obuf = p->obuf;
+ unsigned long thistime;
+
+ thistime = copy_size = (count > p->maxout) ? p->maxout : count;
+ if (copy_from_user(p->obuf, buffer, copy_size))
+ return -EFAULT;
+ maxretry = MAX_RETRY_COUNT;
+ while (thistime) {
+ if (!p->pusb_dev)
+ return -ENODEV;
+ if (signal_pending(current)) {
+ return bytes_written ? bytes_written : -EINTR;
+ }
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial);
+ if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
+ if(!maxretry--)
+ return -ETIME;
+ interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT);
+ continue;
+ } else if (!result & partial) {
+ obuf += partial;
+ thistime -= partial;
+ } else
+ break;
+ };
+ if (result) {
+ /* whoops - let's reset and fail the request */
+// printk("Whoops - %x\n", result);
+ printer_reset(p);
+ interruptible_sleep_on_timeout(&p->wait_q, 5*HZ); /* let reset do its stuff */
+ 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_printer(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ int read_count;
+ int this_read;
+ char buf[64];
+ unsigned long partial;
+ int result;
+
+ if (p->noinput)
+ return -EINVAL;
+
+ read_count = 0;
+ while (count) {
+ if (signal_pending(current)) {
+ return read_count ? read_count : -EINTR;
+ }
+ if (!p->pusb_dev)
+ return -ENODEV;
+ this_read = (count > sizeof(buf)) ? sizeof(buf) : count;
+
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial);
+
+ /* unlike writes, we don't retry a NAK, just stop now */
+ if (!result & partial)
+ count = this_read = partial;
+ else if (result)
+ return -EIO;
+
+ if (this_read) {
+ if (copy_to_user(buffer, p->obuf, this_read))
+ return -EFAULT;
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ return read_count;
+}
+
+static int printer_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+
+ /*
+ * FIXME - this will not cope with combined printer/scanners
+ */
+ if (dev->descriptor.bDeviceClass != 7 ||
+ dev->descriptor.bNumConfigurations != 1 ||
+ dev->config[0].bNumInterfaces != 1) {
+ return -1;
+ }
+
+ interface = dev->config->altsetting->interface;
+
+ /* Lets be paranoid (for the moment)*/
+ if (interface->bInterfaceClass != 7 ||
+ interface->bInterfaceSubClass != 1 ||
+ (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)||
+ interface->bNumEndpoints > 2) {
+ return -1;
+ }
+
+ if (interface->endpoint[0].bEndpointAddress != 0x01 ||
+ interface->endpoint[0].bmAttributes != 0x02 ||
+ (interface->bNumEndpoints > 1 && (
+ interface->endpoint[1].bEndpointAddress != 0x82 ||
+ interface->endpoint[1].bmAttributes != 0x02))) {
+ return -1;
+ }
+
+ for (i=0; i<MAX_PRINTERS; i++) {
+ if (!minor_data[i])
+ break;
+ }
+ if (i >= MAX_PRINTERS) {
+ return -1;
+ }
+
+ printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum);
+
+ if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) {
+ printk( KERN_DEBUG "usb_printer: no memory!\n");
+ return -1;
+ }
+
+ memset(dev->private, 0, sizeof(struct pp_usb_data));
+ minor_data[i] = PPDATA(dev->private);
+ minor_data[i]->minor = i;
+ minor_data[i]->pusb_dev = dev;
+ minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16;
+ if (minor_data[i]->maxout > PAGE_SIZE) {
+ minor_data[i]->maxout = PAGE_SIZE;
+ }
+ if (interface->bInterfaceProtocol != 2)
+ minor_data[i]->noinput = 1;
+ else {
+ minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize;
+ }
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk(KERN_INFO " Failed to set configuration\n");
+ return -1;
+ }
+#if 0
+ {
+ __u8 status;
+ __u8 ieee_id[64];
+ devrequest dr;
+
+ /* Lets get the device id if possible */
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 0;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = sizeof(ieee_id) - 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 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 " Printer ID is %s\n", &ieee_id[2]);
+ }
+ status = printer_read_status(PPDATA(dev->private));
+ printk(KERN_INFO " Status is %s,%s,%s\n",
+ (status & 0x10) ? "Selected" : "Not Selected",
+ (status & 0x20) ? "No Paper" : "Paper",
+ (status & 0x08) ? "No Error" : "Error");
+ }
+#endif
+ return 0;
+}
+
+static void printer_disconnect(struct usb_device *dev)
+{
+ struct pp_usb_data *pp = dev->private;
+
+ if (pp->isopen) {
+ /* better let it finish - the release will do whats needed */
+ pp->pusb_dev = NULL;
+ return;
+ }
+ minor_data[pp->minor] = NULL;
+ kfree(pp);
+ dev->private = NULL; /* just in case */
+ MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver printer_driver = {
+ "printer",
+ printer_probe,
+ printer_disconnect,
+ { NULL, NULL }
+};
+
+static struct file_operations usb_printer_fops = {
+ NULL, /* seek */
+ read_printer,
+ write_printer,
+ NULL, /* readdir */
+ NULL, /* poll - out for the moment */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_printer,
+ NULL, /* flush ? */
+ close_printer,
+ NULL,
+ NULL
+};
+
+int usb_printer_init(void)
+{
+ int result;
+
+ MOD_INC_USE_COUNT;
+
+ if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) {
+ printk(KERN_WARNING "usbprinter: Cannot register device\n");
+ return result;
+ }
+ if (mymajor == 0) {
+ mymajor = result;
+ }
+ usb_register(&printer_driver);
+ printk(KERN_INFO "USB Printer support registered.\n");
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+ return usb_printer_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned int offset;
+
+ usb_deregister(&printer_driver);
+ unregister_chrdev(mymajor, "usblplp");
+}
+#endif
diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c
new file mode 100644
index 000000000..f9f73a051
--- /dev/null
+++ b/drivers/usb/usb-core.c
@@ -0,0 +1,96 @@
+/*
+ * driver/usb/usb-core.c
+ *
+ * (C) Copyright David Waite 1999
+ * based on code from usb.c, by Linus Torvolds
+ *
+ * The purpose of this file is to pull any and all generic modular code from
+ * usb.c and put it in a separate file. This way usb.c is kept as a generic
+ * library, while this file handles starting drivers, etc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include "inits.h"
+#include "usb.h"
+
+#ifndef CONFIG_USB_MODULE
+# ifdef CONFIG_USB_UHCI
+ int uhci_init(void);
+# endif
+# ifdef CONFIG_USB_OHCI
+ int ohci_init(void);
+# endif
+# ifdef CONFIG_USB_OHCI_HCD
+ int ohci_hcd_init(void);
+# endif
+#endif
+
+int usb_init(void)
+{
+#ifndef CONFIG_USB_MODULE
+# ifdef CONFIG_USB_UHCI
+ uhci_init();
+# endif
+# ifdef CONFIG_USB_OHCI
+ ohci_init();
+# endif
+# ifdef CONFIG_USB_OHCI_HCD
+ ohci_hcd_init();
+# endif
+# ifdef CONFIG_USB_MOUSE
+ usb_mouse_init();
+# endif
+# ifdef CONFIG_USB_KBD
+ usb_kbd_init();
+# endif
+# ifdef CONFIG_USB_AUDIO
+ usb_audio_init();
+# endif
+# ifdef CONFIG_USB_ACM
+ usb_acm_init();
+# endif
+# ifdef CONFIG_USB_PRINTER
+ usb_print_init();
+# endif
+# ifdef CONFIG_USB_CPIA
+ usb_cpia_init();
+# endif
+# ifdef CONFIG_USB_HUB
+ usb_hub_init();
+# endif
+# ifdef CONFIG_USB_SCSI
+ usb_scsi_init();
+# endif
+#endif
+ return 0;
+}
+/*
+ * Clean up when unloading the module
+ */
+void cleanup_drivers(void)
+{
+#ifndef MODULE
+# ifdef CONFIG_USB_HUB
+ usb_hub_cleanup();
+# endif
+# ifdef CONFIG_USB_MOUSE
+ usb_mouse_cleanup();
+# endif
+#endif
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_init();
+}
+void cleanup_module(void)
+{
+ cleanup_drivers();
+}
+#endif
+
+
diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c
new file mode 100644
index 000000000..655045bea
--- /dev/null
+++ b/drivers/usb/usb_scsi.c
@@ -0,0 +1,1098 @@
+
+/* Driver for USB scsi like devices
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ * This driver is scitzoid - it makes a USB device appear as both a SCSI device
+ * and a character device. The latter is only available if the device has an
+ * interrupt endpoint, and is used specifically to receive interrupt events.
+ *
+ * In order to support various 'strange' devices, this module supports plug in
+ * device specific filter modules, which can do their own thing when required.
+ *
+ * Further reference.
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the transformation of SCSI command blocks to the
+ * equivalent USB control and data transfer required.
+ * It is important to note that in a number of cases this class exhibits
+ * class-specific exemptions from the USB specification. Notably the
+ * usage of NAK, STALL and ACK differs from the norm, in that they are
+ * used to communicate wait, failed and OK on SCSI commands.
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Basically, this stuff is WIERD!!
+ *
+ */
+
+#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 <asm/spinlock.h>
+#include <linux/smp_lock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb_scsi.h"
+
+/* direction table (what a pain) */
+
+unsigned char us_direction[256/8] = {
+
+#include "usb_scsi_dt.c"
+
+};
+
+/*
+ * Per device data
+ */
+
+static int my_host_number;
+
+int usbscsi_debug = 1;
+
+struct us_data {
+ struct us_data *next; /* next device */
+ struct usb_device *pusb_dev;
+ struct usb_scsi_filter *filter; /* filter driver */
+ void *fdata; /* filter data */
+ unsigned int flags; /* from filter initially*/
+ __u8 ep_in; /* in endpoint */
+ __u8 ep_out; /* out ....... */
+ __u8 ep_int; /* interrupt . */
+ __u8 subclass; /* as in overview */
+ __u8 protocol; /* .............. */
+ int (*pop)(Scsi_Cmnd *); /* protocol specific do cmd */
+ GUID(guid); /* unique dev id */
+ struct Scsi_Host *host; /* our dummy host data */
+ Scsi_Host_Template *htmplt; /* own host template */
+ int host_number; /* to find us */
+ int host_no; /* allocated by scsi */
+ int fixedlength; /* expand commands */
+ Scsi_Cmnd *srb; /* current srb */
+ int action; /* what to do */
+ wait_queue_head_t waitq; /* thread waits */
+ wait_queue_head_t ip_waitq; /* for CBI interrupts */
+ __u16 ip_data; /* interrupt data */
+ int ip_wanted; /* needed */
+ int pid; /* control thread */
+ struct semaphore *notify; /* wait for thread to begin */
+};
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND 1
+#define US_ACT_ABORT 2
+#define US_ACT_DEVICE_RESET 3
+#define US_ACT_BUS_RESET 4
+#define US_ACT_HOST_RESET 5
+
+static struct proc_dir_entry proc_usb_scsi =
+{
+ PROC_SCSI_USB_SCSI,
+ 0,
+ NULL,
+ S_IFDIR | S_IRUGO | S_IXUGO,
+ 2
+};
+
+static struct us_data *us_list;
+
+static struct usb_scsi_filter *filters;
+
+static int scsi_probe(struct usb_device *dev);
+static void scsi_disconnect(struct usb_device *dev);
+static struct usb_driver scsi_driver = {
+ "usb_scsi",
+ scsi_probe,
+ scsi_disconnect,
+ { NULL, NULL }
+};
+
+/* Data handling, using SG if required */
+
+static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+{
+ int max_size = usb_maxpacket(us->pusb_dev, pipe) * 16;
+ int this_xfer;
+ int result;
+ unsigned long partial;
+ int maxtry = 100;
+ while (length) {
+ this_xfer = length > max_size ? max_size : length;
+ length -= this_xfer;
+ do {
+ US_DEBUGP("Bulk xfer %x(%d)\n", (unsigned int)buf, this_xfer);
+ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, pipe, buf,
+ this_xfer, &partial);
+
+ /* we want to retry if the device reported NAK */
+ if (result == USB_ST_TIMEOUT) {
+ if (!maxtry--)
+ break;
+ this_xfer -= partial;
+ buf += partial;
+ } else if (!result && partial != this_xfer) {
+ /* short data - assume end */
+ result = USB_ST_DATAUNDERRUN;
+ break;
+ } else
+ break;
+ } while ( this_xfer );
+ if (result)
+ return result;
+ buf += this_xfer;
+ }
+ return 0;
+
+}
+static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ int i;
+ int result = -1;
+
+ if (srb->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+ for (i = 0; i < srb->use_sg; i++) {
+ result = us_one_transfer(us, dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out),
+ sg[i].address, sg[i].length);
+ if (result)
+ break;
+ }
+ return result;
+ }
+ else
+ return us_one_transfer(us, dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out),
+ srb->request_buffer, srb->request_bufflen);
+}
+
+static unsigned int us_transfer_length(Scsi_Cmnd *srb)
+{
+ int i;
+ unsigned int total = 0;
+
+ /* always zero for some commands */
+ switch (srb->cmnd[0]) {
+ case SEEK_6:
+ case SEEK_10:
+ case REZERO_UNIT:
+ case ALLOW_MEDIUM_REMOVAL:
+ case START_STOP:
+ case TEST_UNIT_READY:
+ return 0;
+
+ default:
+ break;
+ }
+
+ if (srb->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+ for (i = 0; i < srb->use_sg; i++) {
+ total += sg[i].length;
+ }
+ return total;
+ }
+ else
+ return srb->request_bufflen;
+
+}
+
+static int pop_CBI_irq(int state, void *buffer, void *dev_id)
+{
+ struct us_data *us = (struct us_data *)dev_id;
+
+ if (state != USB_ST_REMOVED) {
+ us->ip_data = *(__u16 *)buffer;
+ us->ip_wanted = 0;
+ }
+ wake_up(&us->ip_waitq);
+
+ /* we dont want another interrupt */
+
+ return 0;
+}
+static int pop_CB_command(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ devrequest dr;
+ unsigned char cmd[16];
+ int result;
+ int retry = 1;
+ int done_start = 0;
+
+ while (retry--) {
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+ dr.request = US_CBI_ADSC;
+ dr.value = 0;
+ dr.index = us->pusb_dev->ifnum;
+ dr.length = srb->cmd_len;
+
+ if (us->flags & US_FL_FIXED_COMMAND) {
+ dr.length = us->fixedlength;
+ memset(cmd, 0, us->fixedlength);
+
+ /* fix some commands */
+
+ switch (srb->cmnd[0]) {
+ case WRITE_6:
+ case READ_6:
+ cmd[0] = srb->cmnd[0] | 0x20;
+ cmd[1] = srb->cmnd[1] & 0xE0;
+ cmd[2] = 0;
+ cmd[3] = srb->cmnd[1] & 0x1F;
+ cmd[4] = srb->cmnd[2];
+ cmd[5] = srb->cmnd[3];
+ cmd[8] = srb->cmnd[4];
+ break;
+
+ case MODE_SENSE:
+ case MODE_SELECT:
+ cmd[0] = srb->cmnd[0] | 0x40;
+ cmd[1] = srb->cmnd[1];
+ cmd[2] = srb->cmnd[2];
+ cmd[8] = srb->cmnd[4];
+ break;
+
+ default:
+ memcpy(cmd, srb->cmnd, srb->cmd_len);
+ break;
+ }
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ &dr, cmd, us->fixedlength);
+ if (!done_start && us->subclass == US_SC_UFI && cmd[0] == TEST_UNIT_READY && result) {
+ /* as per spec try a start command, wait and retry */
+
+ done_start++;
+ cmd[0] = START_STOP;
+ cmd[4] = 1; /* start */
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ &dr, cmd, us->fixedlength);
+ wait_ms(100);
+ retry++;
+ continue;
+ }
+ } else
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ &dr, srb->cmnd, srb->cmd_len);
+ if (result != USB_ST_STALL && result != USB_ST_TIMEOUT)
+ return result;
+ }
+ return result;
+}
+
+/* Protocol command handlers */
+
+static int pop_CBI(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ int result;
+
+ /* run the command */
+
+ if ((result = pop_CB_command(srb))) {
+ US_DEBUGP("CBI command %x\n", result);
+ if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
+ return (DID_OK << 16) | 2;
+ return DID_ABORT << 16;
+ }
+
+ /* transfer the data */
+
+ if (us_transfer_length(srb)) {
+ result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ if (result && result != USB_ST_DATAUNDERRUN) {
+ US_DEBUGP("CBI transfer %x\n", result);
+ return DID_ABORT << 16;
+ }
+ }
+
+ /* get status */
+
+ if (us->protocol == US_PR_CBI) {
+ /* get from interrupt pipe */
+
+ /* add interrupt transfer, marked for removal */
+ us->ip_wanted = 1;
+ result = us->pusb_dev->bus->op->request_irq(us->pusb_dev,
+ usb_rcvctrlpipe(us->pusb_dev, us->ep_int),
+ pop_CBI_irq, 0, (void *)us);
+ if (result) {
+ US_DEBUGP("No interrupt for CBI %x\n", result);
+ return DID_ABORT << 16;
+ }
+ sleep_on(&us->ip_waitq);
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return DID_ABORT << 16;
+ }
+
+ US_DEBUGP("Got interrupt data %x\n", us->ip_data);
+
+ /* sort out what it means */
+
+ if (us->subclass == US_SC_UFI) {
+ /* gives us asc and ascq, as per request sense */
+
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return DID_OK << 16;
+ else
+ return (DID_OK << 16) + ((us->ip_data & 0xff) ? 2 : 0);
+ }
+ if (us->ip_data & 0xff) {
+ US_DEBUGP("Bad CBI interrupt data %x\n", us->ip_data);
+ return DID_ABORT << 16;
+ }
+ return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0);
+ } else {
+ /* get from where? */
+ }
+ return DID_ERROR << 16;
+}
+
+static int pop_Bulk_reset(struct us_data *us)
+{
+ devrequest dr;
+ int result;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+ dr.request = US_BULK_RESET;
+ dr.value = US_BULK_RESET_SOFT;
+ dr.index = 0;
+ dr.length = 0;
+
+ US_DEBUGP("Bulk soft reset\n");
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
+ if (result) {
+ US_DEBUGP("Bulk soft reset failed %d\n", result);
+ dr.value = US_BULK_RESET_HARD;
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
+ if (result)
+ US_DEBUGP("Bulk hard reset failed %d\n", result);
+ }
+ usb_clear_halt(us->pusb_dev, us->ep_in | 0x80);
+ usb_clear_halt(us->pusb_dev, us->ep_out);
+ return result;
+}
+/*
+ * The bulk only protocol handler.
+ * Uses the in and out endpoints to transfer commands and data (nasty)
+ */
+static int pop_Bulk(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ struct bulk_cb_wrap bcb;
+ struct bulk_cs_wrap bcs;
+ int result;
+ unsigned long partial;
+ int stall;
+
+ /* set up the command wrapper */
+
+ bcb.Signature = US_BULK_CB_SIGN;
+ bcb.DataTransferLength = us_transfer_length(srb);;
+ bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
+ bcb.Tag = srb->serial_number;
+ bcb.Lun = 0;
+ memset(bcb.CDB, 0, sizeof(bcb.CDB));
+ memcpy(bcb.CDB, srb->cmnd, srb->cmd_len);
+ if (us->flags & US_FL_FIXED_COMMAND) {
+ bcb.Length = us->fixedlength;
+ } else {
+ bcb.Length = srb->cmd_len;
+ }
+
+ /* send it to out endpoint */
+
+ US_DEBUGP("Bulk command S %x T %x L %d F %d CL %d\n", bcb.Signature,
+ bcb.Tag, bcb.DataTransferLength, bcb.Flags, bcb.Length);
+ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out), &bcb,
+ US_BULK_CB_WRAP_LEN, &partial);
+ if (result) {
+ US_DEBUGP("Bulk command result %x\n", result);
+ return DID_ABORT << 16;
+ }
+
+ //return DID_BAD_TARGET << 16;
+ /* send/receive data */
+
+ if (bcb.DataTransferLength) {
+ result = us_transfer(srb, bcb.Flags);
+ if (result && result != USB_ST_DATAUNDERRUN && result != USB_ST_STALL) {
+ US_DEBUGP("Bulk transfer result %x\n", result);
+ return DID_ABORT << 16;
+ }
+ }
+
+ /* get status */
+
+
+ stall = 0;
+ do {
+ //usb_settoggle(us->pusb_dev, us->ep_in, 0); /* AAARgh!! */
+ US_DEBUGP("Toggle is %d\n", usb_gettoggle(us->pusb_dev, us->ep_in));
+ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs,
+ US_BULK_CS_WRAP_LEN, &partial);
+ if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
+ stall++;
+ else
+ break;
+ } while ( stall < 3);
+ if (result && result != USB_ST_DATAUNDERRUN) {
+ US_DEBUGP("Bulk status result = %x\n", result);
+ return DID_ABORT << 16;
+ }
+
+ /* check bulk status */
+
+ US_DEBUGP("Bulk status S %x T %x R %d V %x\n", bcs.Signature, bcs.Tag,
+ bcs.Residue, bcs.Status);
+ if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
+ bcs.Status > US_BULK_STAT_PHASE) {
+ US_DEBUGP("Bulk logical error\n");
+ return DID_ABORT << 16;
+ }
+ switch (bcs.Status) {
+ case US_BULK_STAT_OK:
+ return DID_OK << 16;
+
+ case US_BULK_STAT_FAIL:
+ /* check for underrun - dont report */
+ if (bcs.Residue)
+ return DID_OK << 16;
+ //pop_Bulk_reset(us);
+ break;
+
+ case US_BULK_STAT_PHASE:
+ return DID_ERROR << 16;
+ }
+ return (DID_OK << 16) | 2; /* check sense required */
+
+}
+
+/* Host functions */
+
+/* detect adapter (always true ) */
+static int us_detect(struct SHT *sht)
+{
+ /* FIXME - not nice at all, but how else ? */
+ struct us_data *us = (struct us_data *)sht->proc_dir;
+ char name[32];
+
+ sprintf(name, "usbscsi%d", us->host_number);
+ proc_usb_scsi.namelen = strlen(name);
+ proc_usb_scsi.name = kmalloc(proc_usb_scsi.namelen+1, GFP_KERNEL);
+ if (!proc_usb_scsi.name)
+ return 0;
+ strcpy((char *)proc_usb_scsi.name, name);
+ sht->proc_dir = kmalloc(sizeof(*sht->proc_dir), GFP_KERNEL);
+ if (!sht->proc_dir) {
+ kfree(proc_usb_scsi.name);
+ return 0;
+ }
+ *sht->proc_dir = proc_usb_scsi;
+ sht->name = proc_usb_scsi.name;
+ us->host = scsi_register(sht, sizeof(us));
+ if (us->host) {
+ us->host->hostdata[0] = (unsigned long)us;
+ us->host_no = us->host->host_no;
+ return 1;
+ }
+ kfree(proc_usb_scsi.name);
+ kfree(sht->proc_dir);
+ return 0;
+}
+
+/* release - must be here to stop scsi
+ * from trying to release IRQ etc.
+ * Kill off our data
+ */
+static int us_release(struct Scsi_Host *psh)
+{
+ struct us_data *us = (struct us_data *)psh->hostdata[0];
+ struct us_data *prev = (struct us_data *)&us_list;
+
+ if (us->filter)
+ us->filter->release(us->fdata);
+ if (us->pusb_dev)
+ usb_deregister(&scsi_driver);
+
+ /* FIXME - leaves hanging host template copy */
+ /* (bacause scsi layer uses it after removal !!!) */
+ while(prev->next != us)
+ prev = prev->next;
+ prev->next = us->next;
+ return 0;
+}
+
+/* run command */
+static int us_command( Scsi_Cmnd *srb )
+{
+ US_DEBUGP("Bad use of us_command\n");
+
+ return DID_BAD_TARGET << 16;
+}
+
+/* run command */
+static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
+{
+ struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+ US_DEBUGP("Command wakeup\n");
+ srb->host_scribble = (unsigned char *)us;
+ us->srb = srb;
+ srb->scsi_done = done;
+ us->action = US_ACT_COMMAND;
+
+ /* wake up the process task */
+
+ wake_up_interruptible(&us->waitq);
+
+ return 0;
+}
+
+static int us_abort( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_device_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_host_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_bus_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int usb_scsi_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+ struct us_data *us = us_list;
+ char *pos = buffer;
+ char *vendor;
+ char *product;
+ char *style = "";
+
+ /* find our data from hostno */
+
+ while (us) {
+ if (us->host_no == hostno)
+ break;
+ us = us->next;
+ }
+
+ if (!us)
+ return -ESRCH;
+
+ /* null on outward */
+
+ if (inout)
+ return length;
+
+ if (!(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer)))
+ vendor = "?";
+ if (!(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct)))
+ product = "?";
+
+ switch (us->protocol) {
+ case US_PR_CB:
+ style = "Control/Bulk";
+ break;
+
+ case US_PR_CBI:
+ style = "Control/Bulk/Interrupt";
+ break;
+
+ case US_PR_ZIP:
+ style = "Bulk only";
+ break;
+
+ }
+ SPRINTF ("Host scsi%d: usb-scsi\n", hostno);
+ SPRINTF ("Device: %s %s - GUID " GUID_FORMAT "\n", vendor, product, GUID_ARGS(us->guid) );
+ SPRINTF ("Style: %s\n", style);
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+/*
+ * this defines our 'host'
+ */
+
+static Scsi_Host_Template my_host_template = {
+ NULL, /* next */
+ NULL, /* module */
+ NULL, /* proc_dir */
+ usb_scsi_proc_info,
+ NULL, /* name - points to unique */
+ us_detect,
+ us_release,
+ NULL, /* info */
+ NULL, /* ioctl */
+ us_command,
+ us_queuecommand,
+ NULL, /* eh_strategy */
+ us_abort,
+ us_device_reset,
+ us_bus_reset,
+ us_host_reset,
+ NULL, /* abort */
+ NULL, /* reset */
+ NULL, /* slave_attach */
+ NULL, /* bios_param */
+ 1, /* can_queue */
+ -1, /* this_id */
+ SG_ALL, /* sg_tablesize */
+ 1, /* cmd_per_lun */
+ 0, /* present */
+ FALSE, /* unchecked_isa_dma */
+ FALSE, /* use_clustering */
+ TRUE, /* use_new_eh_code */
+ TRUE /* emulated */
+};
+
+static int usbscsi_control_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+ int action;
+
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ exit_mm(current);
+ exit_files(current);
+ //exit_fs(current);
+
+ sprintf(current->comm, "usbscsi%d", us->host_no);
+
+ unlock_kernel();
+
+ up(us->notify);
+
+ for(;;) {
+ siginfo_t info;
+ int unsigned long signr;
+
+ interruptible_sleep_on(&us->waitq);
+
+ action = us->action;
+ us->action = 0;
+
+ switch (action) {
+ case US_ACT_COMMAND :
+ if (!us->pusb_dev || us->srb->target || us->srb->lun) {
+ /* bad device */
+ US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
+ us->srb->result = DID_BAD_TARGET << 16;
+ } else {
+ US_DEBUG(us_show_command(us->srb));
+ if (us->filter && us->filter->command)
+ us->srb->result = us->filter->command(us->fdata, us->srb);
+ else
+ us->srb->result = us->pop(us->srb);
+ }
+ us->srb->scsi_done(us->srb);
+ break;
+
+ case US_ACT_ABORT :
+ break;
+
+ case US_ACT_DEVICE_RESET :
+ break;
+
+ case US_ACT_BUS_RESET :
+ break;
+
+ case US_ACT_HOST_RESET :
+ break;
+
+ }
+
+ if(signal_pending(current)) {
+ /* sending SIGUSR1 makes us print out some info */
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (signr == SIGUSR2) {
+ printk("USBSCSI debug toggle\n");
+ usbscsi_debug = !usbscsi_debug;
+ } else {
+ break;
+ }
+ }
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ printk("usbscsi_control_thread exiting\n");
+
+ return 0;
+}
+
+static int scsi_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+ char *mf; /* manufacturer */
+ char *prod; /* product */
+ char *serial; /* serial number */
+ struct us_data *ss = NULL;
+ struct usb_scsi_filter *filter = filters;
+ void *fdata = NULL;
+ unsigned int flags = 0;
+ GUID(guid);
+ struct us_data *prev;
+ Scsi_Host_Template *htmplt;
+ int protocol = 0;
+ int subclass = 0;
+
+ GUID_CLEAR(guid);
+ mf = usb_string(dev, dev->descriptor.iManufacturer);
+ prod = usb_string(dev, dev->descriptor.iProduct);
+ serial = usb_string(dev, dev->descriptor.iSerialNumber);
+
+ /* probe with filters first */
+
+ if (mf && prod) {
+ while (filter) {
+ if ((fdata = filter->probe(dev, mf, prod, serial)) != NULL) {
+ flags = filter->flags;
+ printk(KERN_INFO "USB Scsi filter %s\n", filter->name);
+ break;
+ }
+ filter = filter->next;
+ }
+ }
+
+ /* generic devices next */
+
+ if (fdata == NULL) {
+
+ /* some exceptions */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ /* shuttle E-USB */
+ protocol = US_PR_ZIP;
+ subclass = US_SC_8070; /* an assumption */
+ } else if (dev->descriptor.bDeviceClass != 0 ||
+ dev->config->altsetting->interface->bInterfaceClass != 8 ||
+ dev->config->altsetting->interface->bInterfaceSubClass < US_SC_MIN ||
+ dev->config->altsetting->interface->bInterfaceSubClass > US_SC_MAX) {
+ return -1;
+ }
+
+ /* now check if we have seen it before */
+
+ if (dev->descriptor.iSerialNumber &&
+ usb_string(dev, dev->descriptor.iSerialNumber) ) {
+ make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+ usb_string(dev, dev->descriptor.iSerialNumber));
+ for (ss = us_list; ss; ss = ss->next) {
+ if (GUID_EQUAL(guid, ss->guid)) {
+ US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+ break;
+ }
+ }
+ }
+ }
+
+ if (!ss) {
+ if ((ss = (struct us_data *)kmalloc(sizeof(*ss), GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING USB_SCSI "Out of memory\n");
+ if (filter)
+ filter->release(fdata);
+ return -1;
+ }
+ memset(ss, 0, sizeof(struct us_data));
+ }
+
+ interface = dev->config->altsetting->interface;
+ ss->filter = filter;
+ ss->fdata = fdata;
+ ss->flags = flags;
+ if (subclass) {
+ ss->subclass = subclass;
+ ss->protocol = protocol;
+ } else {
+ ss->subclass = interface->bInterfaceSubClass;
+ ss->protocol = interface->bInterfaceProtocol;
+ }
+
+ /* set the protocol op */
+
+ US_DEBUGP("Protocol ");
+ switch (ss->protocol) {
+ case US_PR_CB:
+ US_DEBUGPX("Control/Bulk\n");
+ ss->pop = pop_CBI;
+ break;
+
+ case US_PR_CBI:
+ US_DEBUGPX("Control/Bulk/Interrupt\n");
+ ss->pop = pop_CBI;
+ break;
+
+ default:
+ US_DEBUGPX("Bulk\n");
+ ss->pop = pop_Bulk;
+ break;
+ }
+
+ /*
+ * we are expecting a minimum of 2 endpoints - in and out (bulk)
+ * an optional interrupt is OK (necessary for CBI protocol)
+ * we will ignore any others
+ */
+
+ for (i = 0; i < interface->bNumEndpoints; i++) {
+ if (interface->endpoint[i].bmAttributes == 0x02) {
+ if (interface->endpoint[i].bEndpointAddress & 0x80)
+ ss->ep_in = interface->endpoint[i].bEndpointAddress & 0x0f;
+ else
+ ss->ep_out = interface->endpoint[i].bEndpointAddress & 0x0f;
+ } else if (interface->endpoint[i].bmAttributes == 0x03) {
+ ss->ep_int = interface->endpoint[i].bEndpointAddress & 0x0f;
+ }
+ }
+ US_DEBUGP("Endpoints In %d Out %d Int %d\n", ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* exit if strange looking */
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) ||
+ !ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
+ US_DEBUGP("Problems with device\n");
+ if (ss->host) {
+ scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
+ kfree(ss->htmplt->name);
+ kfree(ss->htmplt);
+ }
+ if (filter)
+ filter->release(fdata);
+ kfree(ss);
+ return -1; /* no endpoints */
+ }
+
+ if (dev->config[0].iConfiguration && usb_string(dev, dev->config[0].iConfiguration))
+ US_DEBUGP("Configuration %s\n", usb_string(dev, dev->config[0].iConfiguration));
+ if (interface->iInterface && usb_string(dev, interface->iInterface))
+ US_DEBUGP("Interface %s\n", usb_string(dev, interface->iInterface));
+
+ ss->pusb_dev = dev;
+
+ /* Now generate a scsi host definition, and register with scsi above us */
+
+ if (!ss->host) {
+
+ /* make unique id if possible */
+
+ if (dev->descriptor.iSerialNumber &&
+ usb_string(dev, dev->descriptor.iSerialNumber) ) {
+ make_guid(ss->guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+ usb_string(dev, dev->descriptor.iSerialNumber));
+ }
+
+ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+
+ /* set class specific stuff */
+
+ US_DEBUGP("SubClass ");
+ switch (ss->subclass) {
+ case US_SC_RBC:
+ US_DEBUGPX("Reduced Block Commands\n");
+ break;
+ case US_SC_8020:
+ US_DEBUGPX("8020\n");
+ break;
+ case US_SC_QIC:
+ US_DEBUGPX("QIC157\n");
+ break;
+ case US_SC_8070:
+ US_DEBUGPX("8070\n");
+ ss->flags |= US_FL_FIXED_COMMAND;
+ ss->fixedlength = 12;
+ break;
+ case US_SC_SCSI:
+ US_DEBUGPX("Transparent SCSI\n");
+ break;
+ case US_SC_UFI:
+ US_DEBUGPX(" UFF\n");
+ ss->flags |= US_FL_FIXED_COMMAND;
+ ss->fixedlength = 12;
+ break;
+
+ default:
+ break;
+ }
+
+ /* create unique host template */
+
+ if ((htmplt = (Scsi_Host_Template *)kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
+ printk(KERN_WARNING USB_SCSI "Out of memory\n");
+ if (filter)
+ filter->release(fdata);
+ kfree(ss);
+ return -1;
+ }
+ memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+ ss->host_number = my_host_number++;
+
+
+ (struct us_data *)htmplt->proc_dir = ss;
+ if (ss->protocol == US_PR_CBI)
+ init_waitqueue_head(&ss->ip_waitq);
+
+ /* start up our thread */
+
+ {
+ DECLARE_MUTEX_LOCKED(sem);
+
+ init_waitqueue_head(&ss->waitq);
+
+ ss->notify = &sem;
+ ss->pid = kernel_thread(usbscsi_control_thread, ss,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (ss->pid < 0) {
+ printk(KERN_WARNING USB_SCSI "Unable to start control thread\n");
+ kfree(htmplt);
+ if (filter)
+ filter->release(fdata);
+ kfree(ss);
+ return -1;
+ }
+
+ /* wait for it to start */
+
+ down(&sem);
+ }
+
+ /* now register - our detect function will be called */
+
+ scsi_register_module(MODULE_SCSI_HA, htmplt);
+
+ /* put us in the list */
+
+ prev = (struct us_data *)&us_list;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = ss;
+
+ }
+
+
+ printk(KERN_INFO "USB SCSI device found at address %d\n", dev->devnum);
+
+ dev->private = ss;
+ return 0;
+}
+
+static void scsi_disconnect(struct usb_device *dev)
+{
+ struct us_data *ss = dev->private;
+
+ if (!ss)
+ return;
+ if (ss->filter)
+ ss->filter->release(ss->fdata);
+ ss->pusb_dev = NULL;
+ dev->private = NULL; /* just in case */
+ MOD_DEC_USE_COUNT;
+}
+
+int usb_scsi_init(void)
+{
+
+ MOD_INC_USE_COUNT;
+#ifdef CONFIG_USB_HP4100
+ hp4100_init();
+#endif
+#ifdef CONFIG_USB_ZIP
+ usb_zip_init();
+#endif
+ usb_register(&scsi_driver);
+ printk(KERN_INFO "USB SCSI support registered.\n");
+ return 0;
+}
+
+
+int usb_scsi_register(struct usb_scsi_filter *filter)
+{
+ struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters;
+
+ while (prev->next)
+ prev = prev->next;
+ prev->next = filter;
+ return 0;
+}
+
+void usb_scsi_deregister(struct usb_scsi_filter *filter)
+{
+ struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters;
+
+ while (prev->next && prev->next != filter)
+ prev = prev->next;
+ if (prev->next)
+ prev->next = filter->next;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+ return usb_scsi_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned int offset;
+
+ usb_deregister(&scsi_driver);
+}
+#endif
diff --git a/drivers/usb/usb_scsi.h b/drivers/usb/usb_scsi.h
new file mode 100644
index 000000000..58367d852
--- /dev/null
+++ b/drivers/usb/usb_scsi.h
@@ -0,0 +1,145 @@
+/* Driver for USB scsi - include file
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ * This driver is scitzoid - it make a USB scanner appear as both a SCSI device
+ * and a character device. The latter is only available if the device has an
+ * interrupt endpoint, and is used specifically to receive interrupt events.
+ *
+ * In order to support various 'strange' scanners, this module supports plug in
+ * device specific filter modules, which can do their own thing when required.
+ *
+ */
+
+#define USB_SCSI "usbscsi: "
+
+extern int usbscsi_debug;
+
+#ifdef CONFIG_USB_SCSI_DEBUG
+void us_show_command(Scsi_Cmnd *srb);
+#define US_DEBUGP(x...) { if(usbscsi_debug) printk( KERN_DEBUG USB_SCSI ## x ); }
+#define US_DEBUGPX(x...) { if(usbscsi_debug) printk( ## x ); }
+#define US_DEBUG(x) { if(usbscsi_debug) x; }
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#define US_DEBUG(x)
+#endif
+
+/* bit set if input */
+extern unsigned char us_direction[256/8];
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+/* Sub Classes */
+
+#define US_SC_RBC 1 /* Typically, flash devices */
+#define US_SC_8020 2 /* CD-ROM */
+#define US_SC_QIC 3 /* QIC-157 Tapes */
+#define US_SC_UFI 4 /* Floppy */
+#define US_SC_8070 5 /* Removable media */
+#define US_SC_SCSI 6 /* Transparent */
+#define US_SC_MIN US_SC_RBC
+#define US_SC_MAX US_SC_SCSI
+
+/* Protocols */
+
+#define US_PR_CB 1 /* Control/Bulk w/o interrupt */
+#define US_PR_CBI 0 /* Control/Bulk/Interrupt */
+#define US_PR_ZIP 0x50 /* bulk only */
+/* #define US_PR_BULK ?? */
+
+/*
+ * Bulk only data structures (Zip 100, for example)
+ */
+
+struct bulk_cb_wrap {
+ __u32 Signature; /* contains 'USBC' */
+ __u32 Tag; /* unique per command id */
+ __u32 DataTransferLength; /* size of data */
+ __u8 Flags; /* direction in bit 0 */
+ __u8 Lun; /* LUN normally 0 */
+ __u8 Length; /* of of the CDB */
+ __u8 CDB[16]; /* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN 31
+#define US_BULK_CB_SIGN 0x43425355
+#define US_BULK_FLAG_IN 1
+#define US_BULK_FLAG_OUT 0
+
+struct bulk_cs_wrap {
+ __u32 Signature; /* should = 'USBS' */
+ __u32 Tag; /* same as original command */
+ __u32 Residue; /* amount not transferred */
+ __u8 Status; /* see below */
+ __u8 Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN 31
+#define US_BULK_CS_SIGN 0x53425355
+#define US_BULK_STAT_OK 0
+#define US_BULK_STAT_FAIL 1
+#define US_BULK_STAT_PHASE 2
+
+#define US_BULK_RESET 0xff
+#define US_BULK_RESET_SOFT 1
+#define US_BULK_RESET_HARD 0
+
+/*
+ * CBI style
+ */
+
+#define US_CBI_ADSC 0
+
+/*
+ * Filter device definitions
+ */
+struct usb_scsi_filter {
+
+ struct usb_scsi_filter * next; /* usb_scsi driver only */
+ char *name; /* not really required */
+
+ unsigned int flags; /* Filter flags */
+ void * (* probe) (struct usb_device *, char *, char *, char *); /* probe device */
+ void (* release)(void *); /* device gone */
+ int (* command)(void *, Scsi_Cmnd *); /* all commands */
+};
+
+#define GUID(x) __u32 x[3]
+#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
+#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
+#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
+#define GUID_FORMAT "%08x%08x%08x"
+#define GUID_ARGS(x) x[0], x[1], x[2]
+
+static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
+{
+ pg[0] = (vendor << 16) | product;
+ pg[1] = pg[2] = 0;
+ while (*serial) {
+ pg[1] <<= 4;
+ pg[1] |= pg[2] >> 28;
+ pg[2] <<= 4;
+ if (*serial >= 'a')
+ *serial -= 'a' - 'A';
+ pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
+ : *serial - 'A' + 10;
+ serial++;
+ }
+}
+
+/* Flag definitions */
+#define US_FL_IP_STATUS 0x00000001 /* status uses interrupt */
+#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
+
+/*
+ * Called by filters to register/unregister the mini driver
+ *
+ * WARNING - the supplied probe function may be called before exiting this fn
+ */
+int usb_scsi_register(struct usb_scsi_filter *);
+void usb_scsi_deregister(struct usb_scsi_filter *);
+
+#ifdef CONFIG_USB_HP4100
+int hp4100_init(void);
+#endif
diff --git a/drivers/usb/usb_scsi_debug.c b/drivers/usb/usb_scsi_debug.c
new file mode 100644
index 000000000..2ca847c08
--- /dev/null
+++ b/drivers/usb/usb_scsi_debug.c
@@ -0,0 +1,104 @@
+
+/* Driver for USB scsi like devices
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#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 <asm/spinlock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb_scsi.h"
+
+void us_show_command(Scsi_Cmnd *srb)
+{
+ char *what;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+ case REZERO_UNIT: what = "REZERO_UNIT"; break;
+ case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+ case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+ case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+ case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+ case READ_6: what = "READ_6"; break;
+ case WRITE_6: what = "WRITE_6"; break;
+ case SEEK_6: what = "SEEK_6"; break;
+ case READ_REVERSE: what = "READ_REVERSE"; break;
+ case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+ case SPACE: what = "SPACE"; break;
+ case INQUIRY: what = "INQUIRY"; break;
+ case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+ case MODE_SELECT: what = "MODE_SELECT"; break;
+ case RESERVE: what = "RESERVE"; break;
+ case RELEASE: what = "RELEASE"; break;
+ case COPY: what = "COPY"; break;
+ case ERASE: what = "ERASE"; break;
+ case MODE_SENSE: what = "MODE_SENSE"; break;
+ case START_STOP: what = "START_STOP"; break;
+ case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+ case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+ case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+ case SET_WINDOW: what = "SET_WINDOW"; break;
+ case READ_CAPACITY: what = "READ_CAPACITY"; break;
+ case READ_10: what = "READ_10"; break;
+ case WRITE_10: what = "WRITE_10"; break;
+ case SEEK_10: what = "SEEK_10"; break;
+ case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+ case VERIFY: what = "VERIFY"; break;
+ case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+ case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+ case SEARCH_LOW: what = "SEARCH_LOW"; break;
+ case SET_LIMITS: what = "SET_LIMITS"; break;
+ case READ_POSITION: what = "READ_POSITION"; break;
+ case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+ case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+ case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+ case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+ case COMPARE: what = "COMPARE"; break;
+ case COPY_VERIFY: what = "COPY_VERIFY"; break;
+ case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+ case READ_BUFFER: what = "READ_BUFFER"; break;
+ case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+ case READ_LONG: what = "READ_LONG"; break;
+ case WRITE_LONG: what = "WRITE_LONG"; break;
+ case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+ case WRITE_SAME: what = "WRITE_SAME"; break;
+ case READ_TOC: what = "READ_TOC"; break;
+ case LOG_SELECT: what = "LOG_SELECT"; break;
+ case LOG_SENSE: what = "LOG_SENSE"; break;
+ case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case READ_12: what = "READ_12"; break;
+ case WRITE_12: what = "WRITE_12"; break;
+ case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+ case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+ case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+ case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+ default: what = "??"; break;
+ }
+ printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len);
+ printk(KERN_DEBUG USB_SCSI " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
+ srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
diff --git a/drivers/usb/usb_scsi_dt.c b/drivers/usb/usb_scsi_dt.c
new file mode 100644
index 000000000..5d615bdff
--- /dev/null
+++ b/drivers/usb/usb_scsi_dt.c
@@ -0,0 +1,4 @@
+0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00