summaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/devices.c22
-rw-r--r--drivers/usb/core/devio.c62
-rw-r--r--drivers/usb/core/drivers.c6
-rw-r--r--drivers/usb/core/file.c11
-rw-r--r--drivers/usb/core/hcd.c10
-rw-r--r--drivers/usb/core/hub.c14
-rw-r--r--drivers/usb/core/inode.c38
-rw-r--r--drivers/usb/core/usb.c195
8 files changed, 223 insertions, 135 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index c15f5f4e60b2..e701a4975071 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -152,8 +152,8 @@ static const struct class_info clas_info[] =
void usbdevfs_conn_disc_event(void)
{
- wake_up(&deviceconndiscwq);
conndiscevcnt++;
+ wake_up(&deviceconndiscwq);
}
static const char *class_decode(const int class)
@@ -239,6 +239,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct
if (start > end)
return start;
+ lock_kernel(); /* driver might be unloaded */
start += sprintf(start, format_iface,
desc->bInterfaceNumber,
desc->bAlternateSetting,
@@ -248,6 +249,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct
desc->bInterfaceSubClass,
desc->bInterfaceProtocol,
iface->driver ? iface->driver->name : "(none)");
+ unlock_kernel();
return start;
}
@@ -597,6 +599,13 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
unlock_kernel();
return POLLIN;
}
+
+ /* we may have dropped BKL - need to check for having lost the race */
+ if (file->private_data) {
+ kfree(st);
+ goto lost_race;
+ }
+
/*
* need to prevent the module from being unloaded, since
* proc_unregister does not call the release method and
@@ -606,6 +615,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
file->private_data = st;
mask = POLLIN;
}
+lost_race:
if (file->f_mode & FMODE_READ)
poll_wait(file, &deviceconndiscwq, wait);
if (st->lastev != conndiscevcnt)
@@ -656,9 +666,9 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
}
struct file_operations usbdevfs_devices_fops = {
- llseek: usb_device_lseek,
- read: usb_device_read,
- poll: usb_device_poll,
- open: usb_device_open,
- release: usb_device_release,
+ .llseek = usb_device_lseek,
+ .read = usb_device_read,
+ .poll = usb_device_poll,
+ .open = usb_device_open,
+ .release = usb_device_release,
};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index b7c95b292a48..e80a3ed170f8 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -324,9 +324,9 @@ static void driver_disconnect(struct usb_device *dev, void *context)
}
struct usb_driver usbdevfs_driver = {
- name: "usbfs",
- probe: driver_probe,
- disconnect: driver_disconnect,
+ .name = "usbfs",
+ .probe = driver_probe,
+ .disconnect = driver_disconnect,
};
static int claimintf(struct dev_state *ps, unsigned int intf)
@@ -361,14 +361,14 @@ static int releaseintf(struct dev_state *ps, unsigned int intf)
if (intf >= 8*sizeof(ps->ifclaimed))
return -EINVAL;
err = -EINVAL;
- lock_kernel();
dev = ps->dev;
+ down(&dev->serialize);
if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) {
iface = &dev->actconfig->interface[intf];
usb_driver_release_interface(&usbdevfs_driver, iface);
err = 0;
}
- unlock_kernel();
+ up(&dev->serialize);
return err;
}
@@ -722,14 +722,11 @@ static int proc_resetdevice(struct dev_state *ps)
if (test_bit(i, &ps->ifclaimed))
continue;
- if (intf->driver) {
- const struct usb_device_id *id;
- down(&intf->driver->serialize);
- intf->driver->disconnect(ps->dev, intf->private_data);
- id = usb_match_id(ps->dev,intf,intf->driver->id_table);
- intf->driver->probe(ps->dev, i, id);
- up(&intf->driver->serialize);
+ lock_kernel();
+ if (intf->driver && ps->dev) {
+ usb_bind_driver(intf->driver,ps->dev, i);
}
+ unlock_kernel();
}
return 0;
@@ -1092,16 +1089,17 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
/* disconnect kernel driver from interface, leaving it unbound. */
case USBDEVFS_DISCONNECT:
+ /* this function is voodoo. without locking it is a maybe thing */
+ lock_kernel();
driver = ifp->driver;
if (driver) {
- down (&driver->serialize);
dbg ("disconnect '%s' from dev %d interface %d",
driver->name, ps->dev->devnum, ctrl.ifno);
- driver->disconnect (ps->dev, ifp->private_data);
+ usb_unbind_driver(ps->dev, ifp);
usb_driver_release_interface (driver, ifp);
- up (&driver->serialize);
} else
retval = -EINVAL;
+ unlock_kernel();
break;
/* let kernel drivers try to (re)bind to the interface */
@@ -1111,18 +1109,28 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
/* talk directly to the interface's driver */
default:
+ lock_kernel(); /* against module unload */
driver = ifp->driver;
- if (driver == 0 || driver->ioctl == 0)
- retval = -ENOSYS;
- else {
- if (ifp->driver->owner)
+ if (driver == 0 || driver->ioctl == 0) {
+ unlock_kernel();
+ retval = -ENOSYS;
+ } else {
+ if (ifp->driver->owner) {
__MOD_INC_USE_COUNT(ifp->driver->owner);
+ unlock_kernel();
+ }
/* ifno might usefully be passed ... */
retval = driver->ioctl (ps->dev, ctrl.ioctl_code, buf);
/* size = min_t(int, size, retval)? */
- if (ifp->driver->owner)
+ if (ifp->driver->owner) {
__MOD_DEC_USE_COUNT(ifp->driver->owner);
+ } else {
+ unlock_kernel();
+ }
}
+
+ if (retval == -ENOIOCTLCMD)
+ retval = -ENOTTY;
}
/* cleanup and return */
@@ -1139,7 +1147,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct dev_state *ps = (struct dev_state *)file->private_data;
- int ret = -ENOIOCTLCMD;
+ int ret = -ENOTTY;
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
@@ -1248,10 +1256,10 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
}
struct file_operations usbdevfs_device_file_operations = {
- llseek: usbdev_lseek,
- read: usbdev_read,
- poll: usbdev_poll,
- ioctl: usbdev_ioctl,
- open: usbdev_open,
- release: usbdev_release,
+ .llseek = usbdev_lseek,
+ .read = usbdev_read,
+ .poll = usbdev_poll,
+ .ioctl = usbdev_ioctl,
+ .open = usbdev_open,
+ .release = usbdev_release,
};
diff --git a/drivers/usb/core/drivers.c b/drivers/usb/core/drivers.c
index 3419792c7fb6..22dc21354868 100644
--- a/drivers/usb/core/drivers.c
+++ b/drivers/usb/core/drivers.c
@@ -66,6 +66,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff
start = page;
end = page + (PAGE_SIZE - 100);
pos = *ppos;
+ lock_kernel(); /* else drivers might be unloaded */
for (; tmp != &usb_driver_list; tmp = tmp->next) {
struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
int minor = driver->fops ? driver->minor : -1;
@@ -80,6 +81,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff
break;
}
}
+ unlock_kernel();
if (start == page)
start += sprintf(start, "(none)\n");
len = start - page;
@@ -120,6 +122,6 @@ static loff_t usb_driver_lseek(struct file * file, loff_t offset, int orig)
}
struct file_operations usbdevfs_drivers_fops = {
- llseek: usb_driver_lseek,
- read: usb_driver_read,
+ .llseek = usb_driver_lseek,
+ .read = usb_driver_read,
};
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index eba43f1d84e5..803fb19e5ad2 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -44,10 +44,13 @@ static int usb_open(struct inode * inode, struct file * file)
spin_lock (&minor_lock);
c = usb_minors[minor];
- spin_unlock (&minor_lock);
- if (!c || !(new_fops = fops_get(c)))
+ if (!c || !(new_fops = fops_get(c))) {
+ spin_unlock(&minor_lock);
return err;
+ }
+ spin_unlock(&minor_lock);
+
old_fops = file->f_op;
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
@@ -62,8 +65,8 @@ static int usb_open(struct inode * inode, struct file * file)
}
static struct file_operations usb_fops = {
- owner: THIS_MODULE,
- open: usb_open,
+ .owner = THIS_MODULE,
+ .open = usb_open,
};
int usb_major_init(void)
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 15a2c346b4af..05bb930ea5fd 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1240,11 +1240,11 @@ static int hcd_free_dev (struct usb_device *udev)
* bus glue for non-PCI system busses will need to use this.
*/
struct usb_operations usb_hcd_operations = {
- allocate: hcd_alloc_dev,
- get_frame_number: hcd_get_frame_number,
- submit_urb: hcd_submit_urb,
- unlink_urb: hcd_unlink_urb,
- deallocate: hcd_free_dev,
+ .allocate = hcd_alloc_dev,
+ .get_frame_number = hcd_get_frame_number,
+ .submit_urb = hcd_submit_urb,
+ .unlink_urb = hcd_unlink_urb,
+ .deallocate = hcd_free_dev,
};
EXPORT_SYMBOL (usb_hcd_operations);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ad1422fabdfa..f80a98621d26 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1046,8 +1046,6 @@ static void usb_hub_events(void)
static int usb_hub_thread(void *__hub)
{
- lock_kernel();
-
/*
* This thread doesn't need any user-level access,
* so get rid of all our resources
@@ -1067,8 +1065,6 @@ static int usb_hub_thread(void *__hub)
} while (!signal_pending(current));
dbg("usb_hub_thread exiting");
-
- unlock_kernel();
complete_and_exit(&khubd_exited, 0);
}
@@ -1083,11 +1079,11 @@ static struct usb_device_id hub_id_table [] = {
MODULE_DEVICE_TABLE (usb, hub_id_table);
static struct usb_driver hub_driver = {
- name: "hub",
- probe: hub_probe,
- ioctl: hub_ioctl,
- disconnect: hub_disconnect,
- id_table: hub_id_table,
+ .name = "hub",
+ .probe = hub_probe,
+ .ioctl = hub_ioctl,
+ .disconnect = hub_disconnect,
+ .id_table = hub_id_table,
};
/*
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 08d08e3516e8..ffb7e6e13284 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -284,23 +284,23 @@ static int default_open (struct inode *inode, struct file *filp)
}
static struct file_operations default_file_operations = {
- read: default_read_file,
- write: default_write_file,
- open: default_open,
- llseek: default_file_lseek,
+ .read = default_read_file,
+ .write = default_write_file,
+ .open = default_open,
+ .llseek = default_file_lseek,
};
static struct inode_operations usbfs_dir_inode_operations = {
- create: usbfs_create,
- lookup: simple_lookup,
- unlink: usbfs_unlink,
- mkdir: usbfs_mkdir,
- rmdir: usbfs_rmdir,
+ .create = usbfs_create,
+ .lookup = simple_lookup,
+ .unlink = usbfs_unlink,
+ .mkdir = usbfs_mkdir,
+ .rmdir = usbfs_rmdir,
};
static struct super_operations usbfs_ops = {
- statfs: simple_statfs,
- drop_inode: generic_delete_inode,
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
};
static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
@@ -468,17 +468,17 @@ static struct super_block *usb_get_sb(struct file_system_type *fs_type,
}
static struct file_system_type usbdevice_fs_type = {
- owner: THIS_MODULE,
- name: "usbdevfs",
- get_sb: usb_get_sb,
- kill_sb: kill_anon_super,
+ .owner = THIS_MODULE,
+ .name = "usbdevfs",
+ .get_sb = usb_get_sb,
+ .kill_sb = kill_anon_super,
};
static struct file_system_type usb_fs_type = {
- owner: THIS_MODULE,
- name: "usbfs",
- get_sb: usb_get_sb,
- kill_sb: kill_anon_super,
+ .owner = THIS_MODULE,
+ .name = "usbfs",
+ .get_sb = usb_get_sb,
+ .kill_sb = kill_anon_super,
};
/* --------------------------------------------------------------------- */
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 55c9e8955d24..ed2e03089e57 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -32,6 +32,7 @@
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
+#include <linux/smp_lock.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
@@ -117,6 +118,108 @@ void usb_scan_devices(void)
up (&usb_bus_list_lock);
}
+/**
+ * usb_unbind_driver - disconnects a driver from a device
+ * @device: usb device to be disconnected
+ * @intf: interface of the device to be disconnected
+ * Context: BKL held
+ *
+ * Handles module usage count correctly
+ */
+
+void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf)
+{
+ struct usb_driver *driver;
+ void *priv;
+ int m;
+
+
+ driver = intf->driver;
+ priv = intf->private_data;
+
+ if (!driver)
+ return;
+
+ /* as soon as we increase the module use count we drop the BKL
+ before that we must not sleep */
+ if (driver->owner) {
+ m = try_inc_mod_count(driver->owner);
+ if (m == 0) {
+ err("Dieing driver still bound to device.\n");
+ return;
+ }
+ unlock_kernel();
+ }
+ down(&driver->serialize); /* if we sleep here on an umanaged driver
+ the holder of the lock guards against
+ module unload */
+
+ driver->disconnect(device, priv);
+
+ up(&driver->serialize);
+ if (driver->owner) {
+ lock_kernel();
+ __MOD_DEC_USE_COUNT(driver->owner);
+ }
+}
+
+/**
+ * usb_bind_driver - connect a driver to a device's interface
+ * @driver: device driver to be bound to a devices interface
+ * @dev: device to be bound
+ * @ifnum: index number of the interface to be used
+ *
+ * Does a save binding of a driver to a device's interface
+ * Returns a pointer to the drivers private description of the binding
+ */
+
+void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum)
+{
+ int i,m;
+ void *private = NULL;
+ const struct usb_device_id *id;
+ struct usb_interface *interface;
+
+ if (driver->owner) {
+ m = try_inc_mod_count(driver->owner);
+ if (m == 0)
+ return NULL; /* this horse is dead - don't ride*/
+ unlock_kernel();
+ }
+
+ interface = &dev->actconfig->interface[ifnum];
+
+ id = driver->id_table;
+ /* new style driver? */
+ if (id) {
+ for (i = 0; i < interface->num_altsetting; i++) {
+ interface->act_altsetting = i;
+ id = usb_match_id(dev, interface, id);
+ if (id) {
+ down(&driver->serialize);
+ private = driver->probe(dev,ifnum,id);
+ up(&driver->serialize);
+ if (private != NULL)
+ break;
+ }
+ }
+
+ /* if driver not bound, leave defaults unchanged */
+ if (private == NULL)
+ interface->act_altsetting = 0;
+ } else { /* "old style" driver */
+ down(&driver->serialize);
+ private = driver->probe(dev, ifnum, NULL);
+ up(&driver->serialize);
+ }
+ if (driver->owner) {
+ lock_kernel();
+ __MOD_DEC_USE_COUNT(driver->owner);
+ }
+
+ return private;
+}
+
/*
* This function is part of a depth-first search down the device tree,
* removing any instances of a device driver.
@@ -136,18 +239,12 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
if (!dev->actconfig)
return;
-
+
for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *interface = &dev->actconfig->interface[i];
-
+
if (interface->driver == driver) {
- if (driver->owner)
- __MOD_INC_USE_COUNT(driver->owner);
- down(&driver->serialize);
- driver->disconnect(dev, interface->private_data);
- up(&driver->serialize);
- if (driver->owner)
- __MOD_DEC_USE_COUNT(driver->owner);
+ usb_unbind_driver(dev, interface);
/* if driver->disconnect didn't release the interface */
if (interface->driver)
usb_driver_release_interface(driver, interface);
@@ -163,7 +260,7 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
/**
* usb_deregister - unregister a USB driver
* @driver: USB operations of the driver to unregister
- * Context: !in_interrupt ()
+ * Context: !in_interrupt (), must be called with BKL held
*
* Unlinks the specified driver from the internal USB driver list.
*
@@ -528,9 +625,7 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
struct list_head *tmp;
struct usb_interface *interface;
void *private;
- const struct usb_device_id *id;
struct usb_driver *driver;
- int i;
if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
err("bad find_interface_driver params");
@@ -545,37 +640,12 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
goto out_err;
private = NULL;
+ lock_kernel();
for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) {
driver = list_entry(tmp, struct usb_driver, driver_list);
tmp = tmp->next;
- if (driver->owner)
- __MOD_INC_USE_COUNT(driver->owner);
- id = driver->id_table;
- /* new style driver? */
- if (id) {
- for (i = 0; i < interface->num_altsetting; i++) {
- interface->act_altsetting = i;
- id = usb_match_id(dev, interface, id);
- if (id) {
- down(&driver->serialize);
- private = driver->probe(dev,ifnum,id);
- up(&driver->serialize);
- if (private != NULL)
- break;
- }
- }
-
- /* if driver not bound, leave defaults unchanged */
- if (private == NULL)
- interface->act_altsetting = 0;
- } else { /* "old style" driver */
- down(&driver->serialize);
- private = driver->probe(dev, ifnum, NULL);
- up(&driver->serialize);
- }
- if (driver->owner)
- __MOD_DEC_USE_COUNT(driver->owner);
+ private = usb_bind_driver(driver, dev, ifnum);
/* probe() may have changed the config on us */
interface = dev->actconfig->interface + ifnum;
@@ -583,9 +653,11 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
if (private) {
usb_driver_claim_interface(driver, interface, private);
up(&dev->serialize);
+ unlock_kernel();
return 0;
}
}
+ unlock_kernel();
out_err:
up(&dev->serialize);
@@ -764,9 +836,9 @@ show_config (struct device *dev, char *buf, size_t count, loff_t off)
return sprintf (buf, "%u\n", udev->actconfig->bConfigurationValue);
}
static struct driver_file_entry usb_config_entry = {
- name: "configuration",
- mode: S_IRUGO,
- show: show_config,
+ .name = "configuration",
+ .mode = S_IRUGO,
+ .show = show_config,
};
/* interfaces have one current setting; alternates
@@ -783,9 +855,9 @@ show_altsetting (struct device *dev, char *buf, size_t count, loff_t off)
return sprintf (buf, "%u\n", interface->altsetting->bAlternateSetting);
}
static struct driver_file_entry usb_altsetting_entry = {
- name: "altsetting",
- mode: S_IRUGO,
- show: show_altsetting,
+ .name = "altsetting",
+ .mode = S_IRUGO,
+ .show = show_altsetting,
};
/* product driverfs file */
@@ -804,9 +876,9 @@ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t
return len+1;
}
static struct driver_file_entry usb_product_entry = {
- name: "product",
- mode: S_IRUGO,
- show: show_product,
+ .name = "product",
+ .mode = S_IRUGO,
+ .show = show_product,
};
/* manufacturer driverfs file */
@@ -826,9 +898,9 @@ show_manufacturer (struct device *dev, char *buf, size_t count, loff_t off)
return len+1;
}
static struct driver_file_entry usb_manufacturer_entry = {
- name: "manufacturer",
- mode: S_IRUGO,
- show: show_manufacturer,
+ .name = "manufacturer",
+ .mode = S_IRUGO,
+ .show = show_manufacturer,
};
/* serial number driverfs file */
@@ -848,9 +920,9 @@ show_serial (struct device *dev, char *buf, size_t count, loff_t off)
return len+1;
}
static struct driver_file_entry usb_serial_entry = {
- name: "serial",
- mode: S_IRUGO,
- show: show_serial,
+ .name = "serial",
+ .mode = S_IRUGO,
+ .show = show_serial,
};
/*
@@ -1121,27 +1193,22 @@ void usb_disconnect(struct usb_device **pdev)
info("USB disconnect on device %d", dev->devnum);
+ lock_kernel();
if (dev->actconfig) {
for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *interface = &dev->actconfig->interface[i];
struct usb_driver *driver = interface->driver;
if (driver) {
- if (driver->owner)
- __MOD_INC_USE_COUNT(driver->owner);
- down(&driver->serialize);
- driver->disconnect(dev, interface->private_data);
- up(&driver->serialize);
+ usb_unbind_driver(dev, interface);
/* if driver->disconnect didn't release the interface */
if (interface->driver)
usb_driver_release_interface(driver, interface);
- /* we don't need the driver any longer */
- if (driver->owner)
- __MOD_DEC_USE_COUNT(driver->owner);
}
/* remove our device node for this interface */
put_device(&interface->dev);
}
}
+ unlock_kernel();
/* Free up all the children.. */
for (i = 0; i < USB_MAXCHILDREN; i++) {
@@ -1416,7 +1483,7 @@ struct list_head *usb_bus_get_list(void)
#endif
struct bus_type usb_bus_type = {
- name: "usb",
+ .name = "usb",
};
/*
@@ -1475,6 +1542,8 @@ EXPORT_SYMBOL(usb_new_device);
EXPORT_SYMBOL(usb_reset_device);
EXPORT_SYMBOL(usb_connect);
EXPORT_SYMBOL(usb_disconnect);
+EXPORT_SYMBOL(usb_bind_driver);
+EXPORT_SYMBOL(usb_unbind_driver);
EXPORT_SYMBOL(__usb_get_extra_descriptor);