diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-11-28 03:58:46 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-11-28 03:58:46 +0000 |
commit | b63ad0882a16a5d28003e57f2b0b81dee3fb322b (patch) | |
tree | 0a343ce219e2b8b38a5d702d66032c57b83d9720 /drivers/usb | |
parent | a9d7bff9a84dba79609a0002e5321b74c4d64c64 (diff) |
Merge with 2.4.0-test11.
Diffstat (limited to 'drivers/usb')
45 files changed, 3093 insertions, 1454 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 9e510e087..d4feefa21 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -37,6 +37,7 @@ comment 'USB Devices' if [ "$CONFIG_USB_SERIAL" != "n" ]; then bool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC dep_tristate ' USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL + dep_tristate ' USB Digi International AccelePort USB Serial Driver' CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL dep_tristate ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL @@ -49,8 +50,8 @@ comment 'USB Devices' bool ' USB Keyspan USA-18X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA18X bool ' USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W fi - dep_tristate ' USB Digi International AccelePort USB Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_DIGI_ACCELEPORT $CONFIG_USB_SERIAL dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL + dep_tristate ' USB Belkin and Peracom Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_BELKIN $CONFIG_USB_SERIAL fi bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG fi diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 24ca4858d..1736c503b 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -477,114 +477,113 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_ * USB probe and disconnect routines. */ -static void *acm_probe(struct usb_device *dev, unsigned int ifnum) +static void *acm_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct acm *acm; struct usb_config_descriptor *cfacm; struct usb_interface_descriptor *ifcom, *ifdata; struct usb_endpoint_descriptor *epctrl, *epread, *epwrite; - int readsize, ctrlsize, minor, i; + int readsize, ctrlsize, minor; unsigned char *buf; - if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0 + /* Since 0 is treated as a wildcard by the USB pattern matching, + we explicitly check bDeviceSubClass and bDeviceProtocol + here. */ + if (dev->descriptor.bDeviceSubClass != 0 || dev->descriptor.bDeviceProtocol != 0) return NULL; - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + cfacm = dev->actconfig; - cfacm = dev->config + i; + dbg("probing config %d", cfacm->bConfigurationValue); - dbg("probing config %d", cfacm->bConfigurationValue); + if (cfacm->bNumInterfaces != 2 || + usb_interface_claimed(cfacm->interface + 0) || + usb_interface_claimed(cfacm->interface + 1)) + return NULL; - if (cfacm->bNumInterfaces != 2 || - usb_interface_claimed(cfacm->interface + 0) || - usb_interface_claimed(cfacm->interface + 1)) - continue; + ifcom = cfacm->interface[0].altsetting + 0; + ifdata = cfacm->interface[1].altsetting + 0; - ifcom = cfacm->interface[0].altsetting + 0; - ifdata = cfacm->interface[1].altsetting + 0; - - if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) { - ifcom = cfacm->interface[1].altsetting + 0; - ifdata = cfacm->interface[0].altsetting + 0; - if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) - continue; - } + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) { + ifcom = cfacm->interface[1].altsetting + 0; + ifdata = cfacm->interface[0].altsetting + 0; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints !=2) + return NULL; + } - if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || - ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1) - continue; + if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || + ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1) + return NULL; - epctrl = ifcom->endpoint + 0; - epread = ifdata->endpoint + 0; - epwrite = ifdata->endpoint + 1; + epctrl = ifcom->endpoint + 0; + epread = ifdata->endpoint + 0; + epwrite = ifdata->endpoint + 1; - if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || - (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || - ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) - continue; + if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || + (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || + ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) + return NULL; - if ((epread->bEndpointAddress & 0x80) != 0x80) { - epread = ifdata->endpoint + 1; - epwrite = ifdata->endpoint + 0; - } + if ((epread->bEndpointAddress & 0x80) != 0x80) { + epread = ifdata->endpoint + 1; + epwrite = ifdata->endpoint + 0; + } - usb_set_configuration(dev, cfacm->bConfigurationValue); + usb_set_configuration(dev, cfacm->bConfigurationValue); - for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); - if (acm_table[minor]) { - err("no more free acm devices"); - return NULL; - } + for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); + if (acm_table[minor]) { + err("no more free acm devices"); + return NULL; + } - if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { - err("out of memory"); - return NULL; - } - memset(acm, 0, sizeof(struct acm)); + if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { + err("out of memory"); + return NULL; + } + memset(acm, 0, sizeof(struct acm)); - ctrlsize = epctrl->wMaxPacketSize; - readsize = epread->wMaxPacketSize; - acm->writesize = epwrite->wMaxPacketSize; - acm->iface = cfacm->interface; - acm->minor = minor; - acm->dev = dev; + ctrlsize = epctrl->wMaxPacketSize; + readsize = epread->wMaxPacketSize; + acm->writesize = epwrite->wMaxPacketSize; + acm->iface = cfacm->interface; + acm->minor = minor; + acm->dev = dev; - acm->tqueue.routine = acm_softint; - acm->tqueue.data = acm; + acm->tqueue.routine = acm_softint; + acm->tqueue.data = acm; - if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { - err("out of memory"); - kfree(acm); - return NULL; - } + if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { + err("out of memory"); + kfree(acm); + return NULL; + } - FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), - buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), + buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); - FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), - buf += ctrlsize, readsize, acm_read_bulk, acm); - acm->readurb.transfer_flags |= USB_NO_FSBR; + FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + buf += ctrlsize, readsize, acm_read_bulk, acm); + acm->readurb.transfer_flags |= USB_NO_FSBR; - FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), - buf += readsize, acm->writesize, acm_write_bulk, acm); - acm->writeurb.transfer_flags |= USB_NO_FSBR; - - printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); + FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + buf += readsize, acm->writesize, acm_write_bulk, acm); + acm->writeurb.transfer_flags |= USB_NO_FSBR; - acm_set_control(acm, acm->ctrlout); + printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); - acm->line.speed = cpu_to_le32(9600); - acm->line.databits = 8; - acm_set_line(acm, &acm->line); + acm_set_control(acm, acm->ctrlout); - usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); - usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); + acm->line.speed = cpu_to_le32(9600); + acm->line.databits = 8; + acm_set_line(acm, &acm->line); - tty_register_devfs(&acm_tty_driver, 0, minor); - return acm_table[minor] = acm; - } + usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); + usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); - return NULL; + tty_register_devfs(&acm_tty_driver, 0, minor); + return acm_table[minor] = acm; } static void acm_disconnect(struct usb_device *dev, void *ptr) @@ -622,10 +621,19 @@ static void acm_disconnect(struct usb_device *dev, void *ptr) * USB driver structure. */ +static struct usb_device_id acm_ids [] = { + { bDeviceClass: 2}, + { bInterfaceClass: 2, bInterfaceSubClass: 2, bInterfaceProtocol: 1}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, acm_ids); + static struct usb_driver acm_driver = { name: "acm", probe: acm_probe, - disconnect: acm_disconnect + disconnect: acm_disconnect, + id_table: acm_ids, }; /* diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 1db828b24..065d31942 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -2691,16 +2691,23 @@ static /*const*/ struct file_operations usb_audio_fops = { /* --------------------------------------------------------------------- */ -static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum); +static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); static void usb_audio_disconnect(struct usb_device *dev, void *ptr); +static struct usb_device_id usb_audio_ids [] = { + { bInterfaceClass: USB_CLASS_AUDIO, bInterfaceSubClass: 1}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_audio_ids); + static struct usb_driver usb_audio_driver = { - "audio", - usb_audio_probe, - usb_audio_disconnect, - LIST_HEAD_INIT(usb_audio_driver.driver_list), - NULL, - 0 + name: "audio", + probe: usb_audio_probe, + disconnect: usb_audio_disconnect, + driver_list: LIST_HEAD_INIT(usb_audio_driver.driver_list), + id_table: usb_audio_ids, }; static void *find_descriptor(void *descstart, unsigned int desclen, void *after, @@ -3640,7 +3647,8 @@ ret: /* we only care for the currently active configuration */ -static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum) +static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_config_descriptor *config = dev->actconfig; unsigned char *buffer; @@ -3653,25 +3661,13 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum) config->interface[ifnum].altsetting[0].bInterfaceClass, config->interface[ifnum].altsetting[0].bInterfaceSubClass); #endif - if (config->interface[ifnum].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || - config->interface[ifnum].altsetting[0].bInterfaceSubClass != 1) { -#if 0 - printk(KERN_DEBUG "usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n", - dev->descriptor.idVendor, dev->descriptor.idProduct); -#endif - return NULL; - } + /* * audiocontrol interface found * find which configuration number is active */ - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) - if (dev->config+i == config) - goto configfound; - printk(KERN_ERR "usbaudio: cannot find active configuration number of device %d\n", dev->devnum); - return NULL; + i = dev->actconfig - config; - configfound: if (usb_set_configuration(dev, config->bConfigurationValue) < 0) { printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue); return NULL; diff --git a/drivers/usb/bluetooth.c b/drivers/usb/bluetooth.c index c543b249e..0509f2347 100644 --- a/drivers/usb/bluetooth.c +++ b/drivers/usb/bluetooth.c @@ -183,14 +183,27 @@ static void bluetooth_ctrl_callback (struct urb *urb); static void bluetooth_read_bulk_callback (struct urb *urb); static void bluetooth_write_bulk_callback (struct urb *urb); -static void * usb_bluetooth_probe (struct usb_device *dev, unsigned int ifnum); +static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); static void usb_bluetooth_disconnect (struct usb_device *dev, void *ptr); +static struct usb_device_id usb_bluetooth_ids [] = { + { + bDeviceClass: WIRELESS_CLASS_CODE, + bDeviceSubClass: RF_SUBCLASS_CODE, + bDeviceProtocol: BLUETOOTH_PROGRAMMING_PROTOCOL_CODE + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids); + static struct usb_driver usb_bluetooth_driver = { name: "bluetooth", probe: usb_bluetooth_probe, disconnect: usb_bluetooth_disconnect, + id_table: usb_bluetooth_ids, }; static int bluetooth_refcount; @@ -950,7 +963,8 @@ static void bluetooth_softint(void *private) } -static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum) +static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_bluetooth *bluetooth = NULL; struct usb_interface_descriptor *interface; @@ -967,16 +981,6 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum) int num_bulk_in = 0; int num_bulk_out = 0; - /* see if this device has the proper class signature */ - if ((dev->descriptor.bDeviceClass != WIRELESS_CLASS_CODE) || - (dev->descriptor.bDeviceSubClass != RF_SUBCLASS_CODE) || - (dev->descriptor.bDeviceProtocol != BLUETOOTH_PROGRAMMING_PROTOCOL_CODE)) { - dbg (__FUNCTION__ " - class signature %d, %d, %d did not match", - dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass, - dev->descriptor.bDeviceProtocol); - return NULL; - } - interface = &dev->actconfig->interface[ifnum].altsetting[0]; control_out_endpoint = interface->bInterfaceNumber; diff --git a/drivers/usb/dabusb.c b/drivers/usb/dabusb.c index d14e8e97b..154ed17cb 100644 --- a/drivers/usb/dabusb.c +++ b/drivers/usb/dabusb.c @@ -718,7 +718,8 @@ static int dabusb_find_struct (void) } /* --------------------------------------------------------------------- */ -static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum) +static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum, + const struct usb_device_id *id) { int devnum; pdabusb_t s; @@ -726,11 +727,6 @@ static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum) dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); - /* the 1234:5678 is just a self assigned test ID */ - if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && - (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999)) - return NULL; - /* We don't handle multiple configurations */ if (usbdev->descriptor.bNumConfigurations != 1) return NULL; @@ -790,14 +786,22 @@ static void dabusb_disconnect (struct usb_device *usbdev, void *ptr) MOD_DEC_USE_COUNT; } +static struct usb_device_id dabusb_ids [] = { + { idVendor: 0x0547, idProduct: 0x2131 }, + { idVendor: 0x0547, idProduct: 0x9999 }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, dabusb_ids); + static struct usb_driver dabusb_driver = { - "dabusb", - dabusb_probe, - dabusb_disconnect, - {NULL, NULL}, - &dabusb_fops, - DABUSB_MINOR + name: "dabusb", + probe: dabusb_probe, + disconnect: dabusb_disconnect, + fops: &dabusb_fops, + minor: DABUSB_MINOR, + id_table: dabusb_ids, }; /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c index 6760e797b..5503e6e66 100644 --- a/drivers/usb/dc2xx.c +++ b/drivers/usb/dc2xx.c @@ -44,11 +44,14 @@ * 03 Nov, 1999 -- update for 2.3.25 kernel API changes. * 08 Jan, 2000 .. multiple camera support * 12 Aug, 2000 .. add some real locking, remove an Oops + * 10 Oct, 2000 .. usb_device_id table created. + * 01 Nov, 2000 .. usb_device_id support added by Adam J. Richter * * Thanks to: the folk who've provided USB product IDs, sent in * patches, and shared their sucesses! */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -59,7 +62,12 @@ #include <linux/init.h> #include <linux/malloc.h> #include <linux/module.h> -#undef DEBUG + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif #include <linux/usb.h> @@ -82,7 +90,7 @@ /* table of cameras that work through this driver */ -static __devinitdata struct usb_device_id camera_table [] = { +static struct usb_device_id camera_table [] = { /* These have the same application level protocol */ { idVendor: 0x040a, idProduct: 0x0120 }, // Kodak DC-240 @@ -105,7 +113,7 @@ static __devinitdata struct usb_device_id camera_table [] = { * means, among other things, no iso or interrupt endpoints. */ - { } // TERMINATING ENTRY + { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, camera_table); @@ -346,7 +354,7 @@ static /* const */ struct file_operations usb_camera_fops = { static void * __devinit -camera_bind (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *camera_info) +camera_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *camera_info) { int i; struct usb_interface_descriptor *interface; @@ -471,7 +479,7 @@ static /* const */ struct usb_driver camera_driver = { name: "dc2xx", id_table: camera_table, - bind: camera_bind, + probe: camera_probe, disconnect: camera_disconnect, fops: &usb_camera_fops, diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index 2a1e12aad..ef46e5b52 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -292,7 +292,8 @@ static void destroy_all_async(struct dev_state *ps) * interface claiming */ -static void *driver_probe(struct usb_device *dev, unsigned int intf) +static void *driver_probe(struct usb_device *dev, unsigned int intf, + const struct usb_device_id *id) { return NULL; } @@ -541,13 +542,17 @@ static int proc_control(struct dev_state *ps, void *arg) i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, ctrl.value, ctrl.index, tbuf, ctrl.length, tmo); if ((i > 0) && ctrl.length) { - if (copy_to_user(ctrl.data, tbuf, ctrl.length)) + if (copy_to_user(ctrl.data, tbuf, ctrl.length)) { + free_page((unsigned long)tbuf); return -EFAULT; + } } } else { if (ctrl.length) { - if (copy_from_user(tbuf, ctrl.data, ctrl.length)) + if (copy_from_user(tbuf, ctrl.data, ctrl.length)) { + free_page((unsigned long)tbuf); return -EFAULT; + } } i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, ctrl.value, ctrl.index, tbuf, ctrl.length, tmo); @@ -583,7 +588,7 @@ static int proc_bulk(struct dev_state *ps, void *arg) return -EINVAL; len1 = bulk.len; if (len1 > PAGE_SIZE) - len1 = PAGE_SIZE; + return -EINVAL; if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; tmo = (bulk.timeout * HZ + 999) / 1000; @@ -594,13 +599,17 @@ static int proc_bulk(struct dev_state *ps, void *arg) } i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); if (!i && len2) { - if (copy_to_user(bulk.data, tbuf, len2)) + if (copy_to_user(bulk.data, tbuf, len2)) { + free_page((unsigned long)tbuf); return -EFAULT; + } } } else { if (len1) { - if (copy_from_user(tbuf, bulk.data, len1)) + if (copy_from_user(tbuf, bulk.data, len1)) { + free_page((unsigned long)tbuf); return -EFAULT; + } } i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); } @@ -697,9 +706,11 @@ static int proc_resetdevice(struct dev_state *ps) continue; if (intf->driver) { + const struct usb_device_id *id; down(&intf->driver->serialize); intf->driver->disconnect(ps->dev, intf->private_data); - intf->driver->probe(ps->dev, i); + id = usb_match_id(ps->dev,intf,intf->driver->id_table); + intf->driver->probe(ps->dev, i, id); up(&intf->driver->serialize); } } diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c index aa6372b91..a59759bb9 100644 --- a/drivers/usb/dsbr100.c +++ b/drivers/usb/dsbr100.c @@ -70,7 +70,8 @@ #define TB_LEN 16 -static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum); +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr); static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, void *arg); @@ -90,28 +91,30 @@ typedef struct static struct video_device usb_dsbr100_radio= { - "D-Link DSB R-100 USB radio", - VID_TYPE_TUNER, - VID_HARDWARE_AZTECH, - usb_dsbr100_open, - usb_dsbr100_close, - NULL, /* Can't read (no capture ability) */ - NULL, /* Can't write */ - NULL, /* No poll */ - usb_dsbr100_ioctl, - NULL, - NULL + name: "D-Link DSB R-100 USB radio", + type: VID_TYPE_TUNER, + hardware: VID_HARDWARE_AZTECH, + open: usb_dsbr100_open, + close: usb_dsbr100_close, + ioctl: usb_dsbr100_ioctl, }; static int users = 0; +static struct usb_device_id usb_dsbr100_table [] = { + { idVendor: DSB100_VENDOR, idProduct: DSB100_PRODUCT }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_dsbr100_table); + static struct usb_driver usb_dsbr100_driver = { name: "dsbr100", probe: usb_dsbr100_probe, disconnect: usb_dsbr100_disconnect, - driver_list: {NULL,NULL}, - fops: NULL, - minor: 0 + fops: NULL, + minor: 0, + id_table: usb_dsbr100_table, }; @@ -164,13 +167,11 @@ static void dsbr100_getstat(usb_dsbr100 *radio) } -static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum) +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { usb_dsbr100 *radio; - if (dev->descriptor.idVendor!=DSB100_VENDOR || - dev->descriptor.idProduct!=DSB100_PRODUCT) - return NULL; if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL))) return NULL; usb_dsbr100_radio.priv = radio; diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c index db4df7931..5acdded23 100644 --- a/drivers/usb/hid.c +++ b/drivers/usb/hid.c @@ -1373,9 +1373,6 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, c if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) && (hid_blacklist[n].idProduct == dev->descriptor.idProduct)) return NULL; - if (interface->bInterfaceClass != USB_INTERFACE_CLASS_HID) - return NULL; - if (usb_get_extra_descriptor(interface, USB_DT_HID, &hdesc) && usb_get_extra_descriptor(&interface->endpoint[0], USB_DT_HID, &hdesc)) { dbg("class descriptor not present\n"); @@ -1471,7 +1468,8 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, c return hid; } -static void* hid_probe(struct usb_device *dev, unsigned int ifnum) +static void* hid_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct hid_device *hid; char name[128]; @@ -1527,10 +1525,18 @@ static void hid_disconnect(struct usb_device *dev, void *ptr) hid_free_device(hid); } +static struct usb_device_id hid_usb_ids [] = { + { bInterfaceClass: USB_INTERFACE_CLASS_HID}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, hid_usb_ids); + static struct usb_driver hid_driver = { name: "hid", probe: hid_probe, - disconnect: hid_disconnect + disconnect: hid_disconnect, + id_table: hid_usb_ids, }; static int __init hid_init(void) diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 8b9b0bfbc..8ecc1221b 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -36,7 +36,7 @@ static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); static int khubd_pid = 0; /* PID of khubd */ -static int khubd_running = 0; +static DECLARE_MUTEX_LOCKED(khubd_exited); static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) { @@ -235,7 +235,9 @@ static int usb_hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor return 0; } -static void *hub_probe(struct usb_device *dev, unsigned int i) +static void *hub_probe(struct usb_device *dev, unsigned int i, + const struct usb_device_id *id) + { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; @@ -244,10 +246,6 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) interface = &dev->actconfig->interface[i].altsetting[0]; - /* Is it a hub? */ - if (interface->bInterfaceClass != USB_CLASS_HUB) - return NULL; - /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((interface->bInterfaceSubClass != 0) && @@ -743,17 +741,13 @@ he_unlock: static int usb_hub_thread(void *__hub) { - khubd_running = 1; - lock_kernel(); /* * This thread doesn't need any user-level access, * so get rid of all our resources */ - exit_files(current); /* daemonize doesn't do exit_files */ - current->files = init_task.files; - atomic_inc(¤t->files->count); + daemonize(); /* Setup a nice name */ @@ -766,16 +760,23 @@ static int usb_hub_thread(void *__hub) } while (!signal_pending(current)); dbg("usb_hub_thread exiting"); - khubd_running = 0; - return 0; + up_and_exit(&khubd_exited, 0); } +static struct usb_device_id hub_id_table [] = { + { bInterfaceClass: USB_CLASS_HUB}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, hub_id_table); + static struct usb_driver hub_driver = { name: "hub", probe: hub_probe, ioctl: hub_ioctl, - disconnect: hub_disconnect + disconnect: hub_disconnect, + id_table: hub_id_table, }; /* @@ -811,18 +812,8 @@ void usb_hub_cleanup(void) /* Kill the thread */ ret = kill_proc(khubd_pid, SIGTERM, 1); - if (!ret) { - /* Wait 10 seconds */ - int count = 10 * 100; - while (khubd_running && --count) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - - if (!count) - err("giving up on killing khubd"); - } + down(&khubd_exited); /* * Hub resources are freed for us by usb_deregister. It calls diff --git a/drivers/usb/ibmcam.c b/drivers/usb/ibmcam.c index 590bcd4bd..a502cbaf1 100644 --- a/drivers/usb/ibmcam.c +++ b/drivers/usb/ibmcam.c @@ -2492,11 +2492,6 @@ static void ibmcam_close(struct video_device *dev) MOD_DEC_USE_COUNT; } -static int ibmcam_init_done(struct video_device *dev) -{ - return 0; -} - static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) { return -EINVAL; @@ -2855,20 +2850,15 @@ static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long } static struct video_device ibmcam_template = { - "CPiA USB Camera", - VID_TYPE_CAPTURE, - VID_HARDWARE_CPIA, - ibmcam_open, - ibmcam_close, - ibmcam_read, - ibmcam_write, - NULL, - ibmcam_ioctl, - ibmcam_mmap, - ibmcam_init_done, - NULL, - 0, - 0 + name: "CPiA USB Camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_CPIA, + open: ibmcam_open, + close: ibmcam_close, + read: ibmcam_read, + write: ibmcam_write, + ioctl: ibmcam_ioctl, + mmap: ibmcam_mmap, }; static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam) @@ -2953,7 +2943,8 @@ static int ibmcam_find_struct(void) * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). * 7/3/00 Fixed endianness bug. */ -static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) +static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_ibmcam *ibmcam = NULL; const struct usb_interface_descriptor *interface; @@ -2967,11 +2958,6 @@ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) if (dev->descriptor.bNumConfigurations != 1) return NULL; - /* Is it an IBM camera? */ - if ((dev->descriptor.idVendor != 0x0545) || - (dev->descriptor.idProduct != 0x8080)) - return NULL; - /* Check the version/revision */ switch (dev->descriptor.bcdDevice) { case 0x0002: @@ -2988,10 +2974,9 @@ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) dev->descriptor.bcdDevice); model = IBMCAM_MODEL_2; break; - default: - printk(KERN_ERR "IBM camera with revision 0x%04x is not supported.\n", - dev->descriptor.bcdDevice); - return NULL; + + /* ibmcam_table contents prevents any other values from ever + being passed to us, so no need for "default" case. */ } /* Validate found interface: must have one ISO endpoint */ @@ -3124,11 +3109,29 @@ static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr) MOD_DEC_USE_COUNT; } +static struct usb_device_id ibmcam_table [] = { + { + idVendor: 0x0545, + idProduct: 0x8080, + bcdDevice_lo: 0x0002, + bcdDevice_hi: 0x0002 + }, + { + idVendor: 0x0545, + idProduct: 0x8080, + bcdDevice_lo: 0X030a, + bcdDevice_hi: 0x030a + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, ibmcam_table); + static struct usb_driver ibmcam_driver = { - "ibmcam", - usb_ibmcam_probe, - usb_ibmcam_disconnect, - { NULL, NULL } + name: "ibmcam", + probe: usb_ibmcam_probe, + disconnect: usb_ibmcam_disconnect, + id_table: ibmcam_table, }; /* diff --git a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c index e4fc25d39..131681c8e 100644 --- a/drivers/usb/mdc800.c +++ b/drivers/usb/mdc800.c @@ -370,7 +370,8 @@ static struct usb_driver mdc800_usb_driver; /* * Callback to search the Mustek MDC800 on the USB Bus */ -static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum ) +static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum, + const struct usb_device_id *id) { int i,j; struct usb_interface_descriptor *intf_desc; @@ -379,11 +380,6 @@ static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum ) dbg ("(mdc800_usb_probe) called."); - if (dev->descriptor.idVendor != MDC800_VENDOR_ID) - return 0; - if (dev->descriptor.idProduct != MDC800_PRODUCT_ID) - return 0; - if (mdc800->dev != 0) { warn ("only one Mustek MDC800 is supported."); @@ -873,17 +869,23 @@ static struct file_operations mdc800_device_ops = +static struct usb_device_id mdc800_table [] = { + { idVendor: MDC800_VENDOR_ID, idProduct: MDC800_PRODUCT_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, mdc800_table); /* * USB Driver Struct for this device */ static struct usb_driver mdc800_usb_driver = { - "mdc800", - mdc800_usb_probe, - mdc800_usb_disconnect, - { 0,0 }, - &mdc800_device_ops, - MDC800_DEVICE_MINOR_BASE + name: "mdc800", + probe: mdc800_usb_probe, + disconnect: mdc800_usb_disconnect, + fops: &mdc800_device_ops, + minor: MDC800_DEVICE_MINOR_BASE, + id_table: mdc800_table }; diff --git a/drivers/usb/microtek.c b/drivers/usb/microtek.c index ab37860cd..daa18c88f 100644 --- a/drivers/usb/microtek.c +++ b/drivers/usb/microtek.c @@ -140,14 +140,17 @@ /* USB layer driver interface */ -static void *mts_usb_probe(struct usb_device *dev, unsigned int interface); +static void *mts_usb_probe(struct usb_device *dev, unsigned int interface, + const struct usb_device_id *id); static void mts_usb_disconnect(struct usb_device *dev, void *ptr); +static struct usb_device_id mts_usb_ids []; + static struct usb_driver mts_usb_driver = { - "microtek", - mts_usb_probe, - mts_usb_disconnect, - { NULL, NULL } /* we need no fops. let gcc fill it */ + name: "microtek", + probe: mts_usb_probe, + disconnect: mts_usb_disconnect, + id_table: mts_usb_ids, }; @@ -338,6 +341,7 @@ static inline void mts_wait_abort(struct mts_desc* desc) while( !atomic_read(&desc->lock.count) ) { /* Is there a function to check if the semaphore is locked? */ + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout( MTS_ABORT_TIMEOUT ); MTS_DEBUG_GOT_HERE(); mts_urb_abort(desc); @@ -791,8 +795,6 @@ static void mts_usb_disconnect (struct usb_device *dev, void *ptr) struct vendor_product { - u16 idVendor; - u16 idProduct; char* name; enum { @@ -807,37 +809,37 @@ struct vendor_product /* These are taken from the msmUSB.inf file on the Windows driver CD */ const static struct vendor_product mts_supported_products[] = { - { - 0x4ce, 0x300,"Phantom 336CX",mts_sup_unknown - }, - { - 0x5da, 0x94,"Phantom 336CX",mts_sup_unknown - }, - { - 0x5da, 0x99,"Scanmaker X6",mts_sup_alpha - }, - { - 0x5da, 0x9a,"Phantom C6",mts_sup_unknown - }, - { - 0x5da, 0xa0,"Phantom 336CX",mts_sup_unknown - }, - { - 0x5da, 0xa3,"ScanMaker V6USL",mts_sup_unknown - }, - { - 0x5da, 0x80a3,"ScanMaker V6USL",mts_sup_unknown - }, - { - 0x5da, 0x80ac,"Scanmaker V6UL",mts_sup_unknown - } -} -; -const static struct vendor_product* mts_last_product = &mts_supported_products[ sizeof(mts_supported_products) / sizeof(struct vendor_product) ]; - /* Must never be derefed, points to one after last entry */ + { "Phantom 336CX", mts_sup_unknown}, + { "Phantom 336CX", mts_sup_unknown}, + { "Scanmaker X6", mts_sup_alpha}, + { "Phantom C6", mts_sup_unknown}, + { "Phantom 336CX", mts_sup_unknown}, + { "ScanMaker V6USL", mts_sup_unknown}, + { "ScanMaker V6USL", mts_sup_unknown}, + { "Scanmaker V6UL", mts_sup_unknown}, +}; + +/* The entries of microtek_table must correspond, line-by-line to + the entries of mts_supported_products[]. */ + +static struct usb_device_id mts_usb_ids [] = +{ + {idVendor: 0x4ce, idProduct: 0x0300}, + {idVendor: 0x5da, idProduct: 0x0094}, + {idVendor: 0x5da, idProduct: 0x0099}, + {idVendor: 0x5da, idProduct: 0x009a}, + {idVendor: 0x5da, idProduct: 0x00a0}, + {idVendor: 0x5da, idProduct: 0x00a3}, + {idVendor: 0x5da, idProduct: 0x80a3}, + {idVendor: 0x5da, idProduct: 0x80ac}, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, mts_usb_ids); -static void * mts_usb_probe (struct usb_device *dev, unsigned int interface) + +static void * mts_usb_probe (struct usb_device *dev, unsigned int interface, + const struct usb_device_id *id) { int i; int result; @@ -860,20 +862,8 @@ static void * mts_usb_probe (struct usb_device *dev, unsigned int interface) (int)dev->descriptor.idVendor ); MTS_DEBUG_GOT_HERE(); - - /* checking IDs */ - for( p = mts_supported_products; p != mts_last_product; p++ ) - if ( dev->descriptor.idVendor == p->idVendor && - dev->descriptor.idProduct == p->idProduct ) - goto is_supported; - else - MTS_DEBUG( "doesn't appear to be model %s\n", p->name ); - - MTS_DEBUG( "returning NULL: unsupported\n" ); - - return NULL; - - is_supported: + + p = &mts_supported_products[id - mts_usb_ids]; MTS_DEBUG_GOT_HERE(); diff --git a/drivers/usb/net1080.c b/drivers/usb/net1080.c index bdfe71eb4..eae365d47 100644 --- a/drivers/usb/net1080.c +++ b/drivers/usb/net1080.c @@ -11,7 +11,9 @@ * The IP-over-USB protocol here may be of interest. Embedded devices * could implement it at the cost of two bulk endpoints, and whatever * other system resources the desired IP-based applications need. - * Some Linux palmtops could support that today. + * Some Linux palmtops could support that today. (Devices that don't + * support the TTL-driven data mangling of the net1080 chip won't need + * the header/trailer support though.) * * STATUS: * @@ -22,6 +24,15 @@ * should handle static and dynamic ("pump") setups. * * RX/TX queue sizes currently fixed at one due to URB unlink problems. + * + * 10-oct-2000 + * usb_device_id table created. + * + * 28-oct-2000 + * misc fixes; mostly, discard more TTL-mangled rx packets. + * + * 01-nov-2000 + * usb_device_id table support added by Adam J. Richter <adam@yggdrasil.com>. * *-------------------------------------------------------------------------*/ @@ -642,6 +653,7 @@ static int net1080_stop (struct net_device *net) dbg ("waited for %d urb completions", temp); } dev->wait = 0; + current->state = TASK_RUNNING; remove_wait_queue (&unlink_wakeup, &wait); mutex_unlock (&dev->mutex); @@ -989,7 +1001,7 @@ static void net1080_disconnect (struct usb_device *udev, void *ptr) // precondition: never called in_interrupt static void * -net1080_bind (struct usb_device *udev, unsigned ifnum, const struct usb_device_id *prod) +net1080_probe (struct usb_device *udev, unsigned ifnum, const struct usb_device_id *prod) { struct net1080 *dev; struct net_device *net; @@ -1073,7 +1085,7 @@ net1080_bind (struct usb_device *udev, unsigned ifnum, const struct usb_device_i static struct usb_driver net1080_driver = { name: "net1080", id_table: products, - bind: net1080_bind, + probe: net1080_probe, disconnect: net1080_disconnect, }; diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 275ff05b7..2f88242f3 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.25"; +static const char version[] = "1.28"; #define __NO_VERSION__ @@ -51,8 +51,6 @@ static const char version[] = "1.25"; #include "ov511.h" -#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */ - #define OV511_I2C_RETRIES 3 /* Video Size 640 x 480 x 3 bytes for RGB */ @@ -60,8 +58,6 @@ static const char version[] = "1.25"; #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) -#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \ - ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24)) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -74,7 +70,7 @@ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ * 5=highly repetitive mesgs * NOTE: This should be changed to 0, 1, or 2 for production kernels */ -static int debug = 3; +static int debug = 0; /* Fix vertical misalignment of red and blue at 640x480 */ static int fix_rgb_offset = 0; @@ -112,6 +108,13 @@ static int compress = 0; /* Display test pattern - doesn't work yet either */ static int testpat = 0; +/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only + * affects RGB24 mode. */ +static int sensor_gbr = 0; + +/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */ +static int dumppix = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure"); MODULE_PARM(debug, "i"); @@ -138,6 +141,10 @@ MODULE_PARM(compress, "i"); MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)"); MODULE_PARM(testpat, "i"); MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)"); +MODULE_PARM(sensor_gbr, "i"); +MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420"); +MODULE_PARM(dumppix, "i"); +MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details"); MODULE_AUTHOR("Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"); MODULE_DESCRIPTION("OV511 USB Camera Driver"); @@ -167,6 +174,15 @@ static struct cam_list clist[] = { { -1, NULL } }; +static __devinitdata struct usb_device_id device_table [] = { + { idVendor: 0x05a9, idProduct: 0x0511 }, /* OV511 */ + { idVendor: 0x05a9, idProduct: 0xA511 }, /* OV511+ */ + { idVendor: 0x0813, idProduct: 0x0002 }, /* Intel Play Me2Cam OV511+ */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, device_table); + #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) static struct palette_list plist[] = { { VIDEO_PALETTE_GREY, "GREY" }, @@ -357,6 +373,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, ov511->bridge == BRG_OV511PLUS ? "OV511+" : "unknown"); out += sprintf (out, "sensor : %s\n", + ov511->sensor == SEN_OV6620 ? "OV6620" : ov511->sensor == SEN_OV7610 ? "OV7610" : ov511->sensor == SEN_OV7620 ? "OV7620" : ov511->sensor == SEN_OV7620AE ? "OV7620AE" : @@ -448,7 +465,6 @@ static void proc_ov511_destroy(void) } #endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ - /********************************************************************** * * Camera interface @@ -700,7 +716,7 @@ static int ov511_reset(struct usb_device *dev, unsigned char reset_type) { int rc; - PDEBUG(3, "Reset: type=0x%X", reset_type); + PDEBUG(4, "Reset: type=0x%X", reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0); @@ -829,14 +845,21 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) } else if ((ov511->sensor == SEN_OV7620) || (ov511->sensor == SEN_OV7620AE)) { #if 0 - cur_con = ov511_i2c_read(dev, OV7610_REG_CNT); - cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT); - // DEBUG_CODE - PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT), - ov511_i2c_read(dev, OV7610_REG_BRT)); + int cur_sat, new_sat, tmp; - if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) + cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE); + + tmp = (p->hue >> 8) - cur_sat; + new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp; + + PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); + + if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0) return -EIO; + + // DEBUG_CODE + PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); + #endif } @@ -882,6 +905,23 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } +/* Returns number of bits per pixel (regardless of where they are located; planar or + * not), or zero for unsupported format. + */ +static int ov511_get_depth(int palette) +{ + switch (palette) { + case VIDEO_PALETTE_GREY: return 8; + case VIDEO_PALETTE_RGB565: return 16; + case VIDEO_PALETTE_RGB24: return 24; + case VIDEO_PALETTE_YUV422: return 16; + case VIDEO_PALETTE_YUYV: return 16; + case VIDEO_PALETTE_YUV420: return 24; + case VIDEO_PALETTE_YUV422P: return 24; /* Planar */ + default: return 0; /* Invalid format */ + } +} + /* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */ static struct mode_list mlist[] = { /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COML */ @@ -917,6 +957,12 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, if (ov511_stop(ov511->dev) < 0) return -EIO; + /* Dumppix only works with RGB24 */ + if (dumppix && (mode != VIDEO_PALETTE_RGB24)) { + err("dumppix only supported with RGB 24"); + return -EINVAL; + } + if (mode == VIDEO_PALETTE_GREY) { ov511_reg_write(dev, 0x16, 0x00); if (ov511->sensor == SEN_OV7610 @@ -953,9 +999,9 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, break; case SEN_OV6620: hwsbase = 0x38; - hwebase = 0x39; - vwsbase = 0x03; - vwebase = 0x04; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; break; case SEN_OV7620: hwsbase = 0x2c; @@ -1039,13 +1085,16 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, /* Calculate and set the clock divisor */ clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) * (mlist[i].color ? 3 : 2) / 2) / 66000; +#if 0 + clock *= cams; +#endif ov511_i2c_write(dev, 0x11, clock); -#ifdef OV511_GBR422 - ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08); -#else - ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); -#endif + /* We only have code to convert GBR -> RGB24 */ + if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr) + ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08)); + else + ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); /* 7620/6620 don't have register 0x35, so play it safe */ if (ov511->sensor == SEN_OV7610 || @@ -1113,7 +1162,7 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, static inline void ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, - int rowPixels, unsigned char * rgb) + int rowPixels, unsigned char * rgb, int bits) { const int rvScale = 91881; const int guScale = -22553; @@ -1134,14 +1183,32 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, yTL *= yScale; yTR *= yScale; yBL *= yScale; yBR *= yScale; - /* Write out top two pixels */ - rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); - rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); + if (bits == 24) { + /* Write out top two pixels */ + rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); + rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 3 * rowPixels; + rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); + rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); + } else if (bits == 16) { + /* Write out top two pixels */ + rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 3) & 0xE0); + rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8); + + rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 3) & 0xE0); + rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8); - /* Skip down to next line to write out bottom two pixels */ - rgb += 3 * rowPixels; - rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); - rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); + /* Skip down to next line to write out bottom two pixels */ + rgb += 2 * rowPixels; + + rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 3) & 0xE0); + rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8); + + rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 3) & 0xE0); + rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8); + } } /* @@ -1167,7 +1234,7 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, * Note that the U and V data in one segment represents a 16 x 16 pixel * area, but the Y data represents a 32 x 8 pixel area. * - * If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments, + * If dumppix module param is set, _parse_data just dumps the incoming segments, * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 * this puts the data on the standard output and can be analyzed with the * parseppm.c utility I wrote. That's a much faster way for figuring out how @@ -1177,14 +1244,8 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, #define HDIV 8 #define WDIV (256/HDIV) -#undef OV511_DUMPPIX - -/* #define this and OV511_DUMPPIX to disable parsing of UV data */ -#undef OV511_FORCE_MONO - -#ifdef OV511_GBR422 static void -ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, +ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) { int k, l, m; @@ -1231,14 +1292,12 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, } } -#else - static void -ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) +ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth, int bits) { -#ifndef OV511_DUMPPIX int k, l, m; + int bytes = bits >> 3; unsigned char *pIn; unsigned char *pOut, *pOut1; @@ -1251,11 +1310,11 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, for (l = 0; l < 8; l++) { for (m = 0; m < 8; m++) { *pOut1 = *pIn++; - pOut1 += 3; + pOut1 += bytes; } - pOut1 += (iWidth - 8) * 3; + pOut1 += (iWidth - 8) * bytes; } - pOut += 8 * 3; + pOut += 8 * bytes; } } @@ -1265,16 +1324,16 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, for (l = 0; l < 4; l++) { for (m=0; m<8; m++) { int y00 = *(pOut); - int y01 = *(pOut+3); - int y10 = *(pOut+iWidth*3); - int y11 = *(pOut+iWidth*3+3); + int y01 = *(pOut+bytes); + int y10 = *(pOut+iWidth*bytes); + int y11 = *(pOut+iWidth*bytes+bytes); int v = *(pIn+64) - 128; int u = *pIn++ - 128; ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, - pOut); - pOut += 6; + pOut, bits); + pOut += 2 * bytes; } - pOut += (iWidth*2 - 16) * 3; + pOut += (iWidth*2 - 16) * bytes; } /* Just copy the other UV rows */ @@ -1282,9 +1341,9 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, for (m = 0; m < 8; m++) { *pOut++ = *(pIn + 64); *pOut = *pIn++; - pOut += 5; + pOut += 2 * bytes - 1; } - pOut += (iWidth*2 - 16) * 3; + pOut += (iWidth*2 - 16) * bytes; } /* Calculate values if it's the second half */ @@ -1302,72 +1361,65 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, int v = *pOut1 - 128; int u = *(pOut1+1) - 128; ov511_move_420_block(y00, y01, y10, - y11, u, v, iWidth, pOut1); - pOut1 += 6; + y11, u, v, iWidth, pOut1, bits); + pOut1 += 2 * bytes; } - pOut1 += (iWidth*2 - 8) * 3; + pOut1 += (iWidth*2 - 8) * bytes; pIn += 8; } - pOut += 8 * 3; + pOut += 8 * bytes; } } -#else - -#ifndef OV511_FORCE_MONO - /* Just dump pix data straight out for debug */ - int i, j; - - pOut0 += iOutY; - for (i = 0; i < HDIV; i++) { - for (j = 0; j < WDIV; j++) { - *pOut0++ = *pIn0++; - *pOut0++ = *pIn0++; - *pOut0++ = *pIn0++; - } - pOut0 += (iWidth - WDIV) * 3; - } -#else +} -#if 1 - /* This converts the Y data to "black-and-white" RGB data */ - /* Useful for experimenting with compression */ - int k, l, m; +static void +ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth) +{ + int i, j, k; unsigned char *pIn, *pOut, *pOut1; - pIn = pIn0 + 128; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - for (m = 0; m < 8; m++) { - *pOut1++ = *pIn; - *pOut1++ = *pIn; - *pOut1++ = *pIn++; + switch (dumppix) { + case 1: /* Just dump YUV data straight out for debug */ + pOut0 += iOutY; + for (i = 0; i < HDIV; i++) { + for (j = 0; j < WDIV; j++) { + *pOut0++ = *pIn0++; + *pOut0++ = *pIn0++; + *pOut0++ = *pIn0++; } - pOut1 += (iWidth - 8) * 3; + pOut0 += (iWidth - WDIV) * 3; } - pOut += 8 * 3; - } -#else - /* This will dump the Y channel data stream as-is */ - int count; - unsigned char *pIn, *pOut; - - pIn = pIn0 + 128; - pOut = pOut0 + output_offset; - for (count = 0; count < 256; count++) { - *pOut++ = *pIn; - *pOut++ = *pIn; - *pOut++ = *pIn++; - output_offset += 3; - } -#endif - -#endif - -#endif + break; + case 2: /* This converts the Y data to "black-and-white" RGB data */ + /* Useful for experimenting with compression */ + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (i = 0; i < 4; i++) { + pOut1 = pOut; + for (j = 0; j < 8; j++) { + for (k = 0; k < 8; k++) { + *pOut1++ = *pIn; + *pOut1++ = *pIn; + *pOut1++ = *pIn++; + } + pOut1 += (iWidth - 8) * 3; + } + pOut += 8 * 3; + } + break; + case 3: /* This will dump only the Y channel data stream as-is */ + pIn = pIn0 + 128; + pOut = pOut0 + output_offset; + for (i = 0; i < 256; i++) { + *pOut++ = *pIn; + *pOut++ = *pIn; + *pOut++ = *pIn++; + output_offset += 3; + } + break; + } /* End switch (dumppix) */ } -#endif /* This converts YUV420 segments to YUYV */ static void @@ -1662,6 +1714,11 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) /* Frame start */ PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); +#if 0 + /* Make sure no previous data carries over; necessary + * for compression experimentation */ + memset(frame->data, 0, MAX_DATA_SIZE); +#endif output_offset = 0; /* Check to see if it's a snapshot frame */ @@ -1740,8 +1797,19 @@ check_middle: ov511_parse_data_grey (pData, pOut, iOutY, frame->width); break; case VIDEO_PALETTE_RGB24: - ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, - iY & 1, frame->width); + if (dumppix) + ov511_dumppix(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + else if (sensor_gbr) + ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + else + ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width, 24); + break; + case VIDEO_PALETTE_RGB565: + ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width, 16); break; case VIDEO_PALETTE_YUV422: case VIDEO_PALETTE_YUYV: @@ -1782,11 +1850,20 @@ check_middle: static void ov511_isoc_irq(struct urb *urb) { int len; - struct usb_ov511 *ov511 = urb->context; + struct usb_ov511 *ov511; struct ov511_sbuf *sbuf; - if (!ov511->dev) + if (!urb->context) { + PDEBUG(4, "no context"); return; + } + + ov511 = (struct usb_ov511 *) urb->context; + + if (!ov511->dev || !ov511->user) { + PDEBUG(4, "no device, or not open"); + return; + } if (!ov511->streaming) { PDEBUG(4, "hmmm... not streaming, but got interrupt"); @@ -1805,6 +1882,8 @@ static void ov511_isoc_irq(struct urb *urb) /* Move to the next sbuf */ ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF; + urb->dev = ov511->dev; + return; } @@ -1872,6 +1951,7 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb; for (n = 0; n < OV511_NUMSBUF; n++) { + ov511->sbuf[n].urb->dev = ov511->dev; err = usb_submit_urb(ov511->sbuf[n].urb); if (err) err("init isoc: usb_submit_urb(%d) ret %d", n, err); @@ -2079,7 +2159,7 @@ static void ov511_dealloc(struct usb_ov511 *ov511, int now) static int ov511_open(struct video_device *dev, int flags) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - int err = 0; + int err; MOD_INC_USE_COUNT; PDEBUG(4, "opening"); @@ -2174,8 +2254,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) b.audios = 0; b.maxwidth = ov511->maxwidth; b.maxheight = ov511->maxheight; - b.minwidth = 32; - b.minheight = 16; + b.minwidth = 160; + b.minheight = 120; if (copy_to_user(arg, &b, sizeof(b))) return -EFAULT; @@ -2237,12 +2317,7 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if (copy_from_user(&p, arg, sizeof(p))) return -EFAULT; - if (p.palette != VIDEO_PALETTE_GREY && - p.palette != VIDEO_PALETTE_RGB24 && - p.palette != VIDEO_PALETTE_YUV422 && - p.palette != VIDEO_PALETTE_YUYV && - p.palette != VIDEO_PALETTE_YUV420 && - p.palette != VIDEO_PALETTE_YUV422P) + if (!ov511_get_depth(p.palette)) return -EINVAL; if (ov7610_set_picture(ov511, &p)) @@ -2373,7 +2448,7 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) case VIDIOCMCAPTURE: { struct video_mmap vm; - int ret; + int ret, depth; if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; @@ -2382,19 +2457,21 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) PDEBUG(4, "frame: %d, size: %dx%d, format: %d", vm.frame, vm.width, vm.height, vm.format); - if (vm.format != VIDEO_PALETTE_RGB24 && - vm.format != VIDEO_PALETTE_YUV422 && - vm.format != VIDEO_PALETTE_YUYV && - vm.format != VIDEO_PALETTE_YUV420 && - vm.format != VIDEO_PALETTE_YUV422P && - vm.format != VIDEO_PALETTE_GREY) + depth = ov511_get_depth(vm.format); + if (!depth) { + err("VIDIOCMCAPTURE: invalid format (%d)", vm.format); return -EINVAL; + } - if ((vm.frame != 0) && (vm.frame != 1)) + if ((vm.frame != 0) && (vm.frame != 1)) { + err("VIDIOCMCAPTURE: invalid frame (%d)", vm.frame); return -EINVAL; - - if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) + } + + if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) { + err("VIDIOCMCAPTURE: requested dimensions too big"); return -EINVAL; + } if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) return -EBUSY; @@ -2422,7 +2499,7 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) ov511->frame[vm.frame].format = vm.format; ov511->frame[vm.frame].sub_flag = ov511->sub_flag; ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format); - ov511->frame[vm.frame].depth = GET_DEPTH(vm.format); + ov511->frame[vm.frame].depth = depth; /* Mark it as ready */ ov511->frame[vm.frame].grabstate = FRAME_READY; @@ -2481,6 +2558,13 @@ redo: goto redo; } case FRAME_DONE: + if (ov511->snap_enabled && !ov511->frame[frame].snapshot) { + int ret; + if ((ret = ov511_new_frame(ov511, frame)) < 0) + return ret; + goto redo; + } + ov511->frame[frame].grabstate = FRAME_UNUSED; /* Reset the hardware snapshot button */ @@ -2657,7 +2741,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); - if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) return -EINVAL; pos = (unsigned long)ov511->fbuf; @@ -3057,7 +3141,7 @@ static int ov511_configure(struct usb_ov511 *ov511) init_waitqueue_head(&ov511->wq); - if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) { + if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) { err("video_register_device failed"); return -EBUSY; } @@ -3144,7 +3228,9 @@ error: * ***************************************************************************/ -static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) +static void * __devinit +ov511_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_interface_descriptor *interface; struct usb_ov511 *ov511; @@ -3158,17 +3244,8 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) interface = &dev->actconfig->interface[ifnum].altsetting[0]; - /* Is it an OV511/OV511+? */ - if (dev->descriptor.idVendor != 0x05a9 - && dev->descriptor.idVendor != 0x0813) - return NULL; - if (dev->descriptor.idProduct != 0x0511 - && dev->descriptor.idProduct != 0xA511 - && dev->descriptor.idProduct != 0x0002) - return NULL; - /* Checking vendor/product should be enough, but what the hell */ - if (interface->bInterfaceClass != 0xFF) + if (interface->bInterfaceClass != 0xFF) return NULL; if (interface->bInterfaceSubClass != 0x00) return NULL; @@ -3263,7 +3340,8 @@ error: } -static void ov511_disconnect(struct usb_device *dev, void *ptr) +static void __devexit +ov511_disconnect(struct usb_device *dev, void *ptr) { struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr; int n; @@ -3321,10 +3399,10 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) } static struct usb_driver ov511_driver = { - "ov511", - ov511_probe, - ov511_disconnect, - { NULL, NULL } + name: "ov511", + id_table: device_table, + probe: ov511_probe, + disconnect: ov511_disconnect }; diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 02c1eff80..3e6ddee49 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -40,7 +40,6 @@ */ -#include <linux/module.h> #include <linux/sched.h> #include <linux/malloc.h> #include <linux/init.h> @@ -48,162 +47,44 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/usb.h> - - -static const char *version = __FILE__ ": v0.4.13 2000/10/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)"; +#include <linux/module.h> +#include "pegasus.h" #define PEGASUS_USE_INTR +#define PEGASUS_WRITE_EEPROM - -#define PEGASUS_II 0x80000000 -#define HAS_HOME_PNA 0x40000000 - -#define PEGASUS_MTU 1500 -#define PEGASUS_MAX_MTU 1536 - -#define EPROM_WRITE 0x01 -#define EPROM_READ 0x02 -#define EPROM_DONE 0x04 -#define EPROM_WR_ENABLE 0x10 -#define EPROM_LOAD 0x20 - -#define MII_BMCR 0x00 -#define MII_BMSR 0x01 -#define BMSR_MEDIA 0x7808 -#define MII_ANLPA 0x05 -#define ANLPA_100TX_FD 0x0100 -#define ANLPA_100TX_HD 0x0080 -#define ANLPA_10T_FD 0x0040 -#define ANLPA_10T_HD 0x0020 -#define PHY_DONE 0x80 -#define PHY_READ 0x40 -#define PHY_WRITE 0x20 -#define DEFAULT_GPIO_RESET 0x24 -#define LINKSYS_GPIO_RESET 0x24 -#define DEFAULT_GPIO_SET 0x26 - -#define PEGASUS_PRESENT 0x00000001 -#define PEGASUS_RUNNING 0x00000002 -#define PEGASUS_TX_BUSY 0x00000004 -#define PEGASUS_RX_BUSY 0x00000008 -#define CTRL_URB_RUNNING 0x00000010 -#define CTRL_URB_SLEEP 0x00000020 -#define PEGASUS_UNPLUG 0x00000040 -#define ETH_REGS_CHANGE 0x40000000 -#define ETH_REGS_CHANGED 0x80000000 - -#define RX_MULTICAST 2 -#define RX_PROMISCUOUS 4 - -#define REG_TIMEOUT (HZ) -#define PEGASUS_TX_TIMEOUT (HZ*10) - -#define TX_UNDERRUN 0x80 -#define EXCESSIVE_COL 0x40 -#define LATE_COL 0x20 -#define NO_CARRIER 0x10 -#define LOSS_CARRIER 0x08 -#define JABBER_TIMEOUT 0x04 - -#define PEGASUS_REQT_READ 0xc0 -#define PEGASUS_REQT_WRITE 0x40 -#define PEGASUS_REQ_GET_REGS 0xf0 -#define PEGASUS_REQ_SET_REGS 0xf1 -#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS -#define NUM_CTRL_URBS 0x10 -#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) - -enum pegasus_registers { - EthCtrl0 = 0, - EthCtrl1 = 1, - EthCtrl2 = 2, - EthID = 0x10, - Reg1d = 0x1d, - EpromOffset = 0x20, - EpromData = 0x21, /* 0x21 low, 0x22 high byte */ - EpromCtrl = 0x23, - PhyAddr = 0x25, - PhyData = 0x26, /* 0x26 low, 0x27 high byte */ - PhyCtrl = 0x28, - UsbStst = 0x2a, - EthTxStat0 = 0x2b, - EthTxStat1 = 0x2c, - EthRxStat = 0x2d, - Reg7b = 0x7b, - Gpio0 = 0x7e, - Gpio1 = 0x7f, - Reg81 = 0x81, -}; - - -typedef struct pegasus { - struct usb_device *usb; - struct net_device *net; - struct net_device_stats stats; - unsigned flags; - unsigned features; - int intr_interval; - struct urb ctrl_urb, rx_urb, tx_urb, intr_urb; - devrequest dr; - wait_queue_head_t ctrl_wait; - struct semaphore ctrl_sem; - unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); - unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); - unsigned char ALIGN(intr_buff[8]); - __u8 eth_regs[4]; - __u8 phy; - __u8 gpio_res; -} pegasus_t; - -struct usb_eth_dev { - char *name; - __u16 vendor; - __u16 device; - __u32 private; /* LSB is gpio reset value */ -}; +static const char *version = __FILE__ ": v0.4.17 2000/11/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)"; static int loopback = 0; static int mii_mode = 0; static int multicast_filter_limit = 32; +static struct usb_eth_dev usb_dev_id[] = { +#define PEGASUS_DEV(pn, vid, pid, flags) \ + {name:pn, vendor:vid, device:pid, private:flags}, +#include "pegasus.h" +#undef PEGASUS_DEV + {NULL, 0, 0, 0} +}; + +static struct usb_device_id pegasus_ids[] = { +#define PEGASUS_DEV(pn, vid, pid, flags) {idVendor:vid, idProduct:pid}, +#include "pegasus.h" +#undef PEGASUS_DEV + { } +}; + MODULE_AUTHOR("Petko Manolov <petkan@dce.bg>"); MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); MODULE_PARM(loopback, "i"); -MODULE_PARM(mode, "i"); +MODULE_PARM(mii_mode, "i"); MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); -MODULE_PARM_DESC(mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0"); +MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); - -static struct usb_eth_dev usb_dev_id[] = { - {"Billionton USB-100", 0x08dd, 0x0986, DEFAULT_GPIO_RESET}, - {"Corega FEter USB-TX", 0x7aa, 0x0004, DEFAULT_GPIO_RESET}, - {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, DEFAULT_GPIO_RESET}, - {"D-Link DSB-650TX", 0x2001, 0x4001, LINKSYS_GPIO_RESET}, - {"D-Link DSB-650TX", 0x2001, 0x4002, LINKSYS_GPIO_RESET}, - {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, - HAS_HOME_PNA | DEFAULT_GPIO_RESET}, - {"D-Link DSB-650", 0x2001, 0xabc1, DEFAULT_GPIO_RESET}, - {"D-Link DU-E10", 0x07b8, 0xabc1, DEFAULT_GPIO_RESET}, - {"D-Link DU-E100", 0x07b8, 0x4002, DEFAULT_GPIO_RESET}, - {"Linksys USB10TX", 0x066b, 0x2202, LINKSYS_GPIO_RESET}, - {"Linksys USB100TX", 0x066b, 0x2203, LINKSYS_GPIO_RESET}, - {"Linksys USB100TX", 0x066b, 0x2204, HAS_HOME_PNA | LINKSYS_GPIO_RESET}, - {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, LINKSYS_GPIO_RESET}, - {"SMC 202 USB Ethernet", 0x0707, 0x0200, DEFAULT_GPIO_RESET}, - {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, - HAS_HOME_PNA | DEFAULT_GPIO_RESET}, - {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, - DEFAULT_GPIO_RESET}, - {"IO DATA USB ET/TX", 0x04bb, 0x0904, DEFAULT_GPIO_RESET}, - {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, DEFAULT_GPIO_RESET}, - {"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, DEFAULT_GPIO_RESET}, - {"ADMtek ADM8511 \"Pegasus II\" USB Ethernet", 0x07a6, 0x8511, - PEGASUS_II | DEFAULT_GPIO_RESET}, - {NULL, 0, 0, 0} -}; +MODULE_DEVICE_TABLE (usb, pegasus_ids); static int update_eth_regs_async( pegasus_t * ); @@ -243,7 +124,7 @@ static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { int ret; - if ( pegasus->flags & ETH_REGS_CHANGED ) { + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP; interruptible_sleep_on( &pegasus->ctrl_wait ); } @@ -274,7 +155,7 @@ static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { int ret; - if ( pegasus->flags & ETH_REGS_CHANGED ) { + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP ; interruptible_sleep_on( &pegasus->ctrl_wait ); } @@ -305,7 +186,7 @@ static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ) { int ret; - if ( pegasus->flags & ETH_REGS_CHANGED ) { + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP; interruptible_sleep_on( &pegasus->ctrl_wait ); } @@ -354,7 +235,7 @@ static int update_eth_regs_async( pegasus_t *pegasus ) } -static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) +static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) { int i; __u8 data[4] = { phy, 0, 0, indx }; @@ -377,7 +258,7 @@ static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) } -static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) +static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) { int i; __u8 data[4] = { phy, 0, 0, indx }; @@ -505,6 +386,15 @@ static inline int reset_mac( pegasus_t *pegasus ) } if ( i == REG_TIMEOUT ) return 1; + + if ( usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || + usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK1 ) { + __u16 auxmode; + + read_mii_word( pegasus, 0, 0x1b, &auxmode ); + write_mii_word( pegasus, 0, 0x1b, auxmode | 4 ); + } + return 0; } @@ -516,13 +406,13 @@ static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ) pegasus_t *pegasus = dev->priv; - if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) - return 2; + if ( read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) + return 1; if ( !(bmsr & 0x20) && !loopback ) warn( "%s: link NOT established (0x%x) - check the cable.", dev->name, bmsr ); - if ( read_phy_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) - return 4; + if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) + return 2; if ( !(linkpart & 1) ) warn( "link partner stat %x", linkpart ); @@ -536,7 +426,7 @@ static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ) data[1] = 0; data[2] = (loopback & 1) ? 0x09 : 0x01; - *(unsigned *)pegasus->eth_regs = *(unsigned *)data; + memcpy( pegasus->eth_regs, data, sizeof(data) ); set_registers( pegasus, EthCtrl0, 3, data ); @@ -562,23 +452,29 @@ static void read_bulk_callback( struct urb *urb ) if ( pegasus->flags & PEGASUS_RX_BUSY ) { pegasus->stats.rx_errors++; + dbg("pegasus Rx busy"); return; } pegasus->flags |= PEGASUS_RX_BUSY; - rx_status = *(int *)(pegasus->rx_buff + count - 4); - - if (urb->status) { - dbg("%s: RX status %d", net->name, urb->status); - goto goon; + switch ( urb->status ) { + case USB_ST_NOERROR: + break; + case USB_ST_NORESPONSE: + dbg( "reset MAC" ); + pegasus->flags &= ~PEGASUS_RX_BUSY; + break; + default: + dbg( "%s: RX status %d", net->name, urb->status ); + goto goon; } if ( !count ) goto goon; + rx_status = *(int *)(pegasus->rx_buff + count - 4); if ( rx_status & 0x000e0000 ) { - - dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000); + dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000); pegasus->stats.rx_errors++; if ( rx_status & 0x060000 ) pegasus->stats.rx_length_errors++; @@ -586,7 +482,6 @@ static void read_bulk_callback( struct urb *urb ) pegasus->stats.rx_crc_errors++; if ( rx_status & 0x100000 ) pegasus->stats.rx_frame_errors++; - goto goon; } @@ -629,6 +524,7 @@ static void write_bulk_callback( struct urb *urb ) if ( urb->status ) info("%s: TX status %d", pegasus->net->name, urb->status); + pegasus->net->trans_start = jiffies; netif_wake_queue( pegasus->net ); } @@ -641,6 +537,16 @@ static void intr_callback( struct urb *urb ) if ( !pegasus ) return; + + switch ( urb->status ) { + case USB_ST_NOERROR: + break; + case USB_ST_URB_KILLED: + return; + default: + info("intr status %d", urb->status); + } + d = urb->transfer_buffer; net = pegasus->net; if ( d[0] & 0xfc ) { @@ -654,31 +560,23 @@ static void intr_callback( struct urb *urb ) if ( d[0] & (NO_CARRIER | LOSS_CARRIER) ) pegasus->stats.tx_carrier_errors++; } - switch ( urb->status ) { - case USB_ST_NOERROR: - break; - case USB_ST_URB_KILLED: - break; - default: - info("intr status %d", urb->status); - } } #endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48) static void pegasus_tx_timeout( struct net_device *net ) { pegasus_t *pegasus = net->priv; if ( !pegasus ) return; - - usb_unlink_urb( &pegasus->tx_urb ); + warn("%s: Tx timed out.", net->name); + pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; + usb_unlink_urb( &pegasus->tx_urb ); pegasus->stats.tx_errors++; - net->trans_start = jiffies; - - netif_wake_queue( net ); } +#endif static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net ) @@ -696,7 +594,6 @@ static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net ) pegasus->tx_buff, PEGASUS_MAX_MTU, write_bulk_callback, pegasus ); pegasus->tx_urb.transfer_buffer_length = count; - pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; if ((res = usb_submit_urb(&pegasus->tx_urb))) { warn("failed tx_urb %d", res); pegasus->stats.tx_errors++; @@ -732,6 +629,14 @@ static inline void get_interrupt_interval( pegasus_t *pegasus ) __u8 data[2]; read_eprom_word( pegasus, 4, (__u16 *)data ); + if ( data[1] < 0x80 ) { + info( "intr interval will be changed from %ums to %ums", + data[1], 0x80 ); + data[1] = 0x80; +#ifdef PEGASUS_WRITE_EEPROM + write_eprom_word( pegasus, 4, *(__u16 *)data ); +#endif + } pegasus->intr_interval = data[1]; } @@ -754,7 +659,6 @@ static int pegasus_open(struct net_device *net) if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) warn( __FUNCTION__ " failed rx_urb %d", res ); #ifdef PEGASUS_USE_INTR - get_interrupt_interval( pegasus ); FILL_INT_URB( &pegasus->intr_urb, pegasus->usb, usb_rcvintpipe(pegasus->usb, 3), pegasus->intr_buff, sizeof(pegasus->intr_buff), @@ -781,8 +685,9 @@ static int pegasus_close( struct net_device *net ) usb_unlink_urb( &pegasus->rx_urb ); usb_unlink_urb( &pegasus->tx_urb ); usb_unlink_urb( &pegasus->ctrl_urb ); +#ifdef PEGASUS_USE_INTR usb_unlink_urb( &pegasus->intr_urb ); - +#endif MOD_DEC_USE_COUNT; return 0; @@ -798,12 +703,12 @@ static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) case SIOCDEVPRIVATE: data[0] = pegasus->phy; case SIOCDEVPRIVATE+1: - read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]); + read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); return 0; case SIOCDEVPRIVATE+2: if ( !capable(CAP_NET_ADMIN) ) return -EPERM; - write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); + write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; @@ -838,34 +743,20 @@ static void pegasus_set_multicast( struct net_device *net ) } -static int check_device_ids( __u16 vendor, __u16 product ) -{ - int i=0; - - while ( usb_dev_id[i].name ) { - if ( (usb_dev_id[i].vendor == vendor) && - (usb_dev_id[i].device == product) ) - return i; - i++; - } - return -1; -} - - static __u8 mii_phy_probe( pegasus_t *pegasus ) { int i; __u16 tmp; for ( i=0; i < 32; i++ ) { - read_phy_word( pegasus, i, MII_BMSR, &tmp ); + read_mii_word( pegasus, i, MII_BMSR, &tmp ); if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 ) continue; else return i; } - return 0; + return 0xff; } @@ -880,15 +771,12 @@ static inline void setup_pegasus_II( pegasus_t *pegasus ) } -static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) +static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { - struct net_device *net; - pegasus_t *pegasus; - int dev_indx; - - if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) { - return NULL; - } + struct net_device *net; + pegasus_t *pegasus; + int dev_index = id - pegasus_ids; if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { err("usb_set_configuration() failed"); @@ -902,7 +790,7 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) usb_inc_dev_use( dev ); memset(pegasus, 0, sizeof(struct pegasus)); - init_MUTEX( &pegasus-> ctrl_sem ); + pegasus->dev_index = dev_index; init_waitqueue_head( &pegasus->ctrl_wait ); net = init_etherdev( NULL, 0 ); @@ -926,7 +814,10 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; - pegasus->features = usb_dev_id[dev_indx].private; + pegasus->features = usb_dev_id[dev_index].private; +#ifdef PEGASUS_USE_INTR + get_interrupt_interval( pegasus ); +#endif if ( reset_mac(pegasus) ) { err("can't reset MAC"); unregister_netdev( pegasus->net ); @@ -935,21 +826,21 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) return NULL; } + info( "%s: %s", net->name, usb_dev_id[dev_index].name ); + set_ethernet_addr( pegasus ); - + if ( pegasus->features & PEGASUS_II ) { info( "setup Pegasus II specific registers" ); setup_pegasus_II( pegasus ); } pegasus->phy = mii_phy_probe( pegasus ); - if ( !pegasus->phy ) { + if ( pegasus->phy == 0xff ) { warn( "can't locate MII phy, using default" ); pegasus->phy = 1; } - info( "%s: %s", net->name, usb_dev_id[dev_indx].name ); - return pegasus; } @@ -975,6 +866,7 @@ static struct usb_driver pegasus_driver = { name: "pegasus", probe: pegasus_probe, disconnect: pegasus_disconnect, + id_table: pegasus_ids, }; int __init pegasus_init(void) diff --git a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h new file mode 100644 index 000000000..740aaf389 --- /dev/null +++ b/drivers/usb/pegasus.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef PEGASUS_DEV + +#define PEGASUS_II 0x80000000 +#define HAS_HOME_PNA 0x40000000 + +#define PEGASUS_MTU 1500 +#define PEGASUS_MAX_MTU 1536 + +#define EPROM_WRITE 0x01 +#define EPROM_READ 0x02 +#define EPROM_DONE 0x04 +#define EPROM_WR_ENABLE 0x10 +#define EPROM_LOAD 0x20 + +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 +#define BMSR_MEDIA 0x7808 +#define MII_ANLPA 0x05 +#define ANLPA_100TX_FD 0x0100 +#define ANLPA_100TX_HD 0x0080 +#define ANLPA_10T_FD 0x0040 +#define ANLPA_10T_HD 0x0020 +#define PHY_DONE 0x80 +#define PHY_READ 0x40 +#define PHY_WRITE 0x20 +#define DEFAULT_GPIO_RESET 0x24 +#define LINKSYS_GPIO_RESET 0x24 +#define DEFAULT_GPIO_SET 0x26 + +#define PEGASUS_PRESENT 0x00000001 +#define PEGASUS_RUNNING 0x00000002 +#define PEGASUS_TX_BUSY 0x00000004 +#define PEGASUS_RX_BUSY 0x00000008 +#define CTRL_URB_RUNNING 0x00000010 +#define CTRL_URB_SLEEP 0x00000020 +#define PEGASUS_UNPLUG 0x00000040 +#define ETH_REGS_CHANGE 0x40000000 +#define ETH_REGS_CHANGED 0x80000000 + +#define RX_MULTICAST 2 +#define RX_PROMISCUOUS 4 + +#define REG_TIMEOUT (HZ) +#define PEGASUS_TX_TIMEOUT (HZ*10) + +#define TX_UNDERRUN 0x80 +#define EXCESSIVE_COL 0x40 +#define LATE_COL 0x20 +#define NO_CARRIER 0x10 +#define LOSS_CARRIER 0x08 +#define JABBER_TIMEOUT 0x04 + +#define PEGASUS_REQT_READ 0xc0 +#define PEGASUS_REQT_WRITE 0x40 +#define PEGASUS_REQ_GET_REGS 0xf0 +#define PEGASUS_REQ_SET_REGS 0xf1 +#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS +#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) + +enum pegasus_registers { + EthCtrl0 = 0, + EthCtrl1 = 1, + EthCtrl2 = 2, + EthID = 0x10, + Reg1d = 0x1d, + EpromOffset = 0x20, + EpromData = 0x21, /* 0x21 low, 0x22 high byte */ + EpromCtrl = 0x23, + PhyAddr = 0x25, + PhyData = 0x26, /* 0x26 low, 0x27 high byte */ + PhyCtrl = 0x28, + UsbStst = 0x2a, + EthTxStat0 = 0x2b, + EthTxStat1 = 0x2c, + EthRxStat = 0x2d, + Reg7b = 0x7b, + Gpio0 = 0x7e, + Gpio1 = 0x7f, + Reg81 = 0x81, +}; + + +typedef struct pegasus { + struct usb_device *usb; + struct net_device *net; + struct net_device_stats stats; + unsigned flags; + unsigned features; + int dev_index; + int intr_interval; + struct urb ctrl_urb, rx_urb, tx_urb, intr_urb; + devrequest dr; + wait_queue_head_t ctrl_wait; + struct semaphore ctrl_sem; + unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(intr_buff[8]); + __u8 eth_regs[4]; + __u8 phy; + __u8 gpio_res; +} pegasus_t; + + +struct usb_eth_dev { + char *name; + __u16 vendor; + __u16 device; + __u32 private; /* LSB is gpio reset value */ +}; + + +#define VENDOR_ACCTON 0x083a +#define VENDOR_ADMTEK 0x07a6 +#define VENDOR_BILLIONTON 0x08dd +#define VENDOR_COREGA 0x07aa +#define VENDOR_DLINK1 0x2001 +#define VENDOR_DLINK2 0x07b8 +#define VENDOR_IODATA 0x04bb +#define VENDOR_LANEED 0x056e +#define VENDOR_LINKSYS 0x066b +#define VENDOR_MELCO 0x0411 +#define VENDOR_SMC 0x0707 +#define VENDOR_SOHOWARE 0x15e8 + + +#else /* PEGASUS_DEV */ + + +PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet", + VENDOR_ADMTEK, 0x8511, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", + VENDOR_ADMTEK, 0x0986, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK1, 0x4001, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK1, 0x4002, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK1, 0x4003, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK1, 0xabc1, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "D-Link DU-E10", VENDOR_DLINK2, 0xabc1, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "D-Link DU-E100", VENDOR_DLINK2, 0x4002, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204, + LINKSYS_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, + DEFAULT_GPIO_RESET ) + + +#endif /* PEGASUS_DEV */ diff --git a/drivers/usb/plusb.c b/drivers/usb/plusb.c index d3d1b4e6a..d247c0b28 100644 --- a/drivers/usb/plusb.c +++ b/drivers/usb/plusb.c @@ -1,9 +1,11 @@ /*****************************************************************************/ /* - * plusb.c -- prolific pl-2302 driver. + * plusb.c -- prolific pl-2301/pl-2302 driver. * * Copyright (C) 2000 Deti Fliegl (deti@fliegl.de) + * Copyright (C) 2000 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2000 Eric Z. Ayers (eric@compgen.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,9 +22,100 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * + * This driver creates a network interface (plusb0, plusb1, ...) that will + * send messages over a USB host-host cable based on the Prolific ASIC. + * It works a lot like plip or PP over an RS-232C null modem cable. + * + * Expect speeds of around 330Kbytes/second over a UHCI host controller. + * OHCI should be faster. Increase the MTU for faster transfers of large + * files. (16384 is a good size) * * $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $ * + * Changelog: + * + * v0.1 deti + * Original Version of driver. + * v0.2 15 Sep 2000 pavel + * Patches to decrease latency by rescheduling the bottom half of + * interrupt code. + * v0.3 10 Oct 2000 eric + * Patches to work in v2.2 backport (v2.4 changes the way net_dev.name + * is allocated) + * v0.4 19 Oct 2000 eric + * Some more performance fixes. Lock re-submitting urbs. + * Lower the number of sk_buff's to queue. + * v0.5 25 Oct 2000 eric + * Removed use of usb_bulk_msg() all together. This caused + * the driver to block in an interrupt context. + * Consolidate read urb submission into read_urb_submit(). + * Performance is the same as v0.4. + * v0.5.1 27 Oct 2000 eric + * Extra debugging messages to help diagnose problem with uchi.o stack. + * v0.5.2 27 Oct 2000 eric + * Set the 'start' flag for the network device in plusb_net_start() + * and plusb_net_stop() (doesn't help) + * v0.5.3 27 Oct 2000 pavel + * Commented out handlers when -EPIPE is received, + * (remove calls to usb_clear_halt()) Since the callback is in + * an interrupt context, it doesn't help, it just panics + * the kernel. (what do we do?) + * Under high load, dev_alloc_skb() fails, the read URB must + * be re-submitted. + * Added plusb_change_mtu() and increased the size of _BULK_DATA_LEN + * v0.5.4 31 Oct 2000 eric + * Fix race between plusb_net_xmit() and plusb_bulk_write_complete() + * v0.5.5 1 Nov 2000 eric + * Remove dev->start field, otherwise, it won't compile in 2.4 + * Use dev_kfree_skb_any(). (important in 2.4 kernel) + * v0.5.6 2 Nov 2000 pavel,eric + * Add calls to netif_stop_queue() and netif_start_queue() + * Drop packets that come in while the free list is empty. + * (This version is being submitted after the release of 2.4-test10) + * v0.5.7 6 Nov 2000 + * Fix to not re-submit the urb on error to help when cables + * are yanked (not tested) + * + * + * KNOWN PROBLEMS: (Any suggestions greatfully accepted!) + * + * 2 Nov 2000 + * - The shutdown for this may not be entirely clean. Sometimes, the + * kernel will Oops when the cable is unplugged, or + * if the plusb module is removed. + * - If you ifdown a device and then ifup it again, the link will not + * always work. You have to 'rmmod plusb ; modprobe plusb' on + * both machines to get it to work again. Something must be wrong with + * plusb_net_open() and plusb_net_start() ? Maybe + * the 'suspend' and 'resume' entry points need to be + * implemented? + * - Needs to handle -EPIPE correctly in bulk complete handlers. + * (replace usb_clear_halt() function with async urbs?) + * - I think this code relies too much on one spinlock and does + * too much in the interrupt handler. The net1080 code is + * much more elegant, and should work for this chip. Its + * only drawback is that it is going to be tough to backport + * it to v2.2. + * - Occasionally the device will hang under the 'uhci.o' + * driver. The workaround is to ifdown the device and + * remove the modules, then re-insert them. You may have + * better luck with the 'usb-uhci.o' driver. + * - After using ifconfig down ; ifconfig up, sometimes packets + * continue to be received, but there is a framing problem. + * + * FUTURE DIRECTIONS: + * + * - Fix the known problems. + * - There isn't much functional difference between the net1080 + * driver and this one. It would be neat if the same driver + * could handle both types of chips. Or if both drivers + * could handle both types of chips - this one is easier to + * backport to the 2.2 kernel. + * - Get rid of plusb_add_buf_tail and the single spinlock. + * Use a separate spinlock for the 2 lists, and use atomic + * operators for writeurb_submitted and readurb_submitted members. + * + * */ /*****************************************************************************/ @@ -40,23 +133,102 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> -//#define DEBUG +//#define DEBUG 1 #include <linux/usb.h> -#include "plusb.h" +/* Definitions formerly in plusb.h relocated. No need to export them -EZA */ + +#define _PLUSB_INTPIPE 0x1 +#define _PLUSB_BULKOUTPIPE 0x2 +#define _PLUSB_BULKINPIPE 0x3 + +#define _SKB_NUM 32 + +/* increase size of BULK_DATA_LEN so we can use bigger MTU's*/ +#define _BULK_DATA_LEN 32768 + + +typedef struct +{ + int connected; /* indicates if this structure is active */ + struct usb_device *usbdev; + /* keep track of USB structure */ + int status; /* Prolific status byte returned from interrupt */ + int in_bh; /* flag to indicate that we are in the bulk handler */ + int opened; /* flag to indicate that network dev is open */ + + spinlock_t lock; /* Lock for the buffer list. re-used for + locking around submitting the readurb member. + */ + urb_t *inturb; /* Read buffer for the interrupt callback */ + unsigned char * interrupt_in_buffer; + /* holds data for the inturb*/ + urb_t *readurb; /* Read buffer for the bulk data callback */ + unsigned char * bulk_in_buffer; + /* kmalloc'ed data for the readurb */ + int readurb_submitted; + /* Flag to indicate that readurb already sent */ + urb_t *writeurb; /* Write buffer for the bulk data callback */ + int writeurb_submitted; + /* Flag to indicate that writeurb already sent */ + + struct list_head tx_skb_list; + /* sk_buff's read from net device */ + struct list_head free_skb_list; + /* free sk_buff list */ + struct net_device net_dev; + /* handle to linux network device */ + struct net_device_stats net_stats; + /* stats to return for ifconfig output */ +} plusb_t,*pplusb_t; + +/* + * skb_list - queue of packets from the network driver to be delivered to USB + */ +typedef struct +{ + struct list_head skb_list; + struct sk_buff *skb; + int state; + plusb_t *s; +} skb_list_t,*pskb_list_t; + /* --------------------------------------------------------------------- */ #define NRPLUSB 4 +/* + * Interrupt endpoint status byte, from Prolific PL-2301 docs + * Check the 'download' link at www.prolifictech.com + */ +#define _PL_INT_RES1 0x80 /* reserved */ +#define _PL_INT_RES2 0x40 /* reserved */ +#define _PL_INT_RXD _PL_INT_RES2 /* Read data ready - Not documented by Prolific, but seems to work! */ +#define _PL_INT_TX_RDY 0x20 /* OK to transmit data */ +#define _PL_INT_RESET_O 0x10 /* reset output pipe */ +#define _PL_INT_RESET_I 0x08 /* reset input pipe */ +#define _PL_INT_TX_C 0x04 /* transmission complete */ +#define _PL_INT_TX_REQ 0x02 /* transmission received */ +#define _PL_INT_PEER_E 0x01 /* peer exists */ + /*-------------------------------------------------------------------*/ static plusb_t plusb[NRPLUSB]; +static void plusb_write_bulk_complete(urb_t *purb); +static void plusb_read_bulk_complete(urb_t *purb); +static void plusb_int_complete(urb_t *purb); + /* --------------------------------------------------------------------- */ + +/* + * plusb_add_buf_tail - Take the head of the src list and append it to + * the tail of the dest list + */ static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src) { - unsigned long flags; + unsigned long flags = 0; struct list_head *tmp; int ret = 0; @@ -76,126 +248,227 @@ static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_he } /*-------------------------------------------------------------------*/ -static int plusb_my_bulk(plusb_t *s, int pipe, void *data, int size, int *actual_length) +/* + * dequeue_next_skb - submit the first thing on the tx_skb_list to the + * USB stack. This function should be called each time we get a new + * message to send to the other host, or each time a message is sucessfully + * sent. + */ +static void dequeue_next_skb(char * func, plusb_t * s) { - int ret; + skb_list_t * skb_list; + unsigned long flags = 0; - dbg("plusb_my_bulk: len:%d",size); + if (!s->connected) + return; + + spin_lock_irqsave (&s->lock, flags); + + if (!list_empty (&s->tx_skb_list) && !s->writeurb_submitted) { + int submit_ret; + skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list); - ret=usb_bulk_msg(s->usbdev, pipe, data, size, actual_length, 500); - if(ret<0) { - err("plusb: usb_bulk_msg failed(%d)",ret); + if (skb_list->skb) { + s->writeurb_submitted = 1; + + /* Use the buffer inside the sk_buff directly. why copy? */ + FILL_BULK_URB_TO(s->writeurb, s->usbdev, + usb_sndbulkpipe(s->usbdev, _PLUSB_BULKOUTPIPE), + skb_list->skb->data, skb_list->skb->len, + plusb_write_bulk_complete, skb_list, 500); + + dbg ("%s: %s: submitting urb. skb_list %p", s->net_dev.name, func, skb_list); + + submit_ret = usb_submit_urb(s->writeurb); + if (submit_ret) { + s->writeurb_submitted = 0; + printk (KERN_CRIT "%s: %s: can't submit writeurb: %d\n", + s->net_dev.name, func, submit_ret); + } + } /* end if the skb value has been filled in */ } - if( ret == -EPIPE ) { - warn("CLEAR_FEATURE request to remove STALL condition."); - if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) - err("request failed"); - } - - dbg("plusb_my_bulk: finished act: %d", *actual_length); - return ret; + spin_unlock_irqrestore (&s->lock, flags); } -/* --------------------------------------------------------------------- */ - -static void plusb_bh(void *context) +/* + * submit_read_urb - re-submit the read URB to the stack + */ +void submit_read_urb(char * func, plusb_t * s) { - plusb_t *s=context; - struct net_device_stats *stats=&s->net_stats; - int ret=0; - int actual_length; - skb_list_t *skb_list; - struct sk_buff *skb; - - dbg("plusb_bh: i:%d",in_interrupt()); - - while(!list_empty(&s->tx_skb_list)) { + unsigned long flags=0; - if(!(s->status&_PLUSB_TXOK)) - break; - - skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list); - if(!skb_list->state) { - dbg("plusb_bh: not yet ready"); - schedule(); - continue; - } - - skb=skb_list->skb; - ret=plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), - skb->data, skb->len, &actual_length); + if (!s->connected) + return; - if(ret || skb->len != actual_length ||!(skb->len%64)) { - plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), - NULL, 0, &actual_length); - } - - if(!ret) { - stats->tx_packets++; - stats->tx_bytes+=skb->len; - } - else { - stats->tx_errors++; - stats->tx_aborted_errors++; + spin_lock_irqsave (&s->lock, flags); + + if (!s->readurb_submitted) { + int ret; + s->readurb_submitted=1; + s->readurb->dev=s->usbdev; + ret = usb_submit_urb(s->readurb); + if (ret) { + printk (KERN_CRIT "%s: %s: error %d submitting read URB\n", + s->net_dev.name, func, ret); + s->readurb_submitted=0; } - - dbg("plusb_bh: dev_kfree_skb"); - - dev_kfree_skb(skb); - skb_list->state=0; - plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list); } - dbg("plusb_bh: finished"); - s->in_bh=0; + spin_unlock_irqrestore (&s->lock, flags); + } - /* --------------------------------------------------------------------- */ +/* + * plusb_net_xmit - callback from the network device driver for outgoing data + * + * Data has arrived to the network device from the local machine and needs + * to be sent over the USB cable. This is in an interrupt, so we don't + * want to spend too much time in this function. + * + */ static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev) { plusb_t *s=dev->priv; skb_list_t *skb_list; - int ret=NET_XMIT_SUCCESS; + unsigned int flags; dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt()); - if(!s->connected || list_empty(&s->free_skb_list)) { - ret=NET_XMIT_CN; - goto lab; - } + if(!s->connected || !s->opened) { + /* + NOTE: If we get to this point, you'll return the error + kernel: virtual device plusb0 asks to queue packet + + Other things we could do: + 1) just drop this packet + 2) drop other packets in the queue + */ + return 1; + } - plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list); + spin_lock_irqsave (&s->lock, flags); + + if (list_empty(&s->free_skb_list) + || plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list)) { + /* The buffers on this side are full. DROP the packet + I think that this shouldn't happen with the correct + use of the netif_XXX functions -EZA + */ + dbg ("plusb: Free list is empty."); + kfree_skb(skb); + s->net_stats.tx_dropped++; + spin_unlock_irqrestore (&s->lock, flags); + return 0; + } + skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list); skb_list->skb=skb; skb_list->state=1; + skb_list->s=s; + + if (list_empty(&s->free_skb_list)) { + /* apply "backpressure". Tell the net layer to stop sending + the driver packets. + */ + netif_stop_queue(dev); + } + + spin_unlock_irqrestore (&s->lock, flags); + + /* If there is no write urb outstanding, pull the first thing + off of the list and submit it to the USB stack + */ + dequeue_next_skb("plusb_net_xmit", s); + + return 0; +} -lab: - if(s->in_bh) - return ret; +/* --------------------------------------------------------------------- */ - dbg("plusb_net_xmit: queue_task"); +/* + * plusb_write_bulk_complete () - callback after the data has been + * sent to the USB device, or a timeout occured. + */ +static void plusb_write_bulk_complete(urb_t *purb) +{ + skb_list_t * skb_list=purb->context; + plusb_t *s=skb_list->s; - s->in_bh=1; - queue_task(&s->bh, &tq_scheduler); + dbg ("%s: plusb_write_bulk_complete: status:%d skb_list:%p\n", + s->net_dev.name, purb->status, skb_list); - dbg("plusb_net_xmit: finished"); - return ret; + skb_list->state=0; -} + if( purb->status == -EPIPE ) { + + printk(KERN_CRIT "%s: plusb_write_bulk_complete: got -EPIPE and don't know what to do!\n", + s->net_dev.name); + } + + if(!purb->status) { + s->net_stats.tx_packets++; + s->net_stats.tx_bytes+=skb_list->skb->len; + } + else { + err ("%s: plusb_write_bulk_complete: returned ERROR status:%d\n", + s->net_dev.name, purb->status); -/* --------------------------------------------------------------------- */ + s->net_stats.tx_errors++; + s->net_stats.tx_aborted_errors++; + } + + dbg("plusb_bh: dev_kfree_skb"); + + +#if (LINUX_VERSION_CODE < 0x020300) + dev_kfree_skb(skb_list->skb); +#else + /* NOTE: In 2.4 it's a problem to call dev_kfree_skb() in a hard IRQ: + Oct 28 23:42:14 bug kernel: Warning: kfree_skb on hard IRQ c023329a + */ + dev_kfree_skb_any(skb_list->skb); +#endif + + skb_list->skb = NULL; + if (plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list)) { + err ("plusb: tx list empty. This shouldn't happen."); + } + + purb->status = 0; + s->writeurb_submitted = 0; + + netif_wake_queue((&s->net_dev)); + + dequeue_next_skb("plusb_write_bulk_complete", s); + + +} -static void plusb_bulk_complete(urb_t *purb) +/* + * plusb_read_bulk_complete - Callback for data arriving from the USB device + * + * This gets called back when a full 'urb' is received from the remote system. + * This urb was allocated by this driver and is kept in the member: s->readurb + * + */ +static void plusb_read_bulk_complete(urb_t *purb) { + plusb_t *s=purb->context; - dbg("plusb_bulk_complete: status:%d length:%d",purb->status,purb->actual_length); + dbg("plusb_read_bulk_complete: status:%d length:%d", purb->status,purb->actual_length); + if(!s->connected) return; - if( !purb->status) { + if( purb->status == -EPIPE ) { + + printk(KERN_CRIT "%s: plusb_read_bulk_complete: got -EPIPE and I don't know what to do!\n", + s->net_dev.name); + + } else if (!purb->status) { struct sk_buff *skb; unsigned char *dst; int len=purb->transfer_buffer_length; @@ -204,31 +477,69 @@ static void plusb_bulk_complete(urb_t *purb) skb=dev_alloc_skb(len); if(!skb) { - err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len); + printk (KERN_CRIT "%s: plusb_read_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame\n", s->net_dev.name, len); stats->rx_dropped++; - return; + } else { + dst=(char *)skb_put(skb, len); + memcpy( dst, purb->transfer_buffer, len); + + skb->dev=&s->net_dev; + skb->protocol=eth_type_trans(skb, skb->dev); + stats->rx_packets++; + stats->rx_bytes+=len; + netif_rx(skb); } + + } + + s->readurb_submitted = 0; + + if (purb->status) { + /* Give the system a chance to "catch its breath". Shortcut + re-submitting the read URB> It will be re-submitted if + another interrupt comes back. The problem scenario is that + the plub is pulled and the read returns an error. + You don't want to resumbit in this case. + */ + err ("%s: plusb_read_bulk_complete: returned status %d\n", + s->net_dev.name, purb->status); + return; + } - dst=(char *)skb_put(skb, len); - memcpy( dst, purb->transfer_buffer, len); - skb->dev=&s->net_dev; - skb->protocol=eth_type_trans(skb, skb->dev); - stats->rx_packets++; - stats->rx_bytes+=len; - netif_rx(skb); - } - else - purb->status=0; + purb->status=0; + + /* Keep it coming! resubmit the URB for reading.. Make sure + we aren't in contention with the interrupt callback. + */ + submit_read_urb("plusb_read_bulk_complete", s); } /* --------------------------------------------------------------------- */ - +/* + * plusb_int_complete - USB driver callback for interrupt msg from the device + * + * Interrupts are scheduled to go off on a periodic basis (see FILL_INT_URB) + * For the prolific device, this is basically just returning a register + * filled with bits. See the macro definitions for _PL_INT_XXX above. + * Most of these bits are for implementing a machine-machine protocol + * and can be set with a special message (described as the "Quicklink" + * feature in the prolific documentation.) + * + * I don't think we need any of that to work as a network device. If a + * message is lost, big deal - that's what UNIX networking expects from + * the physical layer. + * + */ static void plusb_int_complete(urb_t *purb) { plusb_t *s=purb->context; s->status=((unsigned char*)purb->transfer_buffer)[0]&255; + #if 0 + /* This isn't right because 0x20 is TX_RDY and + sometimes will not be set + */ if((s->status&0x3f)!=0x20) { warn("invalid device status %02X", s->status); return; @@ -237,67 +548,95 @@ static void plusb_int_complete(urb_t *purb) if(!s->connected) return; - if(s->status&_PLUSB_RXD) { - int ret; - - if(s->bulkurb->status) { - err("plusb_int_complete: URB still in use"); - return; - } - - s->bulkurb->dev = s->usbdev; - ret=usb_submit_urb(s->bulkurb); - if(ret && ret!=-EBUSY) { - err("plusb_int_complete: usb_submit_urb failed"); - } - } - - if(purb->status || s->status!=160) - dbg("status: %p %d buf: %02X", purb->dev, purb->status, s->status); + /* Don't turn this on unless you want to see the log flooded. */ +#if 0 + printk("plusb_int_complete: PEER_E:%d TX_REQ:%d TX_C:%d RESET_IN:%d RESET_O: %d TX_RDY:%d RES1:%d RES2:%d\n", + s->status & _PL_INT_PEER_E ? 1 : 0, + s->status & _PL_INT_TX_REQ ? 1 : 0, + s->status & _PL_INT_TX_C ? 1 : 0, + s->status & _PL_INT_RESET_I ? 1 : 0, + s->status & _PL_INT_RESET_O ? 1 : 0, + s->status & _PL_INT_TX_RDY ? 1 : 0, + s->status & _PL_INT_RES1 ? 1 : 0, + s->status & _PL_INT_RES2 ? 1 : 0); +#endif + +#if 1 + /* At first glance, this logic appears to not really be needed, but + it can help recover from intermittent problems where the + usb_submit_urb() fails in the read callback. -EZA + */ + + /* Try to submit the read URB again. Make sure + we aren't in contention with the bulk read callback + */ + submit_read_urb ("plusb_int_complete", s); + + /* While we are at it, why not check to see if the + write urb should be re-submitted? + */ + dequeue_next_skb("plusb_int_complete", s); + +#endif + } /* --------------------------------------------------------------------- */ - +/* + * plusb_free_all - deallocate all memory kept for an instance of the device. + */ static void plusb_free_all(plusb_t *s) { struct list_head *skb; skb_list_t *skb_list; dbg("plusb_free_all"); + + /* set a flag to tell all callbacks to cease and desist */ + s->connected = 0; + + /* If the interrupt handler is about to fire, let it finish up */ run_task_queue(&tq_immediate); if(s->inturb) { dbg("unlink inturb"); usb_unlink_urb(s->inturb); - } - - if(s->inturb && s->inturb->transfer_buffer) { - dbg("kfree inturb->transfer_buffer"); - kfree(s->inturb->transfer_buffer); - s->inturb->transfer_buffer=NULL; - } - - if(s->inturb) { dbg("free_urb inturb"); usb_free_urb(s->inturb); s->inturb=NULL; } + + if(s->interrupt_in_buffer) { + dbg("kfree s->interrupt_in_buffer"); + kfree(s->interrupt_in_buffer); + s->interrupt_in_buffer=NULL; + } - if(s->bulkurb) { - dbg("unlink bulkurb"); - usb_unlink_urb(s->bulkurb); + if(s->readurb) { + dbg("unlink readurb"); + usb_unlink_urb(s->readurb); + dbg("free_urb readurb:"); + usb_free_urb(s->readurb); + s->readurb=NULL; } - - if(s->bulkurb && s->bulkurb->transfer_buffer) { - dbg("kfree bulkurb->transfer_buffer"); - kfree(s->bulkurb->transfer_buffer); - s->bulkurb->transfer_buffer=NULL; + + if(s->bulk_in_buffer) { + dbg("kfree s->bulk_in_buffer"); + kfree(s->bulk_in_buffer); + s->bulk_in_buffer=NULL; } - if(s->bulkurb) { - dbg("free_urb bulkurb"); - usb_free_urb(s->bulkurb); - s->bulkurb=NULL; + + s->readurb_submitted = 0; + + if(s->writeurb) { + dbg("unlink writeurb"); + usb_unlink_urb(s->writeurb); + dbg("free_urb writeurb:"); + usb_free_urb(s->writeurb); + s->writeurb=NULL; } + + s->writeurb_submitted = 0; while(!list_empty(&s->free_skb_list)) { skb=s->free_skb_list.next; @@ -310,20 +649,34 @@ static void plusb_free_all(plusb_t *s) skb=s->tx_skb_list.next; list_del(skb); skb_list = list_entry (skb, skb_list_t, skb_list); - kfree(skb_list); + if (skb_list->skb) { + dbg ("Freeing SKB in queue"); +#if (LINUX_VERSION_CODE < 0x020300) + dev_kfree_skb(skb_list->skb); +#else + dev_kfree_skb_any(skb_list->skb); +#endif + skb_list->skb = NULL; + } + kfree(skb_list); } + + s->in_bh=0; + dbg("plusb_free_all: finished"); } /*-------------------------------------------------------------------*/ - +/* + * plusb_alloc - allocate memory associated with one instance of the device + */ static int plusb_alloc(plusb_t *s) { int i; skb_list_t *skb; dbg("plusb_alloc"); - + for(i=0 ; i < _SKB_NUM ; i++) { skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL); if(!skb) { @@ -341,47 +694,63 @@ static int plusb_alloc(plusb_t *s) goto reject; } - dbg("bulkurb allocation:"); - s->bulkurb=usb_alloc_urb(0); - if(!s->bulkurb) { + dbg("bulk read urb allocation:"); + s->readurb=usb_alloc_urb(0); + if(!s->readurb) { err("alloc_urb failed"); goto reject; } - dbg("bulkurb/inturb init:"); - s->inturb->dev=s->usbdev; - s->inturb->pipe=usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE); - s->inturb->transfer_buffer=kmalloc(64, GFP_KERNEL); - if(!s->inturb->transfer_buffer) { - err("kmalloc failed"); + dbg("bulk write urb allocation:"); + s->writeurb=usb_alloc_urb(0); + if(!s->writeurb) { + err("alloc_urb for writeurb failed"); goto reject; } - s->inturb->transfer_buffer_length=1; - s->inturb->complete=plusb_int_complete; - s->inturb->context=s; - s->inturb->interval=10; + dbg("readurb/inturb init:"); + s->interrupt_in_buffer=kmalloc(64, GFP_KERNEL); + if(!s->interrupt_in_buffer) { + err("kmalloc failed"); + goto reject; + } + + /* The original value of '10' makes this interrupt fire off a LOT. + It was set so low because the callback determined when to + sumbit the buld read URB. I've lowered it to 100 - the driver + doesn't depend on that logic anymore. -EZA + */ + FILL_INT_URB(s->inturb, s->usbdev, + usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE), + s->interrupt_in_buffer, 1, + plusb_int_complete, s, HZ); dbg("inturb submission:"); if(usb_submit_urb(s->inturb)<0) { err("usb_submit_urb failed"); goto reject; } - - dbg("bulkurb init:"); - s->bulkurb->dev=s->usbdev; - s->bulkurb->pipe=usb_rcvbulkpipe (s->usbdev, _PLUSB_BULKINPIPE); - s->bulkurb->transfer_buffer=kmalloc(_BULK_DATA_LEN, GFP_KERNEL); - if(!s->bulkurb->transfer_buffer) { - err("kmalloc failed"); - goto reject; - } - - s->bulkurb->transfer_buffer_length=_BULK_DATA_LEN; - s->bulkurb->complete=plusb_bulk_complete; - s->bulkurb->context=s; - dbg("plusb_alloc: finished"); + dbg("readurb init:"); + s->bulk_in_buffer = kmalloc(_BULK_DATA_LEN, GFP_KERNEL); + if (!s->bulk_in_buffer) { + err("kmalloc %d bytes for bulk in buffer failed", _BULK_DATA_LEN); + } + + FILL_BULK_URB(s->readurb, s->usbdev, + usb_rcvbulkpipe(s->usbdev, _PLUSB_BULKINPIPE), + s->bulk_in_buffer, _BULK_DATA_LEN, + plusb_read_bulk_complete, s); + + /* The write urb will be initialized inside the network + interrupt. + */ + + /* get the bulk read going */ + submit_read_urb("plusb_alloc", s); + + dbg ("plusb_alloc: finished. readurb=%p writeurb=%p inturb=%p", + s->readurb, s->writeurb, s->inturb); return 0; @@ -404,8 +773,11 @@ static int plusb_net_open(struct net_device *dev) return -ENOMEM; s->opened=1; - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + dbg("plusb_net_open: success"); return 0; @@ -417,11 +789,14 @@ static int plusb_net_open(struct net_device *dev) static int plusb_net_stop(struct net_device *dev) { plusb_t *s=dev->priv; + + netif_stop_queue(dev); dbg("plusb_net_stop"); - plusb_free_all(s); s->opened=0; + plusb_free_all(s); + MOD_DEC_USE_COUNT; dbg("plusb_net_stop:finished"); return 0; @@ -459,7 +834,6 @@ static void plusb_disconnect (struct usb_device *usbdev, void *ptr) plusb_t *s = ptr; dbg("plusb_disconnect"); - s->connected = 0; plusb_free_all(s); @@ -467,6 +841,11 @@ static void plusb_disconnect (struct usb_device *usbdev, void *ptr) dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); s->net_dev.name[0] = '\0'; +#if (LINUX_VERSION_CODE < 0x020300) + dbg("plusb_disconnect: About to free name"); + kfree (s->net_dev.name); + s->net_dev.name = NULL; +#endif } dbg("plusb_disconnect: finished"); @@ -475,6 +854,22 @@ static void plusb_disconnect (struct usb_device *usbdev, void *ptr) /* --------------------------------------------------------------------- */ +static int plusb_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > _BULK_DATA_LEN)) + return -EINVAL; + + printk("plusb: changing mtu to %d\n", new_mtu); + dev->mtu = new_mtu; + + /* NOTE: Could we change the size of the READ URB here dynamically + to save kernel memory? + */ + return 0; +} + +/* --------------------------------------------------------------------- */ + int plusb_net_init(struct net_device *dev) { dbg("plusb_net_init"); @@ -484,7 +879,14 @@ int plusb_net_init(struct net_device *dev) dev->hard_start_xmit=plusb_net_xmit; dev->get_stats = plusb_net_get_stats; ether_setup(dev); - dev->tx_queue_len = 0; + dev->change_mtu = plusb_change_mtu; + /* Setting the default MTU to 16K gives good performance for + me, and keeps the ping latency low too. Setting it up + to 32K made performance go down. -EZA + Pavel says it would be best not to do this... + */ + /*dev->mtu=16384; */ + dev->tx_queue_len = 0; dev->flags = IFF_POINTOPOINT|IFF_NOARP; @@ -524,6 +926,42 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) return NULL; } +#if (LINUX_VERSION_CODE < 0x020300) + { + int i; + + /* For Kernel version 2.2, the driver is responsible for + allocating this memory. For version 2.4, the rules + have apparently changed, but there is a nifty function + 'init_netdev' that might make this easier... It's in + ../net/net_init.c - but can we get there from here? (no) + -EZA + */ + + /* Find the device number... we seem to have lost it... -EZA */ + for (i=0; i<NRPLUSB; i++) { + if (&plusb[i] == s) + break; + } + + if(!s->net_dev.name) { + s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL); + sprintf (s->net_dev.name, "plusb%d", i); + s->net_dev.init=plusb_net_init; + s->net_dev.priv=s; + + printk ("plusb_probe: Registering Device\n"); + if(!register_netdev(&s->net_dev)) + info("registered: %s", s->net_dev.name); + else { + err("register_netdev failed"); + s->net_dev.name[0] = '\0'; + } + dbg ("plusb_probe: Connected!"); + } + } +#else + /* Kernel version 2.3+ works a little bit differently than 2.2 */ if(!s->net_dev.name[0]) { strcpy(s->net_dev.name, "plusb%d"); s->net_dev.init=plusb_net_init; @@ -535,7 +973,8 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) s->net_dev.name[0] = '\0'; } } - +#endif + s->connected = 1; if(s->opened) { @@ -567,8 +1006,6 @@ static int __init plusb_init (void) for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u]; memset (s, 0, sizeof (plusb_t)); - s->bh.routine = (void (*)(void *))plusb_bh; - s->bh.data = s; INIT_LIST_HEAD (&s->tx_skb_list); INIT_LIST_HEAD (&s->free_skb_list); spin_lock_init (&s->lock); @@ -591,10 +1028,21 @@ static void __exit plusb_cleanup (void) dbg("plusb_cleanup"); for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u]; +#if (LINUX_VERSION_CODE < 0x020300) + if(s->net_dev.name) { + dbg("unregistering netdev: %s",s->net_dev.name); + unregister_netdev(&s->net_dev); + s->net_dev.name[0] = '\0'; + kfree (s->net_dev.name); + s->net_dev.name = NULL; + } +#else if(s->net_dev.name[0]) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); + s->net_dev.name[0] = '\0'; } +#endif } usb_deregister (&plusb_driver); dbg("plusb_cleanup: finished"); diff --git a/drivers/usb/plusb.h b/drivers/usb/plusb.h deleted file mode 100644 index a77ae0f01..000000000 --- a/drivers/usb/plusb.h +++ /dev/null @@ -1,48 +0,0 @@ -#define _PLUSB_INTPIPE 0x1 -#define _PLUSB_BULKOUTPIPE 0x2 -#define _PLUSB_BULKINPIPE 0x3 - -#define _SKB_NUM 1000 -// 7 6 5 4 3 2 1 0 -// tx rx 1 0 -// 1110 0000 rxdata -// 1010 0000 idle -// 0010 0000 tx over -// 0110 tx over + rxd - -#define _PLUSB_RXD 0x40 -#define _PLUSB_TXOK 0x80 - -#ifdef __KERNEL__ -#define _BULK_DATA_LEN 16384 - -typedef struct -{ - struct list_head skb_list; - struct sk_buff *skb; - int state; -} skb_list_t,*pskb_list_t; - -typedef struct -{ - struct usb_device *usbdev; - - int status; - int connected; - int in_bh; - int opened; - - spinlock_t lock; - - urb_t *inturb; - urb_t *bulkurb; - - struct list_head tx_skb_list; - struct list_head free_skb_list; - struct tq_struct bh; - - struct net_device net_dev; - struct net_device_stats net_stats; -} plusb_t,*pplusb_t; - -#endif diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 7614373cf..c77ba35e2 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -393,15 +393,18 @@ static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t return count; } -static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) +static void *usblp_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *epread, *epwrite; struct usblp *usblp; - int minor, i, alts = -1, bidir = 0; + int minor, i, bidir = 0; + int alts = dev->actconfig->interface[ifnum].act_altsetting; int length, err; char *buf; + /* If a bidirectional interface exists, use it. */ for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) { interface = &dev->actconfig->interface[ifnum].altsetting[i]; @@ -411,18 +414,13 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) (interface->bInterfaceProtocol > 1 && interface->bNumEndpoints < 2)) continue; - if (alts == -1) - alts = i; - - if (!bidir && interface->bInterfaceProtocol > 1) { + if (interface->bInterfaceProtocol > 1) { bidir = 1; alts = i; + break; } } - if (alts == -1) - return NULL; - interface = &dev->actconfig->interface[ifnum].altsetting[alts]; if (usb_set_interface(dev, ifnum, alts)) err("can't set desired altsetting %d on interface %d", alts, ifnum); @@ -500,7 +498,7 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) } #ifdef DEBUG - usblp_check_status(usblp); + usblp_check_status(usblp, 0); #endif info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", @@ -544,12 +542,22 @@ static struct file_operations usblp_fops = { release: usblp_release, }; +static struct usb_device_id usblp_ids [] = { + { bInterfaceClass: 7, bInterfaceSubClass: 1, bInterfaceProtocol: 1}, + { bInterfaceClass: 7, bInterfaceSubClass: 1, bInterfaceProtocol: 2}, + { bInterfaceClass: 7, bInterfaceSubClass: 1, bInterfaceProtocol: 3}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usblp_ids); + static struct usb_driver usblp_driver = { name: "usblp", probe: usblp_probe, disconnect: usblp_disconnect, fops: &usblp_fops, - minor: USBLP_MINOR_BASE + minor: USBLP_MINOR_BASE, + id_table: usblp_ids, }; static int __init usblp_init(void) diff --git a/drivers/usb/rio500.c b/drivers/usb/rio500.c index e40e6c30f..e54ed66a2 100644 --- a/drivers/usb/rio500.c +++ b/drivers/usb/rio500.c @@ -407,19 +407,11 @@ read_rio(struct file *file, char *buffer, size_t count, loff_t * ppos) return read_count; } -static void *probe_rio(struct usb_device *dev, unsigned int ifnum) +static void *probe_rio(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct rio_usb_data *rio = &rio_instance; - if (dev->descriptor.idVendor != 0x841) { - return NULL; - } - - if (dev->descriptor.idProduct != 0x1 /* RIO 500 */ ) { - warn(KERN_INFO "Rio player model not supported/tested."); - return NULL; - } - info("USB Rio found at address %d", dev->devnum); rio->present = 1; @@ -470,14 +462,20 @@ file_operations usb_rio_fops = { release: close_rio, }; -static struct -usb_driver rio_driver = { - "rio500", - probe_rio, - disconnect_rio, - {NULL, NULL}, - &usb_rio_fops, - RIO_MINOR +static struct usb_device_id rio_table [] = { + { idVendor: 0x0841, idProduct: 1 }, /* Rio 500 */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, rio_table); + +static struct usb_driver rio_driver = { + name: "rio500", + probe: probe_rio, + disconnect: disconnect_rio, + fops: &usb_rio_fops, + minor: RIO_MINOR, + id_table: rio_table, }; int usb_rio_init(void) diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c index 67ea8c8d8..2ee4ba127 100644 --- a/drivers/usb/scanner.c +++ b/drivers/usb/scanner.c @@ -244,6 +244,82 @@ */ #include "scanner.h" +/* Table of scanners that may work with this driver */ +static struct usb_device_id scanner_device_ids [] = { + /* Acer */ + { idVendor: 0x04a5, idProduct: 0x2060 },/* Prisa Acerscan 620U & 640U (!)*/ + { idVendor: 0x04a5, idProduct: 0x2040 },/* Prisa AcerScan 620U (!) */ + { idVendor: 0x04a5, idProduct: 0x2022 },/* Vuego Scan Brisa 340U */ + /* Agfa */ + { idVendor: 0x06bd, idProduct: 0x0001 }, /* SnapScan 1212U */ + { idVendor: 0x06bd, idProduct: 0x2061 }, /* Another SnapScan 1212U (?)*/ + { idVendor: 0x06bd, idProduct: 0x0100 }, /* SnapScan Touch */ + /* Colorado -- See Primax/Colorado below */ + /* Epson -- See Seiko/Epson below */ + /* Genius */ + { idVendor: 0x0458, idProduct: 0x2001 }, /* ColorPage-Vivid Pro */ + /* Hewlett Packard */ + { idVendor: 0x03f0, idProduct: 0x0205 }, /* 3300C */ + { idVendor: 0x03f0, idProduct: 0x0101 }, /* 4100C */ + { idVendor: 0x03f0, idProduct: 0x0105 }, /* 4200C */ + { idVendor: 0x03f0, idProduct: 0x0102 }, /* PhotoSmart S20 */ + { idVendor: 0x03f0, idProduct: 0x0401 }, /* 5200C */ + { idVendor: 0x03f0, idProduct: 0x0701 }, /* 5300C */ + { idVendor: 0x03f0, idProduct: 0x0201 }, /* 6200C */ + { idVendor: 0x03f0, idProduct: 0x0601 }, /* 6300C */ + /* iVina */ + { idVendor: 0x0638, idProduct: 0x0268 }, /* 1200U */ + /* Microtek */ + { idVendor: 0x05da, idProduct: 0x0099 }, /* ScanMaker X6 - X6U */ + { idVendor: 0x05da, idProduct: 0x0094 }, /* Phantom 336CX - C3 */ + { idVendor: 0x05da, idProduct: 0x00a0 }, /* Phantom 336CX - C3 #2 */ + { idVendor: 0x05da, idProduct: 0x009a }, /* Phantom C6 */ + { idVendor: 0x05da, idProduct: 0x00a3 }, /* ScanMaker V6USL */ + { idVendor: 0x05da, idProduct: 0x80a3 }, /* ScanMaker V6USL #2 */ + { idVendor: 0x05da, idProduct: 0x80ac }, /* ScanMaker V6UL - SpicyU */ + /* Mustek */ + { idVendor: 0x055f, idProduct: 0x0001 }, /* 1200 CU */ + { idVendor: 0x0400, idProduct: 0x1000 }, /* BearPaw 1200 */ + { idVendor: 0x055f, idProduct: 0x0002 }, /* 600 CU */ + { idVendor: 0x055f, idProduct: 0x0003 }, /* 1200 USB */ + { idVendor: 0x055f, idProduct: 0x0006 }, /* 1200 UB */ + /* Primax/Colorado */ + { idVendor: 0x0461, idProduct: 0x0300 }, /* G2-300 #1 */ + { idVendor: 0x0461, idProduct: 0x0380 }, /* G2-600 #1 */ + { idVendor: 0x0461, idProduct: 0x0301 }, /* G2E-300 #1 */ + { idVendor: 0x0461, idProduct: 0x0381 }, /* ReadyScan 636i */ + { idVendor: 0x0461, idProduct: 0x0302 }, /* G2-300 #2 */ + { idVendor: 0x0461, idProduct: 0x0382 }, /* G2-600 #2 */ + { idVendor: 0x0461, idProduct: 0x0303 }, /* G2E-300 #2 */ + { idVendor: 0x0461, idProduct: 0x0383 }, /* G2E-600 */ + { idVendor: 0x0461, idProduct: 0x0340 }, /* Colorado USB 9600 */ + { idVendor: 0x0461, idProduct: 0x0360 }, /* Colorado USB 19200 */ + { idVendor: 0x0461, idProduct: 0x0341 }, /* Colorado 600u */ + { idVendor: 0x0461, idProduct: 0x0361 }, /* Colorado 1200u */ + /* Seiko/Epson Corp. */ + { idVendor: 0x04b8, idProduct: 0x0101 },/* Perfection 636U and 636Photo */ + { idVendor: 0x04b8, idProduct: 0x0103 },/* Perfection 610 */ + { idVendor: 0x04b8, idProduct: 0x0104 },/* Perfection 1200U and 1200Photo*/ + { idVendor: 0x04b8, idProduct: 0x0107 },/* Expression 1600 */ + /* Umax */ + { idVendor: 0x1606, idProduct: 0x0010 }, /* Astra 1220U */ + { idVendor: 0x1606, idProduct: 0x0002 }, /* Astra 1236U */ + { idVendor: 0x1606, idProduct: 0x0030 }, /* Astra 2000U */ + { idVendor: 0x1606, idProduct: 0x0230 }, /* Astra 2200U */ + /* Visioneer */ + { idVendor: 0x04a7, idProduct: 0x0221 }, /* OneTouch 5300 USB */ + { idVendor: 0x04a7, idProduct: 0x0211 }, /* OneTouch 7600 USB */ + { idVendor: 0x04a7, idProduct: 0x0231 }, /* 6100 USB */ + { idVendor: 0x04a7, idProduct: 0x0311 }, /* 6200 EPP/USB */ + { idVendor: 0x04a7, idProduct: 0x0321 }, /* OneTouch 8100 EPP/USB */ + { idVendor: 0x04a7, idProduct: 0x0331 }, /* OneTouch 8600 EPP/USB */ + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, scanner_device_ids); + + static void irq_scanner(struct urb *urb) { @@ -548,7 +624,8 @@ read_scanner(struct file * file, char * buffer, } static void * -probe_scanner(struct usb_device *dev, unsigned int ifnum) +probe_scanner(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct scn_usb_data *scn; struct usb_interface_descriptor *interface; @@ -591,7 +668,7 @@ probe_scanner(struct usb_device *dev, unsigned int ifnum) * Until we detect a device which is pleasing, we silently punt. */ - for (ix = 0; ix < sizeof (scanner_device_ids) / sizeof (struct scanner_device); ix++) { + for (ix = 0; ix < sizeof (scanner_device_ids) / sizeof (struct usb_device_id); ix++) { if ((dev->descriptor.idVendor == scanner_device_ids [ix].idVendor) && (dev->descriptor.idProduct == scanner_device_ids [ix].idProduct)) { valid_device = 1; @@ -875,12 +952,14 @@ file_operations usb_scanner_fops = { static struct usb_driver scanner_driver = { - "usbscanner", - probe_scanner, - disconnect_scanner, - { NULL, NULL }, - &usb_scanner_fops, - SCN_BASE_MNR + name: "usbscanner", + probe: probe_scanner, + disconnect: disconnect_scanner, + fops: &usb_scanner_fops, + minor: SCN_BASE_MNR, + id_table: NULL, /* This would be scanner_device_ids, but we + need to check every USB device, in case + we match a user defined vendor/product ID. */ }; void __exit diff --git a/drivers/usb/scanner.h b/drivers/usb/scanner.h index c1ec964dc..e052e9a4f 100644 --- a/drivers/usb/scanner.h +++ b/drivers/usb/scanner.h @@ -100,79 +100,4 @@ struct scn_usb_data { static struct scn_usb_data *p_scn_table[SCN_MAX_MNR] = { NULL, /* ... */}; -/* table of scanners that may work with this driver */ -static const struct scanner_device { - __u16 idVendor; - __u16 idProduct; -} scanner_device_ids [] = { - /* Acer */ - { 0x04a5, 0x2060 }, /* Prisa Acerscan 620U & 640U (!) */ - { 0x04a5, 0x2040 }, /* Prisa AcerScan 620U (!) */ - { 0x04a5, 0x2022 }, /* Vuego Scan Brisa 340U */ - /* Agfa */ - { 0x06bd, 0x0001 }, /* SnapScan 1212U */ - { 0x06bd, 0x2061 }, /* Another SnapScan 1212U (?) */ - { 0x06bd, 0x0100 }, /* SnapScan Touch */ - /* Colorado -- See Primax/Colorado below */ - /* Epson -- See Seiko/Epson below */ - /* Genius */ - { 0x0458, 0x2001 }, /* ColorPage-Vivid Pro */ - /* Hewlett Packard */ - { 0x03f0, 0x0205 }, /* 3300C */ - { 0x03f0, 0x0101 }, /* 4100C */ - { 0x03f0, 0x0105 }, /* 4200C */ - { 0x03f0, 0x0102 }, /* PhotoSmart S20 */ - { 0x03f0, 0x0401 }, /* 5200C */ - { 0x03f0, 0x0701 }, /* 5300C */ - { 0x03f0, 0x0201 }, /* 6200C */ - { 0x03f0, 0x0601 }, /* 6300C */ - /* iVina */ - { 0x0638, 0x0268 }, /* 1200U */ - /* Microtek */ - { 0x05da, 0x0099 }, /* ScanMaker X6 - X6U */ - { 0x05da, 0x0094 }, /* Phantom 336CX - C3 */ - { 0x05da, 0x00a0 }, /* Phantom 336CX - C3 #2 */ - { 0x05da, 0x009a }, /* Phantom C6 */ - { 0x05da, 0x00a3 }, /* ScanMaker V6USL */ - { 0x05da, 0x80a3 }, /* ScanMaker V6USL #2 */ - { 0x05da, 0x80ac }, /* ScanMaker V6UL - SpicyU */ - /* Mustek */ - { 0x055f, 0x0001 }, /* 1200 CU */ - { 0x0400, 0x1000 }, /* BearPaw 1200 */ - { 0x055f, 0x0002 }, /* 600 CU */ - { 0x055f, 0x0003 }, /* 1200 USB */ - { 0x055f, 0x0006 }, /* 1200 UB */ - /* Primax/Colorado */ - { 0x0461, 0x0300 }, /* G2-300 #1 */ - { 0x0461, 0x0380 }, /* G2-600 #1 */ - { 0x0461, 0x0301 }, /* G2E-300 #1 */ - { 0x0461, 0x0381 }, /* ReadyScan 636i */ - { 0x0461, 0x0302 }, /* G2-300 #2 */ - { 0x0461, 0x0382 }, /* G2-600 #2 */ - { 0x0461, 0x0303 }, /* G2E-300 #2 */ - { 0x0461, 0x0383 }, /* G2E-600 */ - { 0x0461, 0x0340 }, /* Colorado USB 9600 */ - { 0x0461, 0x0360 }, /* Colorado USB 19200 */ - { 0x0461, 0x0341 }, /* Colorado 600u */ - { 0x0461, 0x0361 }, /* Colorado 1200u */ - /* Seiko/Epson Corp. */ - { 0x04b8, 0x0101 }, /* Perfection 636U and 636Photo */ - { 0x04b8, 0x0103 }, /* Perfection 610 */ - { 0x04b8, 0x0104 }, /* Perfection 1200U and 1200Photo */ - { 0x04b8, 0x0107 }, /* Expression 1600 */ - /* Umax */ - { 0x1606, 0x0010 }, /* Astra 1220U */ - { 0x1606, 0x0002 }, /* Astra 1236U */ - { 0x1606, 0x0030 }, /* Astra 2000U */ - { 0x1606, 0x0230 }, /* Astra 2200U */ - /* Visioneer */ - { 0x04a7, 0x0221 }, /* OneTouch 5300 USB */ - { 0x04a7, 0x0211 }, /* OneTouch 7600 USB */ - { 0x04a7, 0x0231 }, /* 6100 USB */ - { 0x04a7, 0x0311 }, /* 6200 EPP/USB */ - { 0x04a7, 0x0321 }, /* OneTouch 8100 EPP/USB */ - { 0x04a7, 0x0331 }, /* OneTouch 8600 EPP/USB */ -}; - -/* Forward declarations */ static struct usb_driver scanner_driver; diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index ce5d7b128..b90d4f52d 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT) += digi_acceleport.o +obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o # Objects that export symbols. export-objs := usbserial.o diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c new file mode 100644 index 000000000..8d38a7a9d --- /dev/null +++ b/drivers/usb/serial/belkin_sa.c @@ -0,0 +1,576 @@ +/* + * Belkin USB Serial Adapter Driver + * + * Copyright (C) 2000 + * William Greathouse (wgreathouse@smva.com) + * + * This program is largely derived from work by the linux-usb group + * and associated source files. Please see the usb/serial files for + * individual credits and copyrights. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * TODO: + * -- Add true modem contol line query capability. Currently we track the + * states reported by the interrupt and the states we request. + * -- Add error reporting back to application for UART error conditions. + * Just point me at how to implement this and I'll do it. I've put the + * framework in, but haven't analyzed the "tty_flip" interface yet. + * -- Add support for flush commands + * -- Add everything that is missing :) + * + * (11/06/2000) gkh + * - Added support for the old Belkin and Peracom devices. + * - Made the port able to be opened multiple times. + * - Added some defaults incase the line settings are things these devices + * can't support. + * + * 18-Oct-2000 William Greathouse + * Released into the wild (linux-usb-devel) + * + * 17-Oct-2000 William Greathouse + * Add code to recognize firmware version and set hardware flow control + * appropriately. Belkin states that firmware prior to 3.05 does not + * operate correctly in hardware handshake mode. I have verified this + * on firmware 2.05 -- for both RTS and DTR input flow control, the control + * line is not reset. The test performed by the Belkin Win* driver is + * to enable hardware flow control for firmware 2.06 or greater and + * for 1.00 or prior. I am only enabling for 2.06 or greater. + * + * 12-Oct-2000 William Greathouse + * First cut at supporting Belkin USB Serial Adapter F5U103 + * I did not have a copy of the original work to support this + * adapter, so pardon any stupid mistakes. All of the information + * I am using to write this driver was acquired by using a modified + * UsbSnoop on Windows2000 and from examining the other USB drivers. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/fcntl.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/spinlock.h> + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include <linux/usb.h> + +#include "usb-serial.h" +#include "belkin_sa.h" + +/* function prototypes for a Belkin USB Serial Adapter F5U103 */ +static int belkin_sa_startup (struct usb_serial *serial); +static void belkin_sa_shutdown (struct usb_serial *serial); +static int belkin_sa_open (struct usb_serial_port *port, struct file *filp); +static void belkin_sa_close (struct usb_serial_port *port, struct file *filp); +static void belkin_sa_read_int_callback (struct urb *urb); +static void belkin_sa_set_termios (struct usb_serial_port *port, struct termios * old); +static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); +static void belkin_sa_break_ctl (struct usb_serial_port *port, int break_state ); + + +static __devinitdata struct usb_device_id id_table_combined [] = { + { idVendor: BELKIN_SA_VID, idProduct: BELKIN_SA_PID }, + { idVendor: BELKIN_OLD_VID, idProduct: BELKIN_OLD_PID }, + { idVendor: PERACOM_VID, idProduct: PERACOM_PID }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id belkin_sa_table [] = { + { idVendor: BELKIN_SA_VID, idProduct: BELKIN_SA_PID }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id belkin_old_table [] = { + { idVendor: BELKIN_OLD_VID, idProduct: BELKIN_OLD_PID }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id peracom_table [] = { + { idVendor: PERACOM_VID, idProduct: PERACOM_PID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table_combined); + +/* All of the device info needed for the Belkin serial converter */ +struct usb_serial_device_type belkin_sa_device = { + name: "Belkin F5U103 USB Serial Adapter", + id_table: belkin_sa_table, /* the Belkin F5U103 device */ + needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 1, + num_bulk_in: 1, + num_bulk_out: 1, + num_ports: 1, + open: belkin_sa_open, + close: belkin_sa_close, + read_int_callback: belkin_sa_read_int_callback, /* How we get the status info */ + ioctl: belkin_sa_ioctl, + set_termios: belkin_sa_set_termios, + break_ctl: belkin_sa_break_ctl, + startup: belkin_sa_startup, + shutdown: belkin_sa_shutdown, +}; + + +/* This driver also supports the "old" school Belkin single port adaptor */ +struct usb_serial_device_type belkin_old_device = { + name: "Belkin USB Serial Adapter", + id_table: belkin_old_table, /* the old Belkin device */ + needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 1, + num_bulk_in: 1, + num_bulk_out: 1, + num_ports: 1, + open: belkin_sa_open, + close: belkin_sa_close, + read_int_callback: belkin_sa_read_int_callback, /* How we get the status info */ + ioctl: belkin_sa_ioctl, + set_termios: belkin_sa_set_termios, + break_ctl: belkin_sa_break_ctl, + startup: belkin_sa_startup, + shutdown: belkin_sa_shutdown, +}; + +/* this driver also works for the Peracom single port adapter */ +struct usb_serial_device_type peracom_device = { + name: "Peracom single port USB Serial Adapter", + id_table: peracom_table, /* the Peracom device */ + needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 1, + num_bulk_in: 1, + num_bulk_out: 1, + num_ports: 1, + open: belkin_sa_open, + close: belkin_sa_close, + read_int_callback: belkin_sa_read_int_callback, /* How we get the status info */ + ioctl: belkin_sa_ioctl, + set_termios: belkin_sa_set_termios, + break_ctl: belkin_sa_break_ctl, + startup: belkin_sa_startup, + shutdown: belkin_sa_shutdown, +}; + + +struct belkin_sa_private { + unsigned long control_state; + unsigned char last_lsr; + unsigned char last_msr; + int bad_flow_control; +}; + + +/* + * *************************************************************************** + * Belkin USB Serial Adapter F5U103 specific driver functions + * *************************************************************************** + */ + +#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ + +/* assumes that struct usb_serial *serial is available */ +#define BSA_USB_CMD(c,v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ + (c), BELKIN_SA_SET_REQUEST_TYPE, \ + (v), 0, NULL, 0, WDR_TIMEOUT) + +/* do some startup allocations not currently performed by usb_serial_probe() */ +static int belkin_sa_startup (struct usb_serial *serial) +{ + struct usb_device *dev = serial->dev; + struct belkin_sa_private *priv; + + /* allocate the private data structure */ + serial->port->private = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL); + if (!serial->port->private) + return (-1); /* error */ + priv = (struct belkin_sa_private *)serial->port->private; + /* set initial values for control structures */ + priv->control_state = 0; + priv->last_lsr = 0; + priv->last_msr = 0; + /* see comments at top of file */ + priv->bad_flow_control = (dev->descriptor.bcdDevice <= 0x0206) ? 1 : 0; + info("bcdDevice: %04x, bfc: %d", dev->descriptor.bcdDevice, priv->bad_flow_control); + + init_waitqueue_head(&serial->port->write_wait); + + return (0); +} + + +static void belkin_sa_shutdown (struct usb_serial *serial) +{ + int i; + + dbg (__FUNCTION__); + + /* stop reads and writes on all ports */ + for (i=0; i < serial->num_ports; ++i) { + while (serial->port[i].open_count > 0) { + belkin_sa_close (&serial->port[i], NULL); + } + /* My special items, the standard routines free my urbs */ + if (serial->port->private) + kfree(serial->port->private); + } +} + + +static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) +{ + unsigned long flags; + + dbg(__FUNCTION__" port %d", port->number); + + spin_lock_irqsave (&port->port_lock, flags); + + ++port->open_count; + MOD_INC_USE_COUNT; + + if (!port->active) { + port->active = 1; + + /*Start reading from the device*/ + /* TODO: Look at possibility of submitting mulitple URBs to device to + * enhance buffering. Win trace shows 16 initial read URBs. + */ + port->read_urb->dev = port->serial->dev; + if (usb_submit_urb(port->read_urb)) + err("usb_submit_urb(read bulk) failed"); + + port->interrupt_in_urb->dev = port->serial->dev; + if (usb_submit_urb(port->interrupt_in_urb)) + err(" usb_submit_urb(read int) failed"); + } + + spin_unlock_irqrestore (&port->port_lock, flags); + + return 0; +} /* belkin_sa_open */ + + +static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) +{ + unsigned long flags; + + dbg(__FUNCTION__" port %d", port->number); + + spin_lock_irqsave (&port->port_lock, flags); + + --port->open_count; + MOD_DEC_USE_COUNT; + + if (port->open_count <= 0) { + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + usb_unlink_urb (port->interrupt_in_urb); /* wgg - do I need this? I think so. */ + port->active = 0; + } + + spin_unlock_irqrestore (&port->port_lock, flags); +} /* belkin_sa_close */ + + +static void belkin_sa_read_int_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct belkin_sa_private *priv = (struct belkin_sa_private *)port->private; + struct usb_serial *serial; + unsigned char *data = urb->transfer_buffer; + + /* the urb might have been killed. */ + if (urb->status) + return; + + if (port_paranoia_check (port, "belkin_sa_read_interrupt")) return; + + serial = port->serial; + if (serial_paranoia_check (serial, "belkin_sa_read_interrupt")) return; + + usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); + + /* Handle known interrupt data */ + /* ignore data[0] and data[1] */ + + priv->last_msr = data[BELKIN_SA_MSR_INDEX]; + + /* Record Control Line states */ + if (priv->last_msr & BELKIN_SA_MSR_DSR) + priv->control_state |= TIOCM_DSR; + else + priv->control_state &= ~TIOCM_DSR; + + if (priv->last_msr & BELKIN_SA_MSR_CTS) + priv->control_state |= TIOCM_CTS; + else + priv->control_state &= ~TIOCM_CTS; + + if (priv->last_msr & BELKIN_SA_MSR_RI) + priv->control_state |= TIOCM_RI; + else + priv->control_state &= ~TIOCM_RI; + + if (priv->last_msr & BELKIN_SA_MSR_CD) + priv->control_state |= TIOCM_CD; + else + priv->control_state &= ~TIOCM_CD; + + /* Now to report any errors */ + priv->last_lsr = data[BELKIN_SA_LSR_INDEX]; +#if 0 + /* + * fill in the flip buffer here, but I do not know the relation + * to the current/next receive buffer or characters. I need + * to look in to this before committing any code. + */ + if (priv->last_lsr & BELKIN_SA_LSR_ERR) { + tty = port->tty; + /* Overrun Error */ + if (priv->last_lsr & BELKIN_SA_LSR_OE) { + } + /* Parity Error */ + if (priv->last_lsr & BELKIN_SA_LSR_PE) { + } + /* Framing Error */ + if (priv->last_lsr & BELKIN_SA_LSR_FE) { + } + /* Break Indicator */ + if (priv->last_lsr & BELKIN_SA_LSR_BI) { + } + } +#endif + + /* INT urbs are automatically re-submitted */ +} + +static void belkin_sa_set_termios (struct usb_serial_port *port, struct termios *old_termios) +{ + struct usb_serial *serial = port->serial; + struct belkin_sa_private *priv = (struct belkin_sa_private *)port->private; + unsigned int iflag = port->tty->termios->c_iflag; + unsigned int cflag = port->tty->termios->c_cflag; + unsigned int old_iflag = old_termios->c_iflag; + unsigned int old_cflag = old_termios->c_cflag; + __u16 urb_value = 0; /* Will hold the new flags */ + + /* Set the baud rate */ + if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { + /* reassert DTR and (maybe) RTS on transition from B0 */ + if( (old_cflag&CBAUD) == B0 ) { + priv->control_state |= (TIOCM_DTR|TIOCM_RTS); + if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0) + err("Set DTR error"); + /* don't set RTS if using hardware flow control */ + if (!(old_cflag&CRTSCTS) ) + if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 1) < 0) + err("Set RTS error"); + } + + switch(cflag & CBAUD) { + case B0: /* handled below */ break; + case B300: urb_value = BELKIN_SA_BAUD(300); break; + case B600: urb_value = BELKIN_SA_BAUD(600); break; + case B1200: urb_value = BELKIN_SA_BAUD(1200); break; + case B2400: urb_value = BELKIN_SA_BAUD(2400); break; + case B4800: urb_value = BELKIN_SA_BAUD(4800); break; + case B9600: urb_value = BELKIN_SA_BAUD(9600); break; + case B19200: urb_value = BELKIN_SA_BAUD(19200); break; + case B38400: urb_value = BELKIN_SA_BAUD(38400); break; + case B57600: urb_value = BELKIN_SA_BAUD(57600); break; + case B115200: urb_value = BELKIN_SA_BAUD(115200); break; + case B230400: urb_value = BELKIN_SA_BAUD(230400); break; + default: err("BELKIN USB Serial Adapter: unsupported baudrate request, using default of 9600"); + urb_value = BELKIN_SA_BAUD(9600); break; + } + if ((cflag & CBAUD) != B0 ) { + if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) + err("Set baudrate error"); + } else { + /* Disable flow control */ + if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0) + err("Disable flowcontrol error"); + + /* Drop RTS and DTR */ + priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); + if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0) + err("DTR LOW error"); + if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0) + err("RTS LOW error"); + } + } + + /* set the parity */ + if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { + if (cflag & PARENB) + urb_value = (cflag & PARODD) ? BELKIN_SA_PARITY_ODD : BELKIN_SA_PARITY_EVEN; + else + urb_value = BELKIN_SA_PARITY_NONE; + if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0) + err("Set parity error"); + } + + /* set the number of data bits */ + if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { + switch (cflag & CSIZE) { + case CS5: urb_value = BELKIN_SA_DATA_BITS(5); break; + case CS6: urb_value = BELKIN_SA_DATA_BITS(6); break; + case CS7: urb_value = BELKIN_SA_DATA_BITS(7); break; + case CS8: urb_value = BELKIN_SA_DATA_BITS(8); break; + default: err("CSIZE was not CS5-CS8, using default of 8"); + urb_value = BELKIN_SA_DATA_BITS(8); + break; + } + if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0) + err("Set data bits error"); + } + + /* set the number of stop bits */ + if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) { + urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2) : BELKIN_SA_STOP_BITS(1); + if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST, urb_value) < 0) + err("Set stop bits error"); + } + + /* Set flow control */ + if( (iflag&IXOFF) != (old_iflag&IXOFF) + || (iflag&IXON) != (old_iflag&IXON) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { + urb_value = 0; + if ((iflag & IXOFF) || (iflag & IXON)) + urb_value |= (BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON); + else + urb_value &= ~(BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON); + + if (cflag & CRTSCTS) + urb_value |= (BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS); + else + urb_value &= ~(BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS); + + if (priv->bad_flow_control) + urb_value &= ~(BELKIN_SA_FLOW_IRTS); + + if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, urb_value) < 0) + err("Set flow control error"); + } +} /* belkin_sa_set_termios */ + + +static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) +{ + struct usb_serial *serial = port->serial; + + if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) + err("Set break_ctl %d", break_state); +} + + +static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct usb_serial *serial = port->serial; + __u16 urb_value; /* Will hold the new flags */ + struct belkin_sa_private *priv = (struct belkin_sa_private *)port->private; + int ret, mask; + + /* Based on code from acm.c and others */ + switch (cmd) { + case TIOCMGET: + return put_user(priv->control_state, (unsigned long *) arg); + break; + + case TIOCMSET: /* Turns on and off the lines as specified by the mask */ + case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ + case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ + if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + + if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) { + /* RTS needs set */ + urb_value = ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) || (cmd == TIOCMBIS) ? 1 : 0; + if (urb_value) + priv->control_state |= TIOCM_RTS; + else + priv->control_state &= ~TIOCM_RTS; + + if ((ret = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, urb_value)) < 0) { + err("Set RTS error %d", ret); + return(ret); + } + } + + if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) { + /* DTR needs set */ + urb_value = ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) || (cmd == TIOCMBIS) ? 1 : 0; + if (urb_value) + priv->control_state |= TIOCM_DTR; + else + priv->control_state &= ~TIOCM_DTR; + if ((ret = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, urb_value)) < 0) { + err("Set DTR error %d", ret); + return(ret); + } + } + break; + + case TIOCMIWAIT: + /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ + /* TODO */ + return( 0 ); + + case TIOCGICOUNT: + /* return count of modemline transitions */ + /* TODO */ + return 0; + + default: + dbg("belkin_sa_ioctl arg not supported - 0x%04x",cmd); + return(-ENOIOCTLCMD); + break; + } + return 0; +} /* belkin_sa_ioctl */ + + +static int __init belkin_sa_init (void) +{ + usb_serial_register (&belkin_sa_device); + usb_serial_register (&belkin_old_device); + usb_serial_register (&peracom_device); + return 0; +} + + +static void __exit belkin_sa_exit (void) +{ + usb_serial_deregister (&belkin_sa_device); + usb_serial_deregister (&belkin_old_device); + usb_serial_deregister (&peracom_device); +} + + +module_init (belkin_sa_init); +module_exit (belkin_sa_exit); + +MODULE_DESCRIPTION("USB Belkin Serial converter driver"); diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h new file mode 100644 index 000000000..ee3603863 --- /dev/null +++ b/drivers/usb/serial/belkin_sa.h @@ -0,0 +1,113 @@ +/* + * Definitions for Belkin USB Serial Adapter Driver + * + * Copyright (C) 2000 + * William Greathouse (wgreathouse@smva.com) + * + * This program is largely derived from work by the linux-usb group + * and associated source files. Please see the usb/serial files for + * individual credits and copyrights. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (11/06/2000) gkh + * Added old Belkin and Peracom device ids, which this driver supports + * + * 12-Oct-2000 William Greathouse + * First cut at supporting Belkin USB Serial Adapter F5U103 + * I did not have a copy of the original work to support this + * adapter, so pardon any stupid mistakes. All of the information + * I am using to write this driver was acquired by using a modified + * UsbSnoop on Windows2000. + * + */ + +#ifndef __LINUX_USB_SERIAL_BSA_H +#define __LINUX_USB_SERIAL_BSA_H + +#define BELKIN_SA_VID 0x050d /* Vendor Id */ +#define BELKIN_SA_PID 0x0103 /* Product Id */ + +#define BELKIN_OLD_VID 0x056c /* Belkin's "old" vendor id */ +#define BELKIN_OLD_PID 0x8007 /* Belkin's "old" single port serial converter's id */ + +#define PERACOM_VID 0x0565 /* Peracom's vendor id */ +#define PERACOM_PID 0x0001 /* Peracom's single port serial converter's id */ + +/* Vendor Request Interface */ +#define BELKIN_SA_SET_BAUDRATE_REQUEST 0 /* Set baud rate */ +#define BELKIN_SA_SET_STOP_BITS_REQUEST 1 /* Set stop bits (1,2) */ +#define BELKIN_SA_SET_DATA_BITS_REQUEST 2 /* Set data bits (5,6,7,8) */ +#define BELKIN_SA_SET_PARITY_REQUEST 3 /* Set parity (None, Even, Odd) */ + +#define BELKIN_SA_SET_DTR_REQUEST 10 /* Set DTR state */ +#define BELKIN_SA_SET_RTS_REQUEST 11 /* Set RTS state */ +#define BELKIN_SA_SET_BREAK_REQUEST 12 /* Set BREAK state */ + +#define BELKIN_SA_SET_FLOW_CTRL_REQUEST 16 /* Set flow control mode */ + + +#ifdef WHEN_I_LEARN_THIS +#define BELKIN_SA_SET_MAGIC_REQUEST 17 /* I don't know, possibly flush */ + /* (always in Wininit sequence before flow control) */ +#define BELKIN_SA_RESET xx /* Reset the port */ +#define BELKIN_SA_GET_MODEM_STATUS xx /* Force return of modem status register */ +#endif + +#define BELKIN_SA_SET_REQUEST_TYPE 0x40 + +#define BELKIN_SA_BAUD(b) (230400/b) + +#define BELKIN_SA_STOP_BITS(b) (b-1) + +#define BELKIN_SA_DATA_BITS(b) (b-5) + +#define BELKIN_SA_PARITY_NONE 0 +#define BELKIN_SA_PARITY_EVEN 1 +#define BELKIN_SA_PARITY_ODD 2 +#define BELKIN_SA_PARITY_MARK 3 +#define BELKIN_SA_PARITY_SPACE 4 + +#define BELKIN_SA_FLOW_NONE 0x0000 /* No flow control */ +#define BELKIN_SA_FLOW_OCTS 0x0001 /* use CTS input to throttle output */ +#define BELKIN_SA_FLOW_ODSR 0x0002 /* use DSR input to throttle output */ +#define BELKIN_SA_FLOW_IDSR 0x0004 /* use DSR input to enable receive */ +#define BELKIN_SA_FLOW_IDTR 0x0008 /* use DTR output for input flow control */ +#define BELKIN_SA_FLOW_IRTS 0x0010 /* use RTS output for input flow control */ +#define BELKIN_SA_FLOW_ORTS 0x0020 /* use RTS to indicate data available to send */ +#define BELKIN_SA_FLOW_ERRSUB 0x0040 /* ???? guess ???? substitute inline errors */ +#define BELKIN_SA_FLOW_OXON 0x0080 /* use XON/XOFF for output flow control */ +#define BELKIN_SA_FLOW_IXON 0x0100 /* use XON/XOFF for input flow control */ + +/* + * It seems that the interrupt pipe is closely modelled after the + * 16550 register layout. This is probably because the adapter can + * be used in a "DOS" environment to simulate a standard hardware port. + */ +#define BELKIN_SA_LSR_INDEX 2 /* Line Status Register */ +#define BELKIN_SA_LSR_RDR 0x01 /* receive data ready */ +#define BELKIN_SA_LSR_OE 0x02 /* overrun error */ +#define BELKIN_SA_LSR_PE 0x04 /* parity error */ +#define BELKIN_SA_LSR_FE 0x08 /* framing error */ +#define BELKIN_SA_LSR_BI 0x10 /* break indicator */ +#define BELKIN_SA_LSR_THE 0x20 /* transmit holding register empty */ +#define BELKIN_SA_LSR_TE 0x40 /* transmit register empty */ +#define BELKIN_SA_LSR_ERR 0x80 /* OE | PE | FE | BI */ + +#define BELKIN_SA_MSR_INDEX 3 /* Modem Status Register */ +#define BELKIN_SA_MSR_DCTS 0x01 /* Delta CTS */ +#define BELKIN_SA_MSR_DDSR 0x02 /* Delta DSR */ +#define BELKIN_SA_MSR_DRI 0x04 /* Delta RI */ +#define BELKIN_SA_MSR_DCD 0x08 /* Delta CD */ +#define BELKIN_SA_MSR_CTS 0x10 /* Current CTS */ +#define BELKIN_SA_MSR_DSR 0x20 /* Current DSR */ +#define BELKIN_SA_MSR_RI 0x40 /* Current RI */ +#define BELKIN_SA_MSR_CD 0x80 /* Current CD */ + +#endif /* __LINUX_USB_SERIAL_BSA_H */ + diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index f5635bb01..197752224 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1,5 +1,5 @@ /* -* Digi AccelePort USB-4 Serial Converter +* Digi AccelePort USB-4 and USB-2 Serial Converters * * Copyright 2000 by Digi International * @@ -14,6 +14,16 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (11/01/2000) Adam J. Richter +* usb_device_id table support +* +* (11/01/2000) pberger and borchers +* -- Turned off the USB_DISABLE_SPD flag for write bulk urbs--it caused +* USB 4 ports to hang on startup. +* -- Serialized access to write urbs by adding the dp_write_urb_in_use +* flag; otherwise, the driver caused SMP system hangs. Watching the +* urb status is not sufficient. +* * (10/05/2000) gkh * -- Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -213,7 +223,7 @@ * - Following Documentation/DocBook/kernel-locking.pdf no spin locks * are held when calling copy_to/from_user or printk. * -* $Id: digi_acceleport.c,v 1.80 2000/08/09 06:36:18 root Exp $ +* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $ */ #include <linux/config.h> @@ -411,6 +421,7 @@ typedef struct digi_port { int dp_in_buf_len; unsigned char dp_in_buf[DIGI_IN_BUF_SIZE]; unsigned char dp_in_flag_buf[DIGI_IN_BUF_SIZE]; + int dp_write_urb_in_use; unsigned int dp_modem_signals; wait_queue_head_t dp_modem_change_wait; int dp_open_count; /* inc on open, dec on close */ @@ -461,15 +472,29 @@ static int digi_read_oob_callback( struct urb *urb ); /* Statics */ +static __devinitdata struct usb_device_id id_table_combined [] = { + { idVendor: DIGI_VENDOR_ID, idProduct: DIGI_2_ID }, + { idVendor: DIGI_VENDOR_ID, idProduct: DIGI_4_ID }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id id_table_2 [] = { + { idVendor: DIGI_VENDOR_ID, idProduct: DIGI_2_ID }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id id_table_4 [] = { + { idVendor: DIGI_VENDOR_ID, idProduct: DIGI_4_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table_combined); + /* device info needed for the Digi serial converter */ -static u16 digi_vendor_id = DIGI_VENDOR_ID; -static u16 digi_product_2_id = DIGI_2_ID; /* USB 2 */ -static u16 digi_product_4_id = DIGI_4_ID; /* USB 4 */ static struct usb_serial_device_type digi_acceleport_2_device = { name: "Digi USB", - idVendor: &digi_vendor_id, - idProduct: &digi_product_2_id, + id_table: id_table_2, needs_interrupt_in: DONT_CARE, needs_bulk_in: MUST_HAVE, needs_bulk_out: MUST_HAVE, @@ -495,8 +520,7 @@ static struct usb_serial_device_type digi_acceleport_2_device = { static struct usb_serial_device_type digi_acceleport_4_device = { name: "Digi USB", - idVendor: &digi_vendor_id, - idProduct: &digi_product_4_id, + id_table: id_table_4, needs_interrupt_in: DONT_CARE, needs_bulk_in: MUST_HAVE, needs_bulk_out: MUST_HAVE, @@ -629,7 +653,8 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, co while( count > 0 ) { - while( oob_port->write_urb->status == -EINPROGRESS ) { + while( oob_port->write_urb->status == -EINPROGRESS + || oob_priv->dp_write_urb_in_use ) { cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags ); @@ -647,8 +672,9 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, co memcpy( oob_port->write_urb->transfer_buffer, buf, len ); oob_port->write_urb->transfer_buffer_length = len; oob_port->write_urb->dev = port->serial->dev; - + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + oob_priv->dp_write_urb_in_use = 1; count -= len; buf += len; } @@ -702,8 +728,8 @@ count ); while( count > 0 && ret == 0 ) { - while( port->write_urb->status == -EINPROGRESS - && jiffies < timeout ) { + while( (port->write_urb->status == -EINPROGRESS + || priv->dp_write_urb_in_use) && jiffies < timeout ) { cond_wait_interruptible_timeout_irqrestore( &port->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); @@ -736,6 +762,7 @@ count ); port->write_urb->dev = port->serial->dev; if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; count -= len; buf += len; @@ -783,7 +810,8 @@ port_priv->dp_port_num, modem_signals ); spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); spin_lock( &port_priv->dp_port_lock ); - while( oob_port->write_urb->status == -EINPROGRESS ) { + while( oob_port->write_urb->status == -EINPROGRESS + || oob_priv->dp_write_urb_in_use ) { spin_unlock( &port_priv->dp_port_lock ); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, @@ -811,6 +839,7 @@ port_priv->dp_port_num, modem_signals ); oob_port->write_urb->dev = port->serial->dev; if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + oob_priv->dp_write_urb_in_use = 1; port_priv->dp_modem_signals = (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); @@ -1249,7 +1278,8 @@ priv->dp_port_num, count, from_user, in_interrupt() ); spin_lock_irqsave( &priv->dp_port_lock, flags ); /* wait for urb status clear to submit another urb */ - if( port->write_urb->status == -EINPROGRESS ) { + if( port->write_urb->status == -EINPROGRESS + || priv->dp_write_urb_in_use ) { /* buffer data if count is 1 (probably put_char) if possible */ if( count == 1 ) { @@ -1292,6 +1322,7 @@ priv->dp_port_num, count, from_user, in_interrupt() ); memcpy( data, from_user ? user_buf : buf, new_len ); if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + priv->dp_write_urb_in_use = 1; ret = new_len; priv->dp_out_buf_len = 0; } @@ -1337,6 +1368,7 @@ dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status ); == ((digi_serial_t *)(serial->private))->ds_oob_port_num ) { dbg( "digi_write_bulk_callback: oob callback" ); spin_lock( &priv->dp_port_lock ); + priv->dp_write_urb_in_use = 0; wake_up_interruptible( &port->write_wait ); spin_unlock( &priv->dp_port_lock ); return; @@ -1349,6 +1381,7 @@ dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status ); /* try to send any buffered data on this port, if it is open */ spin_lock( &priv->dp_port_lock ); + priv->dp_write_urb_in_use = 0; if( priv->dp_open_count && port->write_urb->status != -EINPROGRESS && priv->dp_out_buf_len > 0 ) { @@ -1365,6 +1398,7 @@ dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status ); priv->dp_out_buf_len ); if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; } @@ -1397,7 +1431,8 @@ static int digi_write_room( struct usb_serial_port *port ) spin_lock_irqsave( &priv->dp_port_lock, flags ); - if( port->write_urb->status == -EINPROGRESS ) + if( port->write_urb->status == -EINPROGRESS + || priv->dp_write_urb_in_use ) room = 0; else room = port->bulk_out_size - 2 - priv->dp_out_buf_len; @@ -1416,7 +1451,8 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) digi_port_t *priv = (digi_port_t *)(port->private); - if( port->write_urb->status == -EINPROGRESS ) { + if( port->write_urb->status == -EINPROGRESS + || priv->dp_write_urb_in_use ) { dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 ); /* return( port->bulk_out_size - 2 ); */ return( 256 ); @@ -1601,6 +1637,7 @@ dbg( "digi_close: TOP: port=%d, active=%d, open_count=%d", priv->dp_port_num, po spin_lock_irqsave( &priv->dp_port_lock, flags ); port->active = 0; + priv->dp_write_urb_in_use = 0; priv->dp_in_close = 0; --priv->dp_open_count; MOD_DEC_USE_COUNT; @@ -1641,7 +1678,6 @@ static int digi_startup_device( struct usb_serial *serial ) port = &serial->port[i]; - port->write_urb->transfer_flags |= USB_DISABLE_SPD; port->write_urb->dev = port->serial->dev; if( (ret=usb_submit_urb(port->read_urb)) != 0 ) { @@ -1689,6 +1725,7 @@ dbg( "digi_startup: TOP" ); priv->dp_port_num = i; priv->dp_out_buf_len = 0; priv->dp_in_buf_len = 0; + priv->dp_write_urb_in_use = 0; priv->dp_modem_signals = 0; init_waitqueue_head( &priv->dp_modem_change_wait ); priv->dp_open_count = 0; @@ -2047,5 +2084,5 @@ module_exit(digi_exit); MODULE_AUTHOR("Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>"); -MODULE_DESCRIPTION("Digi AccelePort USB-4 Serial Converter driver"); +MODULE_DESCRIPTION("Digi AccelePort USB-2/USB-4 Serial Converter driver"); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 70f4cd1b0..fcdf71e84 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -12,6 +12,16 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (11/13/2000) Bill Ryder + * Added spinlock protected open code and close code. + * Multiple opens work (sort of - see webpage). + * Cleaned up comments. Removed multiple PID/VID definitions. + * Factorised cts/dtr code + * Made use of __FUNCTION__ in dbg's + * + * (11/01/2000) Adam J. Richter + * usb_device_id table support + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -68,8 +78,17 @@ #include "ftdi_sio.h" -#define FTDI_VENDOR_ID 0x0403 -#define FTDI_SIO_SERIAL_CONVERTER_ID 0x8372 +#define FTDI_VENDOR_ID FTDI_VID +#define FTDI_SIO_SERIAL_CONVERTER_ID FTDI_SIO_PID +#define FTDI_8U232AM_PID 0x6001 + +static __devinitdata struct usb_device_id id_table_sio [] = { + { idVendor: FTDI_VID, idProduct: FTDI_SIO_PID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table_sio); + /* function prototypes for a FTDI serial converter */ static int ftdi_sio_startup (struct usb_serial *serial); @@ -82,12 +101,9 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); /* All of the device info needed for the FTDI SIO serial converter */ -static __u16 ftdi_vendor_id = FTDI_VENDOR_ID; -static __u16 ftdi_sio_product_id = FTDI_SIO_SERIAL_CONVERTER_ID; struct usb_serial_device_type ftdi_sio_device = { name: "FTDI SIO", - idVendor: &ftdi_vendor_id, /* the FTDI vendor ID */ - idProduct: &ftdi_sio_product_id, /* the FTDI SIO product id */ + id_table: id_table_sio, needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ @@ -105,63 +121,55 @@ struct usb_serial_device_type ftdi_sio_device = { startup: ftdi_sio_startup, }; - /* * *************************************************************************** * FTDI SIO Serial Converter specific driver functions * *************************************************************************** * - * Bill Ryder bryder@sgi.com of Silicon Graphics, Inc. did the FTDI_SIO code - * Thanx to FTDI for so kindly providing details of the protocol required - * to talk to the device - http://www.ftdi.co.uk - * - * Tested as at this version - other stuff might work - * 23 March 2000 - * Works: - * Baudrates - 9600, 38400,19200, 57600, 115200 - * TIOCMBIC - TIOCM_DTR / TIOCM_RTS - * TIOCMBIS - TIOCM_DTR / TIOCM_RTS - * TIOCMSET - DTR on/RTSon / DTR off, RTS off - * no parity:CS8 even parity:CS7 odd parity:CS7 - * CRTSCTS flow control - * - * Pilot-xfer zillions of times - * - * cu works with dir option - * - * Not Tested (ie might not work): - * xon/xoff flow control - * ppp (modem handling in general) - * - * KNOWN BUGS: - * Multiple Opens - * ============== - * Seems to have problem when opening an already open port, - * Get I/O error on first attempt, then it lets you in. - * Need to do proper usage counting - keep registered callbacks for first opener. - * - * Reproduce with: - * cu -l /dev/ttyUSB0 dir - * whilst cu is running do: - * stty -a < /dev/ttyUSB0 - * - * from stty get: 'bash: /dev/ttyUSB0: Invalid argument ' - * from cu get - * write: Invalid argument - * - * Initialisation Problem - * ====================== - * Pilot transfer required me to run the serial_loopback program before it would work. - * Still working on this. See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio + * See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date + * testing information + * * */ #define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ +/* utility functions to set and unset dtr and rts */ +#define HIGH 1 +#define LOW 0 +static int set_rts(struct usb_device *dev, + unsigned int pipe, + int high_or_low) +{ + static char buf[1]; + unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_RTS_HIGH : + FTDI_SIO_SET_RTS_LOW); + return(usb_control_msg(dev, pipe, + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + ftdi_high_or_low, 0, + buf, 0, WDR_TIMEOUT)); +} +static int set_dtr(struct usb_device *dev, + unsigned int pipe, + int high_or_low) +{ + static char buf[1]; + unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_DTR_HIGH : + FTDI_SIO_SET_DTR_LOW); + return(usb_control_msg(dev, pipe, + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + ftdi_high_or_low, 0, + buf, 0, WDR_TIMEOUT)); +} + + /* do some startup allocations not currently performed by usb_serial_probe() */ static int ftdi_sio_startup (struct usb_serial *serial) { init_waitqueue_head(&serial->port[0].write_wait); + return (0); } @@ -170,65 +178,55 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) { /* ftdi_sio_open */ struct termios tmp_termios; struct usb_serial *serial = port->serial; + unsigned long flags; /* Used for spinlock */ int result; char buf[1]; /* Needed for the usb_control_msg I think */ - dbg("ftdi_sio_open port %d", port->number); + dbg(__FUNCTION__ " port %d", port->number); - /* FIXME - multiple concurrent opens cause trouble */ - if (port->active) { - err ("port already open"); - return -EINVAL; - } - port->active = 1; /* FIXME - For multiple open this should increment */ + spin_lock_irqsave (&port->port_lock, flags); + + MOD_INC_USE_COUNT; + ++port->open_count; - /* See ftdi_sio.h for description of what is reset */ - usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, - FTDI_SIO_RESET_SIO, - 0, buf, 0, WDR_TIMEOUT); + if (!port->active){ + port->active = 1; + spin_unlock_irqrestore (&port->port_lock, flags); - /* Setup termios */ - port->tty->termios->c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; + /* See ftdi_sio.h for description of what is reset */ + usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, + FTDI_SIO_RESET_SIO, + 0, buf, 0, WDR_TIMEOUT); - - ftdi_sio_set_termios(port, &tmp_termios); + /* Setup termios */ + port->tty->termios->c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; - /* Disable flow control */ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("error from flowcontrol urb"); - return(-EINVAL); - } - - /* Turn on RTS and DTR since we are not flow controlling*/ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - (unsigned)FTDI_SIO_SET_DTR_HIGH, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("Error from DTR HIGH urb"); - } - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - (unsigned)FTDI_SIO_SET_RTS_HIGH, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("Error from RTS HIGH urb"); - } + /* ftdi_sio_set_termios will send usb control messages */ + /* ftdi_sio_set_termios will set up port according to above list */ + + ftdi_sio_set_termios(port, &tmp_termios); + + /* Turn on RTS and DTR since we are not flow controlling*/ + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) { + err("Error from DTR HIGH urb"); + } + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){ + err("Error from RTS HIGH urb"); + } - /* Start reading from the device */ - FILL_BULK_URB(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ftdi_sio_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb); - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); + /* Start reading from the device */ + FILL_BULK_URB(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + ftdi_sio_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb); + if (result) + err(__FUNCTION__ " - failed submitting read urb, error %d", result); + } else { /* the port was already active - so no initialisation was done */ + spin_unlock_irqrestore (&port->port_lock, flags); + } return (0); } /* ftdi_sio_open */ @@ -239,41 +237,48 @@ static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) struct usb_serial *serial = port->serial; unsigned int c_cflag = port->tty->termios->c_cflag; char buf[1]; + unsigned long flags; + + dbg( __FUNCTION__ " port %d", port->number); + + spin_lock_irqsave (&port->port_lock, flags); + --port->open_count; + + if (port->open_count <= 0) { + spin_unlock_irqrestore (&port->port_lock, flags); + if (c_cflag & HUPCL){ + /* Disable flow control */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("error from flowcontrol urb"); + } + + /* drop DTR */ + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ + err("Error from DTR LOW urb"); + } + /* drop RTS */ + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { + err("Error from RTS LOW urb"); + } + } /* Note change no line is hupcl is off */ + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + port->active = 0; + port->open_count = 0; + } else { + spin_unlock_irqrestore (&port->port_lock, flags); + } + + MOD_DEC_USE_COUNT; - dbg("ftdi_sio_close port %d", port->number); - - if (c_cflag & HUPCL){ - /* Disable flow control */ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("error from flowcontrol urb"); - } - /* drop DTR */ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - (unsigned)FTDI_SIO_SET_DTR_LOW, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("Error from DTR LOW urb"); - } - /* drop RTS */ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - (unsigned)FTDI_SIO_SET_RTS_LOW, 0, - buf, 0, WDR_TIMEOUT) < 0) { - err("Error from RTS LOW urb"); - } - } - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->active = 0; } /* ftdi_sio_close */ @@ -292,7 +297,7 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, int result; DECLARE_WAITQUEUE(wait, current); - dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count); + dbg(__FUNCTION__ " port %d, %d bytes", port->number, count); if (count == 0) { err("write request of 0 bytes"); @@ -309,8 +314,10 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, add_wait_queue(&port->write_wait, &wait); set_current_state (TASK_INTERRUPTIBLE); while (port->write_urb->status == -EINPROGRESS) { - dbg("ftdi_sio - write in progress - retrying"); + dbg(__FUNCTION__ " write in progress - retrying"); if (0 /* file->f_flags & O_NONBLOCK */) { + remove_wait_queue(&port->write_wait, &wait); + set_current_state(TASK_RUNNING); rc = -EAGAIN; goto err; } @@ -321,6 +328,7 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, goto err; } schedule(); + set_current_state (TASK_INTERRUPTIBLE); } remove_wait_queue(&port->write_wait, &wait); set_current_state(TASK_RUNNING); @@ -345,7 +353,7 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, first_byte = port->write_urb->transfer_buffer; *first_byte = 1 | ((count-data_offset) << 2) ; - dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]); + dbg(__FUNCTION__ "Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]); usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte); /* send the data out the bulk port */ @@ -360,7 +368,7 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, return 0; } - dbg("write returning: %d", count - data_offset); + dbg(__FUNCTION__ " write returning: %d", count - data_offset); return (count - data_offset); } @@ -412,7 +420,7 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb) int i; int result; - dbg("ftdi_sio read callback"); + dbg(__FUNCTION__); if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) { return; @@ -422,10 +430,6 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb) if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) { return; } - - /* TO DO -- check for hung up line and handle appropriately: */ - /* send hangup (need to find out how to do this) */ - if (urb->status) { /* This will happen at close every time so it is a dbg not an err */ @@ -439,6 +443,12 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb) dbg("Just status"); } + /* TO DO -- check for hung up line and handle appropriately: */ + /* send hangup (need to find out how to do this) */ + /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ + /* if CD is dropped and the line is not CLOCAL then we should hangup */ + + if (urb->actual_length > data_offset) { for (i = data_offset ; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); @@ -468,10 +478,10 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * { /* ftdi_sio_set_termios */ struct usb_serial *serial = port->serial; unsigned int cflag = port->tty->termios->c_cflag; - __u16 urb_value; /* Will hold the new flags */ + __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ - dbg("ftdi_sio_set_termios port %d", port->number); + dbg(__FUNCTION__ " port %d", port->number); /* FIXME -For this cut I don't care if the line is really changing or @@ -522,7 +532,7 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break; case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break; case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break; - default: dbg("FTDI_SIO does not support the baudrate requested"); + default: dbg(__FUNCTION__ "FTDI_SIO does not support the baudrate requested"); /* FIXME - how to return an error for this? */ break; } if ((cflag & CBAUD) == B0 ) { @@ -535,18 +545,10 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * err("error from disable flowcontrol urb"); } /* Drop RTS and DTR */ - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - (unsigned)FTDI_SIO_SET_DTR_LOW, 0, - buf, 0, WDR_TIMEOUT) < 0) { + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ err("Error from DTR LOW urb"); } - if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - (unsigned)FTDI_SIO_SET_RTS_LOW, 0, - buf, 0, WDR_TIMEOUT) < 0) { + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ err("Error from RTS LOW urb"); } @@ -563,7 +565,7 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * /* Set flow control */ /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ if (cflag & CRTSCTS) { - dbg("Setting to CRTSCTS flow control"); + dbg(__FUNCTION__ "Setting to CRTSCTS flow control"); if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, @@ -576,7 +578,7 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * } else { /* CHECK Assuming XON/XOFF handled by stack - not by device */ /* Disable flow control */ - dbg("Turning off hardware flow control"); + dbg(__FUNCTION__ "Turning off hardware flow control"); if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, @@ -597,13 +599,13 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns char buf[1]; int ret, mask; - dbg("ftdi_sio_ioctl - cmd 0x%04x", cmd); + dbg(__FUNCTION__ " cmd 0x%04x", cmd); /* Based on code from acm.c and others */ switch (cmd) { case TIOCMGET: - dbg("TIOCMGET"); + dbg(__FUNCTION__ "TIOCMGET"); /* Request the status from the device */ if ((ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), @@ -611,7 +613,7 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, 0, 0, buf, 1, HZ * 5)) < 0 ) { - dbg("Get not get modem status of device"); + dbg(__FUNCTION__ "Get not get modem status of device"); return(ret); } @@ -623,7 +625,7 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns break; case TIOCMSET: /* Turns on and off the lines as specified by the mask */ - dbg("TIOCMSET"); + dbg(__FUNCTION__ "TIOCMSET"); if ((ret = get_user(mask, (unsigned long *) arg))) return ret; urb_value = ((mask & TIOCM_DTR) ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW); if ((ret = usb_control_msg(serial->dev, @@ -648,26 +650,20 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns break; case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ - dbg("TIOCMBIS"); + dbg(__FUNCTION__ "TIOCMBIS"); if ((ret = get_user(mask, (unsigned long *) arg))) return ret; if (mask & TIOCM_DTR){ - if ((ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - FTDI_SIO_SET_DTR_HIGH , 0, - buf, 0, WDR_TIMEOUT)) < 0){ + if ((ret = set_dtr(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + HIGH)) < 0) { err("Urb to set DTR failed"); return(ret); - } } - if (mask & TIOCM_RTS) { - if ((ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - FTDI_SIO_SET_RTS_HIGH , 0, - buf, 0, WDR_TIMEOUT)) < 0){ + } + if (mask & TIOCM_RTS) { + if ((ret = set_rts(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + HIGH)) < 0){ err("Urb to set RTS failed"); return(ret); } @@ -675,26 +671,20 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns break; case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ - dbg("TIOCMBIC"); + dbg(__FUNCTION__ "TIOCMBIC"); if ((ret = get_user(mask, (unsigned long *) arg))) return ret; if (mask & TIOCM_DTR){ - if ((ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - FTDI_SIO_SET_DTR_LOW , 0, - buf, 0, WDR_TIMEOUT)) < 0){ + if ((ret = set_dtr(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + LOW)) < 0){ err("Urb to unset DTR failed"); return(ret); } } if (mask & TIOCM_RTS) { - if ((ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - FTDI_SIO_SET_RTS_LOW , 0, - buf, 0, WDR_TIMEOUT)) < 0){ + if ((ret = set_rts(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + LOW)) < 0){ err("Urb to unset RTS failed"); return(ret); } @@ -714,11 +704,11 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns /* This is not an error - turns out the higher layers will do * some ioctls itself (see comment above) */ - dbg("ftdi_sio ioctl arg not supported - it was 0x%04x",cmd); + dbg(__FUNCTION__ "arg not supported - it was 0x%04x",cmd); return(-ENOIOCTLCMD); break; } - dbg("ftdi_sio_ioctl returning 0"); + dbg(__FUNCTION__ " returning 0"); return 0; } /* ftdi_sio_ioctl */ diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 9c509ff15..885108cd0 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -22,6 +22,9 @@ Tip 'o the hat to Linuxcare for supporting staff in their work on open source projects. + (11/01/2000) Adam J. Richter + usb_device_id table support. + (10/05/2000) gkh Fixed bug with urb->dev not being set properly, now that the usb core needs it. diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 666946181..8d249916c 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -117,28 +117,96 @@ struct ezusb_hex_record { /* Device info for the Keyspan serial converter */ #define KEYSPAN_VENDOR_ID (0x06cd) -static __u16 keyspan_vendor_id = KEYSPAN_VENDOR_ID; /* Product IDs for the five products supported, pre-renumeration */ -static __u16 keyspan_usa18x_pre_product_id = 0x0105; -static __u16 keyspan_usa19_pre_product_id = 0x0103; -static __u16 keyspan_usa19w_pre_product_id = 0x0106; -static __u16 keyspan_usa28_pre_product_id = 0x0101; -static __u16 keyspan_usa28x_pre_product_id = 0x0102; +#define keyspan_usa18x_pre_product_id 0x0105 +#define keyspan_usa19_pre_product_id 0x0103 +#define keyspan_usa19w_pre_product_id 0x0106 +#define keyspan_usa28_pre_product_id 0x0101 +#define keyspan_usa28x_pre_product_id 0x0102 /* Product IDs post-renumeration */ -static __u16 keyspan_usa18x_product_id = 0x0112; -static __u16 keyspan_usa19_product_id = 0x0107; -static __u16 keyspan_usa19w_product_id = 0x0108; -static __u16 keyspan_usa28_product_id = 0x010f; -static __u16 keyspan_usa28x_product_id = 0x0110; +#define keyspan_usa18x_product_id 0x0112 +#define keyspan_usa19_product_id 0x0107 +#define keyspan_usa19w_product_id 0x0108 +#define keyspan_usa28_product_id 0x010f +#define keyspan_usa28x_product_id 0x0110 + +static __devinitdata struct usb_device_id keyspan_ids_combined[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_pre_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19_pre_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19w_pre_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28_pre_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28x_pre_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19w_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28x_product_id}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, keyspan_ids_combined); + +/* Eventually, we will not need separate id tables for each USB + ID pattern. But, for now, it looks like we need slightly different + behavior for each match. */ + +static __devinitdata struct usb_device_id keyspan_usa18x_pre_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_pre_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa19_pre_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19_pre_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa19w_pre_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19w_pre_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa28_pre_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28_pre_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa28x_pre_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28x_pre_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa18x_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa19_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa19w_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19w_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa28_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28_product_id}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id keyspan_usa28x_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28x_product_id}, + { } /* Terminating entry */ +}; /* Structs for the devices, pre and post renumeration. These are incomplete at present - HAB 20000708 */ struct usb_serial_device_type keyspan_usa18x_pre_device = { name: "Keyspan USA18X - (prerenumeration)", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa18x_pre_product_id, + id_table: keyspan_usa18x_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -151,8 +219,7 @@ struct usb_serial_device_type keyspan_usa18x_pre_device = { struct usb_serial_device_type keyspan_usa19_pre_device = { name: "Keyspan USA19 - (prerenumeration)", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa19_pre_product_id, + id_table: keyspan_usa19_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -166,8 +233,7 @@ struct usb_serial_device_type keyspan_usa19_pre_device = { struct usb_serial_device_type keyspan_usa19w_pre_device = { name: "Keyspan USA19W - (prerenumeration)", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa19w_pre_product_id, + id_table: keyspan_usa19w_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -181,8 +247,7 @@ struct usb_serial_device_type keyspan_usa19w_pre_device = { struct usb_serial_device_type keyspan_usa28_pre_device = { name: "Keyspan USA28 - (prerenumeration)", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa28_pre_product_id, + id_table: keyspan_usa28_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -195,8 +260,7 @@ struct usb_serial_device_type keyspan_usa28_pre_device = { struct usb_serial_device_type keyspan_usa28x_pre_device = { name: "Keyspan USA28X - (prerenumeration)", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa28x_pre_product_id, + id_table: keyspan_usa28x_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -210,8 +274,7 @@ struct usb_serial_device_type keyspan_usa28x_pre_device = { struct usb_serial_device_type keyspan_usa18x_device = { name: "Keyspan USA18X", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa18x_product_id, + id_table: keyspan_usa18x_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -228,8 +291,7 @@ struct usb_serial_device_type keyspan_usa18x_device = { struct usb_serial_device_type keyspan_usa19_device = { name: "Keyspan USA19", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa19_product_id, + id_table: keyspan_usa19_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: MUST_HAVE, needs_bulk_out: MUST_HAVE, @@ -256,8 +318,7 @@ struct usb_serial_device_type keyspan_usa19_device = { struct usb_serial_device_type keyspan_usa19w_device = { name: "Keyspan USA19W", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa19w_product_id, + id_table: keyspan_usa19w_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -275,8 +336,7 @@ struct usb_serial_device_type keyspan_usa19w_device = { struct usb_serial_device_type keyspan_usa28_device = { name: "Keyspan USA28", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa28_product_id, + id_table: keyspan_usa28_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -294,8 +354,7 @@ struct usb_serial_device_type keyspan_usa28_device = { struct usb_serial_device_type keyspan_usa28x_device = { name: "Keyspan USA28X", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_usa28x_product_id, + id_table: keyspan_usa28x_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 51a1f10df..3114322c8 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -12,6 +12,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (11/01/2000) Adam J. Richter + * usb_device_id table support + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -93,12 +96,23 @@ struct keyspan_pda_private { #define KEYSPAN_PDA_FAKE_ID 0x0103 #define KEYSPAN_PDA_ID 0x0104 /* no clue */ -/* All of the device info needed for the Keyspan PDA serial converter */ -static __u16 keyspan_vendor_id = KEYSPAN_VENDOR_ID; -static __u16 keyspan_pda_fake_product_id = KEYSPAN_PDA_FAKE_ID; -static __u16 keyspan_pda_product_id = KEYSPAN_PDA_ID; +static __devinitdata struct usb_device_id id_table_combined [] = { + { idVendor: KEYSPAN_VENDOR_ID, idProduct: KEYSPAN_PDA_FAKE_ID }, + { idVendor: KEYSPAN_VENDOR_ID, idProduct: KEYSPAN_PDA_ID }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, id_table_combined); +static __devinitdata struct usb_device_id id_table_std [] = { + { idVendor: KEYSPAN_VENDOR_ID, idProduct: KEYSPAN_PDA_ID }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id id_table_fake [] = { + { idVendor: KEYSPAN_VENDOR_ID, idProduct: KEYSPAN_PDA_FAKE_ID }, + { } /* Terminating entry */ +}; static void keyspan_pda_wakeup_write( struct usb_serial_port *port ) { @@ -746,8 +760,7 @@ static void keyspan_pda_shutdown (struct usb_serial *serial) struct usb_serial_device_type keyspan_pda_fake_device = { name: "Keyspan PDA - (prerenumeration)", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_pda_fake_product_id, + id_table: id_table_fake, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, needs_bulk_out: DONT_CARE, @@ -760,8 +773,7 @@ struct usb_serial_device_type keyspan_pda_fake_device = { struct usb_serial_device_type keyspan_pda_device = { name: "Keyspan PDA", - idVendor: &keyspan_vendor_id, - idProduct: &keyspan_pda_product_id, + id_table: id_table_std, needs_interrupt_in: MUST_HAVE, needs_bulk_in: DONT_CARE, needs_bulk_out: MUST_HAVE, diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index dc832fa53..2b9f99f7c 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -10,6 +10,9 @@ * * Please report both successes and troubles to the author at omninet@kroah.com * + * (11/01/2000) Adam J. Richter + * usb_device_id table support + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -66,14 +69,17 @@ static int omninet_write (struct usb_serial_port *port, int from_user, const u static int omninet_write_room (struct usb_serial_port *port); static void omninet_shutdown (struct usb_serial *serial); -/* All of the device info needed for the omni.net */ -static __u16 zyxel_vendor_id = ZYXEL_VENDOR_ID; -static __u16 zyxel_omninet_product_id = ZYXEL_OMNINET_ID; +static __devinitdata struct usb_device_id id_table [] = { + { idVendor: ZYXEL_VENDOR_ID, idProduct: ZYXEL_OMNINET_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table); + struct usb_serial_device_type zyxel_omninet_device = { name: "ZyXEL - omni.net lcd plus usb", - idVendor: &zyxel_vendor_id, - idProduct: &zyxel_omninet_product_id, + id_table: id_table, needs_interrupt_in: MUST_HAVE, needs_bulk_in: MUST_HAVE, needs_bulk_out: MUST_HAVE, diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index 5dde77426..913fb2f26 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -112,8 +112,7 @@ struct usb_serial { /* This structure defines the individual serial converter. */ struct usb_serial_device_type { char *name; - __u16 *idVendor; - __u16 *idProduct; + const struct usb_device_id *id_table; char needs_interrupt_in; char needs_bulk_in; char needs_bulk_out; @@ -125,7 +124,9 @@ struct usb_serial_device_type { struct list_head driver_list; /* function call to make before accepting driver */ - int (*startup) (struct usb_serial *serial); /* return 0 to continue initialization, anything else to abort */ + /* return 0 to continue initialization, anything else to abort */ + int (*startup) (struct usb_serial *serial); + void (*shutdown) (struct usb_serial *serial); /* serial function calls */ diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 902a4e1d0..6b5d6e7f0 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -15,6 +15,11 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (11/01/2000) Adam J. Richter + * instead of using idVendor/idProduct pairs, usb serial drivers + * now identify their hardware interest with usb_device_id tables, + * which they usually have anyhow for use with MODULE_DEVICE_TABLE. + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -288,11 +293,12 @@ MODULE_PARM_DESC(vendor, "User specified USB idVendor"); MODULE_PARM(product, "i"); MODULE_PARM_DESC(product, "User specified USB idProduct"); +static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ + /* All of the device info needed for the Generic Serial Converter */ static struct usb_serial_device_type generic_device = { name: "Generic", - idVendor: &vendor, /* use the user specified vendor id */ - idProduct: &product, /* use the user specified product id */ + id_table: generic_device_ids, needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ @@ -316,15 +322,25 @@ static void serial_unthrottle (struct tty_struct * tty); static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); static void serial_set_termios (struct tty_struct *tty, struct termios * old); -static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum); +static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); static void usb_serial_disconnect(struct usb_device *dev, void *ptr); static struct usb_driver usb_serial_driver = { name: "serial", probe: usb_serial_probe, disconnect: usb_serial_disconnect, + id_table: NULL, /* check all devices */ }; +/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead + the MODULE_DEVICE_TABLE declarations in each serial driver + cause the "hotplug" program to pull in whatever module is necessary + via modprobe, and modprobe will load usbserial because the serial + drivers depend on it. +*/ + + static int serial_refcount; static struct tty_driver serial_tty_driver; static struct tty_struct * serial_tty[SERIAL_TTY_MINORS]; @@ -957,7 +973,8 @@ static void port_softint(void *private) -static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) +static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_serial *serial = NULL; struct usb_serial_port *port; @@ -981,19 +998,17 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) int num_bulk_out = 0; int num_ports; int max_endpoints; + const struct usb_device_id *id_pattern = NULL; /* loop through our list of known serial converters, and see if this device matches. */ found = 0; + interface = &dev->actconfig->interface[ifnum]; list_for_each (tmp, &usb_serial_driver_list) { type = list_entry(tmp, struct usb_serial_device_type, driver_list); - dbg ("Looking at %s Vendor id=%.4x Product id=%.4x", - type->name, *(type->idVendor), *(type->idProduct)); - - /* look at the device descriptor */ - if ((dev->descriptor.idVendor == *(type->idVendor)) && - (dev->descriptor.idProduct == *(type->idProduct))) { + id_pattern = usb_match_id(dev, interface, type->id_table); + if (id_pattern != NULL) { dbg("descriptor matches"); found = 1; break; @@ -1009,7 +1024,6 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT; /* check out the endpoints */ - interface = &dev->actconfig->interface[ifnum]; iface_desc = &interface->altsetting[0]; for (i = 0; i < iface_desc->bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i]; @@ -1335,6 +1349,8 @@ int usb_serial_init(void) } #ifdef CONFIG_USB_SERIAL_GENERIC + generic_device_ids[0].idVendor = vendor; + generic_device_ids[0].idProduct = product; /* register our generic driver with ourselves */ usb_serial_register (&generic_device); #endif diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 868333c87..c078704f0 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (11/01/2000) Adam J. Richter + * usb_device_id table support + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -101,13 +104,20 @@ static void visor_set_termios (struct usb_serial_port *port, struct termios *old static void visor_write_bulk_callback (struct urb *urb); static void visor_read_bulk_callback (struct urb *urb); + +static __devinitdata struct usb_device_id id_table [] = { + { idVendor: HANDSPRING_VENDOR_ID, idProduct: HANDSPRING_VISOR_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table); + + + /* All of the device info needed for the Handspring Visor */ -static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID; -static __u16 handspring_product_id = HANDSPRING_VISOR_ID; struct usb_serial_device_type handspring_device = { name: "Handspring Visor", - idVendor: &handspring_vendor_id, /* the Handspring vendor ID */ - idProduct: &handspring_product_id, /* the Handspring Visor product id */ + id_table: id_table, needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 55abab383..83a822225 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (11/01/2000) Adam J. Richter + * usb_device_id table support + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. @@ -77,6 +80,31 @@ #define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 #define CONNECT_TECH_WHITE_HEAT_ID 0x8001 +/* + ID tables for whiteheat are unusual, because we want to different + things for different versions of the device. Eventually, this + will be doable from a single table. But, for now, we define two + separate ID tables, and then a third table that combines them + just for the purpose of exporting the autoloading information. +*/ +static __devinitdata struct usb_device_id id_table_std [] = { + {idVendor: CONNECT_TECH_VENDOR_ID, idProduct: CONNECT_TECH_WHITE_HEAT_ID}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id id_table_prerenumeration [] = { + {idVendor: CONNECT_TECH_VENDOR_ID, idProduct: CONNECT_TECH_WHITE_HEAT_ID}, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id id_table_combined [] = { + {idVendor: CONNECT_TECH_VENDOR_ID, idProduct: CONNECT_TECH_WHITE_HEAT_ID}, + {idVendor: CONNECT_TECH_VENDOR_ID, idProduct: CONNECT_TECH_FAKE_WHITE_HEAT_ID}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table_combined); + /* function prototypes for the Connect Tech WhiteHEAT serial converter */ static int whiteheat_open (struct usb_serial_port *port, struct file *filp); static void whiteheat_close (struct usb_serial_port *port, struct file *filp); @@ -87,14 +115,9 @@ static void whiteheat_unthrottle (struct usb_serial_port *port); static int whiteheat_startup (struct usb_serial *serial); static void whiteheat_shutdown (struct usb_serial *serial); -/* All of the device info needed for the Connect Tech WhiteHEAT */ -static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID; -static __u16 connecttech_whiteheat_fake_product_id = CONNECT_TECH_FAKE_WHITE_HEAT_ID; -static __u16 connecttech_whiteheat_product_id = CONNECT_TECH_WHITE_HEAT_ID; struct usb_serial_device_type whiteheat_fake_device = { name: "Connect Tech - WhiteHEAT - (prerenumeration)", - idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ - idProduct: &connecttech_whiteheat_fake_product_id, /* the White Heat initial product id */ + id_table: id_table_prerenumeration, needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ @@ -104,10 +127,10 @@ struct usb_serial_device_type whiteheat_fake_device = { num_ports: 1, startup: whiteheat_startup }; + struct usb_serial_device_type whiteheat_device = { name: "Connect Tech - WhiteHEAT", - idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ - idProduct: &connecttech_whiteheat_product_id, /* the White Heat real product id */ + id_table: id_table_std, needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 80ace9dac..cf82489de 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,7 +1,7 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.15 2000/10/19 18:44:11 mdharm Exp $ + * $Id: scsiglue.c,v 1.17 2000/11/02 21:27:49 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -47,6 +47,7 @@ #include "scsiglue.h" #include "usb.h" #include "debug.h" +#include "transport.h" #include <linux/malloc.h> @@ -105,7 +106,7 @@ static int detect(struct SHT *sht) /* Release all resources used by the virtual host * - * NOTE: There is no contention here, because we're allready deregistered + * NOTE: There is no contention here, because we're already deregistered * the driver and we're doing each virtual host in turn, not in parallel */ static int release(struct Scsi_Host *psh) @@ -218,6 +219,7 @@ static int bus_reset( Scsi_Cmnd *srb ) { struct us_data *us = (struct us_data *)srb->host->hostdata[0]; int i; + int result; /* we use the usb_reset_device() function to handle this for us */ US_DEBUGP("bus_reset() called\n"); @@ -228,6 +230,15 @@ static int bus_reset( Scsi_Cmnd *srb ) return SUCCESS; } + /* release the IRQ, if we have one */ + down(&(us->irq_urb_sem)); + if (us->irq_urb) { + US_DEBUGP("-- releasing irq URB\n"); + result = usb_unlink_urb(us->irq_urb); + US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); + } + up(&(us->irq_urb_sem)); + /* attempt to reset the port */ if (usb_reset_device(us->pusb_dev) < 0) return FAILED; @@ -237,6 +248,7 @@ static int bus_reset( Scsi_Cmnd *srb ) for (i = 0; i < us->pusb_dev->actconfig->bNumInterfaces; i++) { struct usb_interface *intf = &us->pusb_dev->actconfig->interface[i]; + struct usb_device_id *id; /* if this is an unclaimed interface, skip it */ if (!intf->driver) { @@ -254,10 +266,22 @@ static int bus_reset( Scsi_Cmnd *srb ) US_DEBUGPX("simulating disconnect/reconnect.\n"); down(&intf->driver->serialize); intf->driver->disconnect(us->pusb_dev, intf->private_data); - intf->driver->probe(us->pusb_dev, i); + id = usb_match_id(us->pusb_dev, intf, intf->driver->id_table); + intf->driver->probe(us->pusb_dev, i, id); up(&intf->driver->serialize); } + /* re-allocate the IRQ URB and submit it to restore connectivity + * for CBI devices + */ + if (us->protocol == US_PR_CBI) { + down(&(us->irq_urb_sem)); + us->irq_urb->dev = us->pusb_dev; + result = usb_submit_urb(us->irq_urb); + US_DEBUGP("usb_submit_urb() returns %d\n", result); + up(&(us->irq_urb_sem)); + } + US_DEBUGP("bus_reset() complete\n"); return SUCCESS; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 6551d259c..dd72f334c 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,6 +1,6 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: transport.c,v 1.30 2000/10/24 02:01:18 mdharm Exp $ + * $Id: transport.c,v 1.32 2000/11/03 00:18:04 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -91,11 +91,11 @@ unsigned int usb_stor_transfer_length(Scsi_Cmnd *srb) static char *lengths = - /* 0123456789ABCDEF 0123456789ABCDEF */ + /* 0123456789ABCDEF 0123456789ABCDEF */ "00XLZ6XZBXBBXXXB" "00LBBLG0R0L0GG0X" /* 00-1F */ "XXXXT8XXB4B0BBBB" "ZZZ0B00HCSSZTBHH" /* 20-3F */ - "M0HHB0X000H0HH0X" "XHH00HXX0TH0H0XX" /* 40-5F */ + "M0HHB0X000H0HH0X" "XHH0HHXX0TH0H0XX" /* 40-5F */ "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* 60-7F */ "XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX" /* 80-9F */ "X0XXX00XB0BXBXBB" "ZZZ0XUIDU000XHBX" /* A0-BF */ @@ -185,6 +185,7 @@ unsigned int usb_stor_transfer_length(Scsi_Cmnd *srb) SEND_MESSAGE_6 0a !!! Same as WRITE_6 - is in bytes SEND_MESSAGE_10 2a !!! Same as WRITE_10 - is in bytes SEND_MESSAGE_12 aa !!! Same as WRITE_12 - is in bytes + SEND_OPC 54 SEND_VOLUME_TAG b6 !!! Think this is in bytes SET_LIMITS 33 SET_LIMITS_12 b3 @@ -422,6 +423,7 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, if (status) { /* something went wrong */ up(&(us->current_urb_sem)); + current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); kfree(dr); return status; @@ -479,6 +481,7 @@ int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, if (status) { /* something went wrong */ up(&(us->current_urb_sem)); + current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); return status; } @@ -544,7 +547,7 @@ int usb_stor_transfer_partial(struct us_data *us, char *buf, int length) /* uh oh... we have an error code, so something went wrong. */ if (result) { - /* NAK - that means we've retried a few times allready */ + /* NAK - that means we've retried a few times already */ if (result == -ETIMEDOUT) { US_DEBUGP("usb_stor_transfer_partial(): device NAKed\n"); return US_BULK_TRANSFER_FAILED; diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 8d41b1c0c..a7129ba25 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,6 +1,6 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: usb.c,v 1.51 2000/10/19 18:49:51 mdharm Exp $ + * $Id: usb.c,v 1.54 2000/10/31 21:32:10 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) @@ -243,6 +243,7 @@ static int usb_stor_control_thread(void * __us) /* handle those devices which can't do a START_STOP */ if ((us->srb->cmnd[0] == START_STOP) && (us->flags & US_FL_START_STOP)) { + US_DEBUGP("Skipping START_STOP command\n"); us->srb->result = GOOD << 1; set_current_state(TASK_INTERRUPTIBLE); @@ -311,11 +312,15 @@ static int usb_stor_control_thread(void * __us) US_DEBUGP("-- US_ACT_EXIT command recieved\n"); break; } + + set_current_state(TASK_INTERRUPTIBLE); } /* for (;;) */ /* notify the exit routine that we're actually exiting now */ up(&(us->notify)); + remove_wait_queue(&(us->wqh), &wait); + return 0; } @@ -423,8 +428,8 @@ static struct us_unusual_dev us_unusual_dev_list[] = { { 0x054c, 0x002d, 0x0100, 0x0100, "Sony", "Memorystick MSAC-US1", - US_SC_SCSI, US_PR_CB, NULL, - US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE }, + US_SC_UFI, US_PR_CB, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP }, { 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", @@ -504,7 +509,7 @@ static struct us_unusual_dev us_unusual_dev_list[] = { US_FL_SCM_MULT_TARG }, #ifdef CONFIG_USB_STORAGE_FREECOM - { 0x07ab, 0xfc01, 0x0921, 0x0921, + { 0x07ab, 0xfc01, 0x0000, 0x9999, "Freecom", "USB-IDE", US_SC_QIC, US_PR_FREECOM, freecom_init, 0}, @@ -754,7 +759,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) /* * Now check if we have seen this GUID before * We're looking for a device with a matching GUID that isn't - * allready on the system + * already on the system */ ss = us_list; while ((ss != NULL) && diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index 2105cb595..9d424a61a 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -654,6 +654,7 @@ static int sohci_unlink_urb (urb_t * urb) set_current_state(TASK_UNINTERRUPTIBLE); while (timeout && (urb->status == USB_ST_URB_PENDING)) timeout = schedule_timeout (timeout); + current->state = TASK_RUNNING; remove_wait_queue (&unlink_wakeup, &wait); if (urb->status == USB_ST_URB_PENDING) { err ("unlink URB timeout"); @@ -765,6 +766,7 @@ static int sohci_free_dev (struct usb_device * usb_dev) set_current_state(TASK_UNINTERRUPTIBLE); while (timeout && dev->ed_cnt) timeout = schedule_timeout (timeout); + current->state = TASK_RUNNING; remove_wait_queue (&freedev_wakeup, &wait); if (dev->ed_cnt) { err ("free device %d timeout", usb_dev->devnum); diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 7e59346aa..5b1b76003 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -7,6 +7,9 @@ * (C) Copyright Gregory P. Smith 1999 * (C) Copyright Deti Fliegl 1999 (new USB architecture) * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) * * NOTE! This is not actually a driver at all, rather this is * just a collection of helper routines that implement the @@ -460,6 +463,124 @@ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interfac iface->private_data = NULL; } + +/* usb_match_id searches an array of usb_device_id's and returns + the first one that matches the device and interface. + + Parameters: + "id" is an array of usb_device_id's is terminated by an entry + containing all zeroes. + + "dev" and "interface" are the device and interface for which + a match is sought. + + If no match is found or if the "id" pointer is NULL, then + usb_match_id returns NULL. + + + What constitutes a match: + + A zero in any element of a usb_device_id entry is a wildcard + (i.e., that field always matches). For there to be a match, + *every* nonzero element of the usb_device_id must match the + provided device and interface in. The comparison is for equality, + except for one pair of fields: usb_match_id.bcdDevice_{lo,hi} define + an inclusive range that dev->descriptor.bcdDevice must be in. + + If interface->altsettings does not exist (i.e., there are no + interfaces defined), then bInterface{Class,SubClass,Protocol} + only match if they are all zeroes. + + + What constitutes a good "usb_device_id"? + + The match algorithm is very simple, so that intelligence in + driver selection must come from smart driver id records. + Unless you have good reasons to use another selection policy, + provide match elements only in related groups: + + * device specifiers (vendor and product IDs; and maybe + a revision range for that product); + * generic device specs (class/subclass/protocol); + * interface specs (class/subclass/protocol). + + Within those groups, work from least specific to most specific. + For example, don't give a product version range without vendor + and product IDs. + + "driver_info" is not considered by the kernel matching algorithm, + but you can create a wildcard "matches anything" usb_device_id + as your driver's "modules.usbmap" entry if you provide only an + id with a nonzero "driver_info" field. +*/ + +const struct usb_device_id * +usb_match_id(struct usb_device *dev, struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_interface_descriptor *intf = 0; + + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return NULL; + + /* It is important to check that id->driver_info is nonzero, + since an entry that is all zeroes except for a nonzero + id->driver_info is the way to create an entry that + indicates that the driver want to examine every + device and interface. */ + for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || + id->driver_info; id++) { + + if (id->idVendor && + id->idVendor != dev->descriptor.idVendor) + continue; + + if (id->idProduct && + id->idProduct != dev->descriptor.idProduct) + continue; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if (id->bcdDevice_lo > dev->descriptor.bcdDevice) + continue; + + if (id->bcdDevice_hi && + id->bcdDevice_hi < dev->descriptor.bcdDevice) + continue; + + if (id->bDeviceClass && + id->bDeviceClass != dev->descriptor.bDeviceClass) + continue; + + if (id->bDeviceSubClass && + id->bDeviceSubClass!= dev->descriptor.bDeviceClass) + continue; + + if (id->bDeviceProtocol && + id->bDeviceProtocol != dev->descriptor.bDeviceProtocol) + continue; + + intf = &interface->altsetting [interface->act_altsetting]; + + if (id->bInterfaceClass + && id->bInterfaceClass != intf->bInterfaceClass) + continue; + + if (id->bInterfaceSubClass && + id->bInterfaceSubClass != intf->bInterfaceSubClass) + continue; + + if (id->bInterfaceProtocol + && id->bInterfaceProtocol != intf->bInterfaceProtocol) + continue; + + return id; + } + + return NULL; +} + /* * This entrypoint gets called for each new device. * @@ -478,8 +599,12 @@ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interfac */ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) { - struct list_head *tmp = usb_driver_list.next; + struct list_head *tmp; struct usb_interface *interface; + void *private; + const struct usb_device_id *id; + struct usb_driver *driver; + int i; if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { err("bad find_interface_driver params"); @@ -491,141 +616,52 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) if (usb_interface_claimed(interface)) return -1; - while (tmp != &usb_driver_list) { - void *private; - struct usb_driver *driver = list_entry(tmp, struct usb_driver, - driver_list); - + private = NULL; + for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) { + + driver = list_entry(tmp, struct usb_driver, driver_list); tmp = tmp->next; - down(&driver->serialize); + down(&driver->serialize); + id = driver->id_table; /* new style driver? */ - if (driver->bind) { - const struct usb_device_id *id = driver->id_table; - - if (id) { - /* scan device ids for a match */ - for (;; id++) { - struct usb_interface_descriptor *intf = 0; - - /* done? */ - if (!id->idVendor && !id->bDeviceClass && !id->bInterfaceClass) { - id = 0; - break; - } + if (id) { + for (i = 0; i < interface->num_altsetting; i++) { + interface->act_altsetting = i; + id = usb_match_id(dev, interface, id); + if (id) { + private = driver->probe(dev,ifnum,id); + if (private != NULL) + break; + } + } + /* if driver not bound, leave defaults unchanged */ + if (private == NULL) + interface->act_altsetting = 0; + } + else /* "old style" driver */ + private = driver->probe(dev, ifnum, NULL); - /* Vendor match, possibly product-specific? */ - if (id->idVendor && id->idVendor == dev->descriptor.idVendor) { - if (id->idProduct && id->idProduct != dev->descriptor.idProduct) - continue; - break; - } - - /* Device class match? */ - if (id->bDeviceClass - && id->bDeviceClass == dev->descriptor.bDeviceClass) { - if (id->bDeviceSubClass && id->bDeviceSubClass - != dev->descriptor.bDeviceClass) - continue; - if (id->bDeviceProtocol && id->bDeviceProtocol - != dev->descriptor.bDeviceProtocol) - continue; - break; - } - - /* Interface class match? */ - if (!interface->altsetting || interface->num_altsetting < 1) - continue; - intf = &interface->altsetting [0]; - if (id->bInterfaceClass - && id->bInterfaceClass == intf->bInterfaceClass) { - if (id->bInterfaceSubClass && id->bInterfaceSubClass - != intf->bInterfaceClass) - continue; - if (id->bInterfaceProtocol && id->bInterfaceProtocol - != intf->bInterfaceProtocol) - continue; - break; - } - } - - /* is this driver interested in this interface? */ - if (id) - private = driver->bind(dev, ifnum, id); - else - private = 0; - } else { - /* "old style" driver, but using new interface */ - private = driver->bind(dev, ifnum, 0); - } - - /* "old style" driver */ - } else - private = driver->probe(dev, ifnum); up(&driver->serialize); - if (!private) - continue; - usb_driver_claim_interface(driver, interface, private); - - return 0; + if (private) { + usb_driver_claim_interface(driver, interface, private); + return 0; + } } - + return -1; } -#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG) +#ifdef CONFIG_HOTPLUG /* * USB hotplugging invokes what /proc/sys/kernel/hotplug says * (normally /sbin/hotplug) when USB devices get added or removed. - */ - -static int to_bcd (char *buf, __u16 *bcdValue) -{ - int retval = 0; - char *value = (char *) bcdValue; - int temp; - - /* digits are 0-9 then ":;<=>?" for devices using - * non-bcd (non-standard!) values here ... */ - - /* No leading (or later, trailing) zeroes since scripts do - * literal matches, and that's how they're doing them. */ - if ((temp = value [1] & 0xf0) != 0) { - temp >>= 4; - temp += '0'; - *buf++ = (char) temp; - retval++; - } - - temp = value [1] & 0x0f; - temp += '0'; - *buf++ = (char) temp; - retval++; - - *buf++ = '.'; - retval++; - - temp = value [0] & 0xf0; - temp >>= 4; - temp += '0'; - *buf++ = (char) temp; - retval++; - - if ((temp = value [0] & 0x0f) != 0) { - temp += '0'; - *buf++ = (char) temp; - retval++; - } - *buf++ = 0; - - return retval; -} - -/* + * * This invokes a user mode policy agent, typically helping to load driver - * or other modules, configure the device, or both. + * or other modules, configure the device, and more. Drivers can provide + * a MODULE_DEVICE_TABLE to help with module loading subtasks. * * Some synchronization is important: removes can't start processing * before the add-device processing completes, and vice versa. That keeps @@ -696,9 +732,7 @@ static void call_policy (char *verb, struct usb_device *dev) * all the device descriptors we don't tell them about. Or * even act as usermode drivers. * - * XXX how little intelligence can we hardwire? - * (a) mount point: /devfs, /dev, /proc/bus/usb etc. - * (b) naming convention: bus1/device3, 001/003 etc. + * FIXME reduce hardwired intelligence here */ envp [i++] = "DEVFS=/proc/bus/usb"; envp [i++] = scratch; @@ -706,33 +740,34 @@ static void call_policy (char *verb, struct usb_device *dev) dev->bus->busnum, dev->devnum) + 1; #endif - /* per-device configuration hacks are often necessary */ + /* per-device configuration hacks are common */ envp [i++] = scratch; - scratch += sprintf (scratch, "PRODUCT=%x/%x/", + scratch += sprintf (scratch, "PRODUCT=%x/%x/%x", dev->descriptor.idVendor, - dev->descriptor.idProduct); - scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1; + dev->descriptor.idProduct, + dev->descriptor.bcdDevice) + 1; - /* otherwise, use a simple (so far) generic driver binding model */ + /* class-based driver binding models */ envp [i++] = scratch; + scratch += sprintf (scratch, "TYPE=%d/%d/%d", + dev->descriptor.bDeviceClass, + dev->descriptor.bDeviceSubClass, + dev->descriptor.bDeviceProtocol) + 1; if (dev->descriptor.bDeviceClass == 0) { int alt = dev->actconfig->interface [0].act_altsetting; - /* simple/common case: one config, one interface, one driver - * unsimple cases: everything else + /* a simple/common case: one config, one interface, one driver + * with current altsetting being a reasonable setting. + * everything needs a smart agent and usbdevfs; or can rely on + * device-specific binding policies. */ + envp [i++] = scratch; scratch += sprintf (scratch, "INTERFACE=%d/%d/%d", dev->actconfig->interface [0].altsetting [alt].bInterfaceClass, dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass, dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol) + 1; /* INTERFACE-0, INTERFACE-1, ... ? */ - } else { - /* simple/common case: generic device, handled generically */ - scratch += sprintf (scratch, "TYPE=%d/%d/%d", - dev->descriptor.bDeviceClass, - dev->descriptor.bDeviceSubClass, - dev->descriptor.bDeviceProtocol) + 1; } envp [i++] = 0; /* assert: (scratch - buf) < sizeof buf */ @@ -753,7 +788,7 @@ static inline void call_policy (char *verb, struct usb_device *dev) { } -#endif /* KMOD && HOTPLUG */ +#endif /* KMOD */ /* @@ -916,6 +951,7 @@ static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) if (status) { // something went wrong usb_free_urb(urb); + current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); return status; } @@ -926,6 +962,7 @@ static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) } else status = 1; + current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); if (!status) { @@ -968,6 +1005,11 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, } /*-------------------------------------------------------------------*/ +/* usb_control_msg() - builds control urb, and waits for completion */ +/* Synchronous behavior - don't use this function from within an */ +/* interrupt context, (like a bottom half handler.) In this case, */ +/* use usb_submit_urb() directly instead. */ + int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) { @@ -993,8 +1035,10 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u } /*-------------------------------------------------------------------*/ -/* compatibility wrapper, builds bulk urb, and waits for completion */ -/* synchronous behavior */ +/* usb_bulk_msg() Builds a bulk urb, and waits for completion. */ +/* Synchronous behavior - don't use this function from within an */ +/* interrupt context, (like a bottom half handler.) In this case, */ +/* use usb_submit_urb() directly instead. */ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) @@ -2149,6 +2193,7 @@ EXPORT_SYMBOL(usb_inc_dev_use); EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_interface_claimed); EXPORT_SYMBOL(usb_driver_release_interface); +EXPORT_SYMBOL(usb_match_id); EXPORT_SYMBOL(usb_root_hub_string); EXPORT_SYMBOL(usb_new_device); diff --git a/drivers/usb/usbkbd.c b/drivers/usb/usbkbd.c index bbf4b59ac..7fd1ebd7c 100644 --- a/drivers/usb/usbkbd.c +++ b/drivers/usb/usbkbd.c @@ -59,6 +59,7 @@ static unsigned char usb_kbd_keycode[256] = { struct usb_kbd { struct input_dev dev; + struct usb_device *usbdev; unsigned char new[8]; unsigned char old[8]; struct urb irq, led; @@ -116,6 +117,7 @@ int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, i return 0; kbd->leds = kbd->newleds; + kbd->led.dev = kbd->usbdev; if (usb_submit_urb(&kbd->led)) err("usb_submit_urb(leds) failed"); @@ -133,6 +135,7 @@ static void usb_kbd_led(struct urb *urb) return; kbd->leds = kbd->newleds; + kbd->led.dev = kbd->usbdev; if (usb_submit_urb(&kbd->led)) err("usb_submit_urb(leds) failed"); } @@ -144,6 +147,7 @@ static int usb_kbd_open(struct input_dev *dev) if (kbd->open++) return 0; + kbd->irq.dev = kbd->usbdev; if (usb_submit_urb(&kbd->irq)) return -EIO; @@ -158,20 +162,19 @@ static void usb_kbd_close(struct input_dev *dev) usb_unlink_urb(&kbd->irq); } -static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) +static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { + struct usb_interface *iface; struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_kbd *kbd; int i, pipe, maxp; char *buf; - if (dev->descriptor.bNumConfigurations != 1) return NULL; - interface = dev->config[0].interface[ifnum].altsetting + 0; + iface = &dev->actconfig->interface[ifnum]; + interface = &iface->altsetting[iface->act_altsetting]; - if (interface->bInterfaceClass != 3) return NULL; - if (interface->bInterfaceSubClass != 1) return NULL; - if (interface->bInterfaceProtocol != 1) return NULL; if (interface->bNumEndpoints != 1) return NULL; endpoint = interface->endpoint + 0; @@ -187,6 +190,8 @@ static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL))) return NULL; memset(kbd, 0, sizeof(struct usb_kbd)); + kbd->usbdev = dev; + kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); @@ -251,10 +256,18 @@ static void usb_kbd_disconnect(struct usb_device *dev, void *ptr) kfree(kbd); } +static struct usb_device_id usb_kbd_id_table [] = { + { bInterfaceClass: 3, bInterfaceSubClass: 1, bInterfaceProtocol: 1}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); + static struct usb_driver usb_kbd_driver = { name: "keyboard", probe: usb_kbd_probe, - disconnect: usb_kbd_disconnect + disconnect: usb_kbd_disconnect, + id_table: usb_kbd_id_table, }; static int __init usb_kbd_init(void) diff --git a/drivers/usb/usbmouse.c b/drivers/usb/usbmouse.c index 1e873b285..b6473e11a 100644 --- a/drivers/usb/usbmouse.c +++ b/drivers/usb/usbmouse.c @@ -1,5 +1,5 @@ /* - * $Id: usbmouse.c,v 1.5 2000/05/29 09:01:52 vojtech Exp $ + * $Id: usbmouse.c,v 1.6 2000/08/14 21:05:26 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -41,9 +41,9 @@ MODULE_DESCRIPTION("USB HID Boot Protocol mouse driver"); struct usb_mouse { signed char data[8]; char name[128]; + struct usb_device *usbdev; struct input_dev dev; struct urb irq; - struct usb_device *my_usb_device; // for resubmitting my urb int open; }; @@ -73,7 +73,7 @@ static int usb_mouse_open(struct input_dev *dev) if (mouse->open++) return 0; - mouse->irq.dev = mouse->my_usb_device; + mouse->irq.dev = mouse->usbdev; if (usb_submit_urb(&mouse->irq)) return -EIO; @@ -88,20 +88,19 @@ static void usb_mouse_close(struct input_dev *dev) usb_unlink_urb(&mouse->irq); } -static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) +static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { + struct usb_interface *iface; struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_mouse *mouse; int pipe, maxp; char *buf; - if (dev->descriptor.bNumConfigurations != 1) return NULL; - interface = dev->config[0].interface[ifnum].altsetting + 0; + iface = &dev->actconfig->interface[ifnum]; + interface = &iface->altsetting[iface->act_altsetting]; - if (interface->bInterfaceClass != 3) return NULL; - if (interface->bInterfaceSubClass != 1) return NULL; - if (interface->bInterfaceProtocol != 2) return NULL; if (interface->bNumEndpoints != 1) return NULL; endpoint = interface->endpoint + 0; @@ -116,6 +115,8 @@ static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL; memset(mouse, 0, sizeof(struct usb_mouse)); + mouse->usbdev = dev; + mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); @@ -150,7 +151,6 @@ static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) kfree(buf); - mouse->my_usb_device = dev; FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp, usb_mouse_irq, mouse, endpoint->bInterval); @@ -170,10 +170,18 @@ static void usb_mouse_disconnect(struct usb_device *dev, void *ptr) kfree(mouse); } +static struct usb_device_id usb_mouse_id_table [] = { + { bInterfaceClass: 3, bInterfaceSubClass: 1, bInterfaceProtocol: 2}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); + static struct usb_driver usb_mouse_driver = { name: "usb_mouse", probe: usb_mouse_probe, disconnect: usb_mouse_disconnect, + id_table: usb_mouse_id_table, }; static int __init usb_mouse_init(void) diff --git a/drivers/usb/uss720.c b/drivers/usb/uss720.c index da61b5214..f97f5f639 100644 --- a/drivers/usb/uss720.c +++ b/drivers/usb/uss720.c @@ -32,6 +32,7 @@ * 0.3 10.08.99 fixing merge errors * 0.4 13.08.99 Added Vendor/Product ID of Brad Hard's cable * 0.5 20.09.99 usb_control_msg wrapper used + * Nov01.00 usb_device_table support by Adam J. Richter * */ @@ -534,7 +535,8 @@ static struct parport_operations parport_uss720_ops = /* --------------------------------------------------------------------- */ -static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum) +static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum, + const struct usb_device_id *id) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; @@ -542,11 +544,6 @@ static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum) struct parport *pp; int i; - if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) && - (usbdev->descriptor.idVendor != 0x0557 || usbdev->descriptor.idProduct != 0x2001) && - (usbdev->descriptor.idVendor != 0x0729 || usbdev->descriptor.idProduct != 0x1284)) - return NULL; - printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); @@ -626,11 +623,22 @@ static void uss720_disconnect(struct usb_device *usbdev, void *ptr) MOD_DEC_USE_COUNT; } +/* table of cables that work through this driver */ +static struct usb_device_id uss720_table [] = { + { idVendor: 0x047e, idProduct: 0x1001}, + { idVendor: 0x0557, idProduct: 0x2001}, + { idVendor: 0x0729, idProduct: 0x1284}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, uss720_table); + + static struct usb_driver uss720_driver = { - "uss720", - uss720_probe, - uss720_disconnect, - { NULL, NULL } + name: "uss720", + probe: uss720_probe, + disconnect: uss720_disconnect, + id_table: uss720_table, }; /* --------------------------------------------------------------------- */ diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c index 4dcc86436..af813ceab 100644 --- a/drivers/usb/wacom.c +++ b/drivers/usb/wacom.c @@ -1,10 +1,11 @@ /* - * $Id: wacom.c,v 1.9 2000/05/29 09:01:52 vojtech Exp $ + * $Id: wacom.c,v 1.11 2000/10/18 12:12:26 vojtech Exp $ * * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk> * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at> * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org> + * Copyright (c) 2000 James E. Blair <corvus@gnu.org> * * USB Wacom Graphire and Wacom Intuos tablet support * @@ -21,23 +22,25 @@ * v1.8 (vp) - Submit URB only when operating, moved to CVS, * use input_report_key instead of report_btn and * other cleanups + * v1.11 (vp) - Add URB ->dev setting for new kernels + * v1.11 (jb) - Add support for the 4D Mouse & Lens */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic @@ -70,17 +73,17 @@ MODULE_DESCRIPTION("USB Wacom Graphire and Wacom Intuos tablet driver"); * byte 5: Y high bits * byte 6: pen pressure low bits / mouse wheel * byte 7: pen presure high bits / mouse distance - * + * * There are also two single-byte feature reports (2 and 3). * * Wacom Intuos status packet: * * byte 0: report ID (2) - * byte 1: bit7 1 - sync bit + * byte 1: bit7 1 - sync bit * bit6 pointer in range * bit5 pointer type report * bit4 0 ? - * bit3 0 ? + * bit3 mouse packet type * bit2 pen button2 * bit1 pen button1 * bit0 0 ? @@ -88,12 +91,38 @@ MODULE_DESCRIPTION("USB Wacom Graphire and Wacom Intuos tablet driver"); * byte 3: X low bits * byte 4: Y high bits * byte 5: Y low bits + * + * Pen packet: + * * byte 6: bits 0-7: pressure (bits 2-9) * byte 7: bits 6-7: pressure (bits 0-1) * byte 7: bits 0-5: X tilt (bits 1-6) * byte 8: bit 7: X tilt (bit 0) * byte 8: bits 0-6: Y tilt (bits 0-6) * byte 9: bits 4-7: distance + * + * Mouse packet type 0: + * + * byte 6: bits 0-7: wheel (bits 2-9) + * byte 7: bits 6-7: wheel (bits 0-1) + * byte 7: bits 0-5: 0 + * byte 8: bits 6-7: 0 + * byte 8: bit 5: left extra button + * byte 8: bit 4: right extra button + * byte 8: bit 3: wheel (sign) + * byte 8: bit 2: right button + * byte 8: bit 1: middle button + * byte 8: bit 0: left button + * byte 9: bits 4-7: distance + * + * Mouse packet type 1: + * + * byte 6: bits 0-7: rotation (bits 2-9) + * byte 7: bits 6-7: rotation (bits 0-1) + * byte 7: bit 5: rotation (sign) + * byte 7: bits 0-4: 0 + * byte 8: bits 0-7: 0 + * byte 9: bits 4-7: distance */ #define USB_VENDOR_ID_WACOM 0x056a @@ -192,7 +221,7 @@ static void wacom_intuos_irq(struct urb *urb) case 0x0fa: wacom->tool = BTN_TOOL_RUBBER; break; /* Eraser */ case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH; break; /* Airbrush */ default: wacom->tool = BTN_TOOL_PEN; break; /* Unknown tool */ - } + } input_report_key(dev, wacom->tool, 1); return; } @@ -205,31 +234,63 @@ static void wacom_intuos_irq(struct urb *urb) input_report_abs(dev, ABS_X, ((__u32)data[2] << 8) | data[3]); input_report_abs(dev, ABS_Y, ((__u32)data[4] << 8) | data[5]); - input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); input_report_abs(dev, ABS_DISTANCE, data[9] >> 4); - input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); - input_report_key(dev, BTN_STYLUS, data[1] & 2); - input_report_key(dev, BTN_STYLUS2, data[1] & 4); - input_report_key(dev, BTN_TOUCH, t > 10); + switch (wacom->tool) { + + case BTN_TOOL_PENCIL: + case BTN_TOOL_PEN: + case BTN_TOOL_BRUSH: + case BTN_TOOL_RUBBER: + case BTN_TOOL_AIRBRUSH: + + input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(dev, BTN_STYLUS, data[1] & 2); + input_report_key(dev, BTN_STYLUS2, data[1] & 4); + input_report_key(dev, BTN_TOUCH, t > 10); + break; + + case BTN_TOOL_MOUSE: + case BTN_TOOL_LENS: + + if (data[1] & 0x02) { /* Rotation packet */ + input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? + ((__u32)data[6] << 2) | ((data[7] >> 6) & 3): + (-(((__u32)data[6] << 2) | ((data[7] >> 6) & 3))) - 1); + break; + } + + input_report_key(dev, BTN_LEFT, data[8] & 0x01); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x02); + input_report_key(dev, BTN_RIGHT, data[8] & 0x04); + input_report_key(dev, BTN_SIDE, data[8] & 0x20); + input_report_key(dev, BTN_EXTRA, data[8] & 0x10); + input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? + ((__u32)data[6] << 2) | ((data[7] >> 6) & 3) : + -((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + break; + } } #define WACOM_INTUOS_TOOLS (BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS)) +#define WACOM_INTUOS_BUTTONS (BIT(BTN_SIDE) | BIT(BTN_EXTRA)) +#define WACOM_INTUOS_ABS (BIT(ABS_TILT_X) | BIT(ABS_TILT_Y) | BIT(ABS_RZ) | BIT(ABS_THROTTLE)) struct wacom_features wacom_features[] = { { "Wacom Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq, - BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 }, + BIT(EV_REL), 0, 0, 0 }, { "Wacom Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq, - 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + 0, WACOM_INTUOS_ABS, 0, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS }, { "Wacom Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq, - 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + 0, WACOM_INTUOS_ABS, 0, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS }, { "Wacom Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq, - 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + 0, WACOM_INTUOS_ABS, 0, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS }, { "Wacom Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq, - 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + 0, WACOM_INTUOS_ABS, 0, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS }, { "Wacom Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq, - 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + 0, WACOM_INTUOS_ABS, 0, WACOM_INTUOS_BUTTONS, WACOM_INTUOS_TOOLS }, { NULL , 0 } }; @@ -275,7 +336,7 @@ static void *wacom_probe(struct usb_device *dev, unsigned int ifnum) wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | wacom->features->evbit; wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE) | wacom->features->absbit; wacom->dev.relbit[0] |= wacom->features->relbit; - wacom->dev.keybit[LONG(BTN_LEFT)] |= wacom->features->btnbit; + wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | wacom->features->btnbit; wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | wacom->features->digibit; @@ -286,6 +347,14 @@ static void *wacom_probe(struct usb_device *dev, unsigned int ifnum) wacom->dev.absmax[ABS_TILT_X] = 127; wacom->dev.absmax[ABS_TILT_Y] = 127; + wacom->dev.absmin[ABS_RZ] = -900; + wacom->dev.absmax[ABS_RZ] = 899; + wacom->dev.absmin[ABS_THROTTLE] = -1023; + wacom->dev.absmax[ABS_THROTTLE] = 1023; + + wacom->dev.absfuzz[ABS_X] = 4; + wacom->dev.absfuzz[ABS_Y] = 4; + wacom->dev.private = wacom; wacom->dev.open = wacom_open; wacom->dev.close = wacom_close; |