summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
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/serial
parenta9d7bff9a84dba79609a0002e5321b74c4d64c64 (diff)
Merge with 2.4.0-test11.
Diffstat (limited to 'drivers/usb/serial')
-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
13 files changed, 1125 insertions, 278 deletions
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 */