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

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

diff -u --recursive --new-file v2.3.26/linux/drivers/usb/usb-serial.c linux/drivers/usb/usb-serial.c
@@ -1,32 +1,41 @@
 /*
  * USB Serial Converter driver
  *
- * Greg Kroah-Hartman (greg@kroah.com)
+ *	(C) Copyright (C) 1999
+ *	    Greg Kroah-Hartman (greg@kroah.com)
  *
- * This was based on the ACM driver by Armin Fuerst (which was based
- * on a driver by Brad Keryan)
+ *	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.
  *
- * Currently only works for the Belkin and Peracom Serial converters.
- * Should also work on the Etek serial converter, if anyone knows the
- * vendor and device ids for that device.
+ * This driver was originally based on the ACM driver by Armin Fuerst (which was 
+ * based on a driver by Brad Keryan)
  *
+ * See README.serial for more information on using this driver.
  * 
+ * version 0.2.0 (11/10/99) gkh
+ *	Split up internals to make it easier to add different types of serial 
+ *	converters to the code.
+ *	Added a "generic" driver that gets it's vendor and product id
+ *	from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
+ *	for the idea and sample code (from the usb scanner driver.)
+ *	Cleared up any licensing questions by releasing it under the GNU GPL.
+ *
  * version 0.1.2 (10/25/99) gkh
- *  Fixed bug in detecting device.
+ * 	Fixed bug in detecting device.
  *
  * version 0.1.1 (10/05/99) gkh
- *  Changed the major number to not conflict with anything else.
+ * 	Changed the major number to not conflict with anything else.
  *
  * version 0.1 (09/28/99) gkh
- *  Can recognize the two different devices and start up a read from
- * device when asked to. Writes also work. No control signals yet, this
- * all is vendor specific data (i.e. no spec), also no control for
- * different baud rates or other bit settings.
- * Currently we are using the same devid as the acm driver. This needs
- * to change.
+ * 	Can recognize the two different devices and start up a read from
+ *	device when asked to. Writes also work. No control signals yet, this
+ *	all is vendor specific data (i.e. no spec), also no control for
+ *	different baud rates or other bit settings.
+ *	Currently we are using the same devid as the acm driver. This needs
+ *	to change.
  * 
- * (C) Copyright 1999 Greg Kroah-Hartman (greg@kroah.com)
- *
  */
 
 #include <linux/kernel.h>
@@ -43,8 +52,8 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 
-
 #include "usb.h"
+
 /*#define SERIAL_DEBUG 1*/
 
 #ifdef SERIAL_DEBUG
@@ -54,6 +63,19 @@
 #endif
 
 
+/* Module information */
+MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/");
+MODULE_DESCRIPTION("USB Serial Driver");
+
+static __u16	vendor	= 0;
+static __u16	product	= 0;
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+
 /* USB Serial devices vendor ids and device ids that this driver supports */
 #define BELKIN_VENDOR_ID		0x056c
 #define BELKIN_SERIAL_CONVERTER		0x8007
@@ -70,41 +92,171 @@
 static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);
 static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
 
