patch-2.3.20 linux/drivers/usb/serial.c
Next file: linux/drivers/usb/uhci.c
Previous file: linux/drivers/usb/proc_usb.c
Back to the patch index
Back to the overall index
- Lines: 692
- Date:
Fri Oct 8 10:06:21 1999
- Orig file:
v2.3.19/linux/drivers/usb/serial.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.19/linux/drivers/usb/serial.c linux/drivers/usb/serial.c
@@ -0,0 +1,691 @@
+/*
+ * USB Serial Converter driver
+ *
+ * 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)
+ *
+ * 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.
+ *
+ *
+ * version 0.1.1 (10/05/99) gkh
+ * 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.
+ *
+ * (C) Copyright 1999 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+
+#include "usb.h"
+/*#define SERIAL_DEBUG 1*/
+
+#ifdef SERIAL_DEBUG
+ #define debug_info(message); printk(message);
+#else
+ #define debug_info(message);
+#endif
+
+
+/* USB Serial devices vendor ids and device ids that this driver supports */
+#define BELKIN_VENDOR_ID 0x056c
+#define BELKIN_SERIAL_CONVERTER 0x8007
+#define PERACOM_VENDOR_ID 0x0565
+#define PERACOM_SERIAL_CONVERTER 0x0001
+
+
+#define SERIAL_MAJOR 240
+
+#define NUM_PORTS 4 /* Have to pick a number for now. Need to look */
+ /* into dynamically creating them at insertion time. */
+
+
+static int usb_serial_probe(struct usb_device *dev);
+static void usb_serial_disconnect(struct usb_device *dev);
+
+typedef enum {
+ unknown = 0,
+ Belkin = 1,
+ Peracom = 2
+ } SERIAL_TYPE;
+
+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;
+
+ char interrupt_in_inuse;
+ __u8 interrupt_in_endpoint;
+ __u8 interrupt_in_interval;
+ __u16 interrupt_in_size;
+ unsigned int interrupt_in_pipe;
+ unsigned char * interrupt_in_buffer;
+ void * interrupt_in_transfer;
+
+ char bulk_in_inuse;
+ __u8 bulk_in_endpoint;
+ __u8 bulk_in_interval;
+ __u16 bulk_in_size;
+ unsigned int bulk_in_pipe;
+ unsigned char * bulk_in_buffer;
+ void * bulk_in_transfer;
+
+ char bulk_out_inuse;
+ __u8 bulk_out_endpoint;
+ __u8 bulk_out_interval;
+ __u16 bulk_out_size;
+ unsigned int bulk_out_pipe;
+ unsigned char * bulk_out_buffer;
+ void * bulk_out_transfer;
+};
+
+static struct usb_driver usb_serial_driver = {
+ "serial",
+ usb_serial_probe,
+ usb_serial_disconnect,
+ { NULL, NULL }
+};
+
+static int serial_refcount;
+static struct tty_driver serial_tty_driver;
+static struct tty_struct * serial_tty[NUM_PORTS];
+static struct termios * serial_termios[NUM_PORTS];
+static struct termios * serial_termios_locked[NUM_PORTS];
+static struct usb_serial_state serial_state_table[NUM_PORTS];
+
+
+
+static int serial_read_irq (int state, void *buffer, int count, void *dev_id)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *)dev_id;
+ struct tty_struct *tty = serial->tty;
+ unsigned char* data = buffer;
+ int i;
+
+ debug_info("USB: serial_read_irq\n");
+
+#ifdef SERIAL_DEBUG
+ if (count) {
+ printk("%d %s\n", count, data);
+ }
+#endif
+
+ if (count) {
+ for (i=0;i<count;i++) {
+ tty_insert_flip_char(tty,data[i],0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Continue transfer */
+ /* return (1); */
+
+ /* No more transfer, let the irq schedule us again */
+ serial->bulk_in_inuse = 0;
+ return (0);
+}
+
+
+static int serial_write_irq (int state, void *buffer, int count, void *dev_id)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *) dev_id;
+ struct tty_struct *tty = serial->tty;
+
+ debug_info("USB Serial: serial_write_irq\n");
+
+ if (!serial->bulk_out_inuse) {
+ debug_info("USB Serial: write irq for a finished pipe?\n");
+ return (0);
+ }
+
+ usb_terminate_bulk (serial->dev, serial->bulk_out_transfer);
+ serial->bulk_out_inuse = 0;
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+
+ wake_up_interruptible(&tty->write_wait);
+
+ return 0;
+}
+
+
+static int usb_serial_irq (int state, void *buffer, int len, void *dev_id)
+{
+// struct usb_serial_state *serial = (struct usb_serial_state *) dev_id;
+
+ debug_info("USB Serial: usb_serial_irq\n");
+
+ /* 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);
+
+ return (1);
+}
+
+
+
+
+
+/* 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");
+
+ 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;
+ }
+
+ 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 serial_close(struct tty_struct *tty, struct file * filp)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data;
+ debug_info("USB: serial_close\n");
+
+ if (!serial->present) {
+ debug_info("USB Serial: no device registered\n");
+ return;
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device already open\n");
+ 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;
+ }
+
+ /* 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");
+
+ if (!serial->present) {
+ debug_info("USB Serial: device not registered\n");
+ return (-EINVAL);
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device not opened\n");
+ return (-EINVAL);
+ }
+
+ 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 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: serial_put_char\n");
+
+ if (!serial->present) {
+ debug_info("USB Serial: no device registered\n");
+ return;
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device not open\n");
+ return;
+ }
+
+ 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 serial_write_room (struct tty_struct *tty)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data;
+
+ debug_info("USB Serial: serial_write_room\n");
+
+ if (!serial->present) {
+ debug_info("USB Serial: no device registered\n");
+ return (-EINVAL);
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device not open\n");
+ return (-EINVAL);
+ }
+
+ if (serial->bulk_out_inuse) {
+ return (0);
+ }
+
+ return serial->bulk_out_size;
+}
+
+
+static int serial_chars_in_buffer (struct tty_struct *tty)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *)tty->driver_data;
+
+ debug_info("USB Serial: serial_chars_in_buffer\n");
+
+ if (!serial->present) {
+ debug_info("USB Serial: no device registered\n");
+ return (-EINVAL);
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device not open\n");
+ return (-EINVAL);
+ }
+
+ if (serial->bulk_out_inuse) {
+ return (serial->bulk_out_size);
+ }
+
+ return (0);
+}
+
+
+static void serial_throttle (struct tty_struct * tty)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data;
+
+ debug_info("USB Serial: serial_throttle\n");
+
+ if (!serial->present) {
+ debug_info("USB Serial: no device registered\n");
+ return;
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device not open\n");
+ return;
+ }
+
+
+ /* Change the control signals */
+ /* FIXME!!! */
+
+ return;
+}
+
+
+static void serial_unthrottle (struct tty_struct * tty)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *) tty->driver_data;
+
+ debug_info("USB Serial: serial_unthrottle\n");
+
+ if (!serial->present) {
+ debug_info("USB Serial: no device registered\n");
+ return;
+ }
+
+ if (!serial->active) {
+ debug_info ("USB Serial: device not open\n");
+ return;
+ }
+
+
+ /* Change the control signals */
+ /* FIXME!!! */
+
+ return;
+}
+
+
+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 int usb_serial_probe(struct usb_device *dev)
+{
+ 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;
+
+ /* 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;
+ }
+
+ if ((dev->descriptor.idVendor == PERACOM_VENDOR_ID) &&
+ (dev->descriptor.idProduct == PERACOM_SERIAL_CONVERTER)) {
+ /* This is the Peracom serial convertor */
+ type = Peracom;
+ }
+
+ if (type == unknown)
+ return (-1);
+
+ printk (KERN_INFO "USB serial converter detected.\n");
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk (KERN_INFO " Failed usb_set_configuration: serial\n");
+ return (-1);
+ }
+
+ if (0>(serial_num = Get_Free_Serial())) {
+ debug_info("USB Serial: Too many devices connected\n");
+ return (-1);
+ }
+
+ serial = &serial_state_table[serial_num];
+
+ memset(serial, 0, sizeof(serial));
+ serial->dev = dev;
+ serial->type = type;
+ dev->private = serial;
+
+ /* we should have 1 bulk in, 1 bulk out, and 1 interrupt in endpoints */
+ interface = &dev->config[0].interface[0].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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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->bulk_in_buffer) {
+ printk("USB Serial: Couldn't allocate interrupt_in_buffer\n");
+ goto probe_error;
+ }
+ }
+
+ }
+
+
+ /* 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;
+ }
+
+
+ /* 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 (0);
+
+probe_error:
+ if (serial) {
+ if (serial->bulk_in_buffer)
+ kfree (serial->bulk_in_buffer);
+ if (serial->bulk_out_buffer)
+ kfree (serial->bulk_out_buffer);
+ if (serial->interrupt_in_buffer)
+ kfree (serial->interrupt_in_buffer);
+ }
+ return (-1);
+}
+
+
+static void usb_serial_disconnect(struct usb_device *dev)
+{
+ struct usb_serial_state *serial = (struct usb_serial_state *)dev->private;
+
+ if (serial) {
+ if (!serial->present) {
+ /* something strange is going on */
+ debug_info("USB Serial: disconnect but not present?\n")
+ return;
+ }
+
+ /* need to stop any transfers...*/
+ if (serial->bulk_in_inuse) {
+ usb_terminate_bulk (serial->dev, serial->bulk_in_transfer);
+ serial->bulk_in_inuse = 0;
+ }
+ if (serial->bulk_out_inuse) {
+ usb_terminate_bulk (serial->dev, serial->bulk_out_transfer);
+ serial->bulk_out_inuse = 0;
+ }
+ // usb_release_irq (serial->dev, serial->irq_handle, serial->bulk_in_pipe);
+ if (serial->bulk_in_buffer)
+ kfree (serial->bulk_in_buffer);
+ if (serial->bulk_out_buffer)
+ kfree (serial->bulk_out_buffer);
+ if (serial->interrupt_in_buffer)
+ kfree (serial->interrupt_in_buffer);
+
+ serial->present = 0;
+ serial->active = 0;
+ }
+ dev->private = NULL;
+
+ MOD_DEC_USE_COUNT;
+
+ printk (KERN_INFO "USB Serial device disconnected.\n");
+}
+
+
+
+int usb_serial_init(void)
+{
+ int i;
+
+ /* Initalize our global data */
+ for (i = 0; i < NUM_PORTS; ++i) {
+ memset(&serial_state_table[i], 0x00, sizeof(struct usb_serial_state));
+ }
+
+ /* register the tty driver */
+ memset (&serial_tty_driver, 0, sizeof(struct tty_driver));
+ serial_tty_driver.magic = TTY_DRIVER_MAGIC;
+ serial_tty_driver.driver_name = "usb";
+ serial_tty_driver.name = "ttyUSB";
+ serial_tty_driver.major = SERIAL_MAJOR;
+ serial_tty_driver.minor_start = 0;
+ serial_tty_driver.num = NUM_PORTS;
+ serial_tty_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_tty_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_tty_driver.init_termios = tty_std_termios;
+ serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_tty_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_tty_driver.refcount = &serial_refcount;
+ serial_tty_driver.table = serial_tty;
+ serial_tty_driver.termios = serial_termios;
+ serial_tty_driver.termios_locked = serial_termios_locked;
+
+ serial_tty_driver.open = serial_open;
+ serial_tty_driver.close = serial_close;
+ serial_tty_driver.write = serial_write;
+ serial_tty_driver.put_char = serial_put_char;
+ serial_tty_driver.flush_chars = NULL; //serial_flush_chars;
+ serial_tty_driver.write_room = serial_write_room;
+ serial_tty_driver.ioctl = NULL; //serial_ioctl;
+ serial_tty_driver.set_termios = NULL; //serial_set_termios;
+ serial_tty_driver.set_ldisc = NULL;
+ serial_tty_driver.throttle = serial_throttle;
+ serial_tty_driver.unthrottle = serial_unthrottle;
+ serial_tty_driver.stop = NULL; //serial_stop;
+ serial_tty_driver.start = NULL; //serial_start;
+ serial_tty_driver.hangup = NULL; //serial_hangup;
+ serial_tty_driver.break_ctl = NULL; //serial_break;
+ serial_tty_driver.wait_until_sent = NULL; //serial_wait_until_sent;
+ serial_tty_driver.send_xchar = NULL; //serial_send_xchar;
+ serial_tty_driver.read_proc = NULL; //serial_read_proc;
+ serial_tty_driver.chars_in_buffer = serial_chars_in_buffer;
+ serial_tty_driver.flush_buffer = NULL; //serial_flush_buffer;
+ if (tty_register_driver (&serial_tty_driver)) {
+ printk( "USB Serial: failed to register tty driver\n" );
+ return -EPERM;
+ }
+
+ /* register the USB driver */
+ usb_register(&usb_serial_driver);
+ printk(KERN_INFO "USB Serial support registered.\n");
+ return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_serial_init();
+}
+
+void cleanup_module(void)
+{
+ tty_unregister_driver(&serial_tty_driver);
+ usb_deregister(&usb_serial_driver);
+}
+
+#endif
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)