/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_SUSPEND
+
+static int hcd_hub_suspend (struct usb_bus *bus)
+{
+ struct usb_hcd *hcd;
+
+ hcd = container_of (bus, struct usb_hcd, self);
+ if (hcd->driver->hub_suspend)
+ return hcd->driver->hub_suspend (hcd);
+ return 0;
+}
+
+static int hcd_hub_resume (struct usb_bus *bus)
+{
+ struct usb_hcd *hcd;
+
+ hcd = container_of (bus, struct usb_hcd, self);
+ if (hcd->driver->hub_resume)
+ return hcd->driver->hub_resume (hcd);
+ return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
/* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup.
* we're guaranteed that the device is fully quiesced. also, that each
* endpoint has been hcd_endpoint_disabled.
.buffer_alloc = hcd_buffer_alloc,
.buffer_free = hcd_buffer_free,
.disable = hcd_endpoint_disable,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = hcd_hub_suspend,
+ .hub_resume = hcd_hub_resume,
+#endif
};
EXPORT_SYMBOL (usb_hcd_operations);
void *addr, dma_addr_t dma);
void (*disable)(struct usb_device *udev, int bEndpointAddress);
+
+ /* global suspend/resume of bus */
+ int (*hub_suspend)(struct usb_bus *);
+ int (*hub_resume)(struct usb_bus *);
};
/* each driver provides one of these, and hardware init support */
int (*reset) (struct usb_hcd *hcd);
int (*start) (struct usb_hcd *hcd);
+ /* NOTE: these suspend/resume calls relate to the HC as
+ * a whole, not just the root hub; they're for bus glue.
+ */
/* called after all devices were suspended */
int (*suspend) (struct usb_hcd *hcd, u32 state);
int (*hub_control) (struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
+ int (*hub_suspend)(struct usb_hcd *);
+ int (*hub_resume)(struct usb_hcd *);
};
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
return ret;
}
+#ifdef CONFIG_USB_SUSPEND
+
+ /* no USB_SUSPEND yet! */
+
+#else /* !CONFIG_USB_SUSPEND */
+
+int usb_suspend_device(struct usb_device *udev, u32 state)
+{
+ return 0;
+}
+
+int usb_resume_device(struct usb_device *udev)
+{
+ return 0;
+}
+
+#define hub_suspend 0
+#define hub_resume 0
+#define remote_wakeup(x) 0
+
+#endif /* CONFIG_USB_SUSPEND */
+
+EXPORT_SYMBOL(usb_suspend_device);
+EXPORT_SYMBOL(usb_resume_device);
+
+
+
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
* Between connect detection and reset signaling there must be a delay
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
- if (!hdev->parent)
+ if (!hdev->parent) {
delay = HUB_ROOT_RESET_TIME;
+ if (port + 1 == hdev->bus->otg_port)
+ hdev->bus->b_hnp_enable = 0;
+ }
/* Some low speed devices have problems with the quick delay, so */
/* be a bit pessimistic with those devices. RHbug #23670 */
for (i = 0; i < hdev->maxchild; i++) {
struct usb_device *udev = hdev->children[i];
- int delta;
+ int delta, ceiling;
if (!udev)
continue;
+ /* 100mA per-port ceiling, or 8mA for OTG ports */
+ if (i != (udev->bus->otg_port - 1) || hdev->parent)
+ ceiling = 50;
+ else
+ ceiling = 4;
+
if (udev->actconfig)
delta = udev->actconfig->desc.bMaxPower;
else
- delta = 50;
+ delta = ceiling;
// dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta);
+ if (delta > ceiling)
+ dev_warn(&udev->dev, "%dmA over %dmA budget!\n",
+ 2 * (delta - ceiling), 2 * ceiling);
remaining -= delta;
}
if (remaining < 0) {
}
if (portchange & USB_PORT_STAT_C_SUSPEND) {
+ clear_port_feature(hdev, i + 1,
+ USB_PORT_FEAT_C_SUSPEND);
+ if (hdev->children[i])
+ ret = remote_wakeup(hdev->children[i]);
+ else
+ ret = -ENODEV;
dev_dbg (hub_dev,
- "suspend change on port %d\n",
- i + 1);
- clear_port_feature(hdev,
- i + 1, USB_PORT_FEAT_C_SUSPEND);
+ "resume on port %d, status %d\n",
+ i + 1, ret);
+ if (ret < 0)
+ ret = hub_port_disable(hdev, i);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
+ .suspend = hub_suspend,
+ .resume = hub_resume,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
};
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
-static int usb_device_suspend(struct device *dev, u32 state)
+static int usb_generic_suspend(struct device *dev, u32 state)
{
struct usb_interface *intf;
struct usb_driver *driver;
+ if (dev->driver == &usb_generic_driver)
+ return usb_suspend_device (to_usb_device(dev), state);
+
if ((dev->driver == NULL) ||
- (dev->driver == &usb_generic_driver) ||
(dev->driver_data == &usb_generic_driver_data))
return 0;
return 0;
}
-static int usb_device_resume(struct device *dev)
+static int usb_generic_resume(struct device *dev)
{
struct usb_interface *intf;
struct usb_driver *driver;
+ /* devices resume through their hub */
+ if (dev->driver == &usb_generic_driver)
+ return usb_resume_device (to_usb_device(dev));
+
if ((dev->driver == NULL) ||
- (dev->driver == &usb_generic_driver) ||
(dev->driver_data == &usb_generic_driver_data))
return 0;
.name = "usb",
.match = usb_device_match,
.hotplug = usb_hotplug,
- .suspend = usb_device_suspend,
- .resume = usb_device_resume,
+ .suspend = usb_generic_suspend,
+ .resume = usb_generic_resume,
};
#ifndef MODULE
msleep (100);
#ifdef CONFIG_USB_SUSPEND
- (void) usb_suspend_device (hcd->self.root_hub);
+ (void) usb_suspend_device (hcd->self.root_hub, state);
#else
/* FIXME lock root hub */
(void) ehci_hub_suspend (hcd);
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
+ .hub_suspend = ehci_hub_suspend,
+ .hub_resume = ehci_hub_resume,
};
/*-------------------------------------------------------------------------*/
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
};
/*-------------------------------------------------------------------------*/
msleep (100);
#ifdef CONFIG_USB_SUSPEND
- (void) usb_suspend_device (hcd->self.root_hub);
+ (void) usb_suspend_device (hcd->self.root_hub, state);
#else
down (&hcd->self.root_hub->serialize);
(void) ohci_hub_suspend (hcd);
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
};
/*-------------------------------------------------------------------------*/
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
};
/*-------------------------------------------------------------------------*/
struct device *controller; /* host/master side hardware */
int busnum; /* Bus number (in order of reg) */
char *bus_name; /* stable id (PCI slot_name etc) */
+ u8 otg_port; /* 0, or number of OTG/HNP port */
+ unsigned is_b_host:1; /* true during some HNP roleswitches */
+ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */
int devnum_next; /* Next open device number in round-robin allocation */
void *data, int len, int *actual_length,
int timeout);
+/* selective suspend/resume */
+extern int usb_suspend_device(struct usb_device *dev, u32 state);
+extern int usb_resume_device(struct usb_device *dev);
+
+
/* wrappers around usb_control_msg() for the most common standard requests */
extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype,
unsigned char descindex, void *buf, int size);