[PATCH] usbcore support for root-hub IRQ instead of polling

This is a revised version of an earlier patch to add support to usbcore
for driving root hubs by interrupts rather than polling.

There's a temporary flag added to struct usb_hcd, marking devices whose
drivers are aware of the new mechanism.  By default that flag doesn't get
set so drivers will continue to see the same polling behavior as before.
This way we can convert the HCDs one by one to use interrupt-based event
reporting, and the temporary flag can be removed when they're all done.

Also included is a small change to the hcd_disable_endpoint routine.
Although endpoints normally shouldn't be disabled while a controller is
suspended, it's legal to do so when the controller's driver is being
rmmod'ed.

Lastly the patch adds a new callback, .hub_irq_enable, for use by HCDs
where the root hub's port-change interrupts are level-triggered rather
than edge-triggered.  The callback is invoked each time khubd has finished
processing a root hub, to let the HCD know that the interrupt can safely
be re-enabled.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 325a516..ac57527 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -65,7 +65,8 @@
 	const char		*product_desc;	/* product/vendor string */
 	char			irq_descr[24];	/* driver + bus # */
 
-	struct timer_list	rh_timer;	/* drives root hub */
+	struct timer_list	rh_timer;	/* drives root-hub polling */
+	struct urb		*status_urb;	/* the current status urb */
 
 	/*
 	 * hardware info/state
@@ -76,6 +77,12 @@
 	unsigned		remote_wakeup:1;/* sw should use wakeup? */
 	unsigned		rh_registered:1;/* is root hub registered? */
 
+	/* The next flag is a stopgap, to be removed when all the HCDs
+	 * support the new root-hub polling mechanism. */
+	unsigned		uses_new_polling:1;
+	unsigned		poll_rh:1;	/* poll for rh status? */
+	unsigned		poll_pending:1;	/* status has changed? */
+
 	int			irq;		/* irq allocated */
 	void __iomem		*regs;		/* device memory/io */
 	u64			rsrc_start;	/* memory/io resource start */
@@ -207,6 +214,8 @@
 	int		(*hub_suspend)(struct usb_hcd *);
 	int		(*hub_resume)(struct usb_hcd *);
 	int		(*start_port_reset)(struct usb_hcd *, unsigned port_num);
+	void		(*hub_irq_enable)(struct usb_hcd *);
+		/* Needed only if port-change IRQs are level-triggered */
 };
 
 extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
@@ -243,7 +252,9 @@
 
 /* generic bus glue, needed for host controllers that don't use PCI */
 extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+
 extern void usb_hc_died (struct usb_hcd *hcd);
+extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
 
 /* -------------------------------------------------------------------------- */
 
@@ -360,6 +371,8 @@
 extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
 extern void usb_bus_put (struct usb_bus *bus);
 
+extern void usb_enable_root_hub_irq (struct usb_bus *bus);
+
 extern int usb_find_interface_driver (struct usb_device *dev,
 	struct usb_interface *interface);