USB: add reset_resume method
This patch (as918) introduces a new USB driver method: reset_resume. It is called when a device needs to be reset as part of a resume procedure (whether because of a device quirk or because of the USB-Persist facility), thereby taking over a role formerly assigned to the post_reset method. As a consequence, post_reset no longer needs an argument indicating whether it is being called as part of a reset-resume. This separation of functions makes the code clearer. In addition, the pre_reset and post_reset method return types are changed; they now must return an error code. The return value is unused at present, but at some later time we may unbind drivers and re-probe if they encounter an error during reset handling. The existing pre_reset and post_reset methods in the usbhid, usb-storage, and hub drivers are updated to match the new requirements. For usbhid the post_reset routine is also used for reset_resume (duplicate method pointers); for the other drivers a new reset_resume routine is added. The change to hub.c looks bigger than it really is, because mark_children_for_reset_resume() gets moved down next to the new hub_reset_resume() routine. A minor change to usb-storage makes the usb_stor_report_bus_reset() routine acquire the host lock instead of requiring the caller to hold it already. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Jiri Kosina <jkosina@suse.cz> CC: Matthew Dharm <mdharm-usb@one-eyed-alien.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
624d6c0732
commit
f07600cf9e
@ -1009,20 +1009,22 @@ static int hid_resume(struct usb_interface *intf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Treat USB reset pretty much the same as suspend/resume */
|
/* Treat USB reset pretty much the same as suspend/resume */
|
||||||
static void hid_pre_reset(struct usb_interface *intf)
|
static int hid_pre_reset(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
/* FIXME: What if the interface is already suspended? */
|
/* FIXME: What if the interface is already suspended? */
|
||||||
hid_suspend(intf, PMSG_ON);
|
hid_suspend(intf, PMSG_ON);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_post_reset(struct usb_interface *intf, int reset_resume)
|
/* Same routine used for post_reset and reset_resume */
|
||||||
|
static int hid_post_reset(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
struct usb_device *dev = interface_to_usbdev (intf);
|
struct usb_device *dev = interface_to_usbdev (intf);
|
||||||
|
|
||||||
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
|
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
|
||||||
/* FIXME: Any more reinitialization needed? */
|
/* FIXME: Any more reinitialization needed? */
|
||||||
|
|
||||||
hid_resume(intf);
|
return hid_resume(intf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_device_id hid_usb_ids [] = {
|
static struct usb_device_id hid_usb_ids [] = {
|
||||||
@ -1039,6 +1041,7 @@ static struct usb_driver hid_driver = {
|
|||||||
.disconnect = hid_disconnect,
|
.disconnect = hid_disconnect,
|
||||||
.suspend = hid_suspend,
|
.suspend = hid_suspend,
|
||||||
.resume = hid_resume,
|
.resume = hid_resume,
|
||||||
|
.reset_resume = hid_post_reset,
|
||||||
.pre_reset = hid_pre_reset,
|
.pre_reset = hid_pre_reset,
|
||||||
.post_reset = hid_post_reset,
|
.post_reset = hid_post_reset,
|
||||||
.id_table = hid_usb_ids,
|
.id_table = hid_usb_ids,
|
||||||
|
@ -915,21 +915,37 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
|
|||||||
}
|
}
|
||||||
driver = to_usb_driver(intf->dev.driver);
|
driver = to_usb_driver(intf->dev.driver);
|
||||||
|
|
||||||
if (reset_resume && driver->post_reset)
|
if (reset_resume) {
|
||||||
driver->post_reset(intf, reset_resume);
|
if (driver->reset_resume) {
|
||||||
else if (driver->resume) {
|
status = driver->reset_resume(intf);
|
||||||
status = driver->resume(intf);
|
if (status)
|
||||||
if (status)
|
dev_err(&intf->dev, "%s error %d\n",
|
||||||
dev_err(&intf->dev, "%s error %d\n",
|
"reset_resume", status);
|
||||||
"resume", status);
|
} else {
|
||||||
} else
|
// status = -EOPNOTSUPP;
|
||||||
dev_warn(&intf->dev, "no resume for driver %s?\n",
|
dev_warn(&intf->dev, "no %s for driver %s?\n",
|
||||||
driver->name);
|
"reset_resume", driver->name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (driver->resume) {
|
||||||
|
status = driver->resume(intf);
|
||||||
|
if (status)
|
||||||
|
dev_err(&intf->dev, "%s error %d\n",
|
||||||
|
"resume", status);
|
||||||
|
} else {
|
||||||
|
// status = -EOPNOTSUPP;
|
||||||
|
dev_warn(&intf->dev, "no %s for driver %s?\n",
|
||||||
|
"resume", driver->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
|
dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
mark_active(intf);
|
mark_active(intf);
|
||||||
|
|
||||||
|
/* FIXME: Unbind the driver and reprobe if the resume failed
|
||||||
|
* (not possible if auto_pm is set) */
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,6 +982,18 @@ static int autosuspend_check(struct usb_device *udev)
|
|||||||
"for autosuspend\n");
|
"for autosuspend\n");
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't allow autosuspend if the device will need
|
||||||
|
* a reset-resume and any of its interface drivers
|
||||||
|
* doesn't include support.
|
||||||
|
*/
|
||||||
|
if (udev->quirks & USB_QUIRK_RESET_RESUME) {
|
||||||
|
struct usb_driver *driver;
|
||||||
|
|
||||||
|
driver = to_usb_driver(intf->dev.driver);
|
||||||
|
if (!driver->reset_resume)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1146,7 +1174,8 @@ static int usb_resume_both(struct usb_device *udev)
|
|||||||
status = usb_autoresume_device(parent);
|
status = usb_autoresume_device(parent);
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
status = usb_resume_device(udev);
|
status = usb_resume_device(udev);
|
||||||
if (status) {
|
if (status || udev->state ==
|
||||||
|
USB_STATE_NOTATTACHED) {
|
||||||
usb_autosuspend_device(parent);
|
usb_autosuspend_device(parent);
|
||||||
|
|
||||||
/* It's possible usb_resume_device()
|
/* It's possible usb_resume_device()
|
||||||
|
@ -605,73 +605,26 @@ static void disconnect_all_children(struct usb_hub *hub, int logical)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_USB_PERSIST
|
|
||||||
|
|
||||||
#define USB_PERSIST 1
|
|
||||||
|
|
||||||
/* For "persistent-device" resets we must mark the child devices for reset
|
|
||||||
* and turn off a possible connect-change status (so khubd won't disconnect
|
|
||||||
* them later).
|
|
||||||
*/
|
|
||||||
static void mark_children_for_reset_resume(struct usb_hub *hub)
|
|
||||||
{
|
|
||||||
struct usb_device *hdev = hub->hdev;
|
|
||||||
int port1;
|
|
||||||
|
|
||||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
|
||||||
struct usb_device *child = hdev->children[port1-1];
|
|
||||||
|
|
||||||
if (child) {
|
|
||||||
child->reset_resume = 1;
|
|
||||||
clear_port_feature(hdev, port1,
|
|
||||||
USB_PORT_FEAT_C_CONNECTION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define USB_PERSIST 0
|
|
||||||
|
|
||||||
static inline void mark_children_for_reset_resume(struct usb_hub *hub)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
#endif /* CONFIG_USB_PERSIST */
|
|
||||||
|
|
||||||
/* caller has locked the hub device */
|
/* caller has locked the hub device */
|
||||||
static void hub_pre_reset(struct usb_interface *intf)
|
static int hub_pre_reset(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||||
|
|
||||||
/* This routine doesn't run as part of a reset-resume, so it's safe
|
|
||||||
* to disconnect all the drivers below the hub.
|
|
||||||
*/
|
|
||||||
disconnect_all_children(hub, 0);
|
disconnect_all_children(hub, 0);
|
||||||
hub_quiesce(hub);
|
hub_quiesce(hub);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* caller has locked the hub device */
|
/* caller has locked the hub device */
|
||||||
static void hub_post_reset(struct usb_interface *intf, int reset_resume)
|
static int hub_post_reset(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
struct usb_hub *hub = usb_get_intfdata(intf);
|
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||||
|
|
||||||
hub_power_on(hub);
|
hub_power_on(hub);
|
||||||
if (reset_resume) {
|
|
||||||
if (USB_PERSIST)
|
|
||||||
mark_children_for_reset_resume(hub);
|
|
||||||
else {
|
|
||||||
/* Reset-resume doesn't call pre_reset, so we have to
|
|
||||||
* disconnect the children here. But we may not lock
|
|
||||||
* the child devices, so we have to do a "logical"
|
|
||||||
* disconnect.
|
|
||||||
*/
|
|
||||||
disconnect_all_children(hub, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hub_activate(hub);
|
hub_activate(hub);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int hub_configure(struct usb_hub *hub,
|
static int hub_configure(struct usb_hub *hub,
|
||||||
struct usb_endpoint_descriptor *endpoint)
|
struct usb_endpoint_descriptor *endpoint)
|
||||||
{
|
{
|
||||||
@ -1931,6 +1884,58 @@ static int hub_resume(struct usb_interface *intf)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_PERSIST
|
||||||
|
|
||||||
|
#define USB_PERSIST 1
|
||||||
|
|
||||||
|
/* For "persistent-device" resets we must mark the child devices for reset
|
||||||
|
* and turn off a possible connect-change status (so khubd won't disconnect
|
||||||
|
* them later).
|
||||||
|
*/
|
||||||
|
static void mark_children_for_reset_resume(struct usb_hub *hub)
|
||||||
|
{
|
||||||
|
struct usb_device *hdev = hub->hdev;
|
||||||
|
int port1;
|
||||||
|
|
||||||
|
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||||
|
struct usb_device *child = hdev->children[port1-1];
|
||||||
|
|
||||||
|
if (child) {
|
||||||
|
child->reset_resume = 1;
|
||||||
|
clear_port_feature(hdev, port1,
|
||||||
|
USB_PORT_FEAT_C_CONNECTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define USB_PERSIST 0
|
||||||
|
|
||||||
|
static inline void mark_children_for_reset_resume(struct usb_hub *hub)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
#endif /* CONFIG_USB_PERSIST */
|
||||||
|
|
||||||
|
static int hub_reset_resume(struct usb_interface *intf)
|
||||||
|
{
|
||||||
|
struct usb_hub *hub = usb_get_intfdata(intf);
|
||||||
|
|
||||||
|
hub_power_on(hub);
|
||||||
|
if (USB_PERSIST)
|
||||||
|
mark_children_for_reset_resume(hub);
|
||||||
|
else {
|
||||||
|
/* Reset-resume doesn't call pre_reset, so we have to
|
||||||
|
* disconnect the children here. But we may not lock
|
||||||
|
* the child devices, so we have to do a "logical"
|
||||||
|
* disconnect.
|
||||||
|
*/
|
||||||
|
disconnect_all_children(hub, 1);
|
||||||
|
}
|
||||||
|
hub_activate(hub);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#else /* CONFIG_PM */
|
#else /* CONFIG_PM */
|
||||||
|
|
||||||
static inline int remote_wakeup(struct usb_device *udev)
|
static inline int remote_wakeup(struct usb_device *udev)
|
||||||
@ -1938,8 +1943,9 @@ static inline int remote_wakeup(struct usb_device *udev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define hub_suspend NULL
|
#define hub_suspend NULL
|
||||||
#define hub_resume NULL
|
#define hub_resume NULL
|
||||||
|
#define hub_reset_resume NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -2768,6 +2774,7 @@ static struct usb_driver hub_driver = {
|
|||||||
.disconnect = hub_disconnect,
|
.disconnect = hub_disconnect,
|
||||||
.suspend = hub_suspend,
|
.suspend = hub_suspend,
|
||||||
.resume = hub_resume,
|
.resume = hub_resume,
|
||||||
|
.reset_resume = hub_reset_resume,
|
||||||
.pre_reset = hub_pre_reset,
|
.pre_reset = hub_pre_reset,
|
||||||
.post_reset = hub_post_reset,
|
.post_reset = hub_post_reset,
|
||||||
.ioctl = hub_ioctl,
|
.ioctl = hub_ioctl,
|
||||||
@ -3021,6 +3028,7 @@ int usb_reset_composite_device(struct usb_device *udev,
|
|||||||
drv = to_usb_driver(cintf->dev.driver);
|
drv = to_usb_driver(cintf->dev.driver);
|
||||||
if (drv->pre_reset)
|
if (drv->pre_reset)
|
||||||
(drv->pre_reset)(cintf);
|
(drv->pre_reset)(cintf);
|
||||||
|
/* FIXME: Unbind if pre_reset returns an error or isn't defined */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3038,7 +3046,8 @@ int usb_reset_composite_device(struct usb_device *udev,
|
|||||||
cintf->dev.driver) {
|
cintf->dev.driver) {
|
||||||
drv = to_usb_driver(cintf->dev.driver);
|
drv = to_usb_driver(cintf->dev.driver);
|
||||||
if (drv->post_reset)
|
if (drv->post_reset)
|
||||||
(drv->post_reset)(cintf, 0);
|
(drv->post_reset)(cintf);
|
||||||
|
/* FIXME: Unbind if post_reset returns an error or isn't defined */
|
||||||
}
|
}
|
||||||
if (cintf != iface)
|
if (cintf != iface)
|
||||||
up(&cintf->dev.sem);
|
up(&cintf->dev.sem);
|
||||||
|
@ -321,10 +321,14 @@ void usb_stor_report_device_reset(struct us_data *us)
|
|||||||
|
|
||||||
/* Report a driver-initiated bus reset to the SCSI layer.
|
/* Report a driver-initiated bus reset to the SCSI layer.
|
||||||
* Calling this for a SCSI-initiated reset is unnecessary but harmless.
|
* Calling this for a SCSI-initiated reset is unnecessary but harmless.
|
||||||
* The caller must own the SCSI host lock. */
|
* The caller must not own the SCSI host lock. */
|
||||||
void usb_stor_report_bus_reset(struct us_data *us)
|
void usb_stor_report_bus_reset(struct us_data *us)
|
||||||
{
|
{
|
||||||
scsi_report_bus_reset(us_to_host(us), 0);
|
struct Scsi_Host *host = us_to_host(us);
|
||||||
|
|
||||||
|
scsi_lock(host);
|
||||||
|
scsi_report_bus_reset(host, 0);
|
||||||
|
scsi_unlock(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
|
@ -219,6 +219,20 @@ static int storage_resume(struct usb_interface *iface)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int storage_reset_resume(struct usb_interface *iface)
|
||||||
|
{
|
||||||
|
struct us_data *us = usb_get_intfdata(iface);
|
||||||
|
|
||||||
|
US_DEBUGP("%s\n", __FUNCTION__);
|
||||||
|
|
||||||
|
/* Report the reset to the SCSI core */
|
||||||
|
usb_stor_report_bus_reset(us);
|
||||||
|
|
||||||
|
/* FIXME: Notify the subdrivers that they need to reinitialize
|
||||||
|
* the device */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -226,7 +240,7 @@ static int storage_resume(struct usb_interface *iface)
|
|||||||
* a USB port reset, whether from this driver or a different one.
|
* a USB port reset, whether from this driver or a different one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void storage_pre_reset(struct usb_interface *iface)
|
static int storage_pre_reset(struct usb_interface *iface)
|
||||||
{
|
{
|
||||||
struct us_data *us = usb_get_intfdata(iface);
|
struct us_data *us = usb_get_intfdata(iface);
|
||||||
|
|
||||||
@ -234,26 +248,23 @@ static void storage_pre_reset(struct usb_interface *iface)
|
|||||||
|
|
||||||
/* Make sure no command runs during the reset */
|
/* Make sure no command runs during the reset */
|
||||||
mutex_lock(&us->dev_mutex);
|
mutex_lock(&us->dev_mutex);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void storage_post_reset(struct usb_interface *iface, int reset_resume)
|
static int storage_post_reset(struct usb_interface *iface)
|
||||||
{
|
{
|
||||||
struct us_data *us = usb_get_intfdata(iface);
|
struct us_data *us = usb_get_intfdata(iface);
|
||||||
|
|
||||||
US_DEBUGP("%s\n", __FUNCTION__);
|
US_DEBUGP("%s\n", __FUNCTION__);
|
||||||
|
|
||||||
/* Report the reset to the SCSI core */
|
/* Report the reset to the SCSI core */
|
||||||
scsi_lock(us_to_host(us));
|
|
||||||
usb_stor_report_bus_reset(us);
|
usb_stor_report_bus_reset(us);
|
||||||
scsi_unlock(us_to_host(us));
|
|
||||||
|
|
||||||
/* FIXME: Notify the subdrivers that they need to reinitialize
|
/* FIXME: Notify the subdrivers that they need to reinitialize
|
||||||
* the device */
|
* the device */
|
||||||
|
|
||||||
/* If this is a reset-resume then the pre_reset routine wasn't
|
mutex_unlock(&us->dev_mutex);
|
||||||
* called, so we don't need to unlock the mutex. */
|
return 0;
|
||||||
if (!reset_resume)
|
|
||||||
mutex_unlock(&us->dev_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1061,6 +1072,7 @@ static struct usb_driver usb_storage_driver = {
|
|||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
.suspend = storage_suspend,
|
.suspend = storage_suspend,
|
||||||
.resume = storage_resume,
|
.resume = storage_resume,
|
||||||
|
.reset_resume = storage_reset_resume,
|
||||||
#endif
|
#endif
|
||||||
.pre_reset = storage_pre_reset,
|
.pre_reset = storage_pre_reset,
|
||||||
.post_reset = storage_post_reset,
|
.post_reset = storage_post_reset,
|
||||||
|
@ -839,6 +839,8 @@ struct usbdrv_wrap {
|
|||||||
* do (or don't) show up otherwise in the filesystem.
|
* do (or don't) show up otherwise in the filesystem.
|
||||||
* @suspend: Called when the device is going to be suspended by the system.
|
* @suspend: Called when the device is going to be suspended by the system.
|
||||||
* @resume: Called when the device is being resumed by the system.
|
* @resume: Called when the device is being resumed by the system.
|
||||||
|
* @reset_resume: Called when the suspended device has been reset instead
|
||||||
|
* of being resumed.
|
||||||
* @pre_reset: Called by usb_reset_composite_device() when the device
|
* @pre_reset: Called by usb_reset_composite_device() when the device
|
||||||
* is about to be reset.
|
* is about to be reset.
|
||||||
* @post_reset: Called by usb_reset_composite_device() after the device
|
* @post_reset: Called by usb_reset_composite_device() after the device
|
||||||
@ -885,9 +887,10 @@ struct usb_driver {
|
|||||||
|
|
||||||
int (*suspend) (struct usb_interface *intf, pm_message_t message);
|
int (*suspend) (struct usb_interface *intf, pm_message_t message);
|
||||||
int (*resume) (struct usb_interface *intf);
|
int (*resume) (struct usb_interface *intf);
|
||||||
|
int (*reset_resume)(struct usb_interface *intf);
|
||||||
|
|
||||||
void (*pre_reset) (struct usb_interface *intf);
|
int (*pre_reset)(struct usb_interface *intf);
|
||||||
void (*post_reset) (struct usb_interface *intf, int reset_resume);
|
int (*post_reset)(struct usb_interface *intf);
|
||||||
|
|
||||||
const struct usb_device_id *id_table;
|
const struct usb_device_id *id_table;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user