Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 174912
b: refs/heads/master
c: 253e057
h: refs/heads/master
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Dec 11, 2009
1 parent e244c59 commit 314ffa0
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d697cdda43939a80432863e2e26df6701ce72b63
refs/heads/master: 253e05724f9230910344357b1142ad8642ff9f5a
50 changes: 48 additions & 2 deletions trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct usb_hub {
status change */
unsigned long busy_bits[1]; /* ports being reset or
resumed */
unsigned long removed_bits[1]; /* ports with a "removed"
device present */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
Expand Down Expand Up @@ -635,6 +637,33 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}

/**
* usb_remove_device - disable a device's port on its parent hub
* @udev: device to be disabled and removed
* Context: @udev locked, must be able to sleep.
*
* After @udev's port has been disabled, khubd is notified and it will
* see that the device has been disconnected. When the device is
* physically unplugged and something is plugged in, the events will
* be received and processed normally.
*/
int usb_remove_device(struct usb_device *udev)
{
struct usb_hub *hub;
struct usb_interface *intf;

if (!udev->parent) /* Can't remove a root hub */
return -EINVAL;
hub = hdev_to_hub(udev->parent);
intf = to_usb_interface(hub->intfdev);

usb_autopm_get_interface(intf);
set_bit(udev->portnum, hub->removed_bits);
hub_port_logical_disconnect(hub, udev->portnum);
usb_autopm_put_interface(intf);
return 0;
}

enum hub_activation_type {
HUB_INIT, HUB_INIT2, HUB_INIT3,
HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
Expand Down Expand Up @@ -730,6 +759,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
USB_PORT_FEAT_C_ENABLE);
}

/* We can forget about a "removed" device when there's a
* physical disconnect or the connect status changes.
*/
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
(portchange & USB_PORT_STAT_C_CONNECTION))
clear_bit(port1, hub->removed_bits);

if (!udev || udev->state == USB_STATE_NOTATTACHED) {
/* Tell khubd to disconnect the device or
* check for a new connection
Expand Down Expand Up @@ -2965,6 +3001,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);

/* We can forget about a "removed" device when there's a physical
* disconnect or the connect status changes.
*/
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
(portchange & USB_PORT_STAT_C_CONNECTION))
clear_bit(port1, hub->removed_bits);

if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
Expand All @@ -2978,8 +3021,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
}

/* Return now if debouncing failed or nothing is connected */
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
/* Return now if debouncing failed or nothing is connected or
* the device was "removed".
*/
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
test_bit(port1, hub->removed_bits)) {

/* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
Expand Down
23 changes: 23 additions & 0 deletions trunk/drivers/usb/core/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,28 @@ static ssize_t usb_dev_authorized_store(struct device *dev,
static DEVICE_ATTR(authorized, 0644,
usb_dev_authorized_show, usb_dev_authorized_store);

/* "Safely remove a device" */
static ssize_t usb_remove_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
int rc = 0;

usb_lock_device(udev);
if (udev->state != USB_STATE_NOTATTACHED) {

/* To avoid races, first unconfigure and then remove */
usb_set_configuration(udev, -1);
rc = usb_remove_device(udev);
}
if (rc == 0)
rc = count;
usb_unlock_device(udev);
return rc;
}
static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store);


static struct attribute *dev_attrs[] = {
/* current configuration's attributes */
Expand All @@ -533,6 +555,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_maxchild.attr,
&dev_attr_quirks.attr,
&dev_attr_authorized.attr,
&dev_attr_remove.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/usb/core/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *);
extern int usb_authorize_device(struct usb_device *);
extern void usb_detect_quirks(struct usb_device *udev);
extern int usb_remove_device(struct usb_device *udev);

extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
Expand Down

0 comments on commit 314ffa0

Please sign in to comment.