Hi, you cannot use usb_clear_halt() in a completion handler, as it will sleep. The fix is unfortunately enormous. Please test. Regards Oliver -- commit 4df7424df7e99b6ab16c8219e8d17c15cc81d168 Author: Oliver Neukum <oliver@xxxxxxxxxx> Date: Thu Oct 15 18:04:40 2009 +0200 usb:usbserial:cypress_m8: no sleeping in interrupt cypress_m8 must not use usb_clear_halt() in interrupt the fix is to use an URB which requires large changes diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index a591ebe..6aa799a 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -138,6 +138,8 @@ struct cypress_private { int cmd_count; /* used for statistics */ int cmd_ctrl; /* always set this to 1 before issuing a command */ struct cypress_buf *buf; /* write buffer */ + struct usb_ctrlrequest *dr; /* for clearing halts */ + struct urb *clear_urb; /* for clearing halts */ int write_urb_in_use; /* write urb in use indicator */ int write_urb_interval; /* interval to use for write urb */ int read_urb_interval; /* interval to use for read urb */ @@ -189,6 +191,7 @@ static int cypress_tiocmset(struct tty_struct *tty, struct file *file, static int cypress_chars_in_buffer(struct tty_struct *tty); static void cypress_throttle(struct tty_struct *tty); static void cypress_unthrottle(struct tty_struct *tty); +static void cypress_disconnect(struct usb_serial *serial); static void cypress_set_dead(struct usb_serial_port *port); static void cypress_read_int_callback(struct urb *urb); static void cypress_write_int_callback(struct urb *urb); @@ -215,6 +218,7 @@ static struct usb_serial_driver cypress_earthmate_device = { .num_ports = 1, .attach = cypress_earthmate_startup, .release = cypress_release, + .disconnect = cypress_disconnect, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -242,6 +246,7 @@ static struct usb_serial_driver cypress_hidcom_device = { .num_ports = 1, .attach = cypress_hidcom_startup, .release = cypress_release, + .disconnect = cypress_disconnect, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -269,6 +274,7 @@ static struct usb_serial_driver cypress_ca42v2_device = { .num_ports = 1, .attach = cypress_ca42v2_startup, .release = cypress_release, + .disconnect = cypress_disconnect, .open = cypress_open, .close = cypress_close, .dtr_rts = cypress_dtr_rts, @@ -503,12 +509,18 @@ static int generic_startup(struct usb_serial *serial) priv->comm_is_ok = !0; spin_lock_init(&priv->lock); priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); - if (priv->buf == NULL) { - kfree(priv); - return -ENOMEM; - } + if (priv->buf == NULL) + goto error; init_waitqueue_head(&priv->delta_msr_wait); + priv->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!priv->dr) + goto error; + + priv->clear_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->clear_urb) + goto error; + usb_reset_configuration(serial->dev); priv->cmd_ctrl = 0; @@ -541,6 +553,12 @@ static int generic_startup(struct usb_serial *serial) usb_set_serial_port_data(port, priv); return 0; + +error: + kfree(priv->dr); + kfree(priv->buf); + kfree(priv); + return -ENOMEM; } @@ -627,6 +645,8 @@ static void cypress_release(struct usb_serial *serial) if (priv) { cypress_buf_free(priv->buf); + kfree(priv->dr); + usb_free_urb(priv->clear_urb); kfree(priv); } } @@ -715,8 +735,12 @@ static void cypress_close(struct usb_serial_port *port) } cypress_buf_clear(priv->buf); dbg("%s - stopping urbs", __func__); - usb_kill_urb(port->interrupt_in_urb); - usb_kill_urb(port->interrupt_out_urb); + usb_poison_urb(priv->clear_urb); + usb_poison_urb(port->interrupt_in_urb); + usb_poison_urb(port->interrupt_out_urb); + usb_unpoison_urb(priv->clear_urb); + usb_unpoison_urb(port->interrupt_in_urb); + usb_unpoison_urb(port->interrupt_out_urb); if (stats) @@ -1192,6 +1216,19 @@ static void cypress_unthrottle(struct tty_struct *tty) } } +static void cypress_disconnect(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + struct cypress_private *priv = usb_get_serial_port_data(port); + + usb_poison_urb(priv->clear_urb); + usb_poison_urb(port->interrupt_in_urb); + usb_poison_urb(port->interrupt_out_urb); + usb_unpoison_urb(priv->clear_urb); + usb_unpoison_urb(port->interrupt_in_urb); + usb_unpoison_urb(port->interrupt_out_urb); +} + static void cypress_read_int_callback(struct urb *urb) { @@ -1336,7 +1373,7 @@ continue_read: cypress_read_int_callback, port, priv->read_urb_interval); result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - if (result) { + if (result && result != -EPERM) { dev_err(&urb->dev->dev, "%s - failed resubmitting " "read urb, error %d\n", __func__, result); @@ -1347,6 +1384,29 @@ continue_read: return; } /* cypress_read_int_callback */ +static void cypress_clear_halt_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + int status = urb->status; + int result; + + if (status) { + dbg("%s - clearing halt with status: %d", + __func__, status); + cypress_set_dead(port); + return; + } + + port->interrupt_out_urb->transfer_buffer_length = 1; + port->interrupt_out_urb->dev = port->serial->dev; + result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); + if (result == 0 || result == -EPERM) + return; + dev_err(&urb->dev->dev, + "%s - failed resubmitting write urb, error %d\n", + __func__, result); + cypress_set_dead(port); +} static void cypress_write_int_callback(struct urb *urb) { @@ -1372,20 +1432,28 @@ static void cypress_write_int_callback(struct urb *urb) case -EPIPE: /* no break needed; clear halt and resubmit */ if (!priv->comm_is_ok) break; - usb_clear_halt(port->serial->dev, 0x02); + + priv->dr->bRequestType = USB_RECIP_ENDPOINT; + priv->dr->bRequest = USB_REQ_CLEAR_FEATURE; + priv->dr->wValue = cpu_to_le16(USB_ENDPOINT_HALT); + priv->dr->wIndex = cpu_to_le16(0x02); + priv->dr->wLength = cpu_to_le16(0); + + usb_fill_control_urb(priv->clear_urb, + urb->dev, + usb_sndctrlpipe(urb->dev, 0), + (unsigned char *)priv->dr, + NULL, 0, + cypress_clear_halt_callback, + NULL); + result = usb_submit_urb(priv->clear_urb, GFP_ATOMIC); + /* error in the urb, so we have to resubmit it */ dbg("%s - nonzero write bulk status received: %d", __func__, status); - port->interrupt_out_urb->transfer_buffer_length = 1; - port->interrupt_out_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); - if (!result) - return; - dev_err(&urb->dev->dev, - "%s - failed resubmitting write urb, error %d\n", - __func__, result); - cypress_set_dead(port); - break; + if (result && result != -EPERM) + cypress_set_dead(port); + return; default: dev_err(&urb->dev->dev, "%s - unexpected nonzero write status received: %d\n", -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html