Merge branch 'for-arm-soc-next' of git://git.linaro.org/people/ljones/linux-3.0-ux500...
[linux-3.10.git] / drivers / usb / gadget / u_serial.c
index a0f79df..f173952 100644 (file)
 /* #define VERBOSE_DEBUG */
 
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/slab.h>
+#include <linux/export.h>
 
 #include "u_serial.h"
 
@@ -92,23 +94,24 @@ struct gs_buf {
  * (and thus for each /dev/ node).
  */
 struct gs_port {
+       struct tty_port         port;
        spinlock_t              port_lock;      /* guard port_* access */
 
        struct gserial          *port_usb;
-       struct tty_struct       *port_tty;
 
-       unsigned                open_count;
        bool                    openclose;      /* open/close in progress */
        u8                      port_num;
 
-       wait_queue_head_t       close_wait;     /* wait for last close */
-
        struct list_head        read_pool;
+       int read_started;
+       int read_allocated;
        struct list_head        read_queue;
        unsigned                n_read;
        struct tasklet_struct   push;
 
        struct list_head        write_pool;
+       int write_started;
+       int write_allocated;
        struct gs_buf           port_write_buf;
        wait_queue_head_t       drain_wait;     /* wait while writes drain */
 
@@ -129,11 +132,15 @@ static unsigned   n_ports;
 
 
 #ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
 #define pr_vdebug(fmt, arg...) \
        pr_debug(fmt, ##arg)
+#endif /* pr_vdebug */
 #else
+#ifndef pr_vdebig
 #define pr_vdebug(fmt, arg...) \
        ({ if (0) pr_debug(fmt, ##arg); })
+#endif /* pr_vdebug */
 #endif
 
 /*-------------------------------------------------------------------------*/
@@ -362,6 +369,9 @@ __acquires(&port->port_lock)
                struct usb_request      *req;
                int                     len;
 
+               if (port->write_started >= QUEUE_SIZE)
+                       break;
+
                req = list_entry(pool->next, struct usb_request, list);
                len = gs_send_packet(port, req->buf, in->maxpacket);
                if (len == 0) {
@@ -396,13 +406,15 @@ __acquires(&port->port_lock)
                        break;
                }
 
+               port->write_started++;
+
                /* abort immediately after disconnect */
                if (!port->port_usb)
                        break;
        }
 
-       if (do_tty_wake && port->port_tty)
-               tty_wakeup(port->port_tty);
+       if (do_tty_wake && port->port.tty)
+               tty_wakeup(port->port.tty);
        return status;
 }
 
@@ -417,7 +429,6 @@ __acquires(&port->port_lock)
 {
        struct list_head        *pool = &port->read_pool;
        struct usb_ep           *out = port->port_usb->out;
-       unsigned                started = 0;
 
        while (!list_empty(pool)) {
                struct usb_request      *req;
@@ -425,10 +436,13 @@ __acquires(&port->port_lock)
                struct tty_struct       *tty;
 
                /* no more rx if closed */
-               tty = port->port_tty;
+               tty = port->port.tty;
                if (!tty)
                        break;
 
+               if (port->read_started >= QUEUE_SIZE)
+                       break;
+
                req = list_entry(pool->next, struct usb_request, list);
                list_del(&req->list);
                req->length = out->maxpacket;
@@ -446,13 +460,13 @@ __acquires(&port->port_lock)
                        list_add(&req->list, pool);
                        break;
                }
-               started++;
+               port->read_started++;
 
                /* abort immediately after disconnect */
                if (!port->port_usb)
                        break;
        }
-       return started;
+       return port->read_started;
 }
 
 /*
@@ -475,7 +489,7 @@ static void gs_rx_push(unsigned long _port)
 
        /* hand any queued data to the tty */
        spin_lock_irq(&port->port_lock);
-       tty = port->port_tty;
+       tty = port->port.tty;
        while (!list_empty(queue)) {
                struct usb_request      *req;
 
@@ -534,14 +548,14 @@ static void gs_rx_push(unsigned long _port)
                }
 recycle:
                list_move(&req->list, &port->read_pool);
+               port->read_started--;
        }
 
        /* Push from tty to ldisc; without low_latency set this is handled by
         * a workqueue, so we won't get callbacks and can hold port_lock
         */
-       if (tty && do_push) {
+       if (tty && do_push)
                tty_flip_buffer_push(tty);
-       }
 
 
        /* We want our data queue to become empty ASAP, keeping data
@@ -586,6 +600,7 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
 
        spin_lock(&port->port_lock);
        list_add(&req->list, &port->write_pool);
+       port->write_started--;
 
        switch (req->status) {
        default:
@@ -607,7 +622,8 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
        spin_unlock(&port->port_lock);
 }
 
-static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
+                                                        int *allocated)
 {
        struct usb_request      *req;
 
@@ -615,25 +631,31 @@ static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
                req = list_entry(head->next, struct usb_request, list);
                list_del(&req->list);
                gs_free_req(ep, req);
+               if (allocated)
+                       (*allocated)--;
        }
 }
 
 static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
-               void (*fn)(struct usb_ep *, struct usb_request *))
+               void (*fn)(struct usb_ep *, struct usb_request *),
+               int *allocated)
 {
        int                     i;
        struct usb_request      *req;
+       int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
 
        /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
         * do quite that many this time, don't fail ... we just won't
         * be as speedy as we might otherwise be.
         */
-       for (i = 0; i < QUEUE_SIZE; i++) {
+       for (i = 0; i < n; i++) {
                req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
                if (!req)
                        return list_empty(head) ? -ENOMEM : 0;
                req->complete = fn;
                list_add_tail(&req->list, head);
+               if (allocated)
+                       (*allocated)++;
        }
        return 0;
 }
@@ -660,14 +682,15 @@ static int gs_start_io(struct gs_port *port)
         * configurations may use different endpoints with a given port;
         * and high speed vs full speed changes packet sizes too.
         */
-       status = gs_alloc_requests(ep, head, gs_read_complete);
+       status = gs_alloc_requests(ep, head, gs_read_complete,
+               &port->read_allocated);
        if (status)
                return status;
 
        status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
-                       gs_write_complete);
+                       gs_write_complete, &port->write_allocated);
        if (status) {
-               gs_free_requests(ep, head);
+               gs_free_requests(ep, head, &port->read_allocated);
                return status;
        }
 
