summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-11-28 03:58:46 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-11-28 03:58:46 +0000
commitb63ad0882a16a5d28003e57f2b0b81dee3fb322b (patch)
tree0a343ce219e2b8b38a5d702d66032c57b83d9720 /drivers/usb
parenta9d7bff9a84dba79609a0002e5321b74c4d64c64 (diff)
Merge with 2.4.0-test11.
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Config.in3
-rw-r--r--drivers/usb/acm.c164
-rw-r--r--drivers/usb/audio.c40
-rw-r--r--drivers/usb/bluetooth.c28
-rw-r--r--drivers/usb/dabusb.c28
-rw-r--r--drivers/usb/dc2xx.c18
-rw-r--r--drivers/usb/devio.c25
-rw-r--r--drivers/usb/dsbr100.c39
-rw-r--r--drivers/usb/hid.c16
-rw-r--r--drivers/usb/hub.c41
-rw-r--r--drivers/usb/ibmcam.c69
-rw-r--r--drivers/usb/mdc800.c26
-rw-r--r--drivers/usb/microtek.c90
-rw-r--r--drivers/usb/net1080.c18
-rw-r--r--drivers/usb/ov511.c380
-rw-r--r--drivers/usb/pegasus.c306
-rw-r--r--drivers/usb/pegasus.h198
-rw-r--r--drivers/usb/plusb.c794
-rw-r--r--drivers/usb/plusb.h48
-rw-r--r--drivers/usb/printer.c30
-rw-r--r--drivers/usb/rio500.c34
-rw-r--r--drivers/usb/scanner.c95
-rw-r--r--drivers/usb/scanner.h75
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/belkin_sa.c576
-rw-r--r--drivers/usb/serial/belkin_sa.h113
-rw-r--r--drivers/usb/serial/digi_acceleport.c75
-rw-r--r--drivers/usb/serial/ftdi_sio.c368
-rw-r--r--drivers/usb/serial/keyspan.c3
-rw-r--r--drivers/usb/serial/keyspan.h121
-rw-r--r--drivers/usb/serial/keyspan_pda.c28
-rw-r--r--drivers/usb/serial/omninet.c16
-rw-r--r--drivers/usb/serial/usb-serial.h7
-rw-r--r--drivers/usb/serial/usbserial.c38
-rw-r--r--drivers/usb/serial/visor.c18
-rw-r--r--drivers/usb/serial/whiteheat.c39
-rw-r--r--drivers/usb/storage/scsiglue.c30
-rw-r--r--drivers/usb/storage/transport.c11
-rw-r--r--drivers/usb/storage/usb.c15
-rw-r--r--drivers/usb/usb-ohci.c2
-rw-r--r--drivers/usb/usb.c325
-rw-r--r--drivers/usb/usbkbd.c27
-rw-r--r--drivers/usb/usbmouse.c28
-rw-r--r--drivers/usb/uss720.c28
-rw-r--r--drivers/usb/wacom.c113
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(&current->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;