summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/devio.c35
-rw-r--r--drivers/usb/core/driver.c3
-rw-r--r--drivers/usb/core/generic.c4
-rw-r--r--drivers/usb/core/hub.c88
-rw-r--r--drivers/usb/core/usb.h7
-rw-r--r--include/linux/usbdevice_fs.h2
6 files changed, 133 insertions, 6 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 4247eccf858c..165de5d59005 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -52,6 +52,7 @@
#include "hcd.h" /* for usbcore internals */
#include "usb.h"
+#include "hub.h"
#define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128
@@ -655,6 +656,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
struct async *as;
usb_lock_device(dev);
+ usb_hub_release_all_ports(dev, ps);
/* Protect against simultaneous open */
mutex_lock(&usbfs_mutex);
@@ -1548,6 +1550,29 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
}
#endif
+static int proc_claim_port(struct dev_state *ps, void __user *arg)
+{
+ unsigned portnum;
+ int rc;
+
+ if (get_user(portnum, (unsigned __user *) arg))
+ return -EFAULT;
+ rc = usb_hub_claim_port(ps->dev, portnum, ps);
+ if (rc == 0)
+ snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n",
+ portnum, task_pid_nr(current), current->comm);
+ return rc;
+}
+
+static int proc_release_port(struct dev_state *ps, void __user *arg)
+{
+ unsigned portnum;
+
+ if (get_user(portnum, (unsigned __user *) arg))
+ return -EFAULT;
+ return usb_hub_release_port(ps->dev, portnum, ps);
+}
+
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -1689,6 +1714,16 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
snoop(&dev->dev, "%s: IOCTL\n", __func__);
ret = proc_ioctl_default(ps, p);
break;
+
+ case USBDEVFS_CLAIM_PORT:
+ snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__);
+ ret = proc_claim_port(ps, p);
+ break;
+
+ case USBDEVFS_RELEASE_PORT:
+ snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
+ ret = proc_release_port(ps, p);
+ break;
}
usb_unlock_device(dev);
if (ret >= 0)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 69e5773abfce..1bad4e5a6abb 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -207,6 +207,9 @@ static int usb_probe_interface(struct device *dev)
intf->needs_binding = 0;
+ if (usb_device_is_owned(udev))
+ return -ENODEV;
+
if (udev->authorized == 0) {
dev_err(&intf->dev, "Device is not authorized for usage\n");
return -ENODEV;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 30ecac3af15a..05e6d313961e 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -158,7 +158,9 @@ static int generic_probe(struct usb_device *udev)
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
- if (udev->authorized == 0)
+ if (usb_device_is_owned(udev))
+ ; /* Don't configure if the device is owned */
+ else if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 71f86c60d83c..cb54420ed583 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -78,6 +78,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
+ void **port_owners;
};
@@ -860,19 +861,17 @@ static int hub_configure(struct usb_hub *hub,
u16 wHubCharacteristics;
unsigned int pipe;
int maxp, ret;
- char *message;
+ char *message = "out of memory";
hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
&hub->buffer_dma);
if (!hub->buffer) {
- message = "can't allocate hub irq buffer";
ret = -ENOMEM;
goto fail;
}
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
if (!hub->status) {
- message = "can't kmalloc hub status buffer";
ret = -ENOMEM;
goto fail;
}
@@ -880,7 +879,6 @@ static int hub_configure(struct usb_hub *hub,
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
- message = "can't kmalloc hub descriptor";
ret = -ENOMEM;
goto fail;
}
@@ -904,6 +902,12 @@ static int hub_configure(struct usb_hub *hub,
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
+ hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
+ if (!hub->port_owners) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
@@ -1082,7 +1086,6 @@ static int hub_configure(struct usb_hub *hub,
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
- message = "couldn't allocate interrupt urb";
ret = -ENOMEM;
goto fail;
}
@@ -1131,11 +1134,13 @@ static void hub_disconnect(struct usb_interface *intf)
hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
+ hub->hdev->maxchild = 0;
if (hub->hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
usb_free_urb(hub->urb);
+ kfree(hub->port_owners);
kfree(hub->descriptor);
kfree(hub->status);
usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
@@ -1250,6 +1255,79 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
+/*
+ * Allow user programs to claim ports on a hub. When a device is attached
+ * to one of these "claimed" ports, the program will "own" the device.
+ */
+static int find_port_owner(struct usb_device *hdev, unsigned port1,
+ void ***ppowner)
+{
+ if (hdev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (port1 == 0 || port1 > hdev->maxchild)
+ return -EINVAL;
+
+ /* This assumes that devices not managed by the hub driver
+ * will always have maxchild equal to 0.
+ */
+ *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ return 0;
+}
+
+/* In the following three functions, the caller must hold hdev's lock */
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+{
+ int rc;
+ void **powner;
+
+ rc = find_port_owner(hdev, port1, &powner);
+ if (rc)
+ return rc;
+ if (*powner)
+ return -EBUSY;
+ *powner = owner;
+ return rc;
+}
+
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+{
+ int rc;
+ void **powner;
+
+ rc = find_port_owner(hdev, port1, &powner);
+ if (rc)
+ return rc;
+ if (*powner != owner)
+ return -ENOENT;
+ *powner = NULL;
+ return rc;
+}
+
+void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+{
+ int n;
+ void **powner;
+
+ n = find_port_owner(hdev, 1, &powner);
+ if (n == 0) {
+ for (; n < hdev->maxchild; (++n, ++powner)) {
+ if (*powner == owner)
+ *powner = NULL;
+ }
+ }
+}
+
+/* The caller must hold udev's lock */
+bool usb_device_is_owned(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+
+ if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
+ return false;
+ hub = hdev_to_hub(udev->parent);
+ return !!hub->port_owners[udev->portnum - 1];
+}
+
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index c0e0ae2bb8e7..9a8b15e6377a 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -37,6 +37,13 @@ extern int usb_match_device(struct usb_device *dev,
extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_rebind_intf(struct usb_interface *intf);
+extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
+ void *owner);
+extern int usb_hub_release_port(struct usb_device *hdev, unsigned port,
+ void *owner);
+extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner);
+extern bool usb_device_is_owned(struct usb_device *udev);
+
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
extern int usb_major_init(void);
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index 0044d9b4cb85..00ceebeb9e5c 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -175,4 +175,6 @@ struct usbdevfs_ioctl32 {
#define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int)
#define USBDEVFS_DISCONNECT _IO('U', 22)
#define USBDEVFS_CONNECT _IO('U', 23)
+#define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int)
+#define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int)
#endif /* _LINUX_USBDEVICE_FS_H */