-typedef enum {
-	unknown = 0,
-	Belkin = 1,
-	Peracom = 2
-	} SERIAL_TYPE;
+
+#define MUST_HAVE_NOT	0x01
+#define MUST_HAVE	0x02
+#define DONT_CARE	0x03
+
+#define	HAS		0x02
+#define HAS_NOT		0x01
+
+
+/* local function prototypes */
+static int serial_open (struct tty_struct *tty, struct file * filp);
+static void serial_close (struct tty_struct *tty, struct file * filp);
+static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static void serial_put_char (struct tty_struct *tty, unsigned char ch);
+static int serial_write_room (struct tty_struct *tty);
+static int serial_chars_in_buffer (struct tty_struct *tty);
+static void serial_throttle (struct tty_struct * tty);
+static void serial_unthrottle (struct tty_struct * tty);
+
+
+/* function prototypes for the eTek type converters (this included Belkin and Peracom) */
+static int etek_serial_open (struct tty_struct *tty, struct file * filp);
+static void etek_serial_close (struct tty_struct *tty, struct file * filp);
+static int etek_serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static void etek_serial_put_char (struct tty_struct *tty, unsigned char ch);
+static int etek_write_room (struct tty_struct *tty);
+static int etek_chars_in_buffer (struct tty_struct *tty);
+static void etek_throttle (struct tty_struct * tty);
+static void etek_unthrottle (struct tty_struct * tty);
+
+
+/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
+static int generic_serial_open (struct tty_struct *tty, struct file * filp);
+static void generic_serial_close (struct tty_struct *tty, struct file * filp);
+static int generic_serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static void generic_serial_put_char (struct tty_struct *tty, unsigned char ch);
+static int generic_write_room (struct tty_struct *tty);
+static int generic_chars_in_buffer (struct tty_struct *tty);
+
+
+/* This structure defines the individual serial converter. */
+struct usb_serial_device_type {
+	char	*name;
+	__u16	*idVendor;
+	__u16	*idProduct;
+	char	needs_interrupt_in;
+	char	needs_bulk_in;
+	char	needs_bulk_out;
+	// add function calls
+
+	int  (*open)(struct tty_struct * tty, struct file * filp);
+	void (*close)(struct tty_struct * tty, struct file * filp);
+	int  (*write)(struct tty_struct * tty, int from_user,const unsigned char *buf, int count);
+	void (*put_char)(struct tty_struct *tty, unsigned char ch);
+	int  (*write_room)(struct tty_struct *tty);
+	int  (*chars_in_buffer)(struct tty_struct *tty);
+	void (*throttle)(struct tty_struct * tty);
+	void (*unthrottle)(struct tty_struct * tty);
+
+};
+
+
+/* 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 struct usb_serial_device_type belkin_device = {
+	"Belkin",
+	&belkin_vendor_id,	/* the Belkin vendor id */
+	&belkin_product_id,	/* the Belkin serial converter product id */
+	MUST_HAVE,		/* this device must have an interrupt in endpoint */
+	MUST_HAVE,		/* this device must have a bulk in endpoint */
+	MUST_HAVE,		/* this device must have a bulk out endpoint */
+	etek_serial_open,
+	etek_serial_close,
+	etek_serial_write,
+	etek_serial_put_char,
+	etek_write_room,
+	etek_chars_in_buffer,
+	etek_throttle,
+	etek_unthrottle
+};
+
+/* 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 struct usb_serial_device_type peracom_device = {
+	"Peracom",
+	&peracom_vendor_id,	/* the Peracom vendor id */
+	&peracom_product_id,	/* the Peracom serial converter product id */
+	MUST_HAVE,		/* this device must have an interrupt in endpoint */
+	MUST_HAVE,		/* this device must have a bulk in endpoint */
+	MUST_HAVE,		/* this device must have a bulk out endpoint */
+	etek_serial_open,
+	etek_serial_close,
+	etek_serial_write,
+	etek_serial_put_char,
+	etek_write_room,
+	etek_chars_in_buffer,
+	etek_throttle,
+	etek_unthrottle
+};
+
+/* All of the device info needed for the Generic Serial Converter */
+static struct usb_serial_device_type generic_device = {
+	"Generic",
+	&vendor,		/* use the user specified vendor id */
+	&product,		/* use the user specified product id */
+	DONT_CARE,		/* don't have to have an interrupt in endpoint */
+	DONT_CARE,		/* don't have to have a bulk in endpoint */
+	DONT_CARE,		/* don't have to have a bulk out endpoint */
+	generic_serial_open,
+	generic_serial_close,
+	generic_serial_write,
+	generic_serial_put_char,
+	generic_write_room,
+	generic_chars_in_buffer,
+	NULL,			/* generic driver does not implement any flow control */
+	NULL			/* generic driver does not implement any flow control */
+};
+
+
+/* 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. */
+static struct usb_serial_device_type *usb_serial_devices[] = {
+	&generic_device,
+	&belkin_device,
+	&peracom_device,
+	NULL
+};
+
+
 
 struct usb_serial_state {
-	struct usb_device *	dev;
-	SERIAL_TYPE		type;		/* what manufacturer's type of converter */
-	void *			irq_handle;
-	unsigned int		irqpipe;
-	struct tty_struct	*tty;		/* the coresponding tty for this device */
-	char			present;
-	char			active;
+	struct usb_device *		dev;
+	struct usb_serial_device_type *	type;
+	void *				irq_handle;
+	unsigned int			irqpipe;
+	struct tty_struct *		tty;		/* the coresponding tty for this device */
+	char				present;
+	char				active;
 
-	char			interrupt_in_inuse;
+	char			has_interrupt_in;	/* if this device has an interrupt in pipe or not */
+	char			interrupt_in_inuse;	/* if the interrupt in endpoint is in use */
 	__u8			interrupt_in_endpoint;
 	__u8			interrupt_in_interval;
-	__u16			interrupt_in_size;
+	__u16			interrupt_in_size;	/* the size of the interrupt in endpoint */
 	unsigned int		interrupt_in_pipe;
 	unsigned char *		interrupt_in_buffer;
 	void *			interrupt_in_transfer;
 
-	char			bulk_in_inuse;
+	char			has_bulk_in;		/* if thie device has a bulk in pipe or not */
+	char			bulk_in_inuse;		/* if the bulk in endpoint is in use */
 	__u8			bulk_in_endpoint;
 	__u8			bulk_in_interval;
-	__u16			bulk_in_size;
+	__u16			bulk_in_size;		/* the size of the bulk in endpoint */
 	unsigned int		bulk_in_pipe;
 	unsigned char *		bulk_in_buffer;
 	void *			bulk_in_transfer;
 
-	char			bulk_out_inuse;
+	char			has_bulk_out;		/* if this device has a bulk out pipe or not */
+	char			bulk_out_inuse;		/* if the bulk out endpoint is in use */
 	__u8			bulk_out_endpoint;
 	__u8			bulk_out_interval;
-	__u16			bulk_out_size;
+	__u16			bulk_out_size;		/* the size of the bulk out endpoint */
 	unsigned int		bulk_out_pipe;
 	unsigned char *		bulk_out_buffer;
 	void *			bulk_out_transfer;
@@ -197,36 +349,25 @@
 
 
 
-
-/* tty interface functions */
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
 static int serial_open (struct tty_struct *tty, struct file * filp)
 {
 	struct usb_serial_state *serial;
 	
 	debug_info("USB: serial_open\n");
 
+	/* assign a serial object to the tty pointer */
 	serial = &serial_state_table [MINOR(tty->device)-tty->driver.minor_start];
 	tty->driver_data = serial;
 	serial->tty = tty;
 	 
-	if (!serial->present) {
-		debug_info("USB Serial: no device registered\n");
-		return -EINVAL;
+	/* pass on to the driver specific version of this function */
+	if (serial->type->open) {
+		return (serial->type->open(tty, filp));
 	}
-
-	if (serial->active) {
-		debug_info ("USB Serial: device already open\n");
-		return -EINVAL;
-	}
-	serial->active = 1;
- 
-	/*Start reading from the device*/
-	serial->bulk_in_inuse = 1;
-	serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial);
-
-	/* Need to do device specific setup here (control lines, baud rate, etc.) */
-	/* FIXME!!! */
-				                  
+		
 	return (0);
 }
 
@@ -246,28 +387,16 @@
 		return;
 	}
 
