mirror of https://gitee.com/openkylin/linux.git
USB: add a "remove hardware" sysfs attribute
This patch (as1297) adds a "remove" attribute to each USB device's directory in sysfs. Writing to this attribute causes the device to be deconfigured (the same as writing 0 to the "bConfigurationValue" attribute) and then tells the hub driver to disable the device's upstream port. The device remains locked during these activities so there is no possibility of it getting reconfigured in between. The port will remain disabled until after the device is unplugged. The purpose of this is to provide a means for user programs to imitate the "Safely remove hardware" applet in Windows. Some devices do expect their ports to be disabled before they are unplugged, and they provide visual feedback to users indicating when they can safely be unplugged. The security implications are minimal. Writing to the "remove" attribute is no more dangerous than writing to the "bConfigurationValue" attribute. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: David Zeuthen <davidz@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
d697cdda43
commit
253e05724f
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue