From ea9da1c79eb9a28176550d0b8ba9166e6e5f42b8 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Dec 2011 11:55:44 -0800 Subject: UAS: Re-add workqueue items if submission fails. If the original submission (or allocation) of the URBs for a SCSI command fails, the UAS driver sticks the command structure in a workqueue and schedules uas_do_work() to run. That function removes the entire queue before walking across it and attempting to resubmit. Unfortunately, if the second submission fails, we will leak memory (because an allocated URB was not submitted) and possibly leave the SCSI command partially enqueued on some of the stream rings. Fix this by checking whether the second submission failed and re-queueing the command to the UAS workqueue and scheduling it. Signed-off-by: Sarah Sharp Cc: Matthew Wilcox Signed-off-by: Sebastian Andrzej Siewior diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 1d10d5b..4bbaf6e 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -125,29 +125,38 @@ struct uas_cmd_info { /* I hate forward declarations, but I actually have a loop */ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_dev_info *devinfo, gfp_t gfp); +static void uas_do_work(struct work_struct *work); +static DECLARE_WORK(uas_work, uas_do_work); static DEFINE_SPINLOCK(uas_work_lock); static LIST_HEAD(uas_work_list); static void uas_do_work(struct work_struct *work) { struct uas_cmd_info *cmdinfo; + struct uas_cmd_info *temp; struct list_head list; + int err; spin_lock_irq(&uas_work_lock); list_replace_init(&uas_work_list, &list); spin_unlock_irq(&uas_work_lock); - list_for_each_entry(cmdinfo, &list, list) { + list_for_each_entry_safe(cmdinfo, temp, &list, list) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); - uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); + err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); + if (err) { + list_del(&cmdinfo->list); + spin_lock_irq(&uas_work_lock); + list_add_tail(&cmdinfo->list, &uas_work_list); + spin_unlock_irq(&uas_work_lock); + schedule_work(&uas_work); + } } } -static DECLARE_WORK(uas_work, uas_do_work); - static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu *sense_iu = urb->transfer_buffer; -- cgit v0.10.2 From 9eb445410db99e5f5f660e97a2165a0567bd909e Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Dec 2011 11:55:46 -0800 Subject: UAS: Use unique tags on non-streams devices. UAS can work with either USB 3.0 devices that support bulk streams, or USB 2.0 devices that do not support bulk streams. When we're working with a non-streams device, we need to be able to uniquely identify a SCSI command with a tag in the IU. Devices will barf and abort all queued commands if they find a duplicate tag. uas_queuecommand_lck() sets cmdinfo->stream to zero if the device doesn't support streams, which is later passed into uas_alloc_cmd_urb() as the variable stream. This means the UAS driver was setting the tag in all commands to zero for non-stream devices. So the UAS driver won't currently work with USB 2.0 devices. Use the SCSI command tag instead of the stream ID for the command IU tag. We have to add one to the SCSI command tag because SCSI tags are zero-based, but stream IDs are one-based, and the command tag must match the stream ID that we're queueing the data IUs for. Untagged SCSI commands use stream ID 1. Signed-off-by: Sarah Sharp Cc: Matthew Wilcox Signed-off-by: Sebastian Andrzej Siewior diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 4bbaf6e..28d9b19 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -343,7 +343,10 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; iu->iu_id = IU_ID_COMMAND; - iu->tag = cpu_to_be16(stream_id); + if (blk_rq_tagged(cmnd->request)) + iu->tag = cpu_to_be16(cmnd->request->tag + 1); + else + iu->tag = cpu_to_be16(1); iu->prio_attr = UAS_SIMPLE_TAG; iu->len = len; int_to_scsilun(sdev->lun, &iu->lun); -- cgit v0.10.2 From 96c1eb9873caffc507a1951c36b43fdcf3ddeff3 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Dec 2011 11:55:48 -0800 Subject: UAS: Free status URB when we can't find the SCSI tag. In the UAS status URB completion handler, we need to free the URB, no matter what happens. Fix a bug where we would leak the URB (and its buffer) if we couldn't find a SCSI command that is associated with this status phase. Signed-off-by: Sarah Sharp Cc: Matthew Wilcox Signed-off-by: Sebastian Andrzej Siewior diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 28d9b19..9dd4aaee 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -246,8 +246,10 @@ static void uas_stat_cmplt(struct urb *urb) cmnd = sdev->current_cmnd; else cmnd = scsi_find_tag(sdev, tag); - if (!cmnd) + if (!cmnd) { + usb_free_urb(urb); return; + } switch (iu->iu_id) { case IU_ID_STATUS: -- cgit v0.10.2 From dae51546b6564b06cbae4191d4f2dee7136be3c1 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 19 Dec 2011 17:06:08 +0100 Subject: usb/uas: use unique tags for all LUNs I observed that on a device with multiple LUNs UAS was re-using the same tag number for requests which were issued at the same time to both LUNs. This patch uses scsi_init_shared_tag_map() to use unique tags for all LUNs. With this patch I haven't seen the same tag number during the init sequence anymore. Tag 1 is used for devices which do not adverise command queueing. This patch initilizes the queue before adding the scsi host like the other two user in tree. Signed-off-by: Sebastian Andrzej Siewior diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 9dd4aaee..6974f4b 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -684,6 +684,17 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) } } +static void uas_free_streams(struct uas_dev_info *devinfo) +{ + struct usb_device *udev = devinfo->udev; + struct usb_host_endpoint *eps[3]; + + eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe); + eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe); + eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); + usb_free_streams(devinfo->intf, eps, 3, GFP_KERNEL); +} + /* * XXX: What I'd like to do here is register a SCSI host for each USB host in * the system. Follow usb-storage's design of registering a SCSI host for @@ -713,18 +724,26 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) shost->max_id = 1; shost->sg_tablesize = udev->bus->sg_tablesize; - result = scsi_add_host(shost, &intf->dev); - if (result) - goto free; - shost->hostdata[0] = (unsigned long)devinfo; - devinfo->intf = intf; devinfo->udev = udev; uas_configure_endpoints(devinfo); + result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 1); + if (result) + goto free; + + result = scsi_add_host(shost, &intf->dev); + if (result) + goto deconfig_eps; + + shost->hostdata[0] = (unsigned long)devinfo; + scsi_scan_host(shost); usb_set_intfdata(intf, shost); return result; + +deconfig_eps: + uas_free_streams(devinfo); free: kfree(devinfo); if (shost) @@ -746,18 +765,11 @@ static int uas_post_reset(struct usb_interface *intf) static void uas_disconnect(struct usb_interface *intf) { - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_endpoint *eps[3]; struct Scsi_Host *shost = usb_get_intfdata(intf); struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; scsi_remove_host(shost); - - eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe); - eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe); - eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); - usb_free_streams(intf, eps, 3, GFP_KERNEL); - + uas_free_streams(devinfo); kfree(devinfo); } -- cgit v0.10.2 From 22188f4a933c6e86ac67f52028895c795896492e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 19 Dec 2011 20:22:39 +0100 Subject: usb/uas: use scsi_host_find_tag() to find command from a tag In "usb/uas: use unique tags for all LUNs" we make sure to create unique tags across all LUNs. This patch uses scsi_host_find_tag() to obtain the correct command which is associated with the tag. The following changes are required: - don't use sdev->current_cmnd anymore Since we can have devices which don't support command queueing we must ensure that we can tell the two commands apart. Even if a device supports comand queuing we send the INQUIRY command "untagged" for LUN1 while we can send a tagged command to LUN0 at the same time. devinfo->cmnd is used for stashing the one "untagged" command. - tag number is altered. If stream support is used then the tag number must match the stream number. Therefore we can't use tag 0 and must start at tag 1. In case we have untagged commands (at least the first command) we must be able to distinguish between command tag 0 (which becomes 1) and untagged command (which becomes curently also 1). The following tag numbers are used: 0: never 1: for untagged commands (devinfo->cmnd) 2+: tagged commands. Signed-off-by: Sebastian Andrzej Siewior diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 6974f4b..e2386e8 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -98,6 +98,7 @@ struct uas_dev_info { unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; + struct scsi_cmnd *cmnd; }; enum { @@ -178,8 +179,6 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - if (sdev->current_cmnd) - sdev->current_cmnd = NULL; cmnd->scsi_done(cmnd); usb_free_urb(urb); } @@ -205,8 +204,6 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - if (sdev->current_cmnd) - sdev->current_cmnd = NULL; cmnd->scsi_done(cmnd); usb_free_urb(urb); } @@ -230,8 +227,8 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, static void uas_stat_cmplt(struct urb *urb) { struct iu *iu = urb->transfer_buffer; - struct scsi_device *sdev = urb->context; - struct uas_dev_info *devinfo = sdev->hostdata; + struct Scsi_Host *shost = urb->context; + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct scsi_cmnd *cmnd; u16 tag; @@ -242,10 +239,10 @@ static void uas_stat_cmplt(struct urb *urb) } tag = be16_to_cpup(&iu->tag) - 1; - if (sdev->current_cmnd) - cmnd = sdev->current_cmnd; + if (tag == 0) + cmnd = devinfo->cmnd; else - cmnd = scsi_find_tag(sdev, tag); + cmnd = scsi_host_find_tag(shost, tag - 1); if (!cmnd) { usb_free_urb(urb); return; @@ -253,6 +250,9 @@ static void uas_stat_cmplt(struct urb *urb) switch (iu->iu_id) { case IU_ID_STATUS: + if (devinfo->cmnd == cmnd) + devinfo->cmnd = NULL; + if (urb->actual_length < 16) devinfo->uas_sense_old = 1; if (devinfo->uas_sense_old) @@ -314,7 +314,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), - uas_stat_cmplt, cmnd->device); + uas_stat_cmplt, cmnd->device->host); urb->stream_id = stream_id; urb->transfer_flags |= URB_FREE_BUFFER; out: @@ -346,7 +346,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, iu->iu_id = IU_ID_COMMAND; if (blk_rq_tagged(cmnd->request)) - iu->tag = cpu_to_be16(cmnd->request->tag + 1); + iu->tag = cpu_to_be16(cmnd->request->tag + 2); else iu->tag = cpu_to_be16(1); iu->prio_attr = UAS_SIMPLE_TAG; @@ -458,13 +458,13 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); - if (!cmdinfo->status_urb && sdev->current_cmnd) + if (devinfo->cmnd) return SCSI_MLQUEUE_DEVICE_BUSY; if (blk_rq_tagged(cmnd->request)) { - cmdinfo->stream = cmnd->request->tag + 1; + cmdinfo->stream = cmnd->request->tag + 2; } else { - sdev->current_cmnd = cmnd; + devinfo->cmnd = cmnd; cmdinfo->stream = 1; } @@ -565,7 +565,7 @@ static int uas_slave_configure(struct scsi_device *sdev) { struct uas_dev_info *devinfo = sdev->hostdata; scsi_set_tag_type(sdev, MSG_ORDERED_TAG); - scsi_activate_tcq(sdev, devinfo->qdepth - 1); + scsi_activate_tcq(sdev, devinfo->qdepth - 2); return 0; } @@ -633,6 +633,7 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints; devinfo->uas_sense_old = 0; + devinfo->cmnd = NULL; for (i = 0; i < n_endpoints; i++) { unsigned char *extra = endpoint[i].extra; @@ -728,7 +729,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->udev = udev; uas_configure_endpoints(devinfo); - result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 1); + result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2); if (result) goto free; -- cgit v0.10.2 From ceb3f91fd53c9fbd7b292fc2754ba4efffeeeedb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Dec 2011 14:50:26 +0100 Subject: usb/uas: one only one status URB/host on stream-less connection The status/sense URB is allocated on per-command basis. A read/write looks the following way on a stream-less connection: - send cmd tag X, queue status - receive status, oh it is a read for tag X. queue status & read - receive read - receive status, oh I'm done for tag X. Cool call complete and free status urb. This block repeats itself 1:1 for further commands and looks great so far. Lets take a look now what happens if we do allow multiple commands: - send cmd tag X, queue statusX (belongs to the command with the X tag) - send cmd tag Y, queue statusY (belongs to the command with the Y tag) - receive statusX, oh it is a read for tag X. queue statusX & a read - receive read - receive statusY, oh I'm done for tag X. Cool call complete and free statusY. - receive statusX, oh it is a read for tag Y. queue statusY & before we queue the read the the following message can be observed: |sd 0:0:0:0: [sda] sense urb submission failure followed by a second attempt with the same result. In order to address this problem we will use only one status URB for each scsi host in case we don't have stream support (as suggested by Matthew). This URB is requeued until the device removed. Nothing changes on stream based endpoints. Signed-off-by: Sebastian Andrzej Siewior diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e2386e8..036e969 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -99,6 +99,7 @@ struct uas_dev_info { unsigned use_streams:1; unsigned uas_sense_old:1; struct scsi_cmnd *cmnd; + struct urb *status_urb; /* used only if stream support is available */ }; enum { @@ -117,6 +118,7 @@ struct uas_cmd_info { unsigned int state; unsigned int stream; struct urb *cmd_urb; + /* status_urb is used only if stream support isn't available */ struct urb *status_urb; struct urb *data_in_urb; struct urb *data_out_urb; @@ -180,7 +182,6 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; cmnd->scsi_done(cmnd); - usb_free_urb(urb); } static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) @@ -205,7 +206,6 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) cmnd->result = sense_iu->status; cmnd->scsi_done(cmnd); - usb_free_urb(urb); } static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, @@ -214,7 +214,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; int err; - cmdinfo->state = direction | SUBMIT_STATUS_URB; + cmdinfo->state = direction; err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); if (err) { spin_lock(&uas_work_lock); @@ -231,10 +231,12 @@ static void uas_stat_cmplt(struct urb *urb) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct scsi_cmnd *cmnd; u16 tag; + int ret; if (urb->status) { dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status); - usb_free_urb(urb); + if (devinfo->use_streams) + usb_free_urb(urb); return; } @@ -244,7 +246,13 @@ static void uas_stat_cmplt(struct urb *urb) else cmnd = scsi_host_find_tag(shost, tag - 1); if (!cmnd) { - usb_free_urb(urb); + if (devinfo->use_streams) { + usb_free_urb(urb); + return; + } + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + dev_err(&urb->dev->dev, "failed submit status urb\n"); return; } @@ -270,6 +278,15 @@ static void uas_stat_cmplt(struct urb *urb) scmd_printk(KERN_ERR, cmnd, "Bogus IU (%d) received on status pipe\n", iu->iu_id); } + + if (devinfo->use_streams) { + usb_free_urb(urb); + return; + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + dev_err(&urb->dev->dev, "failed submit status urb\n"); } static void uas_data_cmplt(struct urb *urb) @@ -300,7 +317,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, } static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, - struct scsi_cmnd *cmnd, u16 stream_id) + struct Scsi_Host *shost, u16 stream_id) { struct usb_device *udev = devinfo->udev; struct urb *urb = usb_alloc_urb(0, gfp); @@ -314,7 +331,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, goto free; usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), - uas_stat_cmplt, cmnd->device->host); + uas_stat_cmplt, shost); urb->stream_id = stream_id; urb->transfer_flags |= URB_FREE_BUFFER; out: @@ -376,8 +393,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; if (cmdinfo->state & ALLOC_STATUS_URB) { - cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd, - cmdinfo->stream); + cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, + cmnd->device->host, cmdinfo->stream); if (!cmdinfo->status_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_STATUS_URB; @@ -486,7 +503,8 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, } if (!devinfo->use_streams) { - cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB); + cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB | + ALLOC_STATUS_URB | SUBMIT_STATUS_URB); cmdinfo->stream = 0; } @@ -685,6 +703,29 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo) } } +static int uas_alloc_status_urb(struct uas_dev_info *devinfo, + struct Scsi_Host *shost) +{ + if (devinfo->use_streams) { + devinfo->status_urb = NULL; + return 0; + } + + devinfo->status_urb = uas_alloc_sense_urb(devinfo, GFP_KERNEL, + shost, 0); + if (!devinfo->status_urb) + goto err_s_urb; + + if (usb_submit_urb(devinfo->status_urb, GFP_KERNEL)) + goto err_submit_urb; + + return 0; +err_submit_urb: + usb_free_urb(devinfo->status_urb); +err_s_urb: + return -ENOMEM; +} + static void uas_free_streams(struct uas_dev_info *devinfo) { struct usb_device *udev = devinfo->udev; @@ -739,10 +780,17 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) shost->hostdata[0] = (unsigned long)devinfo; + result = uas_alloc_status_urb(devinfo, shost); + if (result) + goto err_alloc_status; + scsi_scan_host(shost); usb_set_intfdata(intf, shost); return result; +err_alloc_status: + scsi_remove_host(shost); + shost = NULL; deconfig_eps: uas_free_streams(devinfo); free: @@ -770,6 +818,8 @@ static void uas_disconnect(struct usb_interface *intf) struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; scsi_remove_host(shost); + usb_kill_urb(devinfo->status_urb); + usb_free_urb(devinfo->status_urb); uas_free_streams(devinfo); kfree(devinfo); } -- cgit v0.10.2 From 1493138af1463112e42eebcdab5db61452821e97 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 5 Jan 2012 15:39:57 +0100 Subject: USB: code cleanup in suspend/resume path (3rd try) Do the cleanup to avoid behaviorial parameters Linus requested. Signed-off-by: Oliver Neukum Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d40ff95..b7dfdec 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf) int rc; /* Delayed unbind of an existing driver */ - if (intf->dev.driver) { - struct usb_driver *driver = - to_usb_driver(intf->dev.driver); - - dev_dbg(&intf->dev, "forced unbind\n"); - usb_driver_release_interface(driver, intf); - } + if (intf->dev.driver) + usb_forced_unbind_intf(intf); /* Try to rebind the interface */ if (!intf->dev.power.is_prepared) { @@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf) #ifdef CONFIG_PM -#define DO_UNBIND 0 -#define DO_REBIND 1 - -/* Unbind drivers for @udev's interfaces that don't support suspend/resume, - * or rebind interfaces that have been unbound, according to @action. +/* Unbind drivers for @udev's interfaces that don't support suspend/resume + * There is no check for reset_resume here because it can be determined + * only during resume whether reset_resume is needed. * * The caller must hold @udev's device lock. */ -static void do_unbind_rebind(struct usb_device *udev, int action) +static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; @@ -996,23 +989,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action) if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - switch (action) { - case DO_UNBIND: - if (intf->dev.driver) { - drv = to_usb_driver(intf->dev.driver); - if (!drv->suspend || !drv->resume) - usb_forced_unbind_intf(intf); - } - break; - case DO_REBIND: - if (intf->needs_binding) - usb_rebind_intf(intf); - break; + + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); } } } } +/* Unbind drivers for @udev's interfaces that failed to support reset-resume. + * These interfaces have the needs_binding flag set by usb_resume_interface(). + * + * The caller must hold @udev's device lock. + */ +static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->dev.driver && intf->needs_binding) + usb_forced_unbind_intf(intf); + } + } +} + +static void do_rebind_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->needs_binding) + usb_rebind_intf(intf); + } + } +} + static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -1302,7 +1325,12 @@ int usb_suspend(struct device *dev, pm_message_t msg) { struct usb_device *udev = to_usb_device(dev); - do_unbind_rebind(udev, DO_UNBIND); + unbind_no_pm_drivers_interfaces(udev); + + /* From now on we are sure all drivers support suspend/resume + * but not necessarily reset_resume() + * so we may still need to unbind and rebind upon resume + */ choose_wakeup(udev, msg); return usb_suspend_both(udev, msg); } @@ -1313,15 +1341,20 @@ int usb_resume(struct device *dev, pm_message_t msg) struct usb_device *udev = to_usb_device(dev); int status; - /* For PM complete calls, all we do is rebind interfaces */ + /* For PM complete calls, all we do is rebind interfaces + * whose needs_binding flag is set + */ if (msg.event == PM_EVENT_ON) { if (udev->state != USB_STATE_NOTATTACHED) - do_unbind_rebind(udev, DO_REBIND); + do_rebind_interfaces(udev); status = 0; /* For all other calls, take the device back to full power and * tell the PM core in case it was autosuspended previously. - * Unbind the interfaces that will need rebinding later. + * Unbind the interfaces that will need rebinding later, + * because they fail to support reset_resume. + * (This can't be done in usb_resume_interface() + * above because it doesn't own the right set of locks.) */ } else { status = usb_resume_both(udev, msg); @@ -1329,7 +1362,7 @@ int usb_resume(struct device *dev, pm_message_t msg) pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - do_unbind_rebind(udev, DO_REBIND); + unbind_no_reset_resume_drivers_interfaces(udev); } } -- cgit v0.10.2 From 98d9a82e5f753a2483d7b4638802d60e94e5d2e4 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 11 Jan 2012 08:38:35 +0100 Subject: USB: cleanup the handling of the PM complete call This eliminates the last instance of a function's behavior controlled by a parameter as Linus hates such things. Signed-off-by: Oliver Neukum Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b7dfdec..d77daf3 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1336,34 +1336,37 @@ int usb_suspend(struct device *dev, pm_message_t msg) } /* The device lock is held by the PM core */ -int usb_resume(struct device *dev, pm_message_t msg) +int usb_resume_complete(struct device *dev) { - struct usb_device *udev = to_usb_device(dev); - int status; + struct usb_device *udev = to_usb_device(dev); /* For PM complete calls, all we do is rebind interfaces * whose needs_binding flag is set */ - if (msg.event == PM_EVENT_ON) { - if (udev->state != USB_STATE_NOTATTACHED) - do_rebind_interfaces(udev); - status = 0; + if (udev->state != USB_STATE_NOTATTACHED) + do_rebind_interfaces(udev); + return 0; +} - /* For all other calls, take the device back to full power and +/* The device lock is held by the PM core */ +int usb_resume(struct device *dev, pm_message_t msg) +{ + struct usb_device *udev = to_usb_device(dev); + int status; + + /* For all calls, take the device back to full power and * tell the PM core in case it was autosuspended previously. * Unbind the interfaces that will need rebinding later, * because they fail to support reset_resume. * (This can't be done in usb_resume_interface() - * above because it doesn't own the right set of locks.) + * above because it doesn't own the right set of locks.) */ - } else { - status = usb_resume_both(udev, msg); - if (status == 0) { - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - unbind_no_reset_resume_drivers_interfaces(udev); - } + status = usb_resume_both(udev, msg); + if (status == 0) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + unbind_no_reset_resume_drivers_interfaces(udev); } /* Avoid PM error messages for devices disconnected while suspended diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8ca9f99..c74ba7b 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -274,7 +274,7 @@ static int usb_dev_prepare(struct device *dev) static void usb_dev_complete(struct device *dev) { /* Currently used only for rebinding interfaces */ - usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */ + usb_resume_complete(dev); } static int usb_dev_suspend(struct device *dev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 45e8479..71648dc 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -56,6 +56,7 @@ extern void usb_major_cleanup(void); extern int usb_suspend(struct device *dev, pm_message_t msg); extern int usb_resume(struct device *dev, pm_message_t msg); +extern int usb_resume_complete(struct device *dev); extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg); extern int usb_port_resume(struct usb_device *dev, pm_message_t msg); -- cgit v0.10.2 From 2851784f4d820bc697a8cc608509f9e3975c80e5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 13 Jan 2012 19:04:17 +0100 Subject: usb/uhci: initialize sg_table properly Commit 689d6eac ("USB: UHCI: add native scatter-gather support(v1)) added sg support to uhci but forgot to set the sg_table so this feature remained unused. Cc: Ming Lei Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 6b5eb10..e37dea8 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -565,6 +565,9 @@ static int uhci_start(struct usb_hcd *hcd) struct dentry __maybe_unused *dentry; hcd->uses_new_polling = 1; + /* Accept arbitrarily long scatter-gather lists */ + if (!(hcd->driver->flags & HCD_LOCAL_MEM)) + hcd->self.sg_tablesize = ~0; spin_lock_init(&uhci->lock); setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout, -- cgit v0.10.2 From e73b2db6c9bc5bd9a3c080f286964e594351991a Mon Sep 17 00:00:00 2001 From: Huajun Li Date: Sat, 14 Jan 2012 10:15:21 +0800 Subject: usb: Disable dynamic id of USB storage subdrivers Storage subdrivers, like alauda, datafab and others, don't support dynamic id currently, and it needs lots of work but with very little gain to enable the feature, so disable them in the patch. Signed-off-by: Huajun Li Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 51af2fe..bab8c8f 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -1276,6 +1276,7 @@ static struct usb_driver alauda_driver = { .post_reset = usb_stor_post_reset, .id_table = alauda_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(alauda_driver); diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 387cbd47..5fe451d 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -272,6 +272,7 @@ static struct usb_driver cypress_driver = { .post_reset = usb_stor_post_reset, .id_table = cypress_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(cypress_driver); diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 15d41f2..35e9c51 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -751,6 +751,7 @@ static struct usb_driver datafab_driver = { .post_reset = usb_stor_post_reset, .id_table = datafab_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(datafab_driver); diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index a6ade40..30532d9 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2407,6 +2407,7 @@ static struct usb_driver ene_ub6250_driver = { .post_reset = usb_stor_post_reset, .id_table = ene_ub6250_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(ene_ub6250_driver); diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index fa16157..042cf9e 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -553,6 +553,7 @@ static struct usb_driver freecom_driver = { .post_reset = usb_stor_post_reset, .id_table = freecom_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(freecom_driver); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index bd55027..31fa24e 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1566,6 +1566,7 @@ static struct usb_driver isd200_driver = { .post_reset = usb_stor_post_reset, .id_table = isd200_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(isd200_driver); diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index a19211b..e3b9738 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -677,6 +677,7 @@ static struct usb_driver jumpshot_driver = { .post_reset = usb_stor_post_reset, .id_table = jumpshot_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(jumpshot_driver); diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index e720f8e..a8708ea 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -230,6 +230,7 @@ static struct usb_driver karma_driver = { .post_reset = usb_stor_post_reset, .id_table = karma_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(karma_driver); diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index d75155c..886567a 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -312,6 +312,7 @@ static struct usb_driver onetouch_driver = { .post_reset = usb_stor_post_reset, .id_table = onetouch_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(onetouch_driver); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 1f62723..ccf271d 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -1100,6 +1100,7 @@ static struct usb_driver realtek_cr_driver = { .id_table = realtek_cr_ids, .soft_unbind = 1, .supports_autosuspend = 1, + .no_dynamic_id = 1, }; module_usb_driver(realtek_cr_driver); diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 425df7d..3252a62 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1787,6 +1787,7 @@ static struct usb_driver sddr09_driver = { .post_reset = usb_stor_post_reset, .id_table = sddr09_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(sddr09_driver); diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index e4ca5fc..c144078 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -1006,6 +1006,7 @@ static struct usb_driver sddr55_driver = { .post_reset = usb_stor_post_reset, .id_table = sddr55_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(sddr55_driver); diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 1369d25..fa1ceeb 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1863,6 +1863,7 @@ static struct usb_driver usbat_driver = { .post_reset = usb_stor_post_reset, .id_table = usbat_usb_ids, .soft_unbind = 1, + .no_dynamic_id = 1, }; module_usb_driver(usbat_driver); -- cgit v0.10.2 From fd7ff36d6a3f6413f838a2037b957b0e33ba9056 Mon Sep 17 00:00:00 2001 From: Huajun Li Date: Sat, 14 Jan 2012 10:16:40 +0800 Subject: usb: Re-enable usb-storage support dynamic id Enable usb-storage support dynamic id again by using a fixed id entry that describes a device using the Bulk-Only transport with the Transparent SCSI protocol. Signed-off-by: Huajun Li Reviewed-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 3dd7da9..58f5677 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -125,6 +125,9 @@ static struct us_unusual_dev us_unusual_dev_list[] = { { } /* Terminating entry */ }; +static struct us_unusual_dev for_dynamic_ids = + USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0); + #undef UNUSUAL_DEV #undef COMPLIANT_DEV #undef USUAL_DEV @@ -1027,8 +1030,10 @@ EXPORT_SYMBOL_GPL(usb_stor_disconnect); static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct us_unusual_dev *unusual_dev; struct us_data *us; int result; + int size; /* * If libusual is configured, let it decide whether a standard @@ -1047,8 +1052,19 @@ static int storage_probe(struct usb_interface *intf, * table, so we use the index of the id entry to find the * corresponding unusual_devs entry. */ - result = usb_stor_probe1(&us, intf, id, - (id - usb_storage_usb_ids) + us_unusual_dev_list); + + size = ARRAY_SIZE(us_unusual_dev_list); + if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) { + unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list; + } else { + unusual_dev = &for_dynamic_ids; + + US_DEBUGP("%s %s 0x%04x 0x%04x\n", "Use Bulk-Only transport", + "with the Transparent SCSI protocol for dynamic id:", + id->idVendor, id->idProduct); + } + + result = usb_stor_probe1(&us, intf, id, unusual_dev); if (result) return result; @@ -1074,7 +1090,6 @@ static struct usb_driver usb_storage_driver = { .id_table = usb_storage_usb_ids, .supports_autosuspend = 1, .soft_unbind = 1, - .no_dynamic_id = 1, }; static int __init usb_stor_init(void) -- cgit v0.10.2 From 0b238583ac8db66762bba021de1b7c60b6bc29ad Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jan 2012 00:36:47 +0100 Subject: USB: cp210x: fix debug output Remove superfluous newlines from debug statements. Remove unnecessary line breaks. Signed-off-by: Johan Hovold Cc: Preston Fick Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index fba1147..92aadd8 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -279,7 +279,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request, if (result != size) { dbg("%s - Unable to send config request, " - "request=0x%x size=%d result=%d\n", + "request=0x%x size=%d result=%d", __func__, request, size, result); if (result > 0) result = -EPROTO; @@ -333,7 +333,7 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request, if ((size > 2 && result != size) || result < 0) { dbg("%s - Unable to send request, " - "request=0x%x size=%d result=%d\n", + "request=0x%x size=%d result=%d", __func__, request, size, result); if (result > 0) result = -EPROTO; @@ -636,13 +636,13 @@ static void cp210x_set_termios(struct tty_struct *tty, default: dbg("cp210x driver does not " "support the number of bits requested," - " using 8 bit mode\n"); + " using 8 bit mode"); bits |= BITS_DATA_8; break; } if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) dbg("Number of data bits requested " - "not supported by device\n"); + "not supported by device"); } if ((cflag & (PARENB|PARODD|CMSPAR)) != @@ -669,8 +669,7 @@ static void cp210x_set_termios(struct tty_struct *tty, } } if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) - dbg("Parity mode not supported " - "by device\n"); + dbg("Parity mode not supported by device"); } if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { @@ -685,7 +684,7 @@ static void cp210x_set_termios(struct tty_struct *tty, } if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) dbg("Number of stop bits requested " - "not supported by device\n"); + "not supported by device"); } if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { -- cgit v0.10.2 From 19b85b3b87fd1388df1f4a35969823521d35d243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 15:11:58 +0100 Subject: USB: cdc-wdm: no need to fill the in request URB every time it's submitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Filling the same URB with the exact same data is pointless. It can be filled once and resubmitted. And not doing buffer allocation and URB filling at the same place only serves to hide size mismatch bugs Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 1c50baf..9734863 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -159,11 +159,9 @@ static void wdm_int_callback(struct urb *urb) int rv = 0; int status = urb->status; struct wdm_device *desc; - struct usb_ctrlrequest *req; struct usb_cdc_notification *dr; desc = urb->context; - req = desc->irq; dr = (struct usb_cdc_notification *)desc->sbuf; if (status) { @@ -210,24 +208,6 @@ static void wdm_int_callback(struct urb *urb) goto exit; } - req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); - req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; - req->wValue = 0; - req->wIndex = desc->inum; - req->wLength = cpu_to_le16(desc->wMaxCommand); - - usb_fill_control_urb( - desc->response, - interface_to_usbdev(desc->intf), - /* using common endpoint 0 */ - usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), - (unsigned char *)req, - desc->inbuf, - desc->wMaxCommand, - wdm_in_callback, - desc - ); - desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; spin_lock(&desc->iuspin); clear_bit(WDM_READ, &desc->flags); set_bit(WDM_RESPONDING, &desc->flags); @@ -734,6 +714,25 @@ next_desc: ); desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); + desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; + desc->irq->wValue = 0; + desc->irq->wIndex = desc->inum; + desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); + + usb_fill_control_urb( + desc->response, + interface_to_usbdev(desc->intf), + /* using common endpoint 0 */ + usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), + (unsigned char *)desc->irq, + desc->inbuf, + desc->wMaxCommand, + wdm_in_callback, + desc + ); + desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_set_intfdata(intf, desc); rv = usb_register_dev(intf, &wdm_class); if (rv < 0) -- cgit v0.10.2 From cafbe85fb0d00d32988905c4978df433ca9b6512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 15:11:59 +0100 Subject: USB: cdc-wdm: better allocate a buffer that is at least as big as we tell the USB core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As it turns out, there was a mismatch between the allocated inbuf size (desc->bMaxPacketSize0, typically something like 64) and the length we specified in the URB (desc->wMaxCommand, typically something like 2048) Signed-off-by: Bjørn Mork Cc: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 9734863..846dfa6 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -696,7 +696,7 @@ next_desc: goto err; desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), - desc->bMaxPacketSize0, + desc->wMaxCommand, GFP_KERNEL, &desc->response->transfer_dma); if (!desc->inbuf) -- cgit v0.10.2 From 8457d99cab81e91724b43363f7fccd851d766187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 15:12:00 +0100 Subject: USB: cdc-wdm: no need to use usb_alloc_coherent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Documentation/usb/dma.txt states: Most drivers should *NOT* be using these primitives; they don't need to use this type of memory (dma-coherent), and memory returned from kmalloc() will work just fine. This driver handle only very low bandwith transfers. It is not an obvious candidate for usb_alloc_coherent(). Using these calls only serves to complicate the code for no gain, as has been shown by multiple bugs related to this allocation path. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 846dfa6..8909058 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -256,14 +256,8 @@ static void free_urbs(struct wdm_device *desc) static void cleanup(struct wdm_device *desc) { - usb_free_coherent(interface_to_usbdev(desc->intf), - desc->wMaxPacketSize, - desc->sbuf, - desc->validity->transfer_dma); - usb_free_coherent(interface_to_usbdev(desc->intf), - desc->bMaxPacketSize0, - desc->inbuf, - desc->response->transfer_dma); + kfree(desc->sbuf); + kfree(desc->inbuf); kfree(desc->orq); kfree(desc->irq); kfree(desc->ubuf); @@ -688,19 +682,13 @@ next_desc: if (!desc->ubuf) goto err; - desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf), - desc->wMaxPacketSize, - GFP_KERNEL, - &desc->validity->transfer_dma); + desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL); if (!desc->sbuf) goto err; - desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf), - desc->wMaxCommand, - GFP_KERNEL, - &desc->response->transfer_dma); + desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL); if (!desc->inbuf) - goto err2; + goto err; usb_fill_int_urb( desc->validity, @@ -712,7 +700,6 @@ next_desc: desc, ep->bInterval ); - desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; @@ -731,30 +718,22 @@ next_desc: wdm_in_callback, desc ); - desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_set_intfdata(intf, desc); rv = usb_register_dev(intf, &wdm_class); if (rv < 0) - goto err3; + goto err2; else dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n", intf->minor - WDM_MINOR_BASE); out: return rv; -err3: - usb_set_intfdata(intf, NULL); - usb_free_coherent(interface_to_usbdev(desc->intf), - desc->bMaxPacketSize0, - desc->inbuf, - desc->response->transfer_dma); err2: - usb_free_coherent(interface_to_usbdev(desc->intf), - desc->wMaxPacketSize, - desc->sbuf, - desc->validity->transfer_dma); + usb_set_intfdata(intf, NULL); err: free_urbs(desc); + kfree(desc->inbuf); + kfree(desc->sbuf); kfree(desc->ubuf); kfree(desc->orq); kfree(desc->irq); -- cgit v0.10.2 From 8143a8963c374116f84aba15dcaeaf02370c8098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 16 Jan 2012 15:12:01 +0100 Subject: USB: cdc-wdm: kill the now unnecessary bMaxPacketSize0 field and udev variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need bMaxPacketSize0, and keeping all these different size fields around will only cause us to use the wrong one. Seems the udev variable was only used for getting bMaxPacketSize0. We could have used it for the usb_fill_*_urb() calls, but as it wasn't before - why start now? Instead make the interface_to_usbdev() calls consistent. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 8909058..bb8208a 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -80,7 +80,6 @@ struct wdm_device { u16 bufsize; u16 wMaxCommand; u16 wMaxPacketSize; - u16 bMaxPacketSize0; __le16 inum; int reslength; int length; @@ -597,7 +596,6 @@ static void wdm_rxwork(struct work_struct *work) static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) { int rv = -EINVAL; - struct usb_device *udev = interface_to_usbdev(intf); struct wdm_device *desc; struct usb_host_interface *iface; struct usb_endpoint_descriptor *ep; @@ -657,7 +655,6 @@ next_desc: goto err; desc->wMaxPacketSize = usb_endpoint_maxp(ep); - desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); if (!desc->orq) @@ -709,7 +706,7 @@ next_desc: usb_fill_control_urb( desc->response, - interface_to_usbdev(desc->intf), + interface_to_usbdev(intf), /* using common endpoint 0 */ usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), (unsigned char *)desc->irq, -- cgit v0.10.2 From 7e3054a005537f28544ab2870c375458362f7473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Fri, 20 Jan 2012 01:49:57 +0100 Subject: USB: cdc-wdm: Avoid hanging on interface with no USB_CDC_DMM_TYPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe does not strictly require the USB_CDC_DMM_TYPE descriptor, which is a good thing as it makes the driver usable on non-conforming interfaces. A user could e.g. bind to it to a CDC ECM interface by using the new_id and bind sysfs files. But this would fail with a 0 buffer length due to the missing descriptor. Fix by defining a reasonable fallback size: The minimum device receive buffer size required by the CDC WMC standard, revision 1.1 Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index bb8208a..c0197af 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -57,6 +57,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_MAX 16 +/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ +#define WDM_DEFAULT_BUFSIZE 256 static DEFINE_MUTEX(wdm_mutex); @@ -602,7 +604,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_cdc_dmm_desc *dmhd; u8 *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; - u16 maxcom = 0; + u16 maxcom = WDM_DEFAULT_BUFSIZE; if (!buffer) goto out; -- cgit v0.10.2 From 820c629a595ad8d8f2694641e494738b18d29e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Fri, 20 Jan 2012 04:17:25 +0100 Subject: USB: cdc-wdm: avoid printing odd-looking "cdc-wdm-176" names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usb_register_dev() will change our .minor_base to 0 if CONFIG_USB_DYNAMIC_MINORS is set. And it usually is, of course. Use dev_name() to print the proper interface name instead Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index c0197af..c154d4f 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -723,8 +723,7 @@ next_desc: if (rv < 0) goto err2; else - dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n", - intf->minor - WDM_MINOR_BASE); + dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); out: return rv; err2: -- cgit v0.10.2 From 761bbcb74e4611414937ea480ba60bb970648755 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Tue, 24 Jan 2012 22:17:38 +0100 Subject: usb: ehci-fsl: set INCR8 mode for system bus interface on MPC512x Use INCR8 mode for system bus interface of the USB controller on MPC512x. This is a work-around for the AHB bus lock up problem observed on MPC512x when there is heavy simultaneous PATA write or network (FEC) activity. See also "12.4 The USB controller can issue transactions that lock up the AHB bus under certain conditions" in MPC5121e (M36P) Errata. Signed-off-by: Anatolij Gustschin Tested-by: Matthias Fuchs Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index e90344a..42414cd 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -316,7 +316,9 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval; struct fsl_usb2_platform_data *pdata; + struct device *dev; + dev = hcd->self.controller; pdata = hcd->self.controller->platform_data; ehci->big_endian_desc = pdata->big_endian_desc; ehci->big_endian_mmio = pdata->big_endian_mmio; @@ -346,6 +348,16 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) ehci_reset(ehci); + if (of_device_is_compatible(dev->parent->of_node, + "fsl,mpc5121-usb2-dr")) { + /* + * set SBUSCFG:AHBBRST so that control msgs don't + * fail when doing heavy PATA writes. + */ + ehci_writel(ehci, SBUSCFG_INCR8, + hcd->regs + FSL_SOC_USB_SBUSCFG); + } + retval = ehci_fsl_reinit(ehci); return retval; } @@ -469,6 +481,8 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev) ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE, hcd->regs + FSL_SOC_USB_ISIPHYCTRL); + ehci_writel(ehci, SBUSCFG_INCR8, hcd->regs + FSL_SOC_USB_SBUSCFG); + /* restore EHCI registers */ ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable); diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 4918062..0855be8 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -19,6 +19,8 @@ #define _EHCI_FSL_H /* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_SBUSCFG 0x90 +#define SBUSCFG_INCR8 0x02 /* INCR8, specified */ #define FSL_SOC_USB_ULPIVP 0x170 #define FSL_SOC_USB_PORTSC1 0x184 #define PORT_PTS_MSK (3<<30) -- cgit v0.10.2 From f30cdbcb2e6a7b9cdebe2117a7b623a5b3832a75 Mon Sep 17 00:00:00 2001 From: Kelvin Cheung Date: Wed, 18 Jan 2012 14:41:16 +0800 Subject: USB: Add EHCI bus glue for Loongson1x SoCs (UPDATED) Use ehci_setup() in ehci_ls1x_reset(). The Loongson1x SoCs have a built-in EHCI controller. This patch adds the necessary glue code to make the generic EHCI driver usable for them. Signed-off-by: Kelvin Cheung Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 75823a1..0b0afc8 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -76,6 +76,7 @@ config USB_ARCH_HAS_EHCI default y if MICROBLAZE default y if SPARC_LEON default y if ARCH_MMP + default y if MACH_LOONGSON1 default PCI # some non-PCI HCDs implement xHCI diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a007a9f..4918b0c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1376,6 +1376,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_mv_driver #endif +#ifdef CONFIG_MACH_LOONGSON1 +#include "ehci-ls1x.c" +#define PLATFORM_DRIVER ehci_ls1x_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-ls1x.c b/drivers/usb/host/ehci-ls1x.c new file mode 100644 index 0000000..a283e59 --- /dev/null +++ b/drivers/usb/host/ehci-ls1x.c @@ -0,0 +1,159 @@ +/* + * Bus Glue for Loongson LS1X built-in EHCI controller. + * + * Copyright (c) 2012 Zhang, Keguang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + + +#include + +static int ehci_ls1x_reset(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int ret; + + ehci->caps = hcd->regs; + + ret = ehci_setup(hcd); + if (ret) + return ret; + + ehci_port_power(ehci, 0); + + return 0; +} + +static const struct hc_driver ehci_ls1x_hc_driver = { + .description = hcd_name, + .product_desc = "LOONGSON1 EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_ls1x_reset, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_hcd_ls1x_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int irq; + int ret; + + pr_debug("initializing loongson1 ehci USB Controller\n"); + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + hcd = usb_create_hcd(&ehci_ls1x_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + ret = -EBUSY; + goto err_put_hcd; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + ret = -EFAULT; + goto err_release_region; + } + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) + goto err_iounmap; + + return ret; + +err_iounmap: + iounmap(hcd->regs); +err_release_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_put_hcd: + usb_put_hcd(hcd); + return ret; +} + +static int ehci_hcd_ls1x_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver ehci_ls1x_driver = { + .probe = ehci_hcd_ls1x_probe, + .remove = ehci_hcd_ls1x_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ls1x-ehci", + .owner = THIS_MODULE, + }, +}; + +MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ls1x-ehci"); -- cgit v0.10.2 From fec67b45bf045582c3172101970090d640cd56d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Wed, 25 Jan 2012 13:03:29 +0100 Subject: usb: cdc-wdm: Add device-id for Huawei 3G/LTE modems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [v2: Editorial changes suggested by Sergei Shtylyov] These modems use the Qualcomm MSM Interface (QMI) protocol for management of their CDC ECM like wwan interface. This driver is perfect for exporting the protocol to userspace. The created character device will be indistinguishable from a common AT command based Device Management interface, so userspace applications must do some intelligent matching on the USB device. Cc: Sergei Shtylyov Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Acked-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index c154d4f..23cf9d3 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -31,6 +31,8 @@ #define DRIVER_AUTHOR "Oliver Neukum" #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" +#define HUAWEI_VENDOR_ID 0x12D1 + static const struct usb_device_id wdm_ids[] = { { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | @@ -38,6 +40,20 @@ static const struct usb_device_id wdm_ids[] = { .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_DMM }, + { + /* + * Huawei E392, E398 and possibly other Qualcomm based modems + * embed the Qualcomm QMI protocol inside CDC on CDC ECM like + * control interfaces. Userspace access to this is required + * to configure the accompanying data interface + */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = HUAWEI_VENDOR_ID, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */ + }, { } }; -- cgit v0.10.2 From c898add51c7b5b99fcecdeaf4df2ca30949cacb6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 12:42:32 +0100 Subject: usb/uas: only bind if the hcd supports SG The UAS driver requires SG support by the HCD operating the device. This patch stops UAS from operating on a HCD without sg support and prints a message to let him know. The spec says: |For [USB2] backward compatibility, the device shall present [BOT] as |alternate interface zero (primary) and [UAS] as alternate interface one |(secondary). A device which does not need backward compatibility with |[BOT] shall present [UAS] as alternate interface zero. In [USB2] |systems, the [BOT] driver or an associated filter driver may need to |issue a SET INTERFACE request for alternate interface one and then allow |the [UAS] driver to load. If the user used usb_modeswitch to switch to UAS then he can go back to BOT or use a different HCD. In case UAS is the only interface then there is currently no way out. In future usb_sg_wait() should be extended to provide a non-blocking interface so it can work with the UAS driver. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e0133c9..e34c759 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -621,22 +622,34 @@ static int uas_is_interface(struct usb_host_interface *intf) intf->desc.bInterfaceProtocol == USB_PR_UAS); } +static int uas_isnt_supported(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + dev_warn(&udev->dev, "The driver for the USB controller %s does not " + "support scatter-gather which is\n", + hcd->driver->description); + dev_warn(&udev->dev, "required by the UAS driver. Please try an" + "alternative USB controller if you wish to use UAS.\n"); + return -ENODEV; +} + static int uas_switch_interface(struct usb_device *udev, struct usb_interface *intf) { int i; - - if (uas_is_interface(intf->cur_altsetting)) - return 0; + int sg_supported = udev->bus->sg_tablesize != 0; for (i = 0; i < intf->num_altsetting; i++) { struct usb_host_interface *alt = &intf->altsetting[i]; - if (alt == intf->cur_altsetting) - continue; - if (uas_is_interface(alt)) + + if (uas_is_interface(alt)) { + if (!sg_supported) + return uas_isnt_supported(udev); return usb_set_interface(udev, alt->desc.bInterfaceNumber, alt->desc.bAlternateSetting); + } } return -ENODEV; -- cgit v0.10.2 From 348748b0e8cccc675e2f3a1456460ffcd540e1a1 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 12:45:56 +0100 Subject: usb/uas: move UAS structs / defines into a header file The protocol specific structures and defines which are used by UAS are moved into a header files by this patch so it can be accessed by the UAS gadget as well. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e34c759..f98ba40 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -23,49 +24,6 @@ #include #include -/* Common header for all IUs */ -struct iu { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; -}; - -enum { - IU_ID_COMMAND = 0x01, - IU_ID_STATUS = 0x03, - IU_ID_RESPONSE = 0x04, - IU_ID_TASK_MGMT = 0x05, - IU_ID_READ_READY = 0x06, - IU_ID_WRITE_READY = 0x07, -}; - -struct command_iu { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; - __u8 prio_attr; - __u8 rsvd5; - __u8 len; - __u8 rsvd7; - struct scsi_lun lun; - __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ -}; - -/* - * Also used for the Read Ready and Write Ready IUs since they have the - * same first four bytes - */ -struct sense_iu { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; - __be16 status_qual; - __u8 status; - __u8 rsvd7[7]; - __be16 len; - __u8 sense[SCSI_SENSE_BUFFERSIZE]; -}; - /* * The r00-r01c specs define this version of the SENSE IU data structure. * It's still in use by several different firmware releases. @@ -80,18 +38,6 @@ struct sense_iu_old { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; -enum { - CMD_PIPE_ID = 1, - STATUS_PIPE_ID = 2, - DATA_IN_PIPE_ID = 3, - DATA_OUT_PIPE_ID = 4, - - UAS_SIMPLE_TAG = 0, - UAS_HEAD_TAG = 1, - UAS_ORDERED_TAG = 2, - UAS_ACA = 4, -}; - struct uas_dev_info { struct usb_interface *intf; struct usb_device *udev; diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h new file mode 100644 index 0000000..856be7f --- /dev/null +++ b/include/linux/usb/uas.h @@ -0,0 +1,61 @@ +#ifndef __USB_UAS_H__ +#define __USB_UAS_H__ + +#include +#include + +/* Common header for all IUs */ +struct iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; +}; + +enum { + IU_ID_COMMAND = 0x01, + IU_ID_STATUS = 0x03, + IU_ID_RESPONSE = 0x04, + IU_ID_TASK_MGMT = 0x05, + IU_ID_READ_READY = 0x06, + IU_ID_WRITE_READY = 0x07, +}; + +struct command_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __u8 prio_attr; + __u8 rsvd5; + __u8 len; + __u8 rsvd7; + struct scsi_lun lun; + __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ +}; + +/* + * Also used for the Read Ready and Write Ready IUs since they have the + * same first four bytes + */ +struct sense_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __be16 status_qual; + __u8 status; + __u8 rsvd7[7]; + __be16 len; + __u8 sense[SCSI_SENSE_BUFFERSIZE]; +}; + +enum { + CMD_PIPE_ID = 1, + STATUS_PIPE_ID = 2, + DATA_IN_PIPE_ID = 3, + DATA_OUT_PIPE_ID = 4, + + UAS_SIMPLE_TAG = 0, + UAS_HEAD_TAG = 1, + UAS_ORDERED_TAG = 2, + UAS_ACA = 4, +}; +#endif -- cgit v0.10.2 From ee398b59ec1dc3ce1d60518f4ea644907893414b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 12:45:57 +0100 Subject: usb/uas: add usb_pipe_usage_descriptor usb_pipe_usage_descriptor defines the struct which is used to describe the type of the endpoint in UAS (status/command/data in+out). It will be used by the UAS gadget, the host code is using a char array for the access. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h index 856be7f..9a988e4 100644 --- a/include/linux/usb/uas.h +++ b/include/linux/usb/uas.h @@ -47,6 +47,14 @@ struct sense_iu { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; +struct usb_pipe_usage_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bPipeID; + __u8 Reserved; +} __attribute__((__packed__)); + enum { CMD_PIPE_ID = 1, STATUS_PIPE_ID = 2, -- cgit v0.10.2 From e4d8318a85779b25b880187b1b1c44e797bd7d4b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 25 Jan 2012 11:48:40 +0100 Subject: usb/uas: make sure data urb is gone if we receive status before that Just run into the following: - new disk arrived in the system - udev couldn't wait to get its hands on to to run ata_id /dev/sda - this sent the cdb 0xa1 to the device. - my UAS-gadget recevied the cdb and had no idea what to do with it. It decided to send a status URB back with sense set to invalid opcode. - the host side received it status and completed the scsi command. - the host sent another scsi with 4kib data buffer - Now I was confused why the data transfer is only 512 bytes instead of 4kib since the host is always allocating the complete transfer in one go. - Finally the system crashed while walking through the sg list. This patch adds three new flags in order to distinguish between DATA URB completed and outstanding. If we receive status before data, we cancel data and let data complete the command. This solves the problem for IN and OUT transfers but does not work for BIDI. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index f98ba40..8ec8a6e 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -58,6 +58,9 @@ enum { SUBMIT_DATA_OUT_URB = (1 << 5), ALLOC_CMD_URB = (1 << 6), SUBMIT_CMD_URB = (1 << 7), + COMPLETED_DATA_IN = (1 << 8), + COMPLETED_DATA_OUT = (1 << 9), + DATA_COMPLETES_CMD = (1 << 10), }; /* Overrides scsi_pointer */ @@ -111,6 +114,7 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu *sense_iu = urb->transfer_buffer; struct scsi_device *sdev = cmnd->device; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; if (urb->actual_length > 16) { unsigned len = be16_to_cpup(&sense_iu->len); @@ -128,13 +132,15 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - cmnd->scsi_done(cmnd); + if (!(cmdinfo->state & DATA_COMPLETES_CMD)) + cmnd->scsi_done(cmnd); } static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) { struct sense_iu_old *sense_iu = urb->transfer_buffer; struct scsi_device *sdev = cmnd->device; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; if (urb->actual_length > 8) { unsigned len = be16_to_cpup(&sense_iu->len) - 2; @@ -152,7 +158,8 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) } cmnd->result = sense_iu->status; - cmnd->scsi_done(cmnd); + if (!(cmdinfo->state & DATA_COMPLETES_CMD)) + cmnd->scsi_done(cmnd); } static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, @@ -177,6 +184,7 @@ static void uas_stat_cmplt(struct urb *urb) struct Scsi_Host *shost = urb->context; struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct scsi_cmnd *cmnd; + struct uas_cmd_info *cmdinfo; u16 tag; int ret; @@ -202,12 +210,32 @@ static void uas_stat_cmplt(struct urb *urb) dev_err(&urb->dev->dev, "failed submit status urb\n"); return; } + cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: if (devinfo->cmnd == cmnd) devinfo->cmnd = NULL; + if (!(cmdinfo->state & COMPLETED_DATA_IN) && + cmdinfo->data_in_urb) { + if (devinfo->use_streams) { + cmdinfo->state |= DATA_COMPLETES_CMD; + usb_unlink_urb(cmdinfo->data_in_urb); + } else { + usb_free_urb(cmdinfo->data_in_urb); + } + } + if (!(cmdinfo->state & COMPLETED_DATA_OUT) && + cmdinfo->data_out_urb) { + if (devinfo->use_streams) { + cmdinfo->state |= DATA_COMPLETES_CMD; + usb_unlink_urb(cmdinfo->data_in_urb); + } else { + usb_free_urb(cmdinfo->data_out_urb); + } + } + if (urb->actual_length < 16) devinfo->uas_sense_old = 1; if (devinfo->uas_sense_old) @@ -236,27 +264,59 @@ static void uas_stat_cmplt(struct urb *urb) dev_err(&urb->dev->dev, "failed submit status urb\n"); } -static void uas_data_cmplt(struct urb *urb) +static void uas_data_out_cmplt(struct urb *urb) +{ + struct scsi_cmnd *cmnd = urb->context; + struct scsi_data_buffer *sdb = scsi_out(cmnd); + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + + cmdinfo->state |= COMPLETED_DATA_OUT; + + sdb->resid = sdb->length - urb->actual_length; + usb_free_urb(urb); + + if (cmdinfo->state & DATA_COMPLETES_CMD) + cmnd->scsi_done(cmnd); +} + +static void uas_data_in_cmplt(struct urb *urb) { - struct scsi_data_buffer *sdb = urb->context; + struct scsi_cmnd *cmnd = urb->context; + struct scsi_data_buffer *sdb = scsi_in(cmnd); + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + + cmdinfo->state |= COMPLETED_DATA_IN; + sdb->resid = sdb->length - urb->actual_length; usb_free_urb(urb); + + if (cmdinfo->state & DATA_COMPLETES_CMD) + cmnd->scsi_done(cmnd); } static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, - unsigned int pipe, u16 stream_id, - struct scsi_data_buffer *sdb, - enum dma_data_direction dir) + unsigned int pipe, struct scsi_cmnd *cmnd, + enum dma_data_direction dir) { + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct usb_device *udev = devinfo->udev; struct urb *urb = usb_alloc_urb(0, gfp); + struct scsi_data_buffer *sdb; + usb_complete_t complete_fn; + u16 stream_id = cmdinfo->stream; if (!urb) goto out; - usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, - sdb); - if (devinfo->use_streams) - urb->stream_id = stream_id; + if (dir == DMA_FROM_DEVICE) { + sdb = scsi_in(cmnd); + complete_fn = uas_data_in_cmplt; + } else { + sdb = scsi_out(cmnd); + complete_fn = uas_data_out_cmplt; + } + usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, + complete_fn, cmnd); + urb->stream_id = stream_id; urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; urb->sg = sdb->table.sgl; out: @@ -358,8 +418,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_IN_URB) { cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp, - devinfo->data_in_pipe, cmdinfo->stream, - scsi_in(cmnd), DMA_FROM_DEVICE); + devinfo->data_in_pipe, cmnd, + DMA_FROM_DEVICE); if (!cmdinfo->data_in_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_IN_URB; @@ -376,8 +436,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, if (cmdinfo->state & ALLOC_DATA_OUT_URB) { cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp, - devinfo->data_out_pipe, cmdinfo->stream, - scsi_out(cmnd), DMA_TO_DEVICE); + devinfo->data_out_pipe, cmnd, + DMA_TO_DEVICE); if (!cmdinfo->data_out_urb) return SCSI_MLQUEUE_DEVICE_BUSY; cmdinfo->state &= ~ALLOC_DATA_OUT_URB; -- cgit v0.10.2 From 0cb54a3e47cb4baf0bc7463f0a64cfeae5e35697 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 2 Feb 2012 15:38:14 -0500 Subject: USB: debugging code shouldn't alter control flow People have complained that debugging code shouldn't alter the flow of control; it should restrict itself to printing out warnings and error messages. Bowing to popular opinion, this patch (as1518) changes the debugging checks in usb_submit_urb() to follow this guideline. Signed-off-by: Alan Stern Reported-by: Keith Packard CC: Pavel Machek Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 909625b..f4f20c7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -403,20 +403,17 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) * cause problems in HCDs if they get it wrong. */ { - unsigned int orig_flags = urb->transfer_flags; unsigned int allowed; static int pipetypes[4] = { PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT }; /* Check that the pipe's type matches the endpoint's type */ - if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) { - dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", + if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) + dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", usb_pipetype(urb->pipe), pipetypes[xfertype]); - return -EPIPE; /* The most suitable error code :-) */ - } - /* enforce simple/standard policy */ + /* Check against a simple/standard policy */ allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER); switch (xfertype) { @@ -435,14 +432,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) allowed |= URB_ISO_ASAP; break; } - urb->transfer_flags &= allowed; + allowed &= urb->transfer_flags; - /* fail if submitter gave bogus flags */ - if (urb->transfer_flags != orig_flags) { - dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n", - orig_flags, urb->transfer_flags); - return -EINVAL; - } + /* warn if submitter gave bogus flags */ + if (allowed != urb->transfer_flags) + dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n", + urb->transfer_flags, allowed); } #endif /* -- cgit v0.10.2 From de8c46bfc032fbdf490cfb67f534d2a0188ebeb0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 10 Jan 2012 13:43:04 -0500 Subject: SCSI: fix typo in definition of struct scsi_target This patch (as1506) corrects a typo in the definition of the scsi_target structure. pdt_1f_for_no_lun is supposed to be a single-bit flag, not a full-sized integer. Signed-off-by: Alan Stern Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 77273f2..01cb3c4 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -246,8 +246,8 @@ struct scsi_target { unsigned int single_lun:1; /* Indicates we should only * allow I/O to one of the luns * for the device at a time. */ - unsigned int pdt_1f_for_no_lun; /* PDT = 0x1f */ - /* means no lun present */ + unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f + * means no lun present. */ /* commands actually active on LLD. protected by host lock. */ unsigned int target_busy; /* -- cgit v0.10.2 From 09b6b51b0b6c1b9bb61815baf205e4d74c89ff04 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 10 Jan 2012 13:43:30 -0500 Subject: SCSI & usb-storage: add flags for VPD pages and REPORT LUNS This patch (as1507) adds a skip_vpd_pages flag to struct scsi_device and a no_report_luns flag to struct scsi_target. The first is used to control whether sd will look at VPD pages for information on block provisioning, limits, and characteristics. The second prevents scsi_report_lun_scan() from issuing a REPORT LUNS command. The patch also modifies usb-storage to set the new flag bits for all USB devices and targets, and to stop adjusting the scsi_level value. Historically we have seen that USB mass-storage devices often don't support VPD pages or REPORT LUNS properly. Until now we have avoided these things by setting the scsi_level to SCSI_2 for all USB devices. But this has the side effect of storing the LUN bits into the second byte of each CDB, and now we have a report of a device which doesn't like that. The best solution is to stop abusing scsi_level and instead have separate flags for VPD pages and REPORT LUNS. Signed-off-by: Alan Stern Reported-by: Perry Wagle CC: Matthew Dharm Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 89da43f..fd37bfb 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1295,6 +1295,7 @@ EXPORT_SYMBOL(int_to_scsilun); * LUNs even if it's older than SCSI-3. * If BLIST_NOREPORTLUN is set, return 1 always. * If BLIST_NOLUN is set, return 0 always. + * If starget->no_report_luns is set, return 1 always. * * Return: * 0: scan completed (or no memory, so further scanning is futile) @@ -1321,6 +1322,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set. * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does * support more than 8 LUNs. + * Don't attempt if the target doesn't support REPORT LUNS. */ if (bflags & BLIST_NOREPORTLUN) return 1; @@ -1332,6 +1334,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, return 1; if (bflags & BLIST_NOLUN) return 0; + if (starget->no_report_luns) + return 1; if (!(sdev = scsi_device_lookup_by_target(starget, 0))) { sdev = scsi_alloc_sdev(starget, 0, NULL); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c691fb5..d173b90 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2349,7 +2349,7 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp) * some USB ones crash on receiving them, and the pages * we currently ask for are for SPC-3 and beyond */ - if (sdp->scsi_level > SCSI_SPC_2) + if (sdp->scsi_level > SCSI_SPC_2 && !sdp->skip_vpd_pages) return 1; return 0; } diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 13b8bcd..dc68cc9 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -197,6 +197,9 @@ static int slave_configure(struct scsi_device *sdev) * page x08, so we will skip it. */ sdev->skip_ms_page_8 = 1; + /* Some devices don't handle VPD pages correctly */ + sdev->skip_vpd_pages = 1; + /* Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. * If this device makes that mistake, tell the sd driver. */ @@ -217,16 +220,6 @@ static int slave_configure(struct scsi_device *sdev) if (sdev->scsi_level > SCSI_SPC_2) us->fflags |= US_FL_SANE_SENSE; - /* Some devices report a SCSI revision level above 2 but are - * unable to handle the REPORT LUNS command (for which - * support is mandatory at level 3). Since we already have - * a Get-Max-LUN request, we won't lose much by setting the - * revision level down to 2. The only devices that would be - * affected are those with sparse LUNs. */ - if (sdev->scsi_level > SCSI_2) - sdev->sdev_target->scsi_level = - sdev->scsi_level = SCSI_2; - /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable * Hardware Error) when any low-level error occurs, * recoverable or not. Setting this flag tells the SCSI @@ -283,6 +276,18 @@ static int slave_configure(struct scsi_device *sdev) return 0; } +static int target_alloc(struct scsi_target *starget) +{ + /* + * Some USB drives don't support REPORT LUNS, even though they + * report a SCSI revision level above 2. Tell the SCSI layer + * not to issue that command; it will perform a normal sequential + * scan instead. + */ + starget->no_report_luns = 1; + return 0; +} + /* queue a command */ /* This is always called with scsi_lock(host) held */ static int queuecommand_lck(struct scsi_cmnd *srb, @@ -546,6 +551,7 @@ struct scsi_host_template usb_stor_host_template = { .slave_alloc = slave_alloc, .slave_configure = slave_configure, + .target_alloc = target_alloc, /* lots of sg segments can be handled */ .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS, diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 01cb3c4..b3a1c2d 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -136,6 +136,7 @@ struct scsi_device { unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ + unsigned skip_vpd_pages:1; /* do not read VPD pages */ unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */ unsigned no_start_on_add:1; /* do not issue start on add */ unsigned allow_restart:1; /* issue START_UNIT in error handler */ @@ -248,6 +249,8 @@ struct scsi_target { * for the device at a time. */ unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f * means no lun present. */ + unsigned int no_report_luns:1; /* Don't use + * REPORT LUNS for scanning. */ /* commands actually active on LLD. protected by host lock. */ unsigned int target_busy; /* -- cgit v0.10.2 From af74d2dae8f85a0e90a30594beb507f5d954fa3f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 10 Jan 2012 13:43:40 -0500 Subject: usb-storage: reorganize target-specific code Now that usb-storage has a target_alloc() routine, this patch (as1508) moves some existing target-specific code out of the slave_alloc() routine to where it really belongs. Signed-off-by: Alan Stern CC: Matthew Dharm Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index dc68cc9..a324a5d 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -78,8 +78,6 @@ static const char* host_info(struct Scsi_Host *host) static int slave_alloc (struct scsi_device *sdev) { - struct us_data *us = host_to_us(sdev->host); - /* * Set the INQUIRY transfer length to 36. We don't use any of * the extra data and many devices choke if asked for more or @@ -104,18 +102,6 @@ static int slave_alloc (struct scsi_device *sdev) */ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); - /* - * The UFI spec treates the Peripheral Qualifier bits in an - * INQUIRY result as reserved and requires devices to set them - * to 0. However the SCSI spec requires these bits to be set - * to 3 to indicate when a LUN is not present. - * - * Let the scanning code know if this target merely sets - * Peripheral Device Type to 0x1f to indicate no LUN. - */ - if (us->subclass == USB_SC_UFI) - sdev->sdev_target->pdt_1f_for_no_lun = 1; - return 0; } @@ -278,6 +264,8 @@ static int slave_configure(struct scsi_device *sdev) static int target_alloc(struct scsi_target *starget) { + struct us_data *us = host_to_us(dev_to_shost(starget->dev.parent)); + /* * Some USB drives don't support REPORT LUNS, even though they * report a SCSI revision level above 2. Tell the SCSI layer @@ -285,6 +273,19 @@ static int target_alloc(struct scsi_target *starget) * scan instead. */ starget->no_report_luns = 1; + + /* + * The UFI spec treats the Peripheral Qualifier bits in an + * INQUIRY result as reserved and requires devices to set them + * to 0. However the SCSI spec requires these bits to be set + * to 3 to indicate when a LUN is not present. + * + * Let the scanning code know if this target merely sets + * Peripheral Device Type to 0x1f to indicate no LUN. + */ + if (us->subclass == USB_SC_UFI) + starget->pdt_1f_for_no_lun = 1; + return 0; } -- cgit v0.10.2 From 0846e7e9856c0928223447d9349a877202a63f24 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 3 Feb 2012 17:11:54 -0500 Subject: usb: Add support for indicating whether a port is removable Userspace may want to make policy decisions based on whether or not a given USB device is removable. Add a per-device member and support for exposing it in sysfs. Information sources to populate it will be added later. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index b4f5487..7c22a532 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -182,3 +182,14 @@ Description: USB2 hardware LPM is enabled for the device. Developer can write y/Y/1 or n/N/0 to the file to enable/disable the feature. + +What: /sys/bus/usb/devices/.../removable +Date: February 2012 +Contact: Matthew Garrett +Description: + Some information about whether a given USB device is + physically fixed to the platform can be inferred from a + combination of hub decriptor bits and platform-specific data + such as ACPI. This file will read either "removable" or + "fixed" if the information is available, and "unknown" + otherwise. \ No newline at end of file diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9e491ca..566d9f9 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); +static ssize_t +show_removable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + char *state; + + udev = to_usb_device(dev); + + switch (udev->removable) { + case USB_DEVICE_REMOVABLE: + state = "removable"; + break; + case USB_DEVICE_FIXED: + state = "fixed"; + break; + default: + state = "unknown"; + } + + return sprintf(buf, "%s\n", state); +} +static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); #ifdef CONFIG_PM @@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_avoid_reset_quirk.attr, &dev_attr_authorized.attr, &dev_attr_remove.attr, + &dev_attr_removable.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/include/linux/usb.h b/include/linux/usb.h index 27a4e16..b2eb3a4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -376,6 +376,12 @@ struct usb_bus { struct usb_tt; +enum usb_device_removable { + USB_DEVICE_REMOVABLE_UNKNOWN = 0, + USB_DEVICE_REMOVABLE, + USB_DEVICE_FIXED, +}; + /** * struct usb_device - kernel's representation of a USB device * @devnum: device number; address on a USB bus @@ -432,6 +438,7 @@ struct usb_tt; * @wusb_dev: if this is a Wireless USB device, link to the WUSB * specific data for the device. * @slot_id: Slot ID assigned by xHCI + * @removable: Device can be physically removed from this port * * Notes: * Usbcore drivers should not set usbdev->state directly. Instead use @@ -509,6 +516,7 @@ struct usb_device { #endif struct wusb_dev *wusb_dev; int slot_id; + enum usb_device_removable removable; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -- cgit v0.10.2 From d35e70d50a0641ebc1502fd343bef9b4011ada27 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 3 Feb 2012 17:11:55 -0500 Subject: usb: Use hub port data to determine whether a port is removable Hubs have a flag to indicate whether a given port carries removable devices or not. This is not strictly accurate in that some built-in devices will be flagged as removable, but followup patches will make use of platform data to make this more reliable. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a0613d8..2d773cb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1838,6 +1838,37 @@ fail: return err; } +static void set_usb_port_removable(struct usb_device *udev) +{ + struct usb_device *hdev = udev->parent; + struct usb_hub *hub; + u8 port = udev->portnum; + u16 wHubCharacteristics; + bool removable = true; + + if (!hdev) + return; + + hub = hdev_to_hub(udev->parent); + + wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); + + if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) + return; + + if (hub_is_superspeed(hdev)) { + if (hub->descriptor->u.ss.DeviceRemovable & (1 << port)) + removable = false; + } else { + if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8))) + removable = false; + } + + if (removable) + udev->removable = USB_DEVICE_REMOVABLE; + else + udev->removable = USB_DEVICE_FIXED; +} /** * usb_new_device - perform initial device setup (usbcore-internal) @@ -1896,6 +1927,15 @@ int usb_new_device(struct usb_device *udev) announce_device(udev); device_enable_async_suspend(&udev->dev); + + /* + * check whether the hub marks this port as non-removable. Do it + * now so that platform-specific data can override it in + * device_add() + */ + if (udev->parent) + set_usb_port_removable(udev); + /* Register the device. The device driver is responsible * for configuring the device and invoking the add-device * notifier chain (used by usbfs and possibly others). -- cgit v0.10.2 From e688355bfeadf17ef522b1e62cc12f8e88e69667 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 6 Feb 2012 19:22:19 +0100 Subject: USB: serial: add macro for console error reporting Add macro which prints an error message only once if port is used a console. Reporting errors in a write path when port is used as a console could otherwise result in an infinite loop. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 4267a9c..10cb74d 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -389,5 +389,20 @@ do { \ printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \ } while (0) +/* + * Macro for reporting errors in write path to avoid inifinite loop + * when port is used as a console. + */ +#define dev_err_console(usport, fmt, ...) \ +do { \ + static bool __print_once; \ + struct usb_serial_port *__port = (usport); \ + \ + if (!__port->port.console || !__print_once) { \ + __print_once = true; \ + dev_err(&__port->dev, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + #endif /* __LINUX_USB_SERIAL_H */ -- cgit v0.10.2 From 548dd4b6da8a8e428453d55f7fa7b8a46498d147 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 10 Feb 2012 13:20:49 +0100 Subject: USB: serial: fix console error reporting Do not report errors in write path if port is used as a console as this may trigger the same error (and error report) resulting in a loop. Reported-by: Stephen Hemminger Cc: stable Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index f740357..e129fcd 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -217,8 +217,10 @@ retry: clear_bit(i, &port->write_urbs_free); result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - error submitting urb: %d\n", + if (!port->port.console) { + dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, result); + } set_bit(i, &port->write_urbs_free); spin_lock_irqsave(&port->lock, flags); port->tx_bytes -= count; -- cgit v0.10.2 From f1475a00a11b07e6ac7f97971466c1bfbf491957 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 10 Feb 2012 13:20:50 +0100 Subject: USB: serial: use dev_err_console in generic write Use dev_err_console in write path so that an error at least gets reported once. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index e129fcd..2a2fa2d 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -217,10 +217,8 @@ retry: clear_bit(i, &port->write_urbs_free); result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - if (!port->port.console) { - dev_err(&port->dev, "%s - error submitting urb: %d\n", + dev_err_console(port, "%s - error submitting urb: %d\n", __func__, result); - } set_bit(i, &port->write_urbs_free); spin_lock_irqsave(&port->lock, flags); port->tx_bytes -= count; -- cgit v0.10.2 From 22a416c4e0f2179b57028e084ac0ed2c110333bd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 10 Feb 2012 13:20:51 +0100 Subject: USB: serial: use dev_err_console in custom write paths Use dev_err_console in write paths for devices which can be used as a console but do not use the generic write implementation. Compile-only tested. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 3bdeafa..5ae86b3 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -800,7 +800,7 @@ send: cypress_write_int_callback, port, priv->write_urb_interval); result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, + dev_err_console(port, "%s - failed submitting write urb, error %d\n", __func__, result); priv->write_urb_in_use = 0; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index b23bebd..2b1da0c 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -995,7 +995,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, /* return length of new data written, or error */ spin_unlock_irqrestore(&priv->dp_port_lock, flags); if (ret < 0) - dev_err(&port->dev, + dev_err_console(port, "%s: usb_submit_urb failed, ret=%d, port=%d\n", __func__, ret, priv->dp_port_num); dbg("digi_write: returning %d", ret); @@ -1065,7 +1065,7 @@ static void digi_write_bulk_callback(struct urb *urb) spin_unlock(&priv->dp_port_lock); if (ret && ret != -EPERM) - dev_err(&port->dev, + dev_err_console(port, "%s: usb_submit_urb failed, ret=%d, port=%d\n", __func__, ret, priv->dp_port_num); } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 0497575..616b078 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1286,7 +1286,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, count = fifo->count; buffer = kmalloc(count+2, GFP_ATOMIC); if (buffer == NULL) { - dev_err(&edge_port->port->dev, + dev_err_console(edge_port->port, "%s - no more kernel memory...\n", __func__); edge_port->write_in_progress = false; goto exit_send; @@ -1331,7 +1331,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { /* something went wrong */ - dev_err(&edge_port->port->dev, + dev_err_console(edge_port->port, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __func__, status); edge_port->write_in_progress = false; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 65bf06a..311e4bf 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1817,7 +1817,7 @@ static void edge_bulk_out_callback(struct urb *urb) __func__, status); return; default: - dev_err(&urb->dev->dev, "%s - nonzero write bulk status " + dev_err_console(port, "%s - nonzero write bulk status " "received: %d\n", __func__, status); } @@ -2111,7 +2111,7 @@ static void edge_send(struct tty_struct *tty) /* send the data out the bulk port */ result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, + dev_err_console(port, "%s - failed submitting write urb, error %d\n", __func__, result); edge_port->ep_write_urb_in_use = 0; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 4554ee4..4fb29b4a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1294,7 +1294,7 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (urb->transfer_buffer == NULL) { - dev_err(&port->dev, "%s no more kernel memory...\n", + dev_err_console(port, "%s no more kernel memory...\n", __func__); goto exit; } @@ -1315,7 +1315,7 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, /* send it down the pipe */ status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { - dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " + dev_err_console(port, "%s - usb_submit_urb(write bulk) failed " "with status = %d\n", __func__, status); bytes_sent = status; goto exit; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 03b5e249..19b11ce 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1509,7 +1509,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (urb->transfer_buffer == NULL) { - dev_err(&port->dev, "%s no more kernel memory...\n", + dev_err_console(port, "%s no more kernel memory...\n", __func__); goto exit; } @@ -1535,7 +1535,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, if (status) { mos7840_port->busy[i] = 0; - dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " + dev_err_console(port, "%s - usb_submit_urb(write bulk) failed " "with status = %d\n", __func__, status); bytes_sent = status; goto exit; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 8b8d58a..033e8af 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -254,7 +254,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, result = usb_submit_urb(wport->write_urb, GFP_ATOMIC); if (result) { set_bit(0, &wport->write_urbs_free); - dev_err(&port->dev, + dev_err_console(port, "%s - failed submitting write urb, error %d\n", __func__, result); } else diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index e287fd3..343e626 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -302,7 +302,7 @@ static void send_data(struct work_struct *work) if (count != 0) { allow = kmalloc(1, GFP_KERNEL); if (!allow) { - dev_err(&port->dev, "%s(): kmalloc failed\n", + dev_err_console(port, "%s(): kmalloc failed\n", __func__); return; } @@ -334,7 +334,7 @@ static void send_data(struct work_struct *work) port->write_urb->transfer_buffer_length = count; result = usb_submit_urb(port->write_urb, GFP_NOIO); if (result != 0) { - dev_err(&port->dev, "%s(): usb_submit_urb() failed" + dev_err_console(port, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); priv->flags.write_urb_in_use = 0; } @@ -938,7 +938,7 @@ static void oti6858_write_bulk_callback(struct urb *urb) port->write_urb->transfer_buffer_length = 1; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s(): usb_submit_urb() failed," + dev_err_console(port, "%s(): usb_submit_urb() failed," " error %d\n", __func__, result); } else { return; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 8468eb7..91d0dc6 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1248,7 +1248,6 @@ static void ti_bulk_out_callback(struct urb *urb) { struct ti_port *tport = urb->context; struct usb_serial_port *port = tport->tp_port; - struct device *dev = &urb->dev->dev; int status = urb->status; dbg("%s - port %d", __func__, port->number); @@ -1266,7 +1265,7 @@ static void ti_bulk_out_callback(struct urb *urb) wake_up_interruptible(&tport->tp_write_wait); return; default: - dev_err(dev, "%s - nonzero urb status, %d\n", + dev_err_console(port, "%s - nonzero urb status, %d\n", __func__, status); tport->tp_tdev->td_urb_error = 1; wake_up_interruptible(&tport->tp_write_wait); @@ -1335,7 +1334,7 @@ static void ti_send(struct ti_port *tport) result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - submit write urb failed, %d\n", + dev_err_console(port, "%s - submit write urb failed, %d\n", __func__, result); tport->tp_write_urb_in_use = 0; /* TODO: reschedule ti_send */ diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 7e0acf5..007cf3a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -740,7 +740,7 @@ static int whiteheat_write(struct tty_struct *tty, urb->transfer_buffer_length = bytes; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, + dev_err_console(port, "%s - failed submitting write urb, error %d\n", __func__, result); sent = result; -- cgit v0.10.2 From 88044202756925ad47c51c2f634a4f2c17afe068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Fri, 10 Feb 2012 09:44:08 +0100 Subject: usb: cdc-wdm: make reset work with blocking IO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a flag to tell wdm_read/wdm_write that a reset is in progress, and wake any blocking read/write before taking the mutexes. This allows the device to reset without waiting for blocking IO to finish. Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index f63601a..b27bbb9 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -70,6 +70,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_POLL_RUNNING 6 #define WDM_RESPONDING 7 #define WDM_SUSPENDING 8 +#define WDM_RESETTING 9 #define WDM_MAX 16 @@ -340,6 +341,10 @@ static ssize_t wdm_write else if (test_bit(WDM_IN_USE, &desc->flags)) r = -EAGAIN; + + if (test_bit(WDM_RESETTING, &desc->flags)) + r = -EIO; + if (r < 0) { kfree(buf); goto out; @@ -419,6 +424,10 @@ retry: rv = -ENODEV; goto err; } + if (test_bit(WDM_RESETTING, &desc->flags)) { + rv = -EIO; + goto err; + } usb_mark_last_busy(interface_to_usbdev(desc->intf)); if (rv < 0) { rv = -ERESTARTSYS; @@ -859,10 +868,6 @@ static int wdm_pre_reset(struct usb_interface *intf) { struct wdm_device *desc = usb_get_intfdata(intf); - mutex_lock(&desc->rlock); - mutex_lock(&desc->wlock); - kill_urbs(desc); - /* * we notify everybody using poll of * an exceptional situation @@ -870,9 +875,16 @@ static int wdm_pre_reset(struct usb_interface *intf) * message from the device is lost */ spin_lock_irq(&desc->iuspin); + set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */ + set_bit(WDM_READ, &desc->flags); /* unblock read */ + clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */ desc->rerr = -EINTR; spin_unlock_irq(&desc->iuspin); wake_up_all(&desc->wait); + mutex_lock(&desc->rlock); + mutex_lock(&desc->wlock); + kill_urbs(desc); + cancel_work_sync(&desc->rxwork); return 0; } @@ -881,6 +893,7 @@ static int wdm_post_reset(struct usb_interface *intf) struct wdm_device *desc = usb_get_intfdata(intf); int rv; + clear_bit(WDM_RESETTING, &desc->flags); rv = recover_from_urb_loss(desc); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); -- cgit v0.10.2 From 5ee71cf3bef66ad9683c681e48628fae057f8e18 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sat, 11 Feb 2012 21:19:10 +0900 Subject: usb: Fix typo in imx21-dbg.c Correct spelling "alocate" to "allocate" in drivers/usb/host/imx21-dbg.c Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c index 6d75334..ec98ece 100644 --- a/drivers/usb/host/imx21-dbg.c +++ b/drivers/usb/host/imx21-dbg.c @@ -239,7 +239,7 @@ static int debug_status_show(struct seq_file *s, void *v) "ETDs allocated: %d/%d (max=%d)\n" "ETDs in use sw: %d\n" "ETDs in use hw: %d\n" - "DMEM alocated: %d/%d (max=%d)\n" + "DMEM allocated: %d/%d (max=%d)\n" "DMEM blocks: %d\n" "Queued waiting for ETD: %d\n" "Queued waiting for DMEM: %d\n", -- cgit v0.10.2 From d93814cfadb131fb219359d4b7008c728421c552 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 24 Jan 2012 16:39:02 -0800 Subject: xHCI: Kick khubd when USB3 resume really completes. xHCI roothubs go through slightly different port state machines when either a device initiates a remote wakeup and signals resume, or when the host initiates a resume. According to section 4.19.1.2.13 of the xHCI 1.0 spec, on host-initiated resume, the xHC port state machine automatically goes through the U3Exit state into the U0 state, setting the port link state change (PLC) bit in the process. When a device initiates resume, the xHCI port state machine goes into the "Resume" state and sets the PLC bit. Then the xHCI driver writes U0 into the port link state register to transition the port to U0 from the Resume state. We can't be sure the device is actually in the U0 state until we receive the next port status change event with the PLC bit set. We really don't want khubd to be polling the roothub port status bits until the device is really in U0. Signed-off-by: Sarah Sharp Acked-by: Andiry Xu diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b62037b..ca38483 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1321,20 +1321,16 @@ static void handle_port_status(struct xhci_hcd *xhci, } if (DEV_SUPERSPEED(temp)) { - xhci_dbg(xhci, "resume SS port %d\n", port_id); + xhci_dbg(xhci, "remote wake SS port %d\n", port_id); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); xhci_set_link_state(xhci, port_array, faked_port_index, XDEV_U0); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - faked_port_index + 1); - if (!slot_id) { - xhci_dbg(xhci, "slot_id is zero\n"); - goto cleanup; - } - xhci_ring_device(xhci, slot_id); - xhci_dbg(xhci, "resume SS port %d finished\n", port_id); - /* Clear PORT_PLC */ - xhci_test_and_clear_bit(xhci, port_array, - faked_port_index, PORT_PLC); + /* Need to wait until the next link state change + * indicates the device is actually in U0. + */ + bogus_port_status = true; + goto cleanup; } else { xhci_dbg(xhci, "resume HS port %d\n", port_id); bus_state->resume_done[faked_port_index] = jiffies + @@ -1345,6 +1341,15 @@ static void handle_port_status(struct xhci_hcd *xhci, } } + if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 && + DEV_SUPERSPEED(temp)) { + xhci_dbg(xhci, "resume SS port %d finished\n", port_id); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + faked_port_index + 1); + if (slot_id && xhci->devs[slot_id]) + xhci_ring_device(xhci, slot_id); + } + if (hcd->speed != HCD_USB3) xhci_test_and_clear_bit(xhci, port_array, faked_port_index, PORT_PLC); -- cgit v0.10.2 From 623bef9e03a60adc623b09673297ca7a1cdfb367 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 11 Nov 2011 14:57:33 -0800 Subject: USB/xhci: Enable remote wakeup for USB3 devices. When the USB 3.0 hub support went in, I disabled selective suspend for all external USB 3.0 hubs because they used a different mechanism to enable remote wakeup. In fact, other USB 3.0 devices that could signal remote wakeup would have been prevented from going into suspend because they would have stalled the SetFeature Device Remote Wakeup request. This patch adds support for the USB 3.0 way of enabling remote wake up (with a SetFeature Function Suspend request), and enables selective suspend for all hubs during hub_probe. It assumes that all USB 3.0 have only one "function" as defined by the interface association descriptor, which is true of all the USB 3.0 devices I've seen so far. FIXME if that turns out to change later. After a device signals a remote wakeup, it is supposed to send a Device Notification packet to the host controller, signaling which function sent the remote wakeup. The host can then put any other functions back into function suspend. Since we don't have support for function suspend (and no devices currently support it), we'll just assume the hub function will resume the device properly when it received the port status change notification, and simply ignore any device notification events from the xHCI host controller. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2d773cb..50411cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2421,11 +2421,26 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * we don't explicitly enable it here. */ if (udev->do_remote_wakeup) { - status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); + if (!hub_is_superspeed(hub->hdev)) { + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + /* Assume there's only one function on the USB 3.0 + * device and enable remote wake for the first + * interface. FIXME if the interface association + * descriptor shows there's more than one function. + */ + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, + USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + USB_INTRF_FUNC_SUSPEND_RW, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 36cbe22..6b70e7f 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2141,7 +2141,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; struct xhci_segment *seg; - u32 page_size; + u32 page_size, temp; int i; page_size = xhci_readl(xhci, &xhci->op_regs->page_size); @@ -2324,6 +2324,15 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->lpm_failed_devs); + /* Enable USB 3.0 device notifications for function remote wake, which + * is necessary for allowing USB 3.0 devices to do remote wakeup from + * U3 (device suspend). + */ + temp = xhci_readl(xhci, &xhci->op_regs->dev_notification); + temp &= ~DEV_NOTE_MASK; + temp |= DEV_NOTE_FWAKE; + xhci_writel(xhci, temp, &xhci->op_regs->dev_notification); + return 0; fail: diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ca38483..ffe5493 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1237,6 +1237,24 @@ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, return num_similar_speed_ports; } +static void handle_device_notification(struct xhci_hcd *xhci, + union xhci_trb *event) +{ + u32 slot_id; + + slot_id = TRB_TO_SLOT_ID(event->generic.field[3]); + if (!xhci->devs[slot_id]) + xhci_warn(xhci, "Device Notification event for " + "unused slot %u\n", slot_id); + else + xhci_dbg(xhci, "Device Notification event for slot ID %u\n", + slot_id); + /* XXX should we kick khubd for the parent hub? It should have send an + * interrupt transfer when the port started signaling resume, so there's + * probably no need to do so. + */ +} + static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) { @@ -2282,6 +2300,9 @@ static int xhci_handle_event(struct xhci_hcd *xhci) else update_ptrs = 0; break; + case TRB_TYPE(TRB_DEV_NOTE): + handle_device_notification(xhci, event); + break; default: if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >= TRB_TYPE(48)) -- cgit v0.10.2 From 3b9b6acd4798aafe9b9f64ccb7e8eb76f27f904a Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 6 Jan 2012 15:48:30 -0800 Subject: USB: Suspend functions before putting dev into U3. The USB 3.0 bus specification introduces a new type of power management called function suspend. The idea is to be able to suspend different functions (i.e. a scanner or an SD card reader on a USB printer) independently. A device can be in U0, but have one or more functions suspended. Thus, signaling a function resume with the standard device remote wake signaling was not possible. Instead, a device will (without prompt from the host) send a "device notification" for the function remote wake. A new Set Feature Function Remote Wake was developed to turn remote wake up on and off for each function. USB 3.0 devices can still go into device suspend (U3), and signal a remote wakeup to bring the link back into U1. However, they now use the function remote wake device notification to allow the host to know which function woke the device from U3. The spec is a bit ambiguous about whether a function is allowed to signal a remote wakeup if the function has been enabled for remote wakeup, but not placed in function suspend before the device is placed into U3. Section 9.2.5.1 says "Suspending a device with more than one function effectively suspends all the functions within the device." I interpret that to mean that putting a device in U3 suspends all functions, and thus if the host has previously enabled remote wake for those functions, it should be able to signal a remote wake up on port status changes. However, hub vendors may have a different interpretation, and it can't hurt to put the function into suspend before putting the device into U3. I cannot get an answer out of the USB 3.0 spec architects about this ambiguity, so I'm erring on the safe side and always suspending the first function before placing the device in U3. Note, this code should be fixed if we ever find any USB 3.0 devices that have more than one function. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 50411cd..70622d6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2437,7 +2437,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE, USB_INTRF_FUNC_SUSPEND, - USB_INTRF_FUNC_SUSPEND_RW, + USB_INTRF_FUNC_SUSPEND_RW | + USB_INTRF_FUNC_SUSPEND_LP, NULL, 0, USB_CTRL_SET_TIMEOUT); } -- cgit v0.10.2 From 4296c70a5ec316903ef037ed15f154dd3d354ad7 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 6 Jan 2012 10:34:31 -0800 Subject: USB/xHCI: Enable USB 3.0 hub remote wakeup. USB 3.0 hubs have a different remote wakeup policy than USB 2.0 hubs. USB 2.0 hubs, once they have remote wakeup enabled, will always send remote wakes when anything changes on a port. However, USB 3.0 hubs have a per-port remote wake up policy that is off by default. The Set Feature remote wake mask can be changed for any port, enabling remote wakeup for a connect, disconnect, or overcurrent event, much like EHCI and xHCI host controller "wake on" port status bits. The bits are cleared to zero on the initial hub power on, or after the hub has been reset. Without this patch, when a USB 3.0 hub gets suspended, it will not send a remote wakeup on device connect or disconnect. This would show up to the user as "dead ports" unless they ran lsusb -v (since newer versions of lsusb use the sysfs files, rather than sending control transfers). Change the hub driver's suspend method to enable remote wake up for disconnect, connect, and overcurrent for all ports on the hub. Modify the xHCI driver's roothub code to handle that request, and set the "wake on" bits in the port status registers accordingly. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 70622d6..b3137fa 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2731,6 +2731,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; + int status; /* Warn if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -2743,6 +2744,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) return -EBUSY; } } + if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) { + /* Enable hub to send remote wakeup for all ports. */ + for (port1 = 1; port1 <= hdev->maxchild; port1++) { + status = set_port_feature(hdev, + port1 | + USB_PORT_FEAT_REMOTE_WAKE_CONNECT | + USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | + USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, + USB_PORT_FEAT_REMOTE_WAKE_MASK); + } + } dev_dbg(&intf->dev, "%s\n", __func__); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 557b6f3..673ad12 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -422,6 +422,32 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, xhci_writel(xhci, temp, port_array[port_id]); } +void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, + __le32 __iomem **port_array, int port_id, u16 wake_mask) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + temp = xhci_port_state_to_neutral(temp); + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) + temp |= PORT_WKCONN_E; + else + temp &= ~PORT_WKCONN_E; + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT) + temp |= PORT_WKDISC_E; + else + temp &= ~PORT_WKDISC_E; + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT) + temp |= PORT_WKOC_E; + else + temp &= ~PORT_WKOC_E; + + xhci_writel(xhci, temp, port_array[port_id]); +} + /* Test and clear port RWC bit */ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, int port_id, u32 port_bit) @@ -448,6 +474,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, int slot_id; struct xhci_bus_state *bus_state; u16 link_state = 0; + u16 wake_mask = 0; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -593,6 +620,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case SetPortFeature: if (wValue == USB_PORT_FEAT_LINK_STATE) link_state = (wIndex & 0xff00) >> 3; + if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK) + wake_mask = wIndex & 0xff00; wIndex &= 0xff; if (!wIndex || wIndex > max_ports) goto error; @@ -703,6 +732,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; + case USB_PORT_FEAT_REMOTE_WAKE_MASK: + xhci_set_remote_wake_mask(xhci, port_array, + wIndex, wake_mask); + temp = xhci_readl(xhci, port_array[wIndex]); + xhci_dbg(xhci, "set port remote wake mask, " + "actual port %d status = 0x%x\n", + wIndex, temp); + break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; xhci_writel(xhci, temp, port_array[wIndex]); @@ -883,6 +920,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t2 |= PORT_LINK_STROBE | XDEV_U3; set_bit(port_index, &bus_state->bus_suspended); } + /* USB core sets remote wake mask for USB 3.0 hubs, + * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND + * is enabled, so also enable remote wake here. + */ if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 0b83acd..f1d26b6 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -76,6 +76,11 @@ #define USB_PORT_FEAT_C_BH_PORT_RESET 29 #define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30 +/* USB 3.0 hub remote wake mask bits, see table 10-14 */ +#define USB_PORT_FEAT_REMOTE_WAKE_CONNECT (1 << 8) +#define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT (1 << 9) +#define USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT (1 << 10) + /* * Hub Status and Hub Change results * See USB 2.0 spec Table 11-19 and Table 11-20 -- cgit v0.10.2 From 714b07be3bbf94d2dc9838723d63fc827fdbef12 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 24 Jan 2012 13:53:18 -0800 Subject: USB: Refactor hub remote wake handling. Refactor the code to check for a remote wakeup on a port into its own function. Keep the behavior the same, and set connect_change in hub_events if the device disconnected on resume. Cleanup references to hdev->children[i-1] to use a common variable. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b3137fa..ba95094 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3488,6 +3488,39 @@ done: hcd->driver->relinquish_port(hcd, port1); } +/* Returns 1 if there was a remote wakeup and a connect status change. */ +static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, + u16 portchange) +{ + struct usb_device *hdev; + struct usb_device *udev; + int connect_change = 0; + int ret; + + hdev = hub->hdev; + if (!(portchange & USB_PORT_STAT_C_SUSPEND)) + return 0; + clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); + + udev = hdev->children[port-1]; + if (udev) { + /* TRSMRCY = 10 msec */ + msleep(10); + + usb_lock_device(udev); + ret = usb_remote_wakeup(udev); + usb_unlock_device(udev); + if (ret < 0) + connect_change = 1; + } else { + ret = -ENODEV; + hub_port_disable(hub, port, 1); + } + dev_dbg(hub->intfdev, "resume on port %d, status %d\n", + port, ret); + return connect_change; +} + static void hub_events(void) { struct list_head *tmp; @@ -3621,31 +3654,9 @@ static void hub_events(void) } } - if (portchange & USB_PORT_STAT_C_SUSPEND) { - struct usb_device *udev; + if (hub_handle_remote_wakeup(hub, i, portchange)) + connect_change = 1; - clear_port_feature(hdev, i, - USB_PORT_FEAT_C_SUSPEND); - udev = hdev->children[i-1]; - if (udev) { - /* TRSMRCY = 10 msec */ - msleep(10); - - usb_lock_device(udev); - ret = usb_remote_wakeup(hdev-> - children[i-1]); - usb_unlock_device(udev); - if (ret < 0) - connect_change = 1; - } else { - ret = -ENODEV; - hub_port_disable(hub, i, 1); - } - dev_dbg (hub_dev, - "resume on port %d, status %d\n", - i, ret); - } - if (portchange & USB_PORT_STAT_C_OVERCURRENT) { u16 status = 0; u16 unused; -- cgit v0.10.2 From 4ee823b83bc9851743fab756c76b27d6a1e2472b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 14 Nov 2011 18:00:01 -0800 Subject: USB/xHCI: Support device-initiated USB 3.0 resume. USB 3.0 hubs don't have a port suspend change bit (that bit is now reserved). Instead, when a host-initiated resume finishes, the hub sets the port link state change bit. When a USB 3.0 device initiates remote wakeup, the parent hubs with their upstream links in U3 will pass the LFPS up the chain. The first hub that has an upstream link in U0 (which may be the roothub) will reflect that LFPS back down the path to the device. However, the parent hubs in the resumed path will not set their link state change bit. Instead, the device that initiated the resume has to send an asynchronous "Function Wake" Device Notification up to the host controller. Therefore, we need a way to notify the USB core of a device resume without going through the normal hub URB completion method. First, make the xHCI roothub act like an external USB 3.0 hub and not pass up the port link state change bit when a device-initiated resume finishes. Introduce a new xHCI bit field, port_remote_wakeup, so that we can tell the difference between a port coming out of the U3Exit state (host-initiated resume) and the RExit state (ending state of device-initiated resume). Since the USB core can't tell whether a port on a hub has resumed by looking at the Hub Status buffer, we need to introduce a bitfield, wakeup_bits, that indicates which ports have resumed. When the xHCI driver notices a port finishing a device-initiated resume, we call into a new USB core function, usb_wakeup_notification(), that will set the right bit in wakeup_bits, and kick khubd for that hub. We also call usb_wakeup_notification() when the Function Wake Device Notification is received by the xHCI driver. This covers the case where the link between the roothub and the first-tier hub is in U0, and the hub reflects the resume signaling back to the device without giving any indication it has done so until the device sends the Function Wake notification. Change the code in khubd that handles the remote wakeup to look at the state the USB core thinks the device is in, and handle the remote wakeup if the port's wakeup bit is set. This patch only takes care of the case where the device is attached directly to the roothub, or the USB 3.0 hub that is attached to the root hub is the device sending the Function Wake Device Notification (e.g. because a new USB device was attached). The other cases will be covered in a second patch. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ba95094..1c32bba 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -62,6 +62,8 @@ struct usb_hub { resumed */ unsigned long removed_bits[1]; /* ports with a "removed" device present */ + unsigned long wakeup_bits[1]; /* ports that have signaled + remote wakeup */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev) kick_khubd(hub); } +/* + * Let the USB core know that a USB 3.0 device has sent a Function Wake Device + * Notification, which indicates it had initiated remote wakeup. + * + * USB 3.0 hubs do not report the port link state change from U3 to U0 when the + * device initiates resume, so the USB core will not receive notice of the + * resume through the normal hub interrupt URB. + */ +void usb_wakeup_notification(struct usb_device *hdev, + unsigned int portnum) +{ + struct usb_hub *hub; + + if (!hdev) + return; + + hub = hdev_to_hub(hdev); + if (hub) { + set_bit(portnum, hub->wakeup_bits); + kick_khubd(hub); + } +} +EXPORT_SYMBOL_GPL(usb_wakeup_notification); /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb) @@ -807,12 +832,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_ENABLE); } - if (portchange & USB_PORT_STAT_C_LINK_STATE) { - need_debounce_delay = true; - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_PORT_LINK_STATE); - } - if ((portchange & USB_PORT_STAT_C_BH_RESET) && hub_is_superspeed(hub->hdev)) { need_debounce_delay = true; @@ -3498,11 +3517,18 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, int ret; hdev = hub->hdev; - if (!(portchange & USB_PORT_STAT_C_SUSPEND)) - return 0; - clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); - udev = hdev->children[port-1]; + if (!hub_is_superspeed(hdev)) { + if (!(portchange & USB_PORT_STAT_C_SUSPEND)) + return 0; + clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); + } else { + if (!udev || udev->state != USB_STATE_SUSPENDED || + !test_and_clear_bit(udev->portnum, + hub->wakeup_bits)) + return 0; + } + if (udev) { /* TRSMRCY = 10 msec */ msleep(10); @@ -3533,7 +3559,7 @@ static void hub_events(void) u16 portstatus; u16 portchange; int i, ret; - int connect_change; + int connect_change, wakeup_change; /* * We restart the list every time to avoid a deadlock with @@ -3612,8 +3638,9 @@ static void hub_events(void) if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); + wakeup_change = test_bit(i, hub->wakeup_bits); if (!test_and_clear_bit(i, hub->event_bits) && - !connect_change) + !connect_change && !wakeup_change) continue; ret = hub_port_status(hub, i, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ffe5493..3a03324 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1241,18 +1241,20 @@ static void handle_device_notification(struct xhci_hcd *xhci, union xhci_trb *event) { u32 slot_id; + struct usb_device *udev; slot_id = TRB_TO_SLOT_ID(event->generic.field[3]); - if (!xhci->devs[slot_id]) + if (!xhci->devs[slot_id]) { xhci_warn(xhci, "Device Notification event for " "unused slot %u\n", slot_id); - else - xhci_dbg(xhci, "Device Notification event for slot ID %u\n", - slot_id); - /* XXX should we kick khubd for the parent hub? It should have send an - * interrupt transfer when the port started signaling resume, so there's - * probably no need to do so. - */ + return; + } + + xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n", + slot_id); + udev = xhci->devs[slot_id]->udev; + if (udev && udev->parent) + usb_wakeup_notification(udev->parent, udev->portnum); } static void handle_port_status(struct xhci_hcd *xhci, @@ -1340,6 +1342,11 @@ static void handle_port_status(struct xhci_hcd *xhci, if (DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "remote wake SS port %d\n", port_id); + /* Set a flag to say the port signaled remote wakeup, + * so we can tell the difference between the end of + * device and host initiated resume. + */ + bus_state->port_remote_wakeup |= 1 << faked_port_index; xhci_test_and_clear_bit(xhci, port_array, faked_port_index, PORT_PLC); xhci_set_link_state(xhci, port_array, faked_port_index, @@ -1362,10 +1369,27 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 && DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "resume SS port %d finished\n", port_id); + /* We've just brought the device into U0 through either the + * Resume state after a device remote wakeup, or through the + * U3Exit state after a host-initiated resume. If it's a device + * initiated remote wake, don't pass up the link state change, + * so the roothub behavior is consistent with external + * USB 3.0 hub behavior. + */ slot_id = xhci_find_slot_id_by_port(hcd, xhci, faked_port_index + 1); if (slot_id && xhci->devs[slot_id]) xhci_ring_device(xhci, slot_id); + if (bus_state->port_remote_wakeup && (1 << faked_port_index)) { + bus_state->port_remote_wakeup &= + ~(1 << faked_port_index); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); + usb_wakeup_notification(hcd->self.root_hub, + faked_port_index + 1); + bogus_port_status = true; + goto cleanup; + } } if (hcd->speed != HCD_USB3) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index fb99c83..0f49369 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1344,6 +1344,7 @@ struct xhci_bus_state { /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */ u32 port_c_suspend; u32 suspended_ports; + u32 port_remote_wakeup; unsigned long resume_done[USB_MAXCHILDREN]; }; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index b2f62f3..2e6071e 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -412,6 +412,8 @@ extern irqreturn_t usb_hcd_irq(int irq, void *__hcd); extern void usb_hc_died(struct usb_hcd *hcd); extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); +extern void usb_wakeup_notification(struct usb_device *hdev, + unsigned int portnum); /* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */ #define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1) -- cgit v0.10.2 From 72937e1e342f5631d08df4ef0629e55bdcf74c76 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 24 Jan 2012 11:46:50 -0800 Subject: USB: Set wakeup bits for all children hubs. This patch takes care of the race condition between the Function Wake Device Notification and the auto-suspend timeout for this situation: Roothub | (U3) hub A | (U3) hub B | (U3) device C When device C signals a resume, the xHCI driver will set the wakeup_bits for the roothub port that hub A is attached to. However, since USB 3.0 hubs do not set a link state change bit on device-initiated resume, hub A will not indicate a port event when polled. Without this patch, khubd will notice the wakeup-bits are set for the roothub port, it will resume hub A, and then it will poll the events bits for hub A and notice that nothing has changed. Then it will be suspended after 2 seconds. Change hub_activate() to look at the port link state for each USB 3.0 hub port, and set hub->change_bits if the link state is U0, indicating the device has finished resume. Change the resume function called by hub_events(), hub_handle_remote_wakeup(), to check the link status for resume instead of just the port's wakeup_bits. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1c32bba..994aa88 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -853,12 +853,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) set_bit(port1, hub->change_bits); } else if (portstatus & USB_PORT_STAT_ENABLE) { + bool port_resumed = (portstatus & + USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_U0; /* The power session apparently survived the resume. * If there was an overcurrent or suspend change * (i.e., remote wakeup request), have khubd - * take care of it. + * take care of it. Look at the port link state + * for USB 3.0 hubs, since they don't have a suspend + * change bit, and they don't set the port link change + * bit on device-initiated resume. */ - if (portchange) + if (portchange || (hub_is_superspeed(hub->hdev) && + port_resumed)) set_bit(port1, hub->change_bits); } else if (udev->persist_enabled) { @@ -3509,7 +3516,7 @@ done: /* Returns 1 if there was a remote wakeup and a connect status change. */ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, - u16 portchange) + u16 portstatus, u16 portchange) { struct usb_device *hdev; struct usb_device *udev; @@ -3524,8 +3531,8 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); } else { if (!udev || udev->state != USB_STATE_SUSPENDED || - !test_and_clear_bit(udev->portnum, - hub->wakeup_bits)) + (portstatus & USB_PORT_STAT_LINK_STATE) != + USB_SS_PORT_LS_U0) return 0; } @@ -3638,7 +3645,7 @@ static void hub_events(void) if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); - wakeup_change = test_bit(i, hub->wakeup_bits); + wakeup_change = test_and_clear_bit(i, hub->wakeup_bits); if (!test_and_clear_bit(i, hub->event_bits) && !connect_change && !wakeup_change) continue; @@ -3681,7 +3688,8 @@ static void hub_events(void) } } - if (hub_handle_remote_wakeup(hub, i, portchange)) + if (hub_handle_remote_wakeup(hub, i, + portstatus, portchange)) connect_change = 1; if (portchange & USB_PORT_STAT_C_OVERCURRENT) { -- cgit v0.10.2 From 2839f5bcfcfc61f69a36c262107e3cfd6eee9f53 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 6 Jan 2012 16:27:25 -0800 Subject: USB: Turn on auto-suspend for USB 3.0 hubs. Now that USB 3.0 hub remote wakeup on port status changes is enabled, and USB 3.0 device remote wakeup is handled in the USB core properly, let's turn on auto-suspend for all USB 3.0 hubs. Signed-off-by: Sarah Sharp diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 994aa88..d4f0624 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1315,14 +1315,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); - /* Hubs have proper suspend/resume support. USB 3.0 device suspend is - * different from USB 2.0/1.1 device suspend, and unfortunately we - * don't support it yet. So leave autosuspend disabled for USB 3.0 - * external hubs for now. Enable autosuspend for USB 3.0 roothubs, - * since that isn't a "real" hub. - */ - if (!hub_is_superspeed(hdev) || !hdev->parent) - usb_enable_autosuspend(hdev); + /* Hubs have proper suspend/resume support. */ + usb_enable_autosuspend(hdev); if (hdev->level == MAX_TOPO_LEVEL) { dev_err(&intf->dev, -- cgit v0.10.2 From 5407a3c3d942e75d4d123d213fd692bce5acc961 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 15 Feb 2012 09:34:26 +0200 Subject: usb: host: ehci: allow ehci_* symbols to be unused not all platforms will use all of those ehci_* symbols on their hc_driver structure. Sometimes we might need to provide a modified version of a certain method or not provide it at all, as is the case with OMAPs which don't support port handoff feature. Whenever we compile a kernel for an OMAP board with EHCI enabled, we get compile warnings: drivers/usb/host/ehci-hub.c:1079: warning: 'ehci_relinquish_port' \ defined but not used drivers/usb/host/ehci-hub.c:1088: warning: 'ehci_port_handed_over' \ defined but not used In order to cleanup those warnings, we're adding __maybe_unused annotation to those functions. Signed-off-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 77bbb23..01011dd 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ehci->owned_ports = 0; } -static int ehci_port_change(struct ehci_hcd *ehci) +static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci) { int i = HCS_N_PORTS(ehci->hcs_params); @@ -1076,7 +1076,8 @@ error_exit: return retval; } -static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum) +static void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd, + int portnum) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -1085,7 +1086,8 @@ static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum) set_owner(ehci, --portnum, PORT_OWNER); } -static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum) +static int __maybe_unused ehci_port_handed_over(struct usb_hcd *hcd, + int portnum) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); u32 __iomem *reg; -- cgit v0.10.2