#include <asm/irq.h>
#include <asm/byteorder.h>
#include <linux/platform_device.h>
+#include <linux/workqueue.h>
#include <linux/usb.h>
#include "hub.h"
-// #define USB_BANDWIDTH_MESSAGES
-
/*-------------------------------------------------------------------------*/
/*
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
+ * see hub.c:hub_configure() for details. */
+ (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
};
if (urb->status == -EINPROGRESS)
urb->status = status;
spin_unlock (&urb->lock);
- usb_hcd_giveback_urb (hcd, urb, NULL);
+ usb_hcd_giveback_urb (hcd, urb);
local_irq_restore (flags);
return 0;
}
unsigned long flags;
char buffer[4]; /* Any root hubs with > 31 ports? */
+ if (unlikely(!hcd->rh_registered))
+ return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
/* local irqs are always blocked in completions */
if (length > 0)
- usb_hcd_giveback_urb (hcd, urb, NULL);
+ usb_hcd_giveback_urb (hcd, urb);
else
hcd->poll_pending = 1;
local_irq_restore (flags);
urb = NULL; /* wasn't fully queued */
spin_unlock (&hcd_root_hub_lock);
if (urb)
- usb_hcd_giveback_urb (hcd, urb, NULL);
+ usb_hcd_giveback_urb (hcd, urb);
local_irq_restore (flags);
}
}
EXPORT_SYMBOL (usb_calc_bus_time);
-/*
- * usb_check_bandwidth():
- *
- * old_alloc is from host_controller->bandwidth_allocated in microseconds;
- * bustime is from calc_bus_time(), but converted to microseconds.
- *
- * returns <bustime in us> if successful,
- * or -ENOSPC if bandwidth request fails.
- *
- * FIXME:
- * This initial implementation does not use Endpoint.bInterval
- * in managing bandwidth allocation.
- * It probably needs to be expanded to use Endpoint.bInterval.
- * This can be done as a later enhancement (correction).
- *
- * This will also probably require some kind of
- * frame allocation tracking...meaning, for example,
- * that if multiple drivers request interrupts every 10 USB frames,
- * they don't all have to be allocated at
- * frame numbers N, N+10, N+20, etc. Some of them could be at
- * N+11, N+21, N+31, etc., and others at
- * N+12, N+22, N+32, etc.
- *
- * Similarly for isochronous transfers...
- *
- * Individual HCDs can schedule more directly ... this logic
- * is not correct for high speed transfers.
- */
-int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
-{
- unsigned int pipe = urb->pipe;
- long bustime;
- int is_in = usb_pipein (pipe);
- int is_iso = usb_pipeisoc (pipe);
- int old_alloc = dev->bus->bandwidth_allocated;
- int new_alloc;
-
-
- bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,
- usb_maxpacket (dev, pipe, !is_in)));
- if (is_iso)
- bustime /= urb->number_of_packets;
-
- new_alloc = old_alloc + (int) bustime;
- if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {
-#ifdef DEBUG
- char *mode =
-#ifdef CONFIG_USB_BANDWIDTH
- "";
-#else
- "would have ";
-#endif
- dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",
- mode, old_alloc, bustime, new_alloc);
-#endif
-#ifdef CONFIG_USB_BANDWIDTH
- bustime = -ENOSPC; /* report error */
-#endif
- }
-
- return bustime;
-}
-EXPORT_SYMBOL (usb_check_bandwidth);
-
-
-/**
- * usb_claim_bandwidth - records bandwidth for a periodic transfer
- * @dev: source/target of request
- * @urb: request (urb->dev == dev)
- * @bustime: bandwidth consumed, in (average) microseconds per frame
- * @isoc: true iff the request is isochronous
- *
- * Bus bandwidth reservations are recorded purely for diagnostic purposes.
- * HCDs are expected not to overcommit periodic bandwidth, and to record such
- * reservations whenever endpoints are added to the periodic schedule.
- *
- * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's
- * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable
- * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how
- * large its periodic schedule is.
- */
-void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
-{
- dev->bus->bandwidth_allocated += bustime;
- if (isoc)
- dev->bus->bandwidth_isoc_reqs++;
- else
- dev->bus->bandwidth_int_reqs++;
- urb->bandwidth = bustime;
-
-#ifdef USB_BANDWIDTH_MESSAGES
- dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",
- bustime,
- isoc ? "ISOC" : "INTR",
- dev->bus->bandwidth_allocated,
- dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
-#endif
-}
-EXPORT_SYMBOL (usb_claim_bandwidth);
-
-
-/**
- * usb_release_bandwidth - reverses effect of usb_claim_bandwidth()
- * @dev: source/target of request
- * @urb: request (urb->dev == dev)
- * @isoc: true iff the request is isochronous
- *
- * This records that previously allocated bandwidth has been released.
- * Bandwidth is released when endpoints are removed from the host controller's
- * periodic schedule.
- */
-void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
-{
- dev->bus->bandwidth_allocated -= urb->bandwidth;
- if (isoc)
- dev->bus->bandwidth_isoc_reqs--;
- else
- dev->bus->bandwidth_int_reqs--;
-
-#ifdef USB_BANDWIDTH_MESSAGES
- dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",
- urb->bandwidth,
- isoc ? "ISOC" : "INTR",
- dev->bus->bandwidth_allocated,
- dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
-#endif
- urb->bandwidth = 0;
-}
-EXPORT_SYMBOL (usb_release_bandwidth);
-
/*-------------------------------------------------------------------------*/
{
unsigned long flags;
- /* Release any periodic transfer bandwidth */
- if (urb->bandwidth)
- usb_release_bandwidth (urb->dev, urb,
- usb_pipeisoc (urb->pipe));
-
/* clear all state linking urb to this dev (and hcd) */
spin_lock_irqsave (&hcd_data_lock, flags);
return status;
}
+/* Workqueue routine for root-hub remote wakeup */
+static void hcd_resume_work(struct work_struct *work)
+{
+ struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
+ struct usb_device *udev = hcd->self.root_hub;
+
+ usb_lock_device(udev);
+ usb_mark_last_busy(udev);
+ usb_external_resume_device(udev);
+ usb_unlock_device(udev);
+}
+
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
* suspended (with the remote wakeup feature enabled) and a remote
- * wakeup request is received. It queues a request for khubd to
- * resume the root hub (that is, manage its downstream ports again).
+ * wakeup request is received. The routine submits a workqueue request
+ * to resume the root hub (that is, manage its downstream ports again).
*/
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
{
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered)
- usb_resume_root_hub (hcd->self.root_hub);
+ queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
* @urb: urb being returned to the USB device driver.
- * @regs: pt_regs, passed down to the URB completion handler
* Context: in_interrupt()
*
* This hands the URB from HCD to its USB device driver, using its
* the device driver won't cause problems if it frees, modifies,
* or resubmits this URB.
*/
-void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
{
int at_root_hub;
usbmon_urb_complete (&hcd->self, urb);
/* pass ownership to the completion handler */
- urb->complete (urb, regs);
+ urb->complete (urb);
atomic_dec (&urb->use_count);
if (unlikely (urb->reject))
wake_up (&usb_kill_urb_queue);
* If the controller isn't HALTed, calls the driver's irq handler.
* Checks whether the controller is now dead.
*/
-irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
if (unlikely(start == HC_STATE_HALT ||
!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
return IRQ_NONE;
- if (hcd->driver->irq (hcd, r) == IRQ_NONE)
+ if (hcd->driver->irq (hcd) == IRQ_NONE)
return IRQ_NONE;
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
+#ifdef CONFIG_PM
+ INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
+#endif
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
+#ifdef CONFIG_PM
+ flush_workqueue(ksuspend_usb_wq);
+#endif
+
mutex_lock(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
mutex_unlock(&usb_bus_list_lock);
- hcd->poll_rh = 0;
- del_timer_sync(&hcd->rh_timer);
-
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
+ hcd->poll_rh = 0;
+ del_timer_sync(&hcd->rh_timer);
+
if (hcd->irq >= 0)
free_irq(hcd->irq, hcd);
usb_deregister_bus(&hcd->self);