patch-2.3.41 linux/drivers/usb/usb-serial.c

Next file: linux/drivers/usb/usb-uhci.c
Previous file: linux/drivers/usb/usb-ohci.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.40/linux/drivers/usb/usb-serial.c linux/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 @@
 
 /* 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 @@
 #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 @@
 #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 @@
 
 
 #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 @@
 };
 #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 @@
 #ifdef CONFIG_USB_SERIAL_VISOR
 	&handspring_device,
 #endif
+#ifdef CONFIG_USB_SERIAL_FTDI
+	&ftdi_device,
+#endif
 	NULL
 };
 
@@ -438,20 +540,27 @@
 
 	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 @@
 	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 @@
 		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 @@
 	
 	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 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 @@
 {
 	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 @@
 }
 
 
-/*
- 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 @@
 	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 @@
 #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 @@
 	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 @@
 				/* 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 @@
 			       	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 @@
 				}
 
 				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 @@
 			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 @@
 			info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->minor + i);
 		}
 
-		serial_table[serial->minor] = NULL;
 		kfree (serial);
 
 	} else {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)