From 57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 17 Jun 1999 14:08:29 +0000 Subject: The rest of 2.3.6. --- drivers/usb/acm.c | 293 ++++++++++ drivers/usb/cpia.c | 1267 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/cpia.h | 139 +++++ drivers/usb/keymap-mac.c | 50 ++ drivers/usb/maps/mac.map | 350 ++++++++++++ drivers/usb/mkmap.adb | 88 +++ drivers/usb/printer.c | 413 ++++++++++++++ drivers/usb/usb-core.c | 96 ++++ drivers/usb/usb_scsi.c | 1098 ++++++++++++++++++++++++++++++++++++ drivers/usb/usb_scsi.h | 145 +++++ drivers/usb/usb_scsi_debug.c | 104 ++++ drivers/usb/usb_scsi_dt.c | 4 + 12 files changed, 4047 insertions(+) create mode 100644 drivers/usb/acm.c create mode 100644 drivers/usb/cpia.c create mode 100644 drivers/usb/cpia.h create mode 100644 drivers/usb/keymap-mac.c create mode 100644 drivers/usb/maps/mac.map create mode 100644 drivers/usb/mkmap.adb create mode 100644 drivers/usb/printer.c create mode 100644 drivers/usb/usb-core.c create mode 100644 drivers/usb/usb_scsi.c create mode 100644 drivers/usb/usb_scsi.h create mode 100644 drivers/usb/usb_scsi_debug.c create mode 100644 drivers/usb/usb_scsi_dt.c (limited to 'drivers/usb') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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;cfgnumdescriptor.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + +#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() +{ + 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() +# { +# 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() +{ + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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) { + 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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#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(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#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 -- cgit v1.2.3