USB: kl5usb105: reimplement using generic framework
authorJohan Hovold <jhovold@gmail.com>
Tue, 18 May 2010 22:01:40 +0000 (00:01 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 20 May 2010 20:21:50 +0000 (13:21 -0700)
Kill custom read and write implementations (static per-port,
singleton(!) urb pool).

Also remove changelog header (can be retrieved through git).

Read processing and write-buffer handling tested using a cp210x device
in a loopback setup.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/kl5kusb105.c

index ad168255cc04091ef4b37bef6f35c3322faa08fb..cdbe8bf7f674962eccaba56587772dea2a86d621 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * KLSI KL5KUSB105 chip RS232 converter driver
  *
+ *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
  *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
  *
  *   This program is free software; you can redistribute it and/or modify
  *     implement handshaking or decide that we do not support it
  */
 
-/* History:
- *   0.3a - implemented pools of write URBs
- *   0.3  - alpha version for public testing
- *   0.2  - TIOCMGET works, so autopilot(1) can be used!
- *   0.1  - can be used to do pilot-xfer -p /dev/ttyUSB0 -l
- *
- *   The driver skeleton is mainly based on mct_u232.c and various other
- *   pieces of code shamelessly copied from the drivers/usb/serial/ directory.
- */
-
-
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -64,8 +54,8 @@ static int debug;
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.3a"
-#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>"
+#define DRIVER_VERSION "v0.4"
+#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
 #define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
 
 
@@ -73,23 +63,17 @@ static int debug;
  * Function prototypes
  */
 static int  klsi_105_startup(struct usb_serial *serial);
-static void klsi_105_disconnect(struct usb_serial *serial);
 static void klsi_105_release(struct usb_serial *serial);
 static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
 static void klsi_105_close(struct usb_serial_port *port);
-static int  klsi_105_write(struct tty_struct *tty,
-       struct usb_serial_port *port, const unsigned char *buf, int count);
-static void klsi_105_write_bulk_callback(struct urb *urb);
-static int  klsi_105_chars_in_buffer(struct tty_struct *tty);
-static int  klsi_105_write_room(struct tty_struct *tty);
-static void klsi_105_read_bulk_callback(struct urb *urb);
 static void klsi_105_set_termios(struct tty_struct *tty,
                        struct usb_serial_port *port, struct ktermios *old);
-static void klsi_105_throttle(struct tty_struct *tty);
-static void klsi_105_unthrottle(struct tty_struct *tty);
 static int  klsi_105_tiocmget(struct tty_struct *tty, struct file *file);
 static int  klsi_105_tiocmset(struct tty_struct *tty, struct file *file,
                        unsigned int set, unsigned int clear);
+static void klsi_105_process_read_urb(struct urb *urb);
+static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
+                                               void *dest, size_t size);
 
 /*
  * All of the device info needed for the KLSI converters.
@@ -119,22 +103,19 @@ static struct usb_serial_driver kl5kusb105d_device = {
        .usb_driver =           &kl5kusb105d_driver,
        .id_table =             id_table,
        .num_ports =            1,
+       .bulk_out_size =        64,
        .open =                 klsi_105_open,
        .close =                klsi_105_close,
-       .write =                klsi_105_write,
-       .write_bulk_callback =  klsi_105_write_bulk_callback,
-       .chars_in_buffer =      klsi_105_chars_in_buffer,
-       .write_room =           klsi_105_write_room,
-       .read_bulk_callback =   klsi_105_read_bulk_callback,
        .set_termios =          klsi_105_set_termios,
        /*.break_ctl =          klsi_105_break_ctl,*/
        .tiocmget =             klsi_105_tiocmget,
        .tiocmset =             klsi_105_tiocmset,
        .attach =               klsi_105_startup,
-       .disconnect =           klsi_105_disconnect,
        .release =              klsi_105_release,