-	/* Need to change the control lines here */
-	/* FIXME */
-	
-	if (serial->bulk_out_inuse){
-		usb_terminate_bulk (serial->dev, serial->bulk_out_transfer);
-		serial->bulk_out_inuse = 0;
-	}
-	if (serial->bulk_in_inuse){
-		usb_terminate_bulk (serial->dev, serial->bulk_in_transfer);
-		serial->bulk_in_inuse = 0;
+	/* pass on to the driver specific version of this function */
+	if (serial->type->close) {
+		serial->type->close(tty, filp);
 	}
-
-	/* release the irq? */
-	
-	serial->active = 0;
-}
+}	
 
 
 static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
 {
 	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
-	int written;
 	
 	debug_info("USB Serial: serial_write\n");
 
@@ -281,26 +410,14 @@
 		return (-EINVAL);
 	}
 	
-	if (serial->bulk_out_inuse) {
-		debug_info ("USB Serial: already writing\n");
-		return (0);
+	/* pass on to the driver specific version of this function */
+	if (serial->type->write) {
+		return (serial->type->write(tty, from_user, buf, count));
 	}
 
-	written = (count > serial->bulk_out_size) ? serial->bulk_out_size : count;
-	  
-	if (from_user) {
-		copy_from_user(serial->bulk_out_buffer, buf, written);
-	}
-	else {
-		memcpy (serial->bulk_out_buffer, buf, written);
-	}  
-
-	/* send the data out the bulk port */
-	serial->bulk_out_inuse = 1;
-	serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, written, serial);
-
-	return (written);
-} 
+	/* no specific driver, so return that we didn't write anything */
+	return (0);
+}
 
 
 static void serial_put_char (struct tty_struct *tty, unsigned char ch)
@@ -319,18 +436,13 @@
 		return;
 	}
 
