USB: serial: opticon: add write support
authorGreg Kroah-Hartman <gregkh@suse.de>
Sat, 7 Feb 2009 02:30:56 +0000 (18:30 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 24 Mar 2009 23:20:30 +0000 (16:20 -0700)
This patch allows data to be sent to the scanner.

Cc: Kees Stoop <kees.stoop@opticon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/opticon.c

index 00d5c60..8c87a49 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Opticon USB barcode to serial driver
  *
- * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
- * Copyright (C) 2008 Novell Inc.
+ * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2008 - 2009 Novell Inc.
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License version
@@ -40,8 +40,12 @@ struct opticon_private {
        bool throttled;
        bool actually_throttled;
        bool rts;
+       int outstanding_urbs;
 };
 
+/* max number of write urbs in flight */
+#define URB_UPPER_LIMIT        4
+
 static void opticon_bulk_callback(struct urb *urb)
 {
        struct opticon_private *priv = urb->context;
@@ -188,6 +192,120 @@ static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port,
        usb_kill_urb(priv->bulk_read_urb);
 }
 
+static void opticon_write_bulk_callback(struct urb *urb)
+{
+       struct opticon_private *priv = urb->context;
+       int status = urb->status;
+       unsigned long flags;
+
+       /* free up the transfer buffer, as usb_free_urb() does not do this */
+       kfree(urb->transfer_buffer);
+
+       if (status)
+               dbg("%s - nonzero write bulk status received: %d",
+                   __func__, status);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       --priv->outstanding_urbs;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       usb_serial_port_softint(priv->port);
+}
+
+static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
+                        const unsigned char *buf, int count)
+{
+       struct opticon_private *priv = usb_get_serial_data(port->serial);
+       struct usb_serial *serial = port->serial;
+       struct urb *urb;
+       unsigned char *buffer;
+       unsigned long flags;
+       int status;
+
+       dbg("%s - port %d", __func__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               dbg("%s - write limit hit\n", __func__);
+               return 0;
+       }
+       priv->outstanding_urbs++;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       buffer = kmalloc(count, GFP_ATOMIC);
+       if (!buffer) {
+               dev_err(&port->dev, "out of memory\n");
+               count = -ENOMEM;
+               goto error_no_buffer;
+       }
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               dev_err(&port->dev, "no more free urbs\n");
+               count = -ENOMEM;
+               goto error_no_urb;
+       }
+
+       memcpy(buffer, buf, count);
+
+       usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
+
+       usb_fill_bulk_urb(urb, serial->dev,
+                         usb_sndbulkpipe(serial->dev,
+                                         port->bulk_out_endpointAddress),
+                         buffer, count, opticon_write_bulk_callback, priv);
+
+       /* send it down the pipe */
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status) {
+               dev_err(&port->dev,
+                  "%s - usb_submit_urb(write bulk) failed with status = %d\n",
+                                                       __func__, status);
+               count = status;
+               goto error;
+       }
+
+       /* we are done with this urb, so let the host driver
+        * really free it when it is finished with it */
+       usb_free_urb(urb);
+
+       return count;
+error:
+       usb_free_urb(urb);
+error_no_urb:
+       kfree(buffer);
+error_no_buffer:
+       spin_lock_irqsave(&priv->lock, flags);
+       --priv->outstanding_urbs;
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return count;
+}
+
+static int opticon_write_room(struct tty_struct *tty)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct opticon_private *priv = usb_get_serial_data(port->serial);
+       unsigned long flags;
+
+       dbg("%s - port %d", __func__, port->number);
+
+       /*
+        * We really can take almost anything the user throws at us
+        * but let's pick a nice big number to tell the tty
+        * layer that we have lots of free space, unless we don't.
+        */
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               dbg("%s - write limit hit\n", __func__);
+               return 0;
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 2048;
+}
+
 static void opticon_throttle(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -352,6 +470,8 @@ static struct usb_serial_driver opticon_device = {
        .attach =               opticon_startup,
        .open =                 opticon_open,
        .close =                opticon_close,
+       .write =                opticon_write,
+       .write_room =           opticon_write_room,
        .shutdown =             opticon_shutdown,
        .throttle =             opticon_throttle,
        .unthrottle =           opticon_unthrottle,