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
- Lines: 1059
- Date:
Wed Nov 10 08:32:53 1999
- Orig file:
v2.3.26/linux/drivers/usb/usb-serial.c
- Orig date:
Sun Nov 7 16:37:34 1999
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)