Commit 65dacb23 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: change cdc-acm to do RX URB processing in a tasklet

Just for cdc-acm, it pushes RX URB processing into a tasklet;
and has minor cleanups.

I cc'd Vojtech since he's this driver's maintainer.  If this
checks out, usb-serial will need similar changes.


p.s. the issue is a WARN_ON that tells us:

   >> [<c012046c>] local_bh_enable+0x8c/0x90
   >> [<f8991452>] ppp_asynctty_receive+0x62/0xb0 [ppp_async]
   >> [<c02144f3>] flush_to_ldisc+0xa3/0x120
   >> [<f891f20f>] acm_read_bulk+0xbf/0x140 [cdc_acm]
   >> [<c02684c9>] usb_hcd_giveback_urb+0x29/0x50
   >> [<c027670c>] dl_done_list+0x11c/0x130
   >> [<c0277075>] ohci_irq+0x85/0x170
   >> [<c0268526>] usb_hcd_irq+0x36/0x60
   >> [<c010aeba>] handle_IRQ_event+0x3a/0x70
   >> [<c010b227>] do_IRQ+0x97/0x140
   >> [<c0109624>] common_interrupt+0x18/0x20
parent b69a6df3
/* /*
* acm.c Version 0.22 * cdc-acm.c
* *
* Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
* Copyright (c) 1999 Pavel Machek <pavel@suse.cz> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
* v0.21 - revert to probing on device for devices with multiple configs * v0.21 - revert to probing on device for devices with multiple configs
* v0.22 - probe only the control interface. if usbcore doesn't choose the * v0.22 - probe only the control interface. if usbcore doesn't choose the
* config we want, sysadmin changes bConfigurationValue in sysfs. * config we want, sysadmin changes bConfigurationValue in sysfs.
* v0.23 - use softirq for rx processing, as needed by tty layer
*/ */
/* /*
...@@ -44,6 +45,8 @@ ...@@ -44,6 +45,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#undef DEBUG
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -54,14 +57,13 @@ ...@@ -54,14 +57,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#undef DEBUG
#include <linux/usb.h> #include <linux/usb.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v0.21" #define DRIVER_VERSION "v0.23"
#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik" #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
...@@ -147,6 +149,7 @@ struct acm { ...@@ -147,6 +149,7 @@ struct acm {
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
struct acm_line line; /* line coding (bits, stop, parity) */ struct acm_line line; /* line coding (bits, stop, parity) */
struct work_struct work; /* work queue entry for line discipline waking up */ struct work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct bh; /* rx processing */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int writesize; /* max packet size for the output bulk endpoint */
...@@ -184,9 +187,10 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int ...@@ -184,9 +187,10 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
#define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0) #define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
/* /*
* Interrupt handler for various ACM control events * Interrupt handlers for various ACM device responses
*/ */
/* control interface reports status changes with "interrupt" transfers */
static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs) static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
{ {
struct acm *acm = urb->context; struct acm *acm = urb->context;
...@@ -251,20 +255,30 @@ static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs) ...@@ -251,20 +255,30 @@ static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
__FUNCTION__, status); __FUNCTION__, status);
} }
/* data interface returns incoming bytes, or we got unthrottled */
static void acm_read_bulk(struct urb *urb, struct pt_regs *regs) static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
{ {
struct acm *acm = urb->context; struct acm *acm = urb->context;
struct tty_struct *tty = acm->tty;
unsigned char *data = urb->transfer_buffer;
int i = 0;
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return; return;
if (urb->status) if (urb->status)
dbg("nonzero read bulk status received: %d", urb->status); dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
/* calling tty_flip_buffer_push() in_irq() isn't allowed */
tasklet_schedule(&acm->bh);
}
static void acm_rx_tasklet(unsigned long _acm)
{
struct acm *acm = (void *)_acm;
struct urb *urb = acm->readurb;
struct tty_struct *tty = acm->tty;
unsigned char *data = urb->transfer_buffer;
int i = 0;
if (!urb->status && !acm->throttle) { if (urb->actual_length > 0 && !acm->throttle) {
for (i = 0; i < urb->actual_length && !acm->throttle; i++) { for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
/* if we insert more than TTY_FLIPBUF_SIZE characters, /* if we insert more than TTY_FLIPBUF_SIZE characters,
* we drop them. */ * we drop them. */
...@@ -285,10 +299,12 @@ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs) ...@@ -285,10 +299,12 @@ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
urb->actual_length = 0; urb->actual_length = 0;
urb->dev = acm->dev; urb->dev = acm->dev;
if (usb_submit_urb(urb, GFP_ATOMIC)) i = usb_submit_urb(urb, GFP_ATOMIC);
dbg("failed resubmitting read urb"); if (i)
dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i);
} }
/* data interface wrote those outgoing bytes */
static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
{ {
struct acm *acm = (struct acm *)urb->context; struct acm *acm = (struct acm *)urb->context;
...@@ -621,6 +637,8 @@ static int acm_probe (struct usb_interface *intf, ...@@ -621,6 +637,8 @@ static int acm_probe (struct usb_interface *intf,
acm->minor = minor; acm->minor = minor;
acm->dev = dev; acm->dev = dev;
acm->bh.func = acm_rx_tasklet;
acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm); INIT_WORK(&acm->work, acm_softint, acm);
if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment