Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
Browse files Browse the repository at this point in the history
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (45 commits)
  USB: gadget/multi: cdc_do_config: remove redundant check
  usb: r8a66597-hcd: fix removed from an attached hub
  USB: xhci: Make endpoint interval debugging clearer.
  USB: Fix usb_fill_int_urb for SuperSpeed devices
  USB: cp210x: Remove double usb_control_msg from cp210x_set_config
  USB: Remove last bit of CONFIG_USB_BERRY_CHARGE
  USB: gadget: add gadget controller number for s3c-hsotg driver
  USB: ftdi_sio: Fix locking for change_speed() function
  USB: g_mass_storage: fixed module name in Kconfig
  USB: gadget: f_mass_storage::fsg_bind(): fix error handling
  USB: g_mass_storage: fix section mismatch warnings
  USB: gadget: fix Blackfin builds after gadget cleansing
  USB: goku_udc: remove potential null dereference
  USB: option.c: Add Pirelli VID/PID and indicate Pirelli's modem interface is 0xff
  USB: serial: Fix module name typo for qcaux Kconfig entry.
  usb: cdc-wdm: Fix deadlock between write and resume
  usb: cdc-wdm: Fix order in disconnect and fix locking
  usb: cdc-wdm:Fix loss of data due to autosuspend
  usb: cdc-wdm: Fix submission of URB after suspension
  usb: cdc-wdm: Fix race between disconnect and debug messages
  ...
  • Loading branch information
torvalds committed Mar 19, 2010
2 parents fc7f99c + 4cb80cd commit 8fdb7e9
Show file tree
Hide file tree
Showing 32 changed files with 335 additions and 148 deletions.
2 changes: 1 addition & 1 deletion Documentation/ABI/testing/sysfs-bus-usb
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Description:
match the driver to the device. For example:
# echo "046d c315" > /sys/bus/usb/drivers/foo/remove_id

What: /sys/bus/usb/device/.../avoid_reset
What: /sys/bus/usb/device/.../avoid_reset_quirk
Date: December 2009
Contact: Oliver Neukum <[email protected]>
Description:
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/class/cdc-acm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,7 @@ static int acm_resume(struct usb_interface *intf)
wb = acm->delayed_wb;
acm->delayed_wb = NULL;
spin_unlock_irq(&acm->write_lock);
acm_start_wb(acm, acm->delayed_wb);
acm_start_wb(acm, wb);
} else {
spin_unlock_irq(&acm->write_lock);
}
Expand Down
134 changes: 78 additions & 56 deletions drivers/usb/class/cdc-wdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_READ 4
#define WDM_INT_STALL 5
#define WDM_POLL_RUNNING 6

#define WDM_RESPONDING 7
#define WDM_SUSPENDING 8

#define WDM_MAX 16

Expand Down Expand Up @@ -87,9 +88,7 @@ struct wdm_device {
int count;
dma_addr_t shandle;
dma_addr_t ihandle;
struct mutex wlock;
struct mutex rlock;
struct mutex plock;
struct mutex lock;
wait_queue_head_t wait;
struct work_struct rxwork;
int werr;
Expand Down Expand Up @@ -117,21 +116,22 @@ static void wdm_in_callback(struct urb *urb)
int status = urb->status;

spin_lock(&desc->iuspin);
clear_bit(WDM_RESPONDING, &desc->flags);

if (status) {
switch (status) {
case -ENOENT:
dev_dbg(&desc->intf->dev,
"nonzero urb status received: -ENOENT");
break;
goto skip_error;
case -ECONNRESET:
dev_dbg(&desc->intf->dev,
"nonzero urb status received: -ECONNRESET");
break;
goto skip_error;
case -ESHUTDOWN:
dev_dbg(&desc->intf->dev,
"nonzero urb status received: -ESHUTDOWN");
break;
goto skip_error;
case -EPIPE:
dev_err(&desc->intf->dev,
"nonzero urb status received: -EPIPE\n");
Expand All @@ -147,6 +147,7 @@ static void wdm_in_callback(struct urb *urb)
desc->reslength = urb->actual_length;
memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
desc->length += desc->reslength;
skip_error:
wake_up(&desc->wait);

set_bit(WDM_READ, &desc->flags);
Expand Down Expand Up @@ -229,13 +230,16 @@ static void wdm_int_callback(struct urb *urb)
desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
spin_lock(&desc->iuspin);
clear_bit(WDM_READ, &desc->flags);
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
set_bit(WDM_RESPONDING, &desc->flags);
if (!test_bit(WDM_DISCONNECTING, &desc->flags)
&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
rv = usb_submit_urb(desc->response, GFP_ATOMIC);
dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
__func__, rv);
}
spin_unlock(&desc->iuspin);
if (rv < 0) {
clear_bit(WDM_RESPONDING, &desc->flags);
if (rv == -EPERM)
return;
if (rv == -ENOMEM) {
Expand Down Expand Up @@ -305,39 +309,47 @@ static ssize_t wdm_write
if (we < 0)
return -EIO;

r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
if (!buf) {
rv = -ENOMEM;
goto outnl;
}

r = copy_from_user(buf, buffer, count);
if (r > 0) {
kfree(buf);
rv = -EFAULT;
goto outnl;
}

/* concurrent writes and disconnect */
r = mutex_lock_interruptible(&desc->lock);
rv = -ERESTARTSYS;
if (r)
if (r) {
kfree(buf);
goto outnl;
}

if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
kfree(buf);
rv = -ENODEV;
goto outnp;
}

r = usb_autopm_get_interface(desc->intf);
if (r < 0)
if (r < 0) {
kfree(buf);
goto outnp;
}

if (!file->f_flags && O_NONBLOCK)
r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
&desc->flags));
else
if (test_bit(WDM_IN_USE, &desc->flags))
r = -EAGAIN;
if (r < 0)
goto out;

if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
rv = -ENODEV;
goto out;
}

desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
if (!buf) {
rv = -ENOMEM;
goto out;
}

r = copy_from_user(buf, buffer, count);
if (r > 0) {
if (r < 0) {
kfree(buf);
rv = -EFAULT;
goto out;
}

Expand Down Expand Up @@ -374,7 +386,7 @@ static ssize_t wdm_write
out:
usb_autopm_put_interface(desc->intf);
outnp:
mutex_unlock(&desc->wlock);
mutex_unlock(&desc->lock);
outnl:
return rv < 0 ? rv : count;
}
Expand All @@ -387,7 +399,7 @@ static ssize_t wdm_read
struct wdm_device *desc = file->private_data;


rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
if (rv < 0)
return -ERESTARTSYS;

Expand Down Expand Up @@ -424,11 +436,8 @@ static ssize_t wdm_read
spin_lock_irq(&desc->iuspin);

if (desc->rerr) { /* read completed, error happened */
int t = desc->rerr;
desc->rerr = 0;
spin_unlock_irq(&desc->iuspin);
dev_err(&desc->intf->dev,
"reading had resulted in %d\n", t);
rv = -EIO;
goto err;
}
Expand Down Expand Up @@ -465,9 +474,7 @@ static ssize_t wdm_read
rv = cntr;

err:
mutex_unlock(&desc->rlock);
if (rv < 0 && rv != -EAGAIN)
dev_err(&desc->intf->dev, "wdm_read: exit error\n");
mutex_unlock(&desc->lock);
return rv;
}

Expand Down Expand Up @@ -533,7 +540,7 @@ static int wdm_open(struct inode *inode, struct file *file)
}
intf->needs_remote_wakeup = 1;

mutex_lock(&desc->plock);
mutex_lock(&desc->lock);
if (!desc->count++) {
rv = usb_submit_urb(desc->validity, GFP_KERNEL);
if (rv < 0) {
Expand All @@ -544,7 +551,7 @@ static int wdm_open(struct inode *inode, struct file *file)
} else {
rv = 0;
}
mutex_unlock(&desc->plock);
mutex_unlock(&desc->lock);
usb_autopm_put_interface(desc->intf);
out:
mutex_unlock(&wdm_mutex);
Expand All @@ -556,9 +563,9 @@ static int wdm_release(struct inode *inode, struct file *file)
struct wdm_device *desc = file->private_data;

mutex_lock(&wdm_mutex);
mutex_lock(&desc->plock);
mutex_lock(&desc->lock);
desc->count--;
mutex_unlock(&desc->plock);
mutex_unlock(&desc->lock);

if (!desc->count) {
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
Expand Down Expand Up @@ -655,9 +662,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
if (!desc)
goto out;
mutex_init(&desc->wlock);
mutex_init(&desc->rlock);
mutex_init(&desc->plock);
mutex_init(&desc->lock);
spin_lock_init(&desc->iuspin);
init_waitqueue_head(&desc->wait);
desc->wMaxCommand = maxcom;
Expand Down Expand Up @@ -771,37 +776,48 @@ static void wdm_disconnect(struct usb_interface *intf)
/* to terminate pending flushes */
clear_bit(WDM_IN_USE, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags);
cancel_work_sync(&desc->rxwork);
mutex_lock(&desc->lock);
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
mutex_unlock(&desc->lock);
wake_up_all(&desc->wait);
if (!desc->count)
cleanup(desc);
mutex_unlock(&wdm_mutex);
}

#ifdef CONFIG_PM
static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
{
struct wdm_device *desc = usb_get_intfdata(intf);
int rv = 0;

dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);

mutex_lock(&desc->plock);
#ifdef CONFIG_PM
/* if this is an autosuspend the caller does the locking */
if (!(message.event & PM_EVENT_AUTO))
mutex_lock(&desc->lock);
spin_lock_irq(&desc->iuspin);

if ((message.event & PM_EVENT_AUTO) &&
test_bit(WDM_IN_USE, &desc->flags)) {
(test_bit(WDM_IN_USE, &desc->flags)
|| test_bit(WDM_RESPONDING, &desc->flags))) {
spin_unlock_irq(&desc->iuspin);
rv = -EBUSY;
} else {
#endif
cancel_work_sync(&desc->rxwork);

set_bit(WDM_SUSPENDING, &desc->flags);
spin_unlock_irq(&desc->iuspin);
/* callback submits work - order is essential */
kill_urbs(desc);
#ifdef CONFIG_PM
cancel_work_sync(&desc->rxwork);
}
#endif
mutex_unlock(&desc->plock);
if (!(message.event & PM_EVENT_AUTO))
mutex_unlock(&desc->lock);

return rv;
}
#endif

static int recover_from_urb_loss(struct wdm_device *desc)
{
Expand All @@ -815,23 +831,27 @@ static int recover_from_urb_loss(struct wdm_device *desc)
}
return rv;
}

#ifdef CONFIG_PM
static int wdm_resume(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);
int rv;

dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
mutex_lock(&desc->plock);

clear_bit(WDM_SUSPENDING, &desc->flags);
rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->plock);

return rv;
}
#endif

static int wdm_pre_reset(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);

mutex_lock(&desc->plock);
mutex_lock(&desc->lock);
return 0;
}

Expand All @@ -841,17 +861,19 @@ static int wdm_post_reset(struct usb_interface *intf)
int rv;

rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->plock);
mutex_unlock(&desc->lock);
return 0;
}

static struct usb_driver wdm_driver = {
.name = "cdc_wdm",
.probe = wdm_probe,
.disconnect = wdm_disconnect,
#ifdef CONFIG_PM
.suspend = wdm_suspend,
.resume = wdm_resume,
.reset_resume = wdm_resume,
#endif
.pre_reset = wdm_pre_reset,
.post_reset = wdm_post_reset,
.id_table = wdm_ids,
Expand Down
17 changes: 14 additions & 3 deletions drivers/usb/core/devio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,13 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
free_async(as);
return -ENOMEM;
}
/* Isochronous input data may end up being discontiguous
* if some of the packets are short. Clear the buffer so
* that the gaps don't leak kernel data to userspace.
*/
if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
memset(as->urb->transfer_buffer, 0,
uurb->buffer_length);
}
as->urb->dev = ps->dev;
as->urb->pipe = (uurb->type << 30) |
Expand Down Expand Up @@ -1345,10 +1352,14 @@ static int processcompl(struct async *as, void __user * __user *arg)
void __user *addr = as->userurb;
unsigned int i;

if (as->userbuffer && urb->actual_length)
if (copy_to_user(as->userbuffer, urb->transfer_buffer,
urb->actual_length))
if (as->userbuffer && urb->actual_length) {
if (urb->number_of_packets > 0) /* Isochronous */
i = urb->transfer_buffer_length;
else /* Non-Isoc */
i = urb->actual_length;
if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
goto err_out;
}
if (put_user(as->status, &userurb->status))
goto err_out;
if (put_user(urb->actual_length, &userurb->actual_length))
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/core/urb.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (urb->interval > (1 << 15))
return -EINVAL;
max = 1 << 15;
break;
case USB_SPEED_WIRELESS:
if (urb->interval > 16)
return -EINVAL;
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/gadget/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ config USB_MASS_STORAGE
which may be used with composite framework.

Say "y" to link the driver statically, or "m" to build
a dynamically linked module called "g_file_storage". If unsure,
a dynamically linked module called "g_mass_storage". If unsure,
consider File-backed Storage Gadget.

config USB_G_SERIAL
Expand Down
Loading

0 comments on commit 8fdb7e9

Please sign in to comment.