-	if (serial->bulk_out_inuse) {
-		debug_info ("USB Serial: already writing\n");
-		return;
+	/* pass on to the driver specific version of this function */
+	if (serial->type->put_char) {
+		serial->type->put_char(tty, ch);
 	}
 
-	/* send the single character out the bulk port */
-	serial->bulk_out_buffer[0] = ch;
-	serial->bulk_out_inuse = 1;
-	serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, 1, serial);
-
 	return;
-}                   
+}	
 
 
 static int serial_write_room (struct tty_struct *tty) 
@@ -349,11 +461,12 @@
 		return (-EINVAL);
 	}
 	
-	if (serial->bulk_out_inuse) {
-		return (0);
+	/* pass on to the driver specific version of this function */
+	if (serial->type->write_room) {
+		return (serial->type->write_room(tty));
 	}
 
-	return serial->bulk_out_size;
+	return (0);
 }
 
 
@@ -373,8 +486,9 @@
 		return (-EINVAL);
 	}
 	
-	if (serial->bulk_out_inuse) {
-		return (serial->bulk_out_size);
+	/* pass on to the driver specific version of this function */
+	if (serial->type->chars_in_buffer) {
+		return (serial->type->chars_in_buffer(tty));
 	}
 
 	return (0);
@@ -397,9 +511,10 @@
 		return;
 	}
 
-
-	/* Change the control signals */
-	/* FIXME!!! */
+	/* pass on to the driver specific version of this function */
+	if (serial->type->throttle) {
+		serial->type->throttle(tty);
+	}
 
 	return;
 }
@@ -422,6 +537,153 @@
 	}
 
 
+	/* pass on to the driver specific version of this function */
+	if (serial->type->unthrottle) {
+		serial->type->unthrottle(tty);
+	}
+
+	return;
+}
+
+
+/*****************************************************************************
+ * eTek specific driver functions
+ *****************************************************************************/
+static int etek_serial_open (struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+
+	debug_info("USB: etek_serial_open\n");
+
+	if (!serial->present) {
+		debug_info("USB Serial: no device registered\n");
+		return -EINVAL;
+	}
+
+	if (serial->active) {
+		debug_info ("USB Serial: device already open\n");
+		return -EINVAL;
+	}
+	serial->active = 1;
+ 
+	/*Start reading from the device*/
+	serial->bulk_in_inuse = 1;
+	serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial);
+
+	/* Need to do device specific setup here (control lines, baud rate, etc.) */
+	/* FIXME!!! */
+				                  
+	return (0);
+}
+
+
+static void etek_serial_close(struct tty_struct *tty, struct file * filp)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+	debug_info("USB: etek_serial_close\n");
+	
+	/* Need to change the control lines here */
+	/* FIXME */
+	
+	/* shutdown our bulk reads and writes */
+	if (serial->bulk_out_inuse){
+		usb_terminate_bulk (serial->dev, serial->bulk_out_transfer);
+		serial->bulk_out_inuse = 0;
+	}
+	if (serial->bulk_in_inuse){
+		usb_terminate_bulk (serial->dev, serial->bulk_in_transfer);
+		serial->bulk_in_inuse = 0;
+	}
+
+	/* release the irq? */
+	
+	serial->active = 0;
+}
+
+
+static int etek_serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+	int written;
+	
+	debug_info("USB Serial: etek_serial_write\n");
+
+	if (serial->bulk_out_inuse) {
+		debug_info ("USB Serial: already writing\n");
+		return (0);
+	}
+
+	written = (count > serial->bulk_out_size) ? serial->bulk_out_size : count;
+	  
+	if (from_user) {
+		copy_from_user(serial->bulk_out_buffer, buf, written);
+	}
+	else {
+		memcpy (serial->bulk_out_buffer, buf, written);
+	}  
+
+	/* send the data out the bulk port */
+	serial->bulk_out_inuse = 1;
+	serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, written, serial);
+
+	return (written);
+} 
+
+
+static void etek_serial_put_char (struct tty_struct *tty, unsigned char ch)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; 
+	
+	debug_info("USB Serial: etek_serial_put_char\n");
+	
+	if (serial->bulk_out_inuse) {
+		debug_info ("USB Serial: already writing\n");
+		return;
+	}
+
+	/* send the single character out the bulk port */
+	serial->bulk_out_buffer[0] = ch;
+	serial->bulk_out_inuse = 1;
+	serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, 1, serial);
+
+	return;
+}                   
+
+
+static int etek_write_room (struct tty_struct *tty) 
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; 
+
+	debug_info("USB Serial: etek_write_room\n");
+	
+	if (serial->bulk_out_inuse) {
+		return (0);
+	}
+
+	return (serial->bulk_out_size);
+}
+
+
+static int etek_chars_in_buffer (struct tty_struct *tty) 
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; 
+
+	debug_info("USB Serial: etek_chars_in_buffer\n");
+	
+	if (serial->bulk_out_inuse) {
+		return (serial->bulk_out_size);
+	}
+
+	return (0);
+}
+
+
+static void etek_throttle (struct tty_struct * tty)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+
+	debug_info("USB Serial: etek_throttle\n");
+	
 	/* Change the control signals */
 	/* FIXME!!! */
 
@@ -429,136 +691,340 @@
 }
 
 
-static int Get_Free_Serial (void)
+static void etek_unthrottle (struct tty_struct * tty)
 {
-	int i;
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+
+	debug_info("USB Serial: etek_unthrottle\n");
+	
+	/* Change the control signals */
+	/* FIXME!!! */
+
+	return;
+}
+
+
+/*****************************************************************************
+ * generic devices specific driver functions
+ *****************************************************************************/
+static int generic_serial_open (struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+
+	debug_info("USB: generic_serial_open\n");
+
+	if (!serial->present) {
+		debug_info("USB Serial: no device registered\n");
+		return -EINVAL;
+	}
+
+	if (serial->active) {
+		debug_info ("USB Serial: device already open\n");
+		return -EINVAL;
+	}
+	serial->active = 1;
  
-	for (i=0; i < NUM_PORTS; ++i) {
-		if (!serial_state_table[i].present)
-			return (i);
+	/* if we have a bulk interrupt, start reading from it */
+	if (serial->has_bulk_in) {
+		/*Start reading from the device*/
+		serial->bulk_in_inuse = 1;
+		serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial);
 	}
-	return (-1);
+				                  
+	return (0);
 }
 
 
-static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
+static void generic_serial_close(struct tty_struct *tty, struct file * filp)
 {
-	struct usb_serial_state *serial;
-	struct usb_interface_descriptor *interface;
-	struct usb_endpoint_descriptor *endpoint;
-	SERIAL_TYPE type;
-	int serial_num;
-//	int ret;
-	int i;
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+	debug_info("USB: generic_serial_close\n");
 	
-	/* look at the device descriptor to see if it is a type that we	recognize */
-	type = unknown;
-	if ((dev->descriptor.idVendor == BELKIN_VENDOR_ID) &&
-	    (dev->descriptor.idProduct == BELKIN_SERIAL_CONVERTER)) {
-		/* This is the Belkin serial convertor */
-		type = Belkin;
-		}
+	/* shutdown any bulk reads that might be going on */
+	if (serial->bulk_out_inuse){
+		usb_terminate_bulk (serial->dev, serial->bulk_out_transfer);
+		serial->bulk_out_inuse = 0;
+	}
+	if (serial->bulk_in_inuse){
+		usb_terminate_bulk (serial->dev, serial->bulk_in_transfer);
+		serial->bulk_in_inuse = 0;
+	}
+
+	serial->active = 0;
+}
+
+
+static int generic_serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data; 
+	int written;
 	
-	if ((dev->descriptor.idVendor == PERACOM_VENDOR_ID) &&
-	    (dev->descriptor.idProduct == PERACOM_SERIAL_CONVERTER)) {
-		/* This is the Peracom serial convertor */
-		type = Peracom;
+	debug_info("USB Serial: generic_serial_write\n");
+
+	/* only do something if we have a bulk out endpoint */
+	if (serial->has_bulk_out) {
+		if (serial->bulk_out_inuse) {
+			debug_info ("USB Serial: already writing\n");
+			return (0);
 		}
 
-	if (type == unknown)
-		return NULL;	
+		written = (count > serial->bulk_out_size) ? serial->bulk_out_size : count;
+	  
+		if (from_user) {
+			copy_from_user(serial->bulk_out_buffer, buf, written);
+		}
+		else {
+			memcpy (serial->bulk_out_buffer, buf, written);
+		}  
 
-	printk (KERN_INFO "USB serial converter detected.\n");
+		/* send the data out the bulk port */
+		serial->bulk_out_inuse = 1;
+		serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, written, serial);
 
-	if (0>(serial_num = Get_Free_Serial())) {
-		debug_info("USB Serial: Too many devices connected\n");
-		return NULL;
+		return (written);
 	}
 	
-	serial = &serial_state_table[serial_num];
+	/* no bulk out, so return 0 bytes written */
+	return (0);
+} 
 
-       	memset(serial, 0, sizeof(serial));
-       	serial->dev = dev;
-	serial->type = type;
 
-	/* we should have 1 bulk in, 1 bulk out, and 1 interrupt in endpoints */
-	interface = &dev->actconfig->interface[ifnum].altsetting[0];
-	for (i = 0; i < interface->bNumEndpoints; ++i) {
-		endpoint = &interface->endpoint[i];
-		
-		if ((endpoint->bEndpointAddress & 0x80) &&
-		    ((endpoint->bmAttributes & 3) == 0x02)) {
-			/* we found the bulk in endpoint */
-			serial->bulk_in_inuse = 0;
-			serial->bulk_in_endpoint = endpoint->bEndpointAddress;
-			serial->bulk_in_size = endpoint->wMaxPacketSize;
-			serial->bulk_in_interval = endpoint->bInterval;
-			serial->bulk_in_pipe = usb_rcvbulkpipe (dev, serial->bulk_in_endpoint);
-			serial->bulk_in_buffer = kmalloc (serial->bulk_in_size, GFP_KERNEL);
-			if (!serial->bulk_in_buffer) {
-				printk("USB Serial: Couldn't allocate bulk_in_buffer\n");
-				goto probe_error;
-			}
+static void generic_serial_put_char (struct tty_struct *tty, unsigned char ch)
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; 
+	
+	debug_info("USB Serial: generic_serial_put_char\n");
+	
+	/* if we have a bulk out endpoint, then shove a character out it */
+	if (serial->has_bulk_out) {
+		if (serial->bulk_out_inuse) {
+			debug_info ("USB Serial: already writing\n");
+			return;
 		}
 
-		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
-		    ((endpoint->bmAttributes & 3) == 0x02)) {
-			/* we found the bulk out endpoint */
-			serial->bulk_out_inuse = 0;
-			serial->bulk_out_endpoint = endpoint->bEndpointAddress;
-			serial->bulk_out_size = endpoint->wMaxPacketSize;
-			serial->bulk_out_interval = endpoint->bInterval;
-			serial->bulk_out_pipe = usb_rcvbulkpipe (dev, serial->bulk_out_endpoint);
-			serial->bulk_out_buffer = kmalloc (serial->bulk_out_size, GFP_KERNEL);
-			if (!serial->bulk_out_buffer) {
-				printk("USB Serial: Couldn't allocate bulk_out_buffer\n");
-				goto probe_error;
-			}
+		/* send the single character out the bulk port */
+		serial->bulk_out_buffer[0] = ch;
+		serial->bulk_out_inuse = 1;
+		serial->bulk_out_transfer = usb_request_bulk (serial->dev, serial->bulk_out_pipe, serial_write_irq, serial->bulk_out_buffer, 1, serial);
+	}
+
+	return;
+}                   
+
+
+static int generic_write_room (struct tty_struct *tty) 
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; 
+
+	debug_info("USB Serial: generic_write_room\n");
+	
+	if (serial->has_bulk_out) {
+		if (serial->bulk_out_inuse) {
+			return (0);
 		}
-		
-		if ((endpoint->bEndpointAddress & 0x80) &&
-		    ((endpoint->bmAttributes & 3) == 0x03)) {
-			/* we found the interrupt in endpoint */
-			serial->interrupt_in_inuse = 0;
-			serial->interrupt_in_endpoint = endpoint->bEndpointAddress;
-			serial->interrupt_in_size = endpoint->wMaxPacketSize;
-			serial->interrupt_in_interval = endpoint->bInterval;
-			/* serial->interrupt_in_pipe = usb_rcvbulkpipe (dev, serial->bulk_in_endpoint); */
-			serial->interrupt_in_buffer = kmalloc (serial->bulk_in_size, GFP_KERNEL);
-			if (!serial->interrupt_in_buffer) {
-				printk("USB Serial: Couldn't allocate interrupt_in_buffer\n");
-				goto probe_error;
-			}
+		return (serial->bulk_out_size);
+	}
+	
+	return (0);
+}
+
+
+static int generic_chars_in_buffer (struct tty_struct *tty) 
+{
+	struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data; 
+
+	debug_info("USB Serial: generic_chars_in_buffer\n");
+	
+	if (serial->has_bulk_out) {
+		if (serial->bulk_out_inuse) {
+			return (serial->bulk_out_size);
 		}
+	}
 
+	return (0);
+}
+
+
+static int Get_Free_Serial (void)
+{
+	int i;
+ 
+	for (i=0; i < NUM_PORTS; ++i) {
+		if (!serial_state_table[i].present)
+			return (i);
 	}
+	return (-1);
+}
+
+
+static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_serial_state *serial = NULL;
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_endpoint_descriptor *interrupt_in_endpoint = NULL;
+	struct usb_endpoint_descriptor *bulk_in_endpoint = NULL;
+	struct usb_endpoint_descriptor *bulk_out_endpoint = NULL;
+//	SERIAL_TYPE type;
+	struct usb_serial_device_type *type;
+	int device_num;
+	int serial_num;
+//	int ret;
+	int i;
+	char interrupt_pipe;
+	char bulk_in_pipe;
+	char bulk_out_pipe;
+	
+	/* loop through our list of known serial converters, and see if this device matches */
+	device_num = 0;
+	while (usb_serial_devices[device_num] != NULL) {
+		type = usb_serial_devices[device_num];
+		#ifdef SERIAL_DEBUG
+			printk ("Looking at %s\nVendor id=%.4x\nProduct id=%.4x", type->name, *(type->idVendor), *(type->idProduct));
+		#endif		
+
+		/* look at the device descriptor */
+		if ((dev->descriptor.idVendor == *(type->idVendor)) &&
+		    (dev->descriptor.idProduct == *(type->idProduct))) {
+
+			debug_info("descriptor matches...looking at the endpoints\n")
+
+			/* descriptor matches, let's try to find the endpoints needed */
+			interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT;
+			
+			/* check out the endpoints */
+			interface = &dev->actconfig->interface[ifnum].altsetting[0];
+			for (i = 0; i < interface->bNumEndpoints; ++i) {
+				endpoint = &interface->endpoint[i];
+		
+				if ((endpoint->bEndpointAddress & 0x80) &&
+				    ((endpoint->bmAttributes & 3) == 0x02)) {
+					/* we found a bulk in endpoint */
+					debug_info("found bulk in\n");
+					if (bulk_in_pipe == HAS) {
+						printk("USB Serial: can't have more than one bulk in endpoint\n");
+						goto probe_error;
+						}
+					bulk_in_pipe = HAS;
+					bulk_in_endpoint = endpoint;
+				}
+
+				if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+				    ((endpoint->bmAttributes & 3) == 0x02)) {
+					/* we found a bulk out endpoint */
+					debug_info("found bulk out\n");
+					if (bulk_out_pipe == HAS) {
+						printk("USB Serial: can't have more than one bulk out endpoint\n");
+						goto probe_error;
+						}
+					bulk_out_pipe = HAS;
+					bulk_out_endpoint = endpoint;
+				}
+		
+				if ((endpoint->bEndpointAddress & 0x80) &&
+				    ((endpoint->bmAttributes & 3) == 0x03)) {
+					/* we found a interrupt in endpoint */
+					debug_info("found interrupt in\n");
+					if (interrupt_pipe == HAS) {
+						printk("USB Serial: can't have more than one interrupt in endpoint\n");
+						goto probe_error;
+						}
+					interrupt_pipe = HAS;
+					interrupt_in_endpoint = endpoint;
+				}
+
+			}
 	
+			/* verify that we found all of the endpoints that we need */
+			if ((interrupt_pipe & type->needs_interrupt_in) &&
+			    (bulk_in_pipe & type->needs_bulk_in) &&
+			    (bulk_out_pipe & type->needs_bulk_out)) {
+				/* found all that we need */
+				printk (KERN_INFO "USB serial converter detected.\n");
+
+				if (0>(serial_num = Get_Free_Serial())) {
+					debug_info("USB Serial: Too many devices connected\n");
+					return NULL;
+				}
+	
+				serial = &serial_state_table[serial_num];
+
+			       	memset(serial, 0, sizeof(struct usb_serial_state));
+			       	serial->dev = dev;
+				serial->type = type;
+
+				/* set up the endpoint information */
+				if (bulk_in_endpoint) {
+					serial->has_bulk_in = 1;
+					serial->bulk_in_inuse = 0;
+					serial->bulk_in_endpoint = bulk_in_endpoint->bEndpointAddress;
+					serial->bulk_in_size = bulk_in_endpoint->wMaxPacketSize;
+					serial->bulk_in_interval = bulk_in_endpoint->bInterval;
+					serial->bulk_in_pipe = usb_rcvbulkpipe (dev, serial->bulk_in_endpoint);
+					serial->bulk_in_buffer = kmalloc (serial->bulk_in_size, GFP_KERNEL);
+					if (!serial->bulk_in_buffer) {
+						printk("USB Serial: Couldn't allocate bulk_in_buffer\n");
+						goto probe_error;
+					}
+				}
+
+				if (bulk_out_endpoint) {
+					serial->has_bulk_out = 1;
+					serial->bulk_out_inuse = 0;
+					serial->bulk_out_endpoint = bulk_out_endpoint->bEndpointAddress;
+					serial->bulk_out_size = bulk_out_endpoint->wMaxPacketSize;
+					serial->bulk_out_interval = bulk_out_endpoint->bInterval;
+					serial->bulk_out_pipe = usb_rcvbulkpipe (dev, serial->bulk_out_endpoint);
+					serial->bulk_out_buffer = kmalloc (serial->bulk_out_size, GFP_KERNEL);
+					if (!serial->bulk_out_buffer) {
+						printk("USB Serial: Couldn't allocate bulk_out_buffer\n");
+						goto probe_error;
+					}
+				}
+
+				if (interrupt_in_endpoint) {
+					serial->has_interrupt_in = 1;
+					serial->interrupt_in_inuse = 0;
+					serial->interrupt_in_endpoint = interrupt_in_endpoint->bEndpointAddress;
+					serial->interrupt_in_size = interrupt_in_endpoint->wMaxPacketSize;
+					serial->interrupt_in_interval = interrupt_in_endpoint->bInterval;
+					/* serial->interrupt_in_pipe = usb_rcvbulkpipe (dev, serial->bulk_in_endpoint); */
+					serial->interrupt_in_buffer = kmalloc (serial->bulk_in_size, GFP_KERNEL);
+					if (!serial->interrupt_in_buffer) {
+						printk("USB Serial: Couldn't allocate interrupt_in_buffer\n");
+						goto probe_error;
+					}
+				}
+
+				#if 0
+				/* set up an interrupt for out bulk in pipe */
+				/* ask for a bulk read */
+				serial->bulk_in_inuse = 1;
+				serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial);
+
+				/* set up our interrupt to be the time for the bulk in read */
+				ret = usb_request_irq (dev, serial->bulk_in_pipe, usb_serial_irq, serial->bulk_in_interval, serial, &serial->irq_handle);
+				if (ret) {
+					printk(KERN_INFO "USB Serial failed usb_request_irq (0x%x)\n", ret);
+					goto probe_error;
+				}
+				#endif
+
+				serial->present = 1;
+				MOD_INC_USE_COUNT;
+
+				return serial;
+			} else {
+				printk(KERN_INFO "USB Serial, descriptors matched, but endpoints did not\n");
+			}
+		}
 
