tty: cdc_acm add krefs
authorAlan Cox <alan@linux.intel.com>
Thu, 11 Jun 2009 11:36:09 +0000 (12:36 +0100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jun 2009 15:50:58 +0000 (08:50 -0700)
Now we have a port structure begin using the fields and kref counts

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-acm.h

index 41d4ca5..b4e73aa 100644 (file)
@@ -62,7 +62,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <linux/usb.h>
 #include <linux/usb/cdc.h>
 #include <asm/byteorder.h>
@@ -87,7 +87,7 @@ static struct acm *acm_table[ACM_TTY_MINORS];
 
 static DEFINE_MUTEX(open_mutex);
 
-#define ACM_READY(acm) (acm && acm->dev && acm->used)
+#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
 
 static const struct tty_port_operations acm_port_ops = {
 };
@@ -265,6 +265,7 @@ static void acm_ctrl_irq(struct urb *urb)
 {
        struct acm *acm = urb->context;
        struct usb_cdc_notification *dr = urb->transfer_buffer;
+       struct tty_struct *tty;
        unsigned char *data;
        int newctrl;
        int retval;
@@ -297,12 +298,16 @@ static void acm_ctrl_irq(struct urb *urb)
                        break;
 
                case USB_CDC_NOTIFY_SERIAL_STATE:
-
+                       tty = tty_port_tty_get(&acm->port);
                        newctrl = get_unaligned_le16(data);
 
-                       if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
-                               dbg("calling hangup");
-                               tty_hangup(acm->tty);
+                       if (tty) {
+                               if (!acm->clocal &&
+                                   (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
+                                       dbg("calling hangup");
+                                       tty_hangup(tty);
+                               }
+                               tty_kref_put(tty);
                        }
 
                        acm->ctrlin = newctrl;
@@ -374,15 +379,14 @@ static void acm_rx_tasklet(unsigned long _acm)
 {
        struct acm *acm = (void *)_acm;
        struct acm_rb *buf;
-       struct tty_struct *tty = acm->tty;
+       struct tty_struct *tty;
        struct acm_ru *rcv;
        unsigned long flags;
        unsigned char throttled;
 
        dbg("Entering acm_rx_tasklet");
 
-       if (!ACM_READY(acm))
-       {
+       if (!ACM_READY(acm)) {
                dbg("acm_rx_tasklet: ACM not ready");
                return;
        }
@@ -390,12 +394,13 @@ static void acm_rx_tasklet(unsigned long _acm)
        spin_lock_irqsave(&acm->throttle_lock, flags);
        throttled = acm->throttle;
        spin_unlock_irqrestore(&acm->throttle_lock, flags);
-       if (throttled)
-       {
+       if (throttled) {
                dbg("acm_rx_tasklet: throttled");
                return;
        }
 
+       tty = tty_port_tty_get(&acm->port);
+
 next_buffer:
        spin_lock_irqsave(&acm->read_lock, flags);
        if (list_empty(&acm->filled_read_bufs)) {
@@ -409,20 +414,22 @@ next_buffer:
 
        dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
 
-       tty_buffer_request_room(tty, buf->size);
-       spin_lock_irqsave(&acm->throttle_lock, flags);
-       throttled = acm->throttle;
-       spin_unlock_irqrestore(&acm->throttle_lock, flags);
-       if (!throttled)
-               tty_insert_flip_string(tty, buf->base, buf->size);
-       tty_flip_buffer_push(tty);
-
-       if (throttled) {
-               dbg("Throttling noticed");
-               spin_lock_irqsave(&acm->read_lock, flags);
-               list_add(&buf->list, &acm->filled_read_bufs);
-               spin_unlock_irqrestore(&acm->read_lock, flags);
-               return;
+       if (tty) {
+               spin_lock_irqsave(&acm->throttle_lock, flags);
+               throttled = acm->throttle;
+               spin_unlock_irqrestore(&acm->throttle_lock, flags);
+               if (!throttled) {
+                       tty_buffer_request_room(tty, buf->size);
+                       tty_insert_flip_string(tty, buf->base, buf->size);
+                       tty_flip_buffer_push(tty);
+               } else {
+                       tty_kref_put(tty);
+                       dbg("Throttling noticed");
+                       spin_lock_irqsave(&acm->read_lock, flags);
+                       list_add(&buf->list, &acm->filled_read_bufs);
+                       spin_unlock_irqrestore(&acm->read_lock, flags);
+                       return;
+               }
        }
 
        spin_lock_irqsave(&acm->read_lock, flags);
@@ -431,6 +438,8 @@ next_buffer:
        goto next_buffer;
 
 urbs:
+       tty_kref_put(tty);
+
        while (!list_empty(&acm->spare_read_bufs)) {
                spin_lock_irqsave(&acm->read_lock, flags);
                if (list_empty(&acm->spare_read_urbs)) {
@@ -502,11 +511,14 @@ static void acm_write_bulk(struct urb *urb)
 static void acm_softint(struct work_struct *work)
 {
        struct acm *acm = container_of(work, struct acm, work);
+       struct tty_struct *tty;
 
        dev_vdbg(&acm->data->dev, "tx work\n");
        if (!ACM_READY(acm))
                return;
-       tty_wakeup(acm->tty);
+       tty = tty_port_tty_get(&acm->port);
+       tty_wakeup(tty);
+       tty_kref_put(tty);
 }
 
 static void acm_waker(struct work_struct *waker)
@@ -546,8 +558,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
                rv = 0;
 
        set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
+
        tty->driver_data = acm;
-       acm->tty = tty;
+       tty_port_tty_set(&acm->port, tty);
 
        if (usb_autopm_get_interface(acm->control) < 0)
                goto early_bail;
@@ -555,11 +568,10 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
                acm->control->needs_remote_wakeup = 1;
 
        mutex_lock(&acm->mutex);
-       if (acm->used++) {
+       if (acm->port.count++) {
                usb_autopm_put_interface(acm->control);
                goto done;
-        }
-
+       }
 
        acm->ctrlurb->dev = acm->dev;
        if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
@@ -570,6 +582,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
        if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
            (acm->ctrl_caps & USB_CDC_CAP_LINE))
                goto full_bailout;
+
        usb_autopm_put_interface(acm->control);
 
        INIT_LIST_HEAD(&acm->spare_read_urbs);
@@ -585,7 +598,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
        acm->throttle = 0;
 
        tasklet_schedule(&acm->urb_task);
-
+       rv = tty_port_block_til_ready(&acm->port, tty, filp);
 done:
        mutex_unlock(&acm->mutex);
 err_out:
@@ -596,16 +609,17 @@ full_bailout:
        usb_kill_urb(acm->ctrlurb);
 bail_out:
        usb_autopm_put_interface(acm->control);
-       acm->used--;
+       acm->port.count--;
        mutex_unlock(&acm->mutex);
 early_bail:
        mutex_unlock(&open_mutex);
+       tty_port_tty_set(&acm->port, NULL);
        return -EIO;
 }
 
 static void acm_tty_unregister(struct acm *acm)
 {
-       int i,nr;
+       int i, nr;
 
        nr = acm->rx_buflimit;
        tty_unregister_device(acm_tty_driver, acm->minor);
@@ -622,37 +636,51 @@ static void acm_tty_unregister(struct acm *acm)
 
 static int acm_tty_chars_in_buffer(struct tty_struct *tty);
 
+static void acm_port_down(struct acm *acm, int drain)
+{
+       int i, nr = acm->rx_buflimit;
+       mutex_lock(&open_mutex);
+       if (acm->dev) {
+               usb_autopm_get_interface(acm->control);
+               acm_set_control(acm, acm->ctrlout = 0);
+               /* try letting the last writes drain naturally */
+               if (drain) {
+                       wait_event_interruptible_timeout(acm->drain_wait,
+                               (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
+                                       ACM_CLOSE_TIMEOUT * HZ);
+               }
+               usb_kill_urb(acm->ctrlurb);
+               for (i = 0; i < ACM_NW; i++)
+                       usb_kill_urb(acm->wb[i].urb);
+               for (i = 0; i < nr; i++)
+                       usb_kill_urb(acm->ru[i].urb);
+               acm->control->needs_remote_wakeup = 0;
+               usb_autopm_put_interface(acm->control);
+       }
+       mutex_unlock(&open_mutex);
+}
+
+static void acm_tty_hangup(struct tty_struct *tty)
+{
+       struct acm *acm = tty->driver_data;
+       tty_port_hangup(&acm->port);
+       acm_port_down(acm, 0);
+}
+
 static void acm_tty_close(struct tty_struct *tty, struct file *filp)
 {
        struct acm *acm = tty->driver_data;
-       int i,nr;
 
-       if (!acm || !acm->used)
+       /* Perform the closing process and see if we need to do the hardware
+          shutdown */
+       if (tty_port_close_start(&acm->port, tty, filp) == 0)
                return;
-
-       nr = acm->rx_buflimit;
+       acm_port_down(acm, 0);
+       tty_port_close_end(&acm->port, tty);
        mutex_lock(&open_mutex);
-       if (!--acm->used) {
-               if (acm->dev) {
-                       usb_autopm_get_interface(acm->control);
-                       acm_set_control(acm, acm->ctrlout = 0);
-
-                       /* try letting the last writes drain naturally */
-                       wait_event_interruptible_timeout(acm->drain_wait,
-                                       (ACM_NW == acm_wb_is_avail(acm))
-                                               || !acm->dev,
-                                       ACM_CLOSE_TIMEOUT * HZ);
-
-                       usb_kill_urb(acm->ctrlurb);
-                       for (i = 0; i < ACM_NW; i++)
-                               usb_kill_urb(acm->wb[i].urb);
-                       for (i = 0; i < nr; i++)
-                               usb_kill_urb(acm->ru[i].urb);
-                       acm->control->needs_remote_wakeup = 0;
-                       usb_autopm_put_interface(acm->control);
-               } else
-                       acm_tty_unregister(acm);
-       }
+       tty_port_tty_set(&acm->port, NULL);
+       if (!acm->dev)
+               acm_tty_unregister(acm);
        mutex_unlock(&open_mutex);
 }
 
@@ -885,8 +913,8 @@ static int acm_write_buffers_alloc(struct acm *acm)
        return 0;
 }
 
-static int acm_probe (struct usb_interface *intf,
-                     const struct usb_device_id *id)
+static int acm_probe(struct usb_interface *intf,
+                    const struct usb_device_id *id)
 {
        struct usb_cdc_union_desc *union_header = NULL;
        struct usb_cdc_country_functional_desc *cfd = NULL;
@@ -1232,6 +1260,7 @@ static void acm_disconnect(struct usb_interface *intf)
 {
        struct acm *acm = usb_get_intfdata(intf);
        struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct tty_struct *tty;
 
        /* sibling interface is already cleaning up */
        if (!acm)
@@ -1258,16 +1287,18 @@ static void acm_disconnect(struct usb_interface *intf)
        usb_driver_release_interface(&acm_driver, intf == acm->control ?
                                        acm->data : acm->control);
 
-       if (!acm->used) {
+       if (acm->port.count == 0) {
                acm_tty_unregister(acm);
                mutex_unlock(&open_mutex);
                return;
        }
 
        mutex_unlock(&open_mutex);
-
-       if (acm->tty)
-               tty_hangup(acm->tty);
+       tty = tty_port_tty_get(&acm->port);
+       if (tty) {
+               tty_hangup(tty);
+               tty_kref_put(tty);
+       }
 }
 
 #ifdef CONFIG_PM
@@ -1302,7 +1333,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
        */
        mutex_lock(&acm->mutex);
 
-       if (acm->used)
+       if (acm->port.count)
                stop_data_traffic(acm);
 
        mutex_unlock(&acm->mutex);
@@ -1324,7 +1355,7 @@ static int acm_resume(struct usb_interface *intf)
                return 0;
 
        mutex_lock(&acm->mutex);
-       if (acm->used) {
+       if (acm->port.count) {
                rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
                if (rv < 0)
                        goto err_out;
@@ -1434,6 +1465,7 @@ static struct usb_driver acm_driver = {
 static const struct tty_operations acm_ops = {
        .open =                 acm_tty_open,
        .close =                acm_tty_close,
+       .hangup =               acm_tty_hangup,
        .write =                acm_tty_write,
        .write_room =           acm_tty_write_room,
        .ioctl =                acm_tty_ioctl,
index 19967cd..4c38564 100644 (file)
@@ -89,7 +89,6 @@ struct acm {
        struct usb_device *dev;                         /* the corresponding usb device */
        struct usb_interface *control;                  /* control interface */
        struct usb_interface *data;                     /* data interface */
-       struct tty_struct *tty;                         /* the corresponding tty */
        struct tty_port port;                           /* our tty port data */
        struct urb *ctrlurb;                            /* urbs */
        u8 *ctrl_buffer;                                /* buffers of urbs */
@@ -121,7 +120,6 @@ struct acm {
        unsigned int ctrlout;                           /* output control lines (DTR, RTS) */
        unsigned int writesize;                         /* max packet size for the output bulk endpoint */
        unsigned int readsize,ctrlsize;                 /* buffer sizes for freeing */
-       unsigned int used;                              /* someone has this acm's device open */
        unsigned int minor;                             /* acm minor number */
        unsigned char throttle;                         /* throttled by tty layer */
        unsigned char clocal;                           /* termios CLOCAL */