@@ -677,10 +700,11 @@ static int gs_start_io(struct gs_port *port)
 
        /* unblock any pending writes into our circular buffer */
        if (started) {
-               tty_wakeup(port->port_tty);
+               tty_wakeup(port->port.tty);
        } else {
-               gs_free_requests(ep, head);
-               gs_free_requests(port->port_usb->in, &port->write_pool);
+               gs_free_requests(ep, head, &port->read_allocated);
+               gs_free_requests(port->port_usb->in, &port->write_pool,
+                       &port->write_allocated);
                status = -EIO;
        }
 
@@ -702,9 +726,6 @@ static int gs_open(struct tty_struct *tty, struct file *file)
        struct gs_port  *port;
        int             status;
 
-       if (port_num < 0 || port_num >= n_ports)
-               return -ENXIO;
-
        do {
                mutex_lock(&ports[port_num].lock);
                port = ports[port_num].port;
@@ -714,9 +735,9 @@ static int gs_open(struct tty_struct *tty, struct file *file)
                        spin_lock_irq(&port->port_lock);
 
                        /* already open?  Great. */
-                       if (port->open_count) {
+                       if (port->port.count) {
                                status = 0;
-                               port->open_count++;
+                               port->port.count++;
 
                        /* currently opening/closing? wait ... */
                        } else if (port->openclose) {
@@ -773,9 +794,9 @@ static int gs_open(struct tty_struct *tty, struct file *file)
        /* REVISIT maybe wait for "carrier detect" */
 
        tty->driver_data = port;
-       port->port_tty = tty;
+       port->port.tty = tty;
 
-       port->open_count = 1;
+       port->port.count = 1;
        port->openclose = false;
 
        /* if connected, start the I/O stream */
@@ -817,11 +838,11 @@ static void gs_close(struct tty_struct *tty, struct file *file)
 
        spin_lock_irq(&port->port_lock);
 
-       if (port->open_count != 1) {
-               if (port->open_count == 0)
+       if (port->port.count != 1) {
+               if (port->port.count == 0)
                        WARN_ON(1);
                else
-                       --port->open_count;
+                       --port->port.count;
                goto exit;
        }
 
@@ -831,7 +852,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
         * and sleep if necessary
         */
        port->openclose = true;
-       port->open_count = 0;
+       port->port.count = 0;
 
        gser = port->port_usb;
        if (gser && gser->disconnect)
@@ -859,14 +880,14 @@ static void gs_close(struct tty_struct *tty, struct file *file)
                gs_buf_clear(&port->port_write_buf);
 
        tty->driver_data = NULL;
-       port->port_tty = NULL;
+       port->port.tty = NULL;
 
        port->openclose = false;
 
        pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
                        port->port_num, tty, file);
 
-       wake_up_interruptible(&port->close_wait);
+       wake_up_interruptible(&port->port.close_wait);
 exit:
        spin_unlock_irq(&port->port_lock);
 }
@@ -897,7 +918,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
        unsigned long   flags;
        int             status;
 
-       pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
+       pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
                port->port_num, tty, ch, __builtin_return_address(0));
 
        spin_lock_irqsave(&port->port_lock, flags);
@@ -1005,7 +1026,7 @@ static const struct tty_operations gs_tty_ops = {
 
 static struct tty_driver *gs_tty_driver;
 
-static int __init
+static int
 gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
 {
        struct gs_port  *port;
@@ -1014,8 +1035,8 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
        if (port == NULL)
                return -ENOMEM;
 
+       tty_port_init(&port->port);
        spin_lock_init(&port->port_lock);
-       init_waitqueue_head(&port->close_wait);
        init_waitqueue_head(&port->drain_wait);
 
        tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
@@ -1051,7 +1072,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
  *
  * Returns negative errno or zero.
  */
-int __init gserial_setup(struct usb_gadget *g, unsigned count)
+int gserial_setup(struct usb_gadget *g, unsigned count)
 {
        unsigned                        i;
        struct usb_cdc_line_coding      coding;
@@ -1064,7 +1085,6 @@ int __init gserial_setup(struct usb_gadget *g, unsigned count)
        if (!gs_tty_driver)
                return -ENOMEM;
 
-       gs_tty_driver->owner = THIS_MODULE;
        gs_tty_driver->driver_name = "g_serial";
        gs_tty_driver->name = PREFIX;
        /* uses dynamically assigned dev_t values */
@@ -1113,7 +1133,8 @@ int __init gserial_setup(struct usb_gadget *g, unsigned count)
        for (i = 0; i < count; i++) {
                struct device   *tty_dev;
 
-               tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
+               tty_dev = tty_port_register_device(&ports[i].port->port,
+                               gs_tty_driver, i, &g->dev);
                if (IS_ERR(tty_dev))
                        pr_warning("%s: no classdev for port %d, err %ld\n",
                                __func__, i, PTR_ERR(tty_dev));
@@ -1136,7 +1157,7 @@ static int gs_closed(struct gs_port *port)
        int cond;
 
        spin_lock_irq(&port->port_lock);
-       cond = (port->open_count == 0) && !port->openclose;
+       cond = (port->port.count == 0) && !port->openclose;
        spin_unlock_irq(&port->port_lock);
        return cond;
 }
@@ -1175,7 +1196,7 @@ void gserial_cleanup(void)
                tasklet_kill(&port->push);
 
                /* wait for old opens to finish */
-               wait_event(port->close_wait, gs_closed(port));
+               wait_event(port->port.close_wait, gs_closed(port));
 
                WARN_ON(port->port_usb != NULL);
 
@@ -1184,6 +1205,7 @@ void gserial_cleanup(void)
        n_ports = 0;
 
        tty_unregister_driver(gs_tty_driver);
+       put_tty_driver(gs_tty_driver);
        gs_tty_driver = NULL;
 
        pr_debug("%s: cleaned up ttyGS* support\n", __func__);
@@ -1223,12 +1245,12 @@ int gserial_connect(struct gserial *gser, u8 port_num)
        port = ports[port_num].port;
 
        /* activate the endpoints */
-       status = usb_ep_enable(gser->in, gser->in_desc);
+       status = usb_ep_enable(gser->in);
        if (status < 0)
                return status;
        gser->in->driver_data = port;
 
-       status = usb_ep_enable(gser->out, gser->out_desc);
+       status = usb_ep_enable(gser->out);
        if (status < 0)
                goto fail_out;
        gser->out->driver_data = port;
@@ -1248,7 +1270,7 @@ int gserial_connect(struct gserial *gser, u8 port_num)
        /* if it's already open, start I/O ... and notify the serial
         * protocol about open/close status (connect/disconnect).
         */
-       if (port->open_count) {
+       if (port->port.count) {
                pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
                gs_start_io(port);
                if (gser->connect)
@@ -1295,10 +1317,10 @@ void gserial_disconnect(struct gserial *gser)
 
        port->port_usb = NULL;
        gser->ioport = NULL;
-       if (port->open_count > 0 || port->openclose) {
+       if (port->port.count > 0 || port->openclose) {
                wake_up_interruptible(&port->drain_wait);
-               if (port->port_tty)
-                       tty_hangup(port->port_tty);
+               if (port->port.tty)
+                       tty_hangup(port->port.tty);
        }
        spin_unlock_irqrestore(&port->port_lock, flags);
 
@@ -1311,10 +1333,14 @@ void gserial_disconnect(struct gserial *gser)
 
        /* finally, free any unused/unusable I/O buffers */
        spin_lock_irqsave(&port->port_lock, flags);
-       if (port->open_count == 0 && !port->openclose)
+       if (port->port.count == 0 && !port->openclose)
                gs_buf_free(&port->port_write_buf);
-       gs_free_requests(gser->out, &port->read_pool);
-       gs_free_requests(gser->out, &port->read_queue);
-       gs_free_requests(gser->in, &port->write_pool);
+       gs_free_requests(gser->out, &port->read_pool, NULL);
+       gs_free_requests(gser->out, &port->read_queue, NULL);
+       gs_free_requests(gser->in, &port->write_pool, NULL);
+
+       port->read_allocated = port->read_started =
+               port->write_allocated = port->write_started = 0;
+
        spin_unlock_irqrestore(&port->port_lock, flags);
 }