]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/serial/generic.c
Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa
[linux-2.6-omap-h63xx.git] / drivers / usb / serial / generic.c
index 601e0648dec68fbaaa4ecc10e3306820ed9ae154..88a2c7dce335f9b1cde151ae457bf1d55054132d 100644 (file)
 #include <linux/usb/serial.h>
 #include <asm/uaccess.h>
 
-static int generic_probe(struct usb_interface *interface,
-                        const struct usb_device_id *id);
-
 
 static int debug;
 
 #ifdef CONFIG_USB_SERIAL_GENERIC
+
+static int generic_probe(struct usb_interface *interface,
+                        const struct usb_device_id *id);
+
 static __u16 vendor  = 0x05f9;
 static __u16 product = 0xffff;
 
@@ -66,6 +67,9 @@ struct usb_serial_driver usb_serial_generic_device = {
        .num_bulk_out =         NUM_DONT_CARE,
        .num_ports =            1,
        .shutdown =             usb_serial_generic_shutdown,
+       .throttle =             usb_serial_generic_throttle,
+       .unthrottle =           usb_serial_generic_unthrottle,
+       .resume =               usb_serial_generic_resume,
 };
 
 static int generic_probe(struct usb_interface *interface,
@@ -115,6 +119,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
 {
        struct usb_serial *serial = port->serial;
        int result = 0;
+       unsigned long flags;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -124,7 +129,13 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
        if (port->tty)
                port->tty->low_latency = 1;
 
-       /* if we have a bulk interrupt, start reading from it */
+       /* clear the throttle flags */
+       spin_lock_irqsave(&port->lock, flags);
+       port->throttled = 0;
+       port->throttle_req = 0;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       /* if we have a bulk endpoint, start reading from it */
        if (serial->num_bulk_in) {
                /* Start reading from the device */
                usb_fill_bulk_urb (port->read_urb, serial->dev,
@@ -159,6 +170,23 @@ static void generic_cleanup (struct usb_serial_port *port)
        }
 }
 
+int usb_serial_generic_resume(struct usb_serial *serial)
+{
+       struct usb_serial_port *port;
+       int i, c = 0, r;
+
+       for (i = 0; i < serial->num_ports; i++) {
+               port = serial->port[i];
+               if (port->open_count && port->read_urb) {
+                       r = usb_submit_urb(port->read_urb, GFP_NOIO);
+                       if (r < 0)
+                               c++;
+               }
+       }
+
+       return c ? -EIO : 0;
+}
+
 void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp)
 {
        dbg("%s - port %d", __FUNCTION__, port->number);
@@ -253,54 +281,82 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port)
        return (chars);
 }
 
+
+static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags)
+{
+       struct urb *urb = port->read_urb;
+       struct usb_serial *serial = port->serial;
+       int result;
+
+       /* Continue reading from device */
+       usb_fill_bulk_urb (urb, serial->dev,
+                          usb_rcvbulkpipe (serial->dev,
+                                           port->bulk_in_endpointAddress),
+                          urb->transfer_buffer,
+                          urb->transfer_buffer_length,
+                          ((serial->type->read_bulk_callback) ? 
+                            serial->type->read_bulk_callback : 
+                            usb_serial_generic_read_bulk_callback), port);
+       result = usb_submit_urb(urb, mem_flags);
+       if (result)
+               dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+}
+
+/* Push data to tty layer and resubmit the bulk read URB */
+static void flush_and_resubmit_read_urb (struct usb_serial_port *port)
+{
+       struct urb *urb = port->read_urb;
+       struct tty_struct *tty = port->tty;
+       int room;
+
+       /* Push data to tty */
+       if (tty && urb->actual_length) {
+               room = tty_buffer_request_room(tty, urb->actual_length);
+               if (room) {
+                       tty_insert_flip_string(tty, urb->transfer_buffer, room);
+                       tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */
+               }
+       }
+
+       resubmit_read_urb(port, GFP_ATOMIC);
+}
+
 void usb_serial_generic_read_bulk_callback (struct urb *urb)
 {
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct usb_serial *serial = port->serial;
-       struct tty_struct *tty;
        unsigned char *data = urb->transfer_buffer;
-       int result;
+       int status = urb->status;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (urb->status) {
-               dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+       if (unlikely(status != 0)) {
+               dbg("%s - nonzero read bulk status received: %d",
+                   __FUNCTION__, status);
                return;
        }
 
        usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
-       tty = port->tty;
-       if (tty && urb->actual_length) {
-               tty_buffer_request_room(tty, urb->actual_length);
-               tty_insert_flip_string(tty, data, urb->actual_length);
-               tty_flip_buffer_push(tty);
-       }
-
-       /* Continue trying to always read  */
-       usb_fill_bulk_urb (port->read_urb, serial->dev,
-                          usb_rcvbulkpipe (serial->dev,
-                                           port->bulk_in_endpointAddress),
-                          port->read_urb->transfer_buffer,
-                          port->read_urb->transfer_buffer_length,
-                          ((serial->type->read_bulk_callback) ? 
-                            serial->type->read_bulk_callback : 
-                            usb_serial_generic_read_bulk_callback), port);
-       result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-       if (result)
-               dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+       /* Throttle the device if requested by tty */
+       spin_lock(&port->lock);
+       if (!(port->throttled = port->throttle_req))
+               /* Handle data and continue reading from device */
+               flush_and_resubmit_read_urb(port);
+       spin_unlock(&port->lock);
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
 
 void usb_serial_generic_write_bulk_callback (struct urb *urb)
 {
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       int status = urb->status;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
        port->write_urb_busy = 0;
-       if (urb->status) {
-               dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+       if (status) {
+               dbg("%s - nonzero write bulk status received: %d",
+                   __FUNCTION__, status);
                return;
        }
 
@@ -308,6 +364,38 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb)
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
 
+void usb_serial_generic_throttle (struct usb_serial_port *port)
+{
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* Set the throttle request flag. It will be picked up
+        * by usb_serial_generic_read_bulk_callback(). */
+       spin_lock_irqsave(&port->lock, flags);
+       port->throttle_req = 1;
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+void usb_serial_generic_unthrottle (struct usb_serial_port *port)
+{
+       int was_throttled;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* Clear the throttle flags */
+       spin_lock_irqsave(&port->lock, flags);
+       was_throttled = port->throttled;
+       port->throttled = port->throttle_req = 0;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (was_throttled) {
+               /* Resume reading from device */
+               resubmit_read_urb(port, GFP_KERNEL);
+       }
+}
+
 void usb_serial_generic_shutdown (struct usb_serial *serial)
 {
        int i;