-       .throttle =             klsi_105_throttle,
-       .unthrottle =           klsi_105_unthrottle,
+       .throttle =             usb_serial_generic_throttle,
+       .unthrottle =           usb_serial_generic_unthrottle,
+       .process_read_urb =     klsi_105_process_read_urb,
+       .prepare_write_buffer = klsi_105_prepare_write_buffer,
 };
 
 struct klsi_105_port_settings {
@@ -145,18 +126,11 @@ struct klsi_105_port_settings {
        __u8    unknown2;
 } __attribute__ ((packed));
 
-/* we implement a pool of NUM_URBS urbs per usb_serial */
-#define NUM_URBS                       1
-#define URB_TRANSFER_BUFFER_SIZE       64
 struct klsi_105_private {
        struct klsi_105_port_settings   cfg;
        struct ktermios                 termios;
        unsigned long                   line_state; /* modem line settings */
-       /* write pool */
-       struct urb                      *write_urb_pool[NUM_URBS];
        spinlock_t                      lock;
-       unsigned long                   bytes_in;
-       unsigned long                   bytes_out;
 };
 
 
@@ -259,7 +233,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
 static int klsi_105_startup(struct usb_serial *serial)
 {
        struct klsi_105_private *priv;
-       int i, j;
+       int i;
 
        /* check if we support the product id (see keyspan.c)
         * FIXME
@@ -283,29 +257,9 @@ static int klsi_105_startup(struct usb_serial *serial)
 
                priv->line_state    = 0;
 
-               priv->bytes_in      = 0;
-               priv->bytes_out     = 0;
                usb_set_serial_port_data(serial->port[i], priv);
 
                spin_lock_init(&priv->lock);
-               for (j = 0; j < NUM_URBS; j++) {
-                       struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
-
-                       priv->write_urb_pool[j] = urb;
-                       if (urb == NULL) {
-                               dev_err(&serial->dev->dev, "No more urbs???\n");
-                               goto err_cleanup;
-                       }
-
-                       urb->transfer_buffer =
-                               kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
-                       if (!urb->transfer_buffer) {
-                               dev_err(&serial->dev->dev,
-                                       "%s - out of memory for urb buffers.\n",
-                                       __func__);
-                               goto err_cleanup;
-                       }
-               }
 
                /* priv->termios is left uninitalized until port opening */
                init_waitqueue_head(&serial->port[i]->write_wait);
@@ -316,56 +270,20 @@ static int klsi_105_startup(struct usb_serial *serial)
 err_cleanup:
        for (; i >= 0; i--) {
                priv = usb_get_serial_port_data(serial->port[i]);
-               for (j = 0; j < NUM_URBS; j++) {
-                       if (priv->write_urb_pool[j]) {
-                               kfree(priv->write_urb_pool[j]->transfer_buffer);
-                               usb_free_urb(priv->write_urb_pool[j]);
-                       }
-               }
                kfree(priv);
                usb_set_serial_port_data(serial->port[i], NULL);
        }
        return -ENOMEM;
 }
 
-static void klsi_105_disconnect(struct usb_serial *serial)
-{
-       int i;
-
-       dbg("%s", __func__);
-
-       /* stop reads and writes on all ports */
-       for (i = 0; i < serial->num_ports; ++i) {
-               struct klsi_105_private *priv =
-                               usb_get_serial_port_data(serial->port[i]);
-
-               if (priv) {
-                       /* kill our write urb pool */
-                       int j;
-                       struct urb **write_urbs = priv->write_urb_pool;
-
-                       for (j = 0; j < NUM_URBS; j++) {
-                               if (write_urbs[j]) {
-                                       usb_kill_urb(write_urbs[j]);
-                                       usb_free_urb(write_urbs[j]);
-                               }
-                       }
-               }
-       }
-}
-
 static void klsi_105_release(struct usb_serial *serial)
 {
        int i;
 
        dbg("%s", __func__);
 
-       for (i = 0; i < serial->num_ports; ++i) {
-               struct klsi_105_private *priv =
-                               usb_get_serial_port_data(serial->port[i]);
-
-               kfree(priv);
-       }
+       for (i = 0; i < serial->num_ports; ++i)
+               kfree(usb_get_serial_port_data(serial->port[i]));
 }
 
 static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
@@ -416,18 +334,8 @@ static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
        spin_unlock_irqrestore(&priv->lock, flags);
 
        /* READ_ON and urb submission */
-       usb_fill_bulk_urb(port->read_urb, port->serial->dev,
-                     usb_rcvbulkpipe(port->serial->dev,
-                                     port->bulk_in_endpointAddress),
-                     port->read_urb->transfer_buffer,
-                     port->read_urb->transfer_buffer_length,
-                     klsi_105_read_bulk_callback,
-                     port);
-
-       rc = usb_submit_urb(port->read_urb, GFP_KERNEL);
+       rc = usb_serial_generic_open(tty, port);
        if (rc) {
-               dev_err(&port->dev, "%s - failed submitting read urb, "
-                       "error %d\n", __func__, rc);
                retval = rc;
                goto exit;
        }
@@ -464,7 +372,6 @@ exit:
 
 static void klsi_105_close(struct usb_serial_port *port)
 {
-       struct klsi_105_private *priv = usb_get_serial_port_data(port);
        int rc;
 
        dbg("%s port %d", __func__, port->number);
@@ -487,232 +394,61 @@ static void klsi_105_close(struct usb_serial_port *port)
        mutex_unlock(&port->serial->disc_mutex);
 
        /* shutdown our bulk reads and writes */
-       usb_kill_urb(port->write_urb);
-       usb_kill_urb(port->read_urb);
-       /* unlink our write pool */
-       /* FIXME */
+       usb_serial_generic_close(port);
+
        /* wgg - do I need this? I think so. */
        usb_kill_urb(port->interrupt_in_urb);
-       dev_info(&port->serial->dev->dev,
-                "port stats: %ld bytes in, %ld bytes out\n",
-                priv->bytes_in, priv->bytes_out);
 }
 
 /* We need to write a complete 64-byte data block and encode the
  * number actually sent in the first double-byte, LSB-order. That
  * leaves at most 62 bytes of payload.
  */
-#define KLSI_105_DATA_OFFSET   2   /* in the bulk urb data block */
-
-static int klsi_105_write(struct tty_struct *tty,
-       struct usb_serial_port *port, const unsigned char *buf, int count)
-{
-       struct klsi_105_private *priv = usb_get_serial_port_data(port);
-       int result, size;
-       int bytes_sent = 0;
-
-       dbg("%s - port %d", __func__, port->number);
-
-       while (count > 0) {
-               /* try to find a free urb (write 0 bytes if none) */
-               struct urb *urb = NULL;
-               unsigned long flags;
-               int i;
-               /* since the pool is per-port we might not need
-                  the spin lock !? */
-               spin_lock_irqsave(&priv->lock, flags);
-               for (i = 0; i < NUM_URBS; i++) {
-                       if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
-                               urb = priv->write_urb_pool[i];
-                               dbg("%s - using pool URB %d", __func__, i);
-                               break;
-                       }
-               }
-               spin_unlock_irqrestore(&priv->lock, flags);
-
-               if (urb == NULL) {
-                       dbg("%s - no more free urbs", __func__);
-                       goto exit;
-               }
-
-               if (urb->transfer_buffer == NULL) {
-                       urb->transfer_buffer =
-                               kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC);
-                       if (urb->transfer_buffer == NULL) {
-                               dev_err(&port->dev,
-                                       "%s - no more kernel memory...\n",
-                                       __func__);
-                               goto exit;
-                       }
-               }
-
-               size = min(count, port->bulk_out_size - KLSI_105_DATA_OFFSET);
-               size = min(size, URB_TRANSFER_BUFFER_SIZE -
-                                                       KLSI_105_DATA_OFFSET);
-
-               memcpy(urb->transfer_buffer + KLSI_105_DATA_OFFSET, buf, size);
-
-               /* write payload size into transfer buffer */
-               ((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF);
-               ((__u8 *)urb->transfer_buffer)[1] = (__u8) ((size & 0xFF00)>>8);
-
-               /* set up our urb */
-               usb_fill_bulk_urb(urb, port->serial->dev,
-                             usb_sndbulkpipe(port->serial->dev,
-                                             port->bulk_out_endpointAddress),
-                             urb->transfer_buffer,
-                             URB_TRANSFER_BUFFER_SIZE,
-                             klsi_105_write_bulk_callback,
-                             port);
-
-               /* send the data out the bulk port */
-               result = usb_submit_urb(urb, GFP_ATOMIC);
-               if (result) {
-                       dev_err(&port->dev,
-                               "%s - failed submitting write urb, error %d\n",
-                               __func__, result);
-                       goto exit;
-               }
-               buf += size;
-               bytes_sent += size;
-               count -= size;
-       }
-exit:
-       /* lockless, but it's for debug info only... */
-       priv->bytes_out += bytes_sent;
-
-       return bytes_sent;      /* that's how much we wrote */
-}
-
-static void klsi_105_write_bulk_callback(struct urb *urb)
+#define KLSI_HDR_LEN           2
+static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
+                                               void *dest, size_t size)
 {
-       struct usb_serial_port *port = urb->context;
-       int status = urb->status;
-
-       dbg("%s - port %d", __func__, port->number);
+       unsigned char *buf = dest;
+       int count;
 
-       if (status) {
-               dbg("%s - nonzero write bulk status received: %d", __func__,
-                   status);
-               return;
-       }
+       count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
+                                                               &port->lock);
+       put_unaligned_le16(count, buf);
 
-       usb_serial_port_softint(port);
+       return count + KLSI_HDR_LEN;
 }
 
-/* return number of characters currently in the writing process */
-static int klsi_105_chars_in_buffer(struct tty_struct *tty)
-{
-       struct usb_serial_port *port = tty->driver_data;
-       int chars = 0;
-       int i;
-       unsigned long flags;
-       struct klsi_105_private *priv = usb_get_serial_port_data(port);
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       for (i = 0; i < NUM_URBS; ++i) {
-               if (priv->write_urb_pool[i]->status == -EINPROGRESS)
-                       chars += URB_TRANSFER_BUFFER_SIZE;
-       }
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       dbg("%s - returns %d", __func__, chars);
-       return chars;
-}
-
-static int klsi_105_write_room(struct tty_struct *tty)
-{
-       struct usb_serial_port *port = tty->driver_data;
-       unsigned long flags;
-       int i;
-       int room = 0;
-       struct klsi_105_private *priv = usb_get_serial_port_data(port);
-
-       spin_lock_irqsave(&priv->lock, flags);
-       for (i = 0; i < NUM_URBS; ++i) {
-               if (priv->write_urb_pool[i]->status != -EINPROGRESS)
-                       room += URB_TRANSFER_BUFFER_SIZE;
-       }
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       dbg("%s - returns %d", __func__, room);
-       return room;
-}
-
-static void klsi_105_read_bulk_callback(struct urb *urb)
+/* The data received is preceded by a length double-byte in LSB-first order.
+ */
+static void klsi_105_process_read_urb(struct urb *urb)
 {
        struct usb_serial_port *port = urb->context;
-       struct klsi_105_private *priv = usb_get_serial_port_data(port);
-       struct tty_struct *tty;
        unsigned char *data = urb->transfer_buffer;
-       int rc;
-       int status = urb->status;
+       struct tty_struct *tty;
+       unsigned len;
 
-       dbg("%s - port %d", __func__, port->number);
+       /* empty urbs seem to happen, we ignore them */
+       if (!urb->actual_length)
+               return;
 
-       /* The urb might have been killed. */
-       if (status) {
-               dbg("%s - nonzero read bulk status received: %d", __func__,
-                   status);
+       if (urb->actual_length <= KLSI_HDR_LEN) {
+               dbg("%s - malformed packet", __func__);
                return;
        }
 
-       /* The data received is again preceded by a length double-byte in LSB-
-        * first order (see klsi_105_write() )
-        */
-       if (urb->actual_length == 0) {
-               /* empty urbs seem to happen, we ignore them */
-               /* dbg("%s - emtpy URB", __func__); */
-              ;
-       } else if (urb->actual_length <= 2) {
-               dbg("%s - size %d URB not understood", __func__,
-                   urb->actual_length);
-               usb_serial_debug_data(debug, &port->dev, __func__,
-                                     urb->actual_length, data);
-       } else {
-               int bytes_sent = ((__u8 *) data)[0] +
-                                ((unsigned int) ((__u8 *) data)[1] << 8);
-               tty = tty_port_tty_get(&port->port);
-               /* we should immediately resubmit the URB, before attempting
-                * to pass the data on to the tty layer. But that needs locking
-                * against re-entry an then mixed-up data because of
-                * intermixed tty_flip_buffer_push()s
-                * FIXME
-                */
-               usb_serial_debug_data(debug, &port->dev, __func__,
-                                     urb->actual_length, data);
-
-               if (bytes_sent + 2 > urb->actual_length) {
-                       dbg("%s - trying to read more data than available"
-                           " (%d vs. %d)", __func__,
-                           bytes_sent+2, urb->actual_length);
-                       /* cap at implied limit */
-                       bytes_sent = urb->actual_length - 2;
-               }
-
-               tty_insert_flip_string(tty, data + 2, bytes_sent);
-               tty_flip_buffer_push(tty);
-               tty_kref_put(tty);
+       tty = tty_port_tty_get(&port->port);
+       if (!tty)
+               return;
 
-               /* again lockless, but debug info only */
-               priv->bytes_in += bytes_sent;
+       len = get_unaligned_le16(data);
+       if (len > urb->actual_length - KLSI_HDR_LEN) {
+               dbg("%s - packet length mismatch", __func__);
+               len = urb->actual_length - KLSI_HDR_LEN;
        }
-       /* Continue trying to always read  */
-       usb_fill_bulk_urb(port->read_urb, port->serial->dev,
-                     usb_rcvbulkpipe(port->serial->dev,
-                                     port->bulk_in_endpointAddress),
-                     port->read_urb->transfer_buffer,
-                     port->read_urb->transfer_buffer_length,
-                     klsi_105_read_bulk_callback,
-                     port);
-       rc = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-       if (rc)
-               dev_err(&port->dev,
-                       "%s - failed resubmitting read urb, error %d\n",
-                       __func__, rc);
+
+       tty_insert_flip_string(tty, data + KLSI_HDR_LEN, len);
+       tty_flip_buffer_push(tty);
+       tty_kref_put(tty);
 }
 
 static void klsi_105_set_termios(struct tty_struct *tty,
@@ -954,28 +690,6 @@ static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file,
        return retval;
 }
 
-static void klsi_105_throttle(struct tty_struct *tty)
-{
-       struct usb_serial_port *port = tty->driver_data;
-       dbg("%s - port %d", __func__, port->number);
-       usb_kill_urb(port->read_urb);
-}
-
-static void klsi_105_unthrottle(struct tty_struct *tty)
-{
-       struct usb_serial_port *port = tty->driver_data;
-       int result;
-
-       dbg("%s - port %d", __func__, port->number);
-
-       port->read_urb->dev = port->serial->dev;
-       result = usb_submit_urb(port->read_urb, GFP_KERNEL);
-       if (result)
-               dev_err(&port->dev,
-                       "%s - failed submitting read urb, error %d\n",
-                       __func__, result);
-}
-
 
 static int __init klsi_105_init(void)
 {