summaryrefslogtreecommitdiffstats
path: root/drivers/usb/usb-serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usb-serial.c')
-rw-r--r--drivers/usb/usb-serial.c315
1 files changed, 245 insertions, 70 deletions
diff --git a/drivers/usb/usb-serial.c b/drivers/usb/usb-serial.c
index 8826bc522..9f37fcc9e 100644
--- a/drivers/usb/usb-serial.c
+++ b/drivers/usb/usb-serial.c
@@ -14,6 +14,24 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (01/25/2000) gkh
+ * Added initial framework for FTDI serial converter so that Bill Ryder
+ * has a place to put his code.
+ * Added the vendor specific info from Handspring. Now we can print out
+ * informational debug messages as well as understand what is happening.
+ *
+ * (01/23/2000) gkh
+ * Fixed problem of crash when trying to open a port that didn't have a
+ * device assigned to it. Made the minor node finding a little smarter,
+ * now it looks to find a continous space for the new device.
+ *
+ * (01/21/2000) gkh
+ * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
+ * Fixed get_serial_by_minor which was all messed up for multi port
+ * devices. Fixed multi port problem for generic devices. Now the number
+ * of ports is determined by the number of bulk out endpoints for the
+ * generic device.
+ *
* (01/19/2000) gkh
* Removed lots of cruft that was around from the old (pre urb) driver
* interface.
@@ -141,15 +159,16 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
/* USB Serial devices vendor ids and device ids that this driver supports */
#define BELKIN_VENDOR_ID 0x056c
-#define BELKIN_SERIAL_CONVERTER 0x8007
+#define BELKIN_SERIAL_CONVERTER_ID 0x8007
#define PERACOM_VENDOR_ID 0x0565
-#define PERACOM_SERIAL_CONVERTER 0x0001
+#define PERACOM_SERIAL_CONVERTER_ID 0x0001
#define CONNECT_TECH_VENDOR_ID 0x0710
#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
#define CONNECT_TECH_WHITE_HEAT_ID 0x8001
#define HANDSPRING_VENDOR_ID 0x082d
#define HANDSPRING_VISOR_ID 0x0100
-
+#define FTDI_VENDOR_ID 0x0403
+#define FTDI_SERIAL_CONVERTER_ID 0x8372
#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
#define SERIAL_TTY_MINORS 16 /* Actually we are allowed 255, but this is good for now */
@@ -267,7 +286,7 @@ static void etek_serial_close (struct tty_struct *tty, struct file *filp);
#ifdef CONFIG_USB_SERIAL_BELKIN
/* All of the device info needed for the Belkin Serial Converter */
static __u16 belkin_vendor_id = BELKIN_VENDOR_ID;
-static __u16 belkin_product_id = BELKIN_SERIAL_CONVERTER;
+static __u16 belkin_product_id = BELKIN_SERIAL_CONVERTER_ID;
static struct usb_serial_device_type belkin_device = {
name: "Belkin",
idVendor: &belkin_vendor_id, /* the Belkin vendor id */
@@ -291,7 +310,7 @@ static struct usb_serial_device_type belkin_device = {
#ifdef CONFIG_USB_SERIAL_PERACOM
/* All of the device info needed for the Peracom Serial Converter */
static __u16 peracom_vendor_id = PERACOM_VENDOR_ID;
-static __u16 peracom_product_id = PERACOM_SERIAL_CONVERTER;
+static __u16 peracom_product_id = PERACOM_SERIAL_CONVERTER_ID;
static struct usb_serial_device_type peracom_device = {
name: "Peracom",
idVendor: &peracom_vendor_id, /* the Peracom vendor id */
@@ -359,6 +378,58 @@ static struct usb_serial_device_type whiteheat_device = {
#ifdef CONFIG_USB_SERIAL_VISOR
+
+/****************************************************************************
+ * Handspring Visor Vendor specific request codes (bRequest values)
+ * A big thank you to Handspring for providing the following information.
+ * If anyone wants the original file where these values and structures came
+ * from, send email to <greg@kroah.com>.
+ ****************************************************************************/
+
+/****************************************************************************
+ * VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ ****************************************************************************/
+#define VISOR_REQUEST_BYTES_AVAILABLE 0x01
+
+/****************************************************************************
+ * VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ ****************************************************************************/
+#define VISOR_CLOSE_NOTIFICATION 0x02
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ ****************************************************************************/
+#define VISOR_GET_CONNECTION_INFORMATION 0x03
+
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ ****************************************************************************/
+struct visor_connection_info {
+ __u16 num_ports;
+ struct {
+ __u8 port_function_id;
+ __u8 port;
+ } connections[2];
+};
+
+
+/* struct visor_connection_info.connection[x].port defines: */
+#define VISOR_ENDPOINT_1 0x01
+#define VISOR_ENDPOINT_2 0x02
+
+/* struct visor_connection_info.connection[x].port_function_id defines: */
+#define VISOR_FUNCTION_GENERIC 0x00
+#define VISOR_FUNCTION_DEBUGGER 0x01
+#define VISOR_FUNCTION_HOTSYNC 0x02
+#define VISOR_FUNCTION_CONSOLE 0x03
+#define VISOR_FUNCTION_REMOTE_FILE_SYS 0x04
+
+
/* function prototypes for a handspring visor */
static int visor_serial_open (struct tty_struct *tty, struct file *filp);
static void visor_serial_close (struct tty_struct *tty, struct file *filp);
@@ -391,6 +462,34 @@ static struct usb_serial_device_type handspring_device = {
};
#endif
+
+#ifdef CONFIG_USB_SERIAL_FTDI
+/* function prototypes for a FTDI serial converter */
+static int ftdi_serial_open (struct tty_struct *tty, struct file *filp);
+static void ftdi_serial_close (struct tty_struct *tty, struct file *filp);
+
+/* All of the device info needed for the Handspring Visor */
+static __u16 ftdi_vendor_id = FTDI_VENDOR_ID;
+static __u16 ftdi_product_id = FTDI_SERIAL_CONVERTER_ID;
+static struct usb_serial_device_type ftdi_device = {
+ name: "FTDI",
+ idVendor: &ftdi_vendor_id, /* the FTDI vendor ID */
+ idProduct: &ftdi_product_id, /* the FTDI product id */
+ 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 */
+ num_interrupt_in: 0,
+ num_bulk_in: 1,
+ num_bulk_out: 1,
+ num_ports: 1,
+ open: ftdi_serial_open,
+ close: ftdi_serial_close,
+ write: generic_serial_write,
+ write_room: generic_write_room,
+ chars_in_buffer: generic_chars_in_buffer
+};
+#endif
+
/* To add support for another serial converter, create a usb_serial_device_type
structure for that device, and add it to this list, making sure that the last
entry is NULL. */
@@ -411,6 +510,9 @@ static struct usb_serial_device_type *usb_serial_devices[] = {
#ifdef CONFIG_USB_SERIAL_VISOR
&handspring_device,
#endif
+#ifdef CONFIG_USB_SERIAL_FTDI
+ &ftdi_device,
+#endif
NULL
};
@@ -438,20 +540,27 @@ static struct usb_serial *get_serial_by_minor (int minor)
dbg("get_serial_by_minor %d", minor);
- for (i = 0; i < SERIAL_TTY_MINORS; ++i)
- if (serial_table[i])
- if (serial_table[i] != SERIAL_PTR_EMPTY)
- if (serial_table[i]->minor == minor)
- return (serial_table[i]);
+ if (serial_table[minor] == NULL)
+ return (NULL);
- return (NULL);
+ if (serial_table[minor] != SERIAL_PTR_EMPTY)
+ return (serial_table[minor]);
+
+ i = minor;
+ while (serial_table[i] == SERIAL_PTR_EMPTY) {
+ if (i == 0)
+ return (NULL);
+ --i;
+ }
+ return (serial_table[i]);
}
static struct usb_serial *get_free_serial (int num_ports, int *minor)
{
struct usb_serial *serial = NULL;
- int i;
+ int i, j;
+ int good_spot;
dbg("get_free_serial %d", num_ports);
@@ -459,6 +568,14 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor)
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
if (serial_table[i])
continue;
+
+ good_spot = 1;
+ for (j = 0; j < num_ports-1; ++j)
+ if (serial_table[i+j])
+ good_spot = 0;
+ if (good_spot == 0)
+ continue;
+
if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) {
err("Out of memory");
return NULL;
@@ -467,7 +584,7 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor)
serial_table[i] = serial;
*minor = i;
dbg("minor base = %d", *minor);
- for (i = *minor+1; (i < num_ports) && (i < SERIAL_TTY_MINORS); ++i)
+ for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
serial_table[i] = SERIAL_PTR_EMPTY;
return (serial);
}
@@ -538,6 +655,9 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
dbg("serial_open");
+ /* initialize the pointer incase something fails */
+ tty->driver_data = NULL;
+
/* get the serial object associated with this tty pointer */
serial = get_serial_by_minor (MINOR(tty->device));
@@ -567,15 +687,20 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
static void serial_close(struct tty_struct *tty, struct file * filp)
{
struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
+ int port;
+
+ dbg("serial_close");
- dbg("serial_close port %d", port);
-
- /* do some sanity checking that we really have a device present */
if (!serial) {
dbg("serial == NULL!");
return;
}
+
+ port = MINOR(tty->device) - serial->minor;
+
+ dbg("serial_close port %d", port);
+
+ /* do some sanity checking that we really have a device present */
if (!serial->type) {
dbg("serial->type == NULL!");
return;
@@ -997,9 +1122,18 @@ static void visor_serial_close(struct tty_struct *tty, struct file * filp)
{
struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
int port = MINOR(tty->device) - serial->minor;
+ unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL);
dbg("visor_serial_close port %d", port);
+ if (!transfer_buffer) {
+ err("visor_serial_close: kmalloc(%d) failed.\n", 0x12);
+ } else {
+ /* send a shutdown message to the device */
+ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION,
+ 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+ }
+
/* shutdown our bulk reads and writes */
usb_unlink_urb (&serial->write_urb[port]);
usb_unlink_urb (&serial->read_urb[port]);
@@ -1034,51 +1168,10 @@ static void visor_unthrottle (struct tty_struct * tty)
}
-/*
- Here's the raw dump of the vendor specific command data that the Visor sends on Win98
-______________________________________________________________________
-SETUP(0xB4) ADDR(0x02) ENDP(0x0) CRC5(0x15)
-______________________________________________________________________
-DATA0(0xC3) DATA(C2 03 00 00 00 00 12 00 ) CRC16(0xB0BB)
-______________________________________________________________________
-ACK(0x4B)
-______________________________________________________________________
-IN(0x96) ADDR(0x02) ENDP(0x0) CRC5(0x15)
-______________________________________________________________________
-DATA1(0xD2) DATA(02 00 00 01 02 02 ) CRC16(0xF4E6)
-______________________________________________________________________
-ACK(0x4B)
-______________________________________________________________________
-OUT(0x87) ADDR(0x02) ENDP(0x0) CRC5(0x15)
-______________________________________________________________________
-DATA1(0xD2) DATA() CRC16(0x0000)
-______________________________________________________________________
-ACK(0x4B)
-______________________________________________________________________
-SETUP(0xB4) ADDR(0x02) ENDP(0x0) CRC5(0x15)
-______________________________________________________________________
-DATA0(0xC3) DATA(C2 01 00 00 05 00 02 00 ) CRC16(0xC488)
-______________________________________________________________________
-ACK(0x4B)
-______________________________________________________________________
-IN(0x96) ADDR(0x02) ENDP(0x0) CRC5(0x15)
-______________________________________________________________________
-DATA1(0xD2) DATA(01 00 ) CRC16(0xFFFB)
-______________________________________________________________________
-ACK(0x4B)
-______________________________________________________________________
-OUT(0x87) ADDR(0x02) ENDP(0x0) CRC5(0x15)
-______________________________________________________________________
-DATA1(0xD2) DATA() CRC16(0x0000)
-______________________________________________________________________
-ACK(0x4B)
-______________________________________________________________________
-*/
-
static int visor_startup (struct usb_serial *serial)
{
- /* send out two unknown commands that I found by looking at a Win98 trace */
int response;
+ int i;
unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL);
if (!transfer_buffer) {
@@ -1091,18 +1184,44 @@ static int visor_startup (struct usb_serial *serial)
dbg("visor_setup: Set config to 1");
usb_set_configuration (serial->dev, 1);
- response = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x03, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+ /* send a get connection info request */
+ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION,
+ 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
if (response < 0) {
- err("visor_startup: error getting first vendor specific message");
+ err("visor_startup: error getting connection information");
} else {
- dbg("visor_startup: First vendor specific message successful");
+#ifdef DEBUG
+ struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer;
+ char *string;
+ dbg("%s: Number of ports: %d", serial->type->name, connection_info->num_ports);
+ for (i = 0; i < connection_info->num_ports; ++i) {
+ switch (connection_info->connections[i].port_function_id) {
+ case VISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case VISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case VISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case VISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ dbg("%s: port %d, is for %s", serial->type->name, connection_info->connections[i].port, string);
+ }
+#endif
}
- response = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x01, 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
+ /* ask for the number of bytes available, but ignore the response as it is broken */
+ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE,
+ 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
if (response < 0) {
- err("visor_startup: error getting second vendor specific message");
- } else {
- dbg("visor_startup: Second vendor specific message successful");
+ err("visor_startup: error getting bytes available request");
}
kfree (transfer_buffer);
@@ -1115,6 +1234,54 @@ static int visor_startup (struct usb_serial *serial)
#endif /* CONFIG_USB_SERIAL_VISOR*/
+#ifdef CONFIG_USB_SERIAL_FTDI
+/******************************************************************************
+ * FTDI Serial Converter specific driver functions
+ ******************************************************************************/
+static int ftdi_serial_open (struct tty_struct *tty, struct file *filp)
+{
+ struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+ int port = MINOR(tty->device) - serial->minor;
+
+ dbg("ftdi_serial_open port %d", port);
+
+ if (serial->active[port]) {
+ dbg ("device already open");
+ return -EINVAL;
+ }
+ serial->active[port] = 1;
+
+ /*Start reading from the device*/
+ if (usb_submit_urb(&serial->read_urb[port]))
+ dbg("usb_submit_urb(read bulk) failed");
+
+ /* Need to do device specific setup here (control lines, baud rate, etc.) */
+ /* FIXME!!! */
+
+ return (0);
+}
+
+
+static void ftdi_serial_close (struct tty_struct *tty, struct file *filp)
+{
+ struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+ int port = MINOR(tty->device) - serial->minor;
+
+ dbg("ftdi_serial_close port %d", port);
+
+ /* Need to change the control lines here */
+ /* FIXME */
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (&serial->write_urb[port]);
+ usb_unlink_urb (&serial->read_urb[port]);
+ serial->active[port] = 0;
+}
+
+
+#endif
+
+
/*****************************************************************************
* generic devices specific driver functions
*****************************************************************************/
@@ -1260,6 +1427,7 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
int num_interrupt_in = 0;
int num_bulk_in = 0;
int num_bulk_out = 0;
+ int num_ports;
/* loop through our list of known serial converters, and see if this device matches */
device_num = 0;
@@ -1317,7 +1485,14 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
/* found all that we need */
info("%s converter detected", type->name);
- serial = get_free_serial (type->num_ports, &minor);
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ if (type == &generic_device)
+ num_ports = num_bulk_out;
+ else
+#endif
+ num_ports = type->num_ports;
+
+ serial = get_free_serial (num_ports, &minor);
if (serial == NULL) {
err("No more free serial devices");
return NULL;
@@ -1326,7 +1501,7 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
serial->dev = dev;
serial->type = type;
serial->minor = minor;
- serial->num_ports = type->num_ports;
+ serial->num_ports = num_ports;
serial->num_bulk_in = num_bulk_in;
serial->num_bulk_out = num_bulk_out;
serial->num_interrupt_in = num_interrupt_in;
@@ -1350,7 +1525,7 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
}
for (i = 0; i < num_bulk_out; ++i) {
- serial->bulk_out_size[i] = bulk_out_endpoint[i]->wMaxPacketSize;
+ serial->bulk_out_size[i] = bulk_out_endpoint[i]->wMaxPacketSize * 2;
serial->bulk_out_buffer[i] = kmalloc (serial->bulk_out_size[i], GFP_KERNEL);
if (!serial->bulk_out_buffer[i]) {
err("Couldn't allocate bulk_out_buffer");
@@ -1417,6 +1592,7 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
usb_unlink_urb (&serial->write_urb[i]);
usb_unlink_urb (&serial->read_urb[i]);
serial->active[i] = 0;
+ serial_table[serial->minor + i] = NULL;
}
/* free up any memory that we allocated */
@@ -1434,7 +1610,6 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->minor + i);
}
- serial_table[serial->minor] = NULL;
kfree (serial);
} else {