-	/* verify that we found all of the endpoints that we need */
-	if ((!serial->bulk_in_buffer) || 
-	    (!serial->bulk_out_buffer) ||
-	    (!serial->interrupt_in_buffer)) {
-		printk("USB Serial: did not find all of the required endpoints\n");
-		goto probe_error;
+		/* look at the next type in our list */
+		++device_num;
 	}
-		
 
-	/* set up an interrupt for out bulk in pipe */
-	/* ask for a bulk read */
-//	serial->bulk_in_inuse = 1;
-//	serial->bulk_in_transfer = usb_request_bulk (serial->dev, serial->bulk_in_pipe, serial_read_irq, serial->bulk_in_buffer, serial->bulk_in_size, serial);
 
-	/* set up our interrupt to be the time for the bulk in read */
-//	ret = usb_request_irq (dev, serial->bulk_in_pipe, usb_serial_irq, serial->bulk_in_interval, serial, &serial->irq_handle);
-//	if (ret) {
-//		printk(KERN_INFO "USB Serial failed usb_request_irq (0x%x)\n", ret);
-//		goto probe_error;
-//	}
-	
-	serial->present = 1;
-	MOD_INC_USE_COUNT;
 
-	return serial;
 
 probe_error:
 	if (serial) {
@@ -636,6 +1102,8 @@
 	serial_tty_driver.flags			= TTY_DRIVER_REAL_RAW;
 	serial_tty_driver.refcount		= &serial_refcount;
 	serial_tty_driver.table			= serial_tty;
+	serial_tty_driver.proc_entry		= NULL;
+	serial_tty_driver.other			= NULL;
 	serial_tty_driver.termios		= serial_termios;
 	serial_tty_driver.termios_locked	= serial_termios_locked;
 	

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