* Maintainer: Alan Stern <stern@rowland.harvard.edu>
*
* Copyright (C) 2003 David Brownell
- * Copyright (C) 2003, 2004 Alan Stern
+ * Copyright (C) 2003-2005 Alan Stern
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <linux/version.h>
-
+#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/usb_gadget.h>
#define DRIVER_DESC "USB Host+Gadget Emulator"
-#define DRIVER_VERSION "14 Mar 2004"
+#define DRIVER_VERSION "02 May 2005"
static const char driver_name [] = "dummy_hcd";
static const char driver_desc [] = "USB Host+Gadget Emulator";
struct usb_request req;
};
+static inline struct dummy_ep *usb_ep_to_dummy_ep (struct usb_ep *_ep)
+{
+ return container_of (_ep, struct dummy_ep, ep);
+}
+
+static inline struct dummy_request *usb_request_to_dummy_request
+ (struct usb_request *_req)
+{
+ return container_of (_req, struct dummy_request, req);
+}
+
/*-------------------------------------------------------------------------*/
/*
/* or like sa1100: two fixed function endpoints */
"ep1out-bulk", "ep2in-bulk",
};
-#define DUMMY_ENDPOINTS (sizeof(ep_name)/sizeof(char *))
+#define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name)
+
+/*-------------------------------------------------------------------------*/
#define FIFO_SIZE 64
+struct urbp {
+ struct urb *urb;
+ struct list_head urbp_list;
+};
+
+
+enum dummy_rh_state {
+ DUMMY_RH_RESET,
+ DUMMY_RH_SUSPENDED,
+ DUMMY_RH_RUNNING
+};
+
struct dummy {
spinlock_t lock;
struct dummy_request fifo_req;
u8 fifo_buf [FIFO_SIZE];
u16 devstatus;
-
- struct hcd_dev *hdev;
+ unsigned udc_suspended:1;
+ unsigned pullup:1;
+ unsigned active:1;
+ unsigned old_active:1;
/*
* MASTER/HOST side support
*/
- struct usb_hcd hcd;
- struct platform_device pdev;
+ enum dummy_rh_state rh_state;
struct timer_list timer;
u32 port_status;
- int started;
- struct completion released;
+ u32 old_status;
unsigned resuming:1;
unsigned long re_timeout;
+
+ struct usb_device *udev;
+ struct list_head urbp_list;
};
-static struct dummy *the_controller;
+static inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd)
+{
+ return (struct dummy *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *dummy_to_hcd (struct dummy *dum)
+{
+ return container_of((void *) dum, struct usb_hcd, hcd_priv);
+}
+
+static inline struct device *dummy_dev (struct dummy *dum)
+{
+ return dummy_to_hcd(dum)->self.controller;
+}
+
+static inline struct device *udc_dev (struct dummy *dum)
+{
+ return dum->gadget.dev.parent;
+}
static inline struct dummy *ep_to_dummy (struct dummy_ep *ep)
{
return container_of (ep->gadget, struct dummy, gadget);
}
+static inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget)
+{
+ return container_of (gadget, struct dummy, gadget);
+}
+
static inline struct dummy *gadget_dev_to_dummy (struct device *dev)
{
return container_of (dev, struct dummy, gadget.dev);
}
-/*
- * This "hardware" may look a bit odd in diagnostics since it's got both
- * host and device sides; and it binds different drivers to each side.
- */
-#define hardware (&the_controller->pdev.dev)
+static struct dummy *the_controller;
/*-------------------------------------------------------------------------*/
-static struct device_driver dummy_driver = {
- .name = (char *) driver_name,
- .bus = &platform_bus_type,
-};
+/* SLAVE/GADGET SIDE UTILITY ROUTINES */
+
+/* called with spinlock held */
+static void nuke (struct dummy *dum, struct dummy_ep *ep)
+{
+ while (!list_empty (&ep->queue)) {
+ struct dummy_request *req;
+
+ req = list_entry (ep->queue.next, struct dummy_request, queue);
+ list_del_init (&req->queue);
+ req->req.status = -ESHUTDOWN;
+
+ spin_unlock (&dum->lock);
+ req->req.complete (&ep->ep, &req->req);
+ spin_lock (&dum->lock);
+ }
+}
+
+/* caller must hold lock */
+static void
+stop_activity (struct dummy *dum)
+{
+ struct dummy_ep *ep;
+
+ /* prevent any more requests */
+ dum->address = 0;
+
+ /* The timer is left running so that outstanding URBs can fail */
+
+ /* nuke any pending requests first, so driver i/o is quiesced */
+ list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
+ nuke (dum, ep);
+
+ /* driver now does any non-usb quiescing necessary */
+}
+
+/* caller must hold lock */
+static void
+set_link_state (struct dummy *dum)
+{
+ dum->active = 0;
+ if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
+ dum->port_status = 0;
+
+ /* UDC suspend must cause a disconnect */
+ else if (!dum->pullup || dum->udc_suspended) {
+ dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_HIGH_SPEED |
+ USB_PORT_STAT_SUSPEND);
+ if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
+ dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+ } else {
+ dum->port_status |= USB_PORT_STAT_CONNECTION;
+ if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
+ dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+ if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0)
+ dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+ else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+ dum->rh_state != DUMMY_RH_SUSPENDED)
+ dum->active = 1;
+ }
+
+ if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active)
+ dum->resuming = 0;
+
+ if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+ (dum->port_status & USB_PORT_STAT_RESET) != 0) {
+ if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
+ (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
+ dum->driver) {
+ stop_activity (dum);
+ spin_unlock (&dum->lock);
+ dum->driver->disconnect (&dum->gadget);
+ spin_lock (&dum->lock);
+ }
+ } else if (dum->active != dum->old_active) {
+ if (dum->old_active && dum->driver->suspend) {
+ spin_unlock (&dum->lock);
+ dum->driver->suspend (&dum->gadget);
+ spin_lock (&dum->lock);
+ } else if (!dum->old_active && dum->driver->resume) {
+ spin_unlock (&dum->lock);
+ dum->driver->resume (&dum->gadget);
+ spin_lock (&dum->lock);
+ }
+ }
+
+ dum->old_status = dum->port_status;
+ dum->old_active = dum->active;
+}
/*-------------------------------------------------------------------------*/
* drivers would do real i/o using dma, fifos, irqs, timers, etc.
*/
-#define is_enabled() \
- (the_controller->port_status & USB_PORT_STAT_ENABLE)
+#define is_enabled(dum) \
+ (dum->port_status & USB_PORT_STAT_ENABLE)
static int
dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
unsigned max;
int retval;
- ep = container_of (_ep, struct dummy_ep, ep);
+ ep = usb_ep_to_dummy_ep (_ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT)
- if (!the_controller->driver || !is_enabled ())
+ return -EINVAL;
+ dum = ep_to_dummy (ep);
+ if (!dum->driver || !is_enabled (dum))
return -ESHUTDOWN;
- max = desc->wMaxPacketSize & 0x3ff;
+ max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
/* drivers must not request bad settings, since lower levels
* (hardware or its drivers) may not check. some endpoints
* have some extra sanity checks. (there could be more though,
* especially for "ep9out" style fixed function ones.)
*/
- dum = container_of (ep->gadget, struct dummy, gadget);
retval = -EINVAL;
switch (desc->bmAttributes & 0x03) {
case USB_ENDPOINT_XFER_BULK:
_ep->maxpacket = max;
ep->desc = desc;
- dev_dbg (hardware, "enabled %s (ep%d%s-%s) maxpacket %d\n",
+ dev_dbg (udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n",
_ep->name,
desc->bEndpointAddress & 0x0f,
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
return retval;
}
-/* called with spinlock held */
-static void nuke (struct dummy *dum, struct dummy_ep *ep)
-{
- while (!list_empty (&ep->queue)) {
- struct dummy_request *req;
-
- req = list_entry (ep->queue.next, struct dummy_request, queue);
- list_del_init (&req->queue);
- req->req.status = -ESHUTDOWN;
-
- spin_unlock (&dum->lock);
- req->req.complete (&ep->ep, &req->req);
- spin_lock (&dum->lock);
- }
-}
-
static int dummy_disable (struct usb_ep *_ep)
{
struct dummy_ep *ep;
unsigned long flags;
int retval;
- ep = container_of (_ep, struct dummy_ep, ep);
+ ep = usb_ep_to_dummy_ep (_ep);
if (!_ep || !ep->desc || _ep->name == ep0name)
return -EINVAL;
dum = ep_to_dummy (ep);
spin_lock_irqsave (&dum->lock, flags);
- ep->desc = 0;
+ ep->desc = NULL;
retval = 0;
nuke (dum, ep);
spin_unlock_irqrestore (&dum->lock, flags);
- dev_dbg (hardware, "disabled %s\n", _ep->name);
+ dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name);
return retval;
}
static struct usb_request *
-dummy_alloc_request (struct usb_ep *_ep, int mem_flags)
+dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags)
{
struct dummy_ep *ep;
struct dummy_request *req;
- ep = container_of (_ep, struct dummy_ep, ep);
if (!_ep)
- return 0;
+ return NULL;
+ ep = usb_ep_to_dummy_ep (_ep);
req = kmalloc (sizeof *req, mem_flags);
if (!req)
- return 0;
+ return NULL;
memset (req, 0, sizeof *req);
INIT_LIST_HEAD (&req->queue);
return &req->req;
struct dummy_ep *ep;
struct dummy_request *req;
- ep = container_of (_ep, struct dummy_ep, ep);
+ ep = usb_ep_to_dummy_ep (_ep);
if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
return;
- req = container_of (_req, struct dummy_request, req);
+ req = usb_request_to_dummy_request (_req);
WARN_ON (!list_empty (&req->queue));
kfree (req);
}
struct usb_ep *_ep,
unsigned bytes,
dma_addr_t *dma,
- int mem_flags
+ gfp_t mem_flags
) {
- char *retval;
+ char *retval;
+ struct dummy_ep *ep;
+ struct dummy *dum;
- if (!the_controller->driver)
- return 0;
+ ep = usb_ep_to_dummy_ep (_ep);
+ dum = ep_to_dummy (ep);
+
+ if (!dum->driver)
+ return NULL;
retval = kmalloc (bytes, mem_flags);
*dma = (dma_addr_t) retval;
return retval;
static void
fifo_complete (struct usb_ep *ep, struct usb_request *req)
{
-#if 0
- dev_dbg (hardware, "fifo_complete: %d\n", req->status);
-#endif
}
static int
-dummy_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
+dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t mem_flags)
{
struct dummy_ep *ep;
struct dummy_request *req;
struct dummy *dum;
unsigned long flags;
- req = container_of (_req, struct dummy_request, req);
+ req = usb_request_to_dummy_request (_req);
if (!_req || !list_empty (&req->queue) || !_req->complete)
return -EINVAL;
- ep = container_of (_ep, struct dummy_ep, ep);
+ ep = usb_ep_to_dummy_ep (_ep);
if (!_ep || (!ep->desc && _ep->name != ep0name))
return -EINVAL;
- if (!the_controller->driver || !is_enabled ())
+ dum = ep_to_dummy (ep);
+ if (!dum->driver || !is_enabled (dum))
return -ESHUTDOWN;
- dum = container_of (ep->gadget, struct dummy, gadget);
-
#if 0
- dev_dbg (hardware, "ep %p queue req %p to %s, len %d buf %p\n",
+ dev_dbg (udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
ep, _req, _ep->name, _req->length, _req->buf);
#endif
struct dummy *dum;
int retval = -EINVAL;
unsigned long flags;
- struct dummy_request *req = 0;
-
- if (!the_controller->driver)
- return -ESHUTDOWN;
+ struct dummy_request *req = NULL;
if (!_ep || !_req)
return retval;
- ep = container_of (_ep, struct dummy_ep, ep);
- dum = container_of (ep->gadget, struct dummy, gadget);
+ ep = usb_ep_to_dummy_ep (_ep);
+ dum = ep_to_dummy (ep);
+
+ if (!dum->driver)
+ return -ESHUTDOWN;
spin_lock_irqsave (&dum->lock, flags);
list_for_each_entry (req, &ep->queue, queue) {
spin_unlock_irqrestore (&dum->lock, flags);
if (retval == 0) {
- dev_dbg (hardware, "dequeued req %p from %s, len %d buf %p\n",
+ dev_dbg (udc_dev(dum),
+ "dequeued req %p from %s, len %d buf %p\n",
req, _ep->name, _req->length, _req->buf);
-
_req->complete (_ep, _req);
}
return retval;
dummy_set_halt (struct usb_ep *_ep, int value)
{
struct dummy_ep *ep;
+ struct dummy *dum;
if (!_ep)
return -EINVAL;
- if (!the_controller->driver)
+ ep = usb_ep_to_dummy_ep (_ep);
+ dum = ep_to_dummy (ep);
+ if (!dum->driver)
return -ESHUTDOWN;
- ep = container_of (_ep, struct dummy_ep, ep);
if (!value)
ep->halted = 0;
else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
{
struct dummy *dum;
- dum = container_of (_gadget, struct dummy, gadget);
- if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0
- || !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)))
+ dum = gadget_to_dummy (_gadget);
+ if (!(dum->devstatus & ( (1 << USB_DEVICE_B_HNP_ENABLE)
+ | (1 << USB_DEVICE_REMOTE_WAKEUP))))
return -EINVAL;
+ if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0)
+ return -ENOLINK;
+ if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+ dum->rh_state != DUMMY_RH_SUSPENDED)
+ return -EIO;
+
+ /* FIXME: What if the root hub is suspended but the port isn't? */
/* hub notices our request, issues downstream resume, etc */
dum->resuming = 1;
- dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+ dum->re_timeout = jiffies + msecs_to_jiffies(20);
+ mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout);
return 0;
}
{
struct dummy *dum;
- dum = container_of (_gadget, struct dummy, gadget);
+ dum = gadget_to_dummy (_gadget);
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
return 0;
}
+static int dummy_pullup (struct usb_gadget *_gadget, int value)
+{
+ struct dummy *dum;
+ unsigned long flags;
+
+ dum = gadget_to_dummy (_gadget);
+ spin_lock_irqsave (&dum->lock, flags);
+ dum->pullup = (value != 0);
+ set_link_state (dum);
+ spin_unlock_irqrestore (&dum->lock, flags);
+
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ return 0;
+}
+
static const struct usb_gadget_ops dummy_ops = {
.get_frame = dummy_g_get_frame,
.wakeup = dummy_wakeup,
.set_selfpowered = dummy_set_selfpowered,
+ .pullup = dummy_pullup,
};
/*-------------------------------------------------------------------------*/
/* "function" sysfs attribute */
static ssize_t
-show_function (struct device *_dev, char *buf)
+show_function (struct device *dev, struct device_attribute *attr, char *buf)
{
- struct dummy *dum = the_controller;
+ struct dummy *dum = gadget_dev_to_dummy (dev);
- if (!dum->driver->function
- || strlen (dum->driver->function) > PAGE_SIZE)
+ if (!dum->driver || !dum->driver->function)
return 0;
- return snprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function);
+ return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function);
}
-DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
/*-------------------------------------------------------------------------*/
* for each driver that registers: just add to a big root hub.
*/
-static void
-dummy_udc_release (struct device *dev)
-{
- struct dummy *dum = gadget_dev_to_dummy (dev);
-
- complete (&dum->released);
-}
-
-static void
-dummy_hc_release (struct device *dev)
-{
- struct dummy *dum = dev_get_drvdata (dev);
-
- complete (&dum->released);
-}
-
-static int
-dummy_register_udc (struct dummy *dum)
-{
- int rc;
-
- strcpy (dum->gadget.dev.bus_id, "udc");
- dum->gadget.dev.parent = &dum->pdev.dev;
- dum->gadget.dev.release = dummy_udc_release;
-
- rc = device_register (&dum->gadget.dev);
- if (rc == 0)
- device_create_file (&dum->gadget.dev, &dev_attr_function);
- return rc;
-}
-
-static void
-dummy_unregister_udc (struct dummy *dum)
-{
- device_remove_file (&dum->gadget.dev, &dev_attr_function);
- init_completion (&dum->released);
- device_unregister (&dum->gadget.dev);
- wait_for_completion (&dum->released);
-}
-
int
usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
* SLAVE side init ... the layer above hardware, which
* can't enumerate without help from the driver we're binding.
*/
- dum->gadget.name = gadget_name;
- dum->gadget.ops = &dummy_ops;
- dum->gadget.is_dualspeed = 1;
dum->devstatus = 0;
- dum->resuming = 0;
INIT_LIST_HEAD (&dum->gadget.ep_list);
for (i = 0; i < DUMMY_ENDPOINTS; i++) {
ep->ep.maxpacket = ~0;
ep->last_io = jiffies;
ep->gadget = &dum->gadget;
- ep->desc = 0;
+ ep->desc = NULL;
INIT_LIST_HEAD (&ep->queue);
}
dum->driver = driver;
dum->gadget.dev.driver = &driver->driver;
- dev_dbg (hardware, "binding gadget driver '%s'\n", driver->driver.name);
+ dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
+ driver->driver.name);
if ((retval = driver->bind (&dum->gadget)) != 0) {
- dum->driver = 0;
- dum->gadget.dev.driver = 0;
+ dum->driver = NULL;
+ dum->gadget.dev.driver = NULL;
return retval;
}
- // FIXME: Check these calls for errors and re-order
- driver->driver.bus = dum->pdev.dev.bus;
+ driver->driver.bus = dum->gadget.dev.parent->bus;
driver_register (&driver->driver);
-
device_bind_driver (&dum->gadget.dev);
/* khubd will enumerate this in a while */
- dum->port_status |= USB_PORT_STAT_CONNECTION
- | (1 << USB_PORT_FEAT_C_CONNECTION);
+ spin_lock_irq (&dum->lock);
+ dum->pullup = 1;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
-/* caller must hold lock */
-static void
-stop_activity (struct dummy *dum, struct usb_gadget_driver *driver)
-{
- struct dummy_ep *ep;
-
- /* prevent any more requests */
- dum->hdev = 0;
- dum->address = 0;
-
- /* this might not succeed ... */
- del_timer (&dum->timer);
-
- /* nuke any pending requests first, so driver i/o is quiesced */
- list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
- nuke (dum, ep);
-
- /* driver now does any non-usb quiescing necessary */
- if (driver) {
- spin_unlock (&dum->lock);
- driver->disconnect (&dum->gadget);
- spin_lock (&dum->lock);
- }
-}
-
int
usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
{
if (!driver || driver != dum->driver)
return -EINVAL;
- dev_dbg (hardware, "unregister gadget driver '%s'\n",
+ dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
driver->driver.name);
spin_lock_irqsave (&dum->lock, flags);
- stop_activity (dum, driver);
- dum->port_status &= ~USB_PORT_STAT_CONNECTION;
- dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+ dum->pullup = 0;
+ set_link_state (dum);
spin_unlock_irqrestore (&dum->lock, flags);
driver->unbind (&dum->gadget);
- dum->driver = 0;
+ dum->driver = NULL;
device_release_driver (&dum->gadget.dev);
-
driver_unregister (&driver->driver);
- del_timer_sync (&dum->timer);
+ spin_lock_irqsave (&dum->lock, flags);
+ dum->pullup = 0;
+ set_link_state (dum);
+ spin_unlock_irqrestore (&dum->lock, flags);
+
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
}
EXPORT_SYMBOL (usb_gadget_unregister_driver);
#undef is_enabled
+/* just declare this in any driver that really need it */
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
{
return -ENOSYS;
}
EXPORT_SYMBOL (net2280_set_fifo_mode);
+
+/* The gadget structure is stored inside the hcd structure and will be
+ * released along with it. */
+static void
+dummy_gadget_release (struct device *dev)
+{
+#if 0 /* usb_bus_put isn't EXPORTed! */
+ struct dummy *dum = gadget_dev_to_dummy (dev);
+
+ usb_bus_put (&dummy_to_hcd (dum)->self);
+#endif
+}
+
+static int dummy_udc_probe (struct platform_device *pdev)
+{
+ struct dummy *dum = the_controller;
+ int rc;
+
+ dum->gadget.name = gadget_name;
+ dum->gadget.ops = &dummy_ops;
+ dum->gadget.is_dualspeed = 1;
+
+ /* maybe claim OTG support, though we won't complete HNP */
+ dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
+
+ strcpy (dum->gadget.dev.bus_id, "gadget");
+ dum->gadget.dev.parent = &pdev->dev;
+ dum->gadget.dev.release = dummy_gadget_release;
+ rc = device_register (&dum->gadget.dev);
+ if (rc < 0)
+ return rc;
+
+#if 0 /* usb_bus_get isn't EXPORTed! */
+ usb_bus_get (&dummy_to_hcd (dum)->self);
+#endif
+
+ platform_set_drvdata (pdev, dum);
+ device_create_file (&dum->gadget.dev, &dev_attr_function);
+ return rc;
+}
+
+static int dummy_udc_remove (struct platform_device *pdev)
+{
+ struct dummy *dum = platform_get_drvdata (pdev);
+
+ platform_set_drvdata (pdev, NULL);
+ device_remove_file (&dum->gadget.dev, &dev_attr_function);
+ device_unregister (&dum->gadget.dev);
+ return 0;
+}
+
+static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state)
+{
+ struct dummy *dum = platform_get_drvdata(pdev);
+
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ spin_lock_irq (&dum->lock);
+ dum->udc_suspended = 1;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ pdev->dev.power.power_state = state;
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ return 0;
+}
+
+static int dummy_udc_resume (struct platform_device *pdev)
+{
+ struct dummy *dum = platform_get_drvdata(pdev);
+
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ spin_lock_irq (&dum->lock);
+ dum->udc_suspended = 0;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ pdev->dev.power.power_state = PMSG_ON;
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ return 0;
+}
+
+static struct platform_driver dummy_udc_driver = {
+ .probe = dummy_udc_probe,
+ .remove = dummy_udc_remove,
+ .suspend = dummy_udc_suspend,
+ .resume = dummy_udc_resume,
+ .driver = {
+ .name = (char *) gadget_name,
+ .owner = THIS_MODULE,
+ },
+};
+
/*-------------------------------------------------------------------------*/
/* MASTER/HOST SIDE DRIVER
*/
static int dummy_urb_enqueue (
- struct usb_hcd *hcd,
- struct urb *urb,
- int mem_flags
+ struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb,
+ gfp_t mem_flags
) {
struct dummy *dum;
+ struct urbp *urbp;
unsigned long flags;
- /* patch to usb_sg_init() is in 2.5.60 */
- BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length);
+ if (!urb->transfer_buffer && urb->transfer_buffer_length)
+ return -EINVAL;
+
+ urbp = kmalloc (sizeof *urbp, mem_flags);
+ if (!urbp)
+ return -ENOMEM;
+ urbp->urb = urb;
- dum = container_of (hcd, struct dummy, hcd);
+ dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
- dum->hdev = urb->dev->hcpriv;
- urb->hcpriv = dum;
+ if (!dum->udev) {
+ dum->udev = urb->dev;
+ usb_get_dev (dum->udev);
+ } else if (unlikely (dum->udev != urb->dev))
+ dev_err (dummy_dev(dum), "usb_device address has changed!\n");
+
+ list_add_tail (&urbp->urbp_list, &dum->urbp_list);
+ urb->hcpriv = urbp;
if (usb_pipetype (urb->pipe) == PIPE_CONTROL)
urb->error_count = 1; /* mark as a new urb */
static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
- /* giveback happens automatically in timer callback */
+ struct dummy *dum;
+ unsigned long flags;
+
+ /* giveback happens automatically in timer callback,
+ * so make sure the callback happens */
+ dum = hcd_to_dummy (hcd);
+ spin_lock_irqsave (&dum->lock, flags);
+ if (dum->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum->urbp_list))
+ mod_timer (&dum->timer, jiffies);
+ spin_unlock_irqrestore (&dum->lock, flags);
return 0;
}
int tmp;
/* high bandwidth mode */
- tmp = ep->desc->wMaxPacketSize;
- tmp = le16_to_cpu (tmp);
+ tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
tmp = (tmp >> 11) & 0x03;
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
static void dummy_timer (unsigned long _dum)
{
struct dummy *dum = (struct dummy *) _dum;
- struct hcd_dev *hdev = dum->hdev;
- struct list_head *entry, *tmp;
+ struct urbp *urbp, *tmp;
unsigned long flags;
int limit, total;
int i;
- if (!hdev) {
- dev_err (hardware, "timer fired with device gone?\n");
- return;
- }
-
/* simplistic model for one frame's bandwidth */
switch (dum->gadget.speed) {
case USB_SPEED_LOW:
total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
break;
default:
- dev_err (hardware, "bogus device speed\n");
+ dev_err (dummy_dev(dum), "bogus device speed\n");
return;
}
/* look at each urb queued by the host side driver */
spin_lock_irqsave (&dum->lock, flags);
+
+ if (!dum->udev) {
+ dev_err (dummy_dev(dum),
+ "timer fired with no URBs pending?\n");
+ spin_unlock_irqrestore (&dum->lock, flags);
+ return;
+ }
+
for (i = 0; i < DUMMY_ENDPOINTS; i++) {
if (!ep_name [i])
break;
}
restart:
- list_for_each_safe (entry, tmp, &hdev->urb_list) {
+ list_for_each_entry_safe (urbp, tmp, &dum->urbp_list, urbp_list) {
struct urb *urb;
struct dummy_request *req;
u8 address;
- struct dummy_ep *ep = 0;
+ struct dummy_ep *ep = NULL;
int type;
- urb = list_entry (entry, struct urb, urb_list);
+ urb = urbp->urb;
if (urb->status != -EINPROGRESS) {
/* likely it was just unlinked */
goto return_urb;
- }
+ } else if (dum->rh_state != DUMMY_RH_RUNNING)
+ continue;
type = usb_pipetype (urb->pipe);
/* used up this frame's non-periodic bandwidth?
ep = find_endpoint(dum, address);
if (!ep) {
/* set_configuration() disagreement */
- dev_err (hardware,
+ dev_dbg (dummy_dev(dum),
"no ep configured for urb %p\n",
urb);
- maybe_set_status (urb, -ETIMEDOUT);
+ maybe_set_status (urb, -EPROTO);
goto return_urb;
}
}
if (ep->halted && !ep->setup_stage) {
/* NOTE: must not be iso! */
- dev_dbg (hardware, "ep %s halted, urb %p\n",
+ dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n",
ep->ep.name, urb);
maybe_set_status (urb, -EPIPE);
goto return_urb;
struct usb_ctrlrequest setup;
int value = 1;
struct dummy_ep *ep2;
+ unsigned w_index;
+ unsigned w_value;
setup = *(struct usb_ctrlrequest*) urb->setup_packet;
- le16_to_cpus (&setup.wIndex);
- le16_to_cpus (&setup.wValue);
- le16_to_cpus (&setup.wLength);
- if (setup.wLength != urb->transfer_buffer_length) {
+ w_index = le16_to_cpu(setup.wIndex);
+ w_value = le16_to_cpu(setup.wValue);
+ if (le16_to_cpu(setup.wLength) !=
+ urb->transfer_buffer_length) {
maybe_set_status (urb, -EOVERFLOW);
goto return_urb;
}
list_for_each_entry (req, &ep->queue, queue) {
list_del_init (&req->queue);
req->req.status = -EOVERFLOW;
- dev_dbg (hardware, "stale req = %p\n", req);
+ dev_dbg (udc_dev(dum), "stale req = %p\n",
+ req);
spin_unlock (&dum->lock);
req->req.complete (&ep->ep, &req->req);
case USB_REQ_SET_ADDRESS:
if (setup.bRequestType != Dev_Request)
break;
- dum->address = setup.wValue;
+ dum->address = w_value;
maybe_set_status (urb, 0);
- dev_dbg (hardware, "set_address = %d\n",
- setup.wValue);
+ dev_dbg (udc_dev(dum), "set_address = %d\n",
+ w_value);
value = 0;
break;
case USB_REQ_SET_FEATURE:
if (setup.bRequestType == Dev_Request) {
value = 0;
- switch (setup.wValue) {
+ switch (w_value) {
case USB_DEVICE_REMOTE_WAKEUP:
break;
+ case USB_DEVICE_B_HNP_ENABLE:
+ dum->gadget.b_hnp_enable = 1;
+ break;
+ case USB_DEVICE_A_HNP_SUPPORT:
+ dum->gadget.a_hnp_support = 1;
+ break;
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ dum->gadget.a_alt_hnp_support
+ = 1;
+ break;
default:
value = -EOPNOTSUPP;
}
if (value == 0) {
dum->devstatus |=
- (1 << setup.wValue);
+ (1 << w_value);
maybe_set_status (urb, 0);
}
} else if (setup.bRequestType == Ep_Request) {
// endpoint halt
- ep2 = find_endpoint (dum,
- setup.wIndex);
+ ep2 = find_endpoint (dum, w_index);
if (!ep2) {
value = -EOPNOTSUPP;
break;
break;
case USB_REQ_CLEAR_FEATURE:
if (setup.bRequestType == Dev_Request) {
- switch (setup.wValue) {
+ switch (w_value) {
case USB_DEVICE_REMOTE_WAKEUP:
dum->devstatus &= ~(1 <<
USB_DEVICE_REMOTE_WAKEUP);
}
} else if (setup.bRequestType == Ep_Request) {
// endpoint halt
- ep2 = find_endpoint (dum,
- setup.wIndex);
+ ep2 = find_endpoint (dum, w_index);
if (!ep2) {
value = -EOPNOTSUPP;
break;
if (urb->transfer_buffer_length > 0) {
if (setup.bRequestType ==
Ep_InRequest) {
- ep2 = find_endpoint (dum, setup.wIndex);
+ ep2 = find_endpoint (dum, w_index);
if (!ep2) {
value = -EOPNOTSUPP;
break;
if (value < 0) {
if (value != -EOPNOTSUPP)
- dev_dbg (hardware,
+ dev_dbg (udc_dev(dum),
"setup --> %d\n",
value);
maybe_set_status (urb, -EPIPE);
continue;
return_urb:
- urb->hcpriv = 0;
+ urb->hcpriv = NULL;
+ list_del (&urbp->urbp_list);
+ kfree (urbp);
if (ep)
ep->already_seen = ep->setup_stage = 0;
spin_unlock (&dum->lock);
- usb_hcd_giveback_urb (&dum->hcd, urb, 0);
+ usb_hcd_giveback_urb (dummy_to_hcd(dum), urb, NULL);
spin_lock (&dum->lock);
goto restart;
}
- /* want a 1 msec delay here */
- if (!list_empty (&hdev->urb_list))
- mod_timer (&dum->timer, jiffies + 1);
+ if (list_empty (&dum->urbp_list)) {
+ usb_put_dev (dum->udev);
+ dum->udev = NULL;
+ } else if (dum->rh_state == DUMMY_RH_RUNNING) {
+ /* want a 1 msec delay here */
+ mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
+ }
spin_unlock_irqrestore (&dum->lock, flags);
}
/*-------------------------------------------------------------------------*/
#define PORT_C_MASK \
- ((1 << USB_PORT_FEAT_C_CONNECTION) \
- | (1 << USB_PORT_FEAT_C_ENABLE) \
- | (1 << USB_PORT_FEAT_C_SUSPEND) \
- | (1 << USB_PORT_FEAT_C_OVER_CURRENT) \
- | (1 << USB_PORT_FEAT_C_RESET))
+ ((USB_PORT_STAT_C_CONNECTION \
+ | USB_PORT_STAT_C_ENABLE \
+ | USB_PORT_STAT_C_SUSPEND \
+ | USB_PORT_STAT_C_OVERCURRENT \
+ | USB_PORT_STAT_C_RESET) << 16)
static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
{
struct dummy *dum;
unsigned long flags;
- int retval;
+ int retval = 0;
- dum = container_of (hcd, struct dummy, hcd);
+ dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
- if (!(dum->port_status & PORT_C_MASK))
- retval = 0;
- else {
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ goto done;
+
+ if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
+ dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+ dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+ set_link_state (dum);
+ }
+
+ if ((dum->port_status & PORT_C_MASK) != 0) {
*buf = (1 << 1);
- dev_dbg (hardware, "port status 0x%08x has changes\n",
- dum->port_status);
+ dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
+ dum->port_status);
retval = 1;
+ if (dum->rh_state == DUMMY_RH_SUSPENDED)
+ usb_hcd_resume_root_hub (hcd);
}
+done:
spin_unlock_irqrestore (&dum->lock, flags);
return retval;
}
memset (desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
- desc->wHubCharacteristics = __constant_cpu_to_le16 (0x0001);
+ desc->wHubCharacteristics = (__force __u16)
+ (__constant_cpu_to_le16 (0x0001));
desc->bNbrPorts = 1;
desc->bitmap [0] = 0xff;
desc->bitmap [1] = 0xff;
int retval = 0;
unsigned long flags;
- dum = container_of (hcd, struct dummy, hcd);
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ return -ETIMEDOUT;
+
+ dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
switch (typeReq) {
case ClearHubFeature:
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- /* 20msec resume signaling */
- dum->resuming = 1;
- dum->re_timeout = jiffies + ((HZ * 20)/1000);
+ if (dum->port_status & USB_PORT_STAT_SUSPEND) {
+ /* 20msec resume signaling */
+ dum->resuming = 1;
+ dum->re_timeout = jiffies +
+ msecs_to_jiffies(20);
+ }
break;
case USB_PORT_FEAT_POWER:
- dum->port_status = 0;
- dum->resuming = 0;
- stop_activity(dum, dum->driver);
- break;
+ if (dum->port_status & USB_PORT_STAT_POWER)
+ dev_dbg (dummy_dev(dum), "power-off\n");
+ /* FALLS THROUGH */
default:
dum->port_status &= ~(1 << wValue);
+ set_link_state (dum);
}
break;
case GetHubDescriptor:
hub_descriptor ((struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
- *(u32 *) buf = __constant_cpu_to_le32 (0);
+ *(__le32 *) buf = __constant_cpu_to_le32 (0);
break;
case GetPortStatus:
if (wIndex != 1)
/* whoever resets or resumes must GetPortStatus to
* complete it!!
*/
- if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
- dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
- dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
- dum->resuming = 0;
- dum->re_timeout = 0;
- if (dum->driver->resume) {
- spin_unlock (&dum->lock);
- dum->driver->resume (&dum->gadget);
- spin_lock (&dum->lock);
- }
+ if (dum->resuming &&
+ time_after_eq (jiffies, dum->re_timeout)) {
+ dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+ dum->port_status &= ~USB_PORT_STAT_SUSPEND;
}
- if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0
- && time_after (jiffies, dum->re_timeout)) {
- dum->port_status |= (1 << USB_PORT_FEAT_C_RESET);
- dum->port_status &= ~(1 << USB_PORT_FEAT_RESET);
- dum->re_timeout = 0;
- if (dum->driver) {
+ if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
+ time_after_eq (jiffies, dum->re_timeout)) {
+ dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
+ dum->port_status &= ~USB_PORT_STAT_RESET;
+ if (dum->pullup) {
dum->port_status |= USB_PORT_STAT_ENABLE;
/* give it the best speed we agree on */
dum->gadget.speed = dum->driver->speed;
}
}
}
- ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
- ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
+ set_link_state (dum);
+ ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
+ ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND);
- if (dum->driver->suspend) {
- spin_unlock (&dum->lock);
- dum->driver->suspend (&dum->gadget);
- spin_lock (&dum->lock);
+ if (dum->active) {
+ dum->port_status |= USB_PORT_STAT_SUSPEND;
+
+ /* HNP would happen here; for now we
+ * assume b_bus_req is always true.
+ */
+ set_link_state (dum);
+ if (((1 << USB_DEVICE_B_HNP_ENABLE)
+ & dum->devstatus) != 0)
+ dev_dbg (dummy_dev(dum),
+ "no HNP yet!\n");
}
break;
+ case USB_PORT_FEAT_POWER:
+ dum->port_status |= USB_PORT_STAT_POWER;
+ set_link_state (dum);
+ break;
case USB_PORT_FEAT_RESET:
- /* if it's already running, disconnect first */
- if (dum->port_status & USB_PORT_STAT_ENABLE) {
- dum->port_status &= ~(USB_PORT_STAT_ENABLE
- | USB_PORT_STAT_LOW_SPEED
- | USB_PORT_STAT_HIGH_SPEED);
- if (dum->driver) {
- dev_dbg (hardware, "disconnect\n");
- stop_activity (dum, dum->driver);
- }
-
- /* FIXME test that code path! */
- }
+ /* if it's already enabled, disable */
+ dum->port_status &= ~(USB_PORT_STAT_ENABLE
+ | USB_PORT_STAT_LOW_SPEED
+ | USB_PORT_STAT_HIGH_SPEED);
+ dum->devstatus = 0;
/* 50msec reset signaling */
- dum->re_timeout = jiffies + ((HZ * 50)/1000);
- /* FALLTHROUGH */
+ dum->re_timeout = jiffies + msecs_to_jiffies(50);
+ /* FALLS THROUGH */
default:
- dum->port_status |= (1 << wValue);
+ if ((dum->port_status & USB_PORT_STAT_POWER) != 0) {
+ dum->port_status |= (1 << wValue);
+ set_link_state (dum);
+ }
}
break;
default:
- dev_dbg (hardware,
+ dev_dbg (dummy_dev(dum),
"hub control req%04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
retval = -EPIPE;
}
spin_unlock_irqrestore (&dum->lock, flags);
+
+ if ((dum->port_status & PORT_C_MASK) != 0)
+ usb_hcd_poll_rh_status (hcd);
return retval;
}
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_hcd *dummy_alloc (void)
+static int dummy_bus_suspend (struct usb_hcd *hcd)
{
- struct dummy *dum;
+ struct dummy *dum = hcd_to_dummy (hcd);
- dum = kmalloc (sizeof *dum, SLAB_KERNEL);
- if (dum == NULL)
- return 0;
- memset (dum, 0, sizeof *dum);
- return &dum->hcd;
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__);
+
+ spin_lock_irq (&dum->lock);
+ dum->rh_state = DUMMY_RH_SUSPENDED;
+ set_link_state (dum);
+ hcd->state = HC_STATE_SUSPENDED;
+ spin_unlock_irq (&dum->lock);
+ return 0;
}
-static void dummy_free (struct usb_hcd *hcd)
+static int dummy_bus_resume (struct usb_hcd *hcd)
{
- struct dummy *dum;
-
- dum = container_of (hcd, struct dummy, hcd);
- WARN_ON (dum->driver != 0);
- kfree (dum);
+ struct dummy *dum = hcd_to_dummy (hcd);
+ int rc = 0;
+
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__);
+
+ spin_lock_irq (&dum->lock);
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n");
+ rc = -ENODEV;
+ } else {
+ dum->rh_state = DUMMY_RH_RUNNING;
+ set_link_state (dum);
+ if (!list_empty(&dum->urbp_list))
+ mod_timer (&dum->timer, jiffies);
+ hcd->state = HC_STATE_RUNNING;
+ }
+ spin_unlock_irq (&dum->lock);
+ return rc;
}
/*-------------------------------------------------------------------------*/
}
static ssize_t
-show_urbs (struct device *dev, char *buf)
+show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
{
- struct dummy *dum = dev_get_drvdata(dev);
- struct urb *urb;
+ struct usb_hcd *hcd = dev_get_drvdata (dev);
+ struct dummy *dum = hcd_to_dummy (hcd);
+ struct urbp *urbp;
size_t size = 0;
unsigned long flags;
spin_lock_irqsave (&dum->lock, flags);
- if (dum->hdev) {
- list_for_each_entry (urb, &dum->hdev->urb_list, urb_list) {
- size_t temp;
+ list_for_each_entry (urbp, &dum->urbp_list, urbp_list) {
+ size_t temp;
- temp = show_urb (buf, PAGE_SIZE - size, urb);
- buf += temp;
- size += temp;
- }
+ temp = show_urb (buf, PAGE_SIZE - size, urbp->urb);
+ buf += temp;
+ size += temp;
}
spin_unlock_irqrestore (&dum->lock, flags);
}
static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
-
-static const struct hc_driver dummy_hcd;
-
static int dummy_start (struct usb_hcd *hcd)
{
struct dummy *dum;
- struct usb_bus *bus;
- struct usb_device *root;
- int retval;
- dum = container_of (hcd, struct dummy, hcd);
+ dum = hcd_to_dummy (hcd);
/*
* MASTER side init ... we emulate a root hub that'll only ever
* just like more familiar pci-based HCDs.
*/
spin_lock_init (&dum->lock);
-
- retval = driver_register (&dummy_driver);
- if (retval < 0)
- return retval;
-
- dum->pdev.name = "hc";
- dum->pdev.dev.driver = &dummy_driver;
- dev_set_drvdata(&dum->pdev.dev, dum);
- dum->pdev.dev.release = dummy_hc_release;
- retval = platform_device_register (&dum->pdev);
- if (retval < 0) {
- driver_unregister (&dummy_driver);
- return retval;
- }
- dev_info (&dum->pdev.dev, "%s, driver " DRIVER_VERSION "\n",
- driver_desc);
-
- hcd->self.controller = &dum->pdev.dev;
-
- /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
- device_create_file (hcd->self.controller, &dev_attr_urbs);
-
init_timer (&dum->timer);
dum->timer.function = dummy_timer;
dum->timer.data = (unsigned long) dum;
+ dum->rh_state = DUMMY_RH_RUNNING;
- /* root hub will appear as another device */
- dum->hcd.driver = (struct hc_driver *) &dummy_hcd;
- dum->hcd.description = dummy_hcd.description;
- dum->hcd.product_desc = "Dummy host controller";
-
- bus = hcd_to_bus (&dum->hcd);
- bus->bus_name = dum->pdev.dev.bus_id;
- usb_bus_init (bus);
- bus->op = &usb_hcd_operations;
- bus->hcpriv = &dum->hcd;
-
- /* FIXME don't require the pci-based buffer/alloc impls;
- * the "generic dma" implementation still requires them,
- * it's not very generic yet.
- */
- if ((retval = hcd_buffer_create (&dum->hcd)) != 0) {
-clean0:
- init_completion (&dum->released);
- platform_device_unregister (&dum->pdev);
- wait_for_completion (&dum->released);
- driver_unregister (&dummy_driver);
- return retval;
- }
-
- INIT_LIST_HEAD (&hcd->dev_list);
- usb_register_bus (bus);
-
- root = usb_alloc_dev (0, bus, 0);
- if (!root) {
- retval = -ENOMEM;
-clean1:
- hcd_buffer_destroy (&dum->hcd);
- usb_deregister_bus (bus);
- goto clean0;
- }
-
- /* root hub enters addressed state... */
- dum->hcd.state = USB_STATE_RUNNING;
- root->speed = USB_SPEED_HIGH;
-
- /* ...then configured, so khubd sees us. */
- if ((retval = hcd_register_root (root, &dum->hcd)) != 0) {
- usb_put_dev (root);
-clean2:
- dum->hcd.state = USB_STATE_QUIESCING;
- goto clean1;
- }
+ INIT_LIST_HEAD (&dum->urbp_list);
/* only show a low-power port: just 8mA */
- hub_set_power_budget (root, 8);
+ hcd->power_budget = 8;
+ hcd->state = HC_STATE_RUNNING;
+ hcd->uses_new_polling = 1;
- dum->started = 1;
+#ifdef CONFIG_USB_OTG
+ hcd->self.otg_port = 1;
+#endif
- if ((retval = dummy_register_udc (dum)) != 0) {
- dum->started = 0;
- usb_disconnect (&bus->root_hub);
- goto clean2;
- }
+ /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
+ device_create_file (dummy_dev(dum), &dev_attr_urbs);
return 0;
}
static void dummy_stop (struct usb_hcd *hcd)
{
struct dummy *dum;
- struct usb_bus *bus;
- dum = container_of (hcd, struct dummy, hcd);
- if (!dum->started)
- return;
- dum->started = 0;
+ dum = hcd_to_dummy (hcd);
+ device_remove_file (dummy_dev(dum), &dev_attr_urbs);
usb_gadget_unregister_driver (dum->driver);
- dummy_unregister_udc (dum);
-
- bus = hcd_to_bus (&dum->hcd);
- hcd->state = USB_STATE_QUIESCING;
- dev_dbg (hardware, "remove root hub\n");
- usb_disconnect (&bus->root_hub);
-
- hcd_buffer_destroy (&dum->hcd);
- usb_deregister_bus (bus);
-
- dev_info (hardware, "stopped\n");
-
- device_remove_file (hcd->self.controller, &dev_attr_urbs);
- init_completion (&dum->released);
- platform_device_unregister (&dum->pdev);
- wait_for_completion (&dum->released);
-
- driver_unregister (&dummy_driver);
+ dev_info (dummy_dev(dum), "stopped\n");
}
/*-------------------------------------------------------------------------*/
static int dummy_h_get_frame (struct usb_hcd *hcd)
{
- return dummy_g_get_frame (0);
+ return dummy_g_get_frame (NULL);
}
static const struct hc_driver dummy_hcd = {
.description = (char *) driver_name,
+ .product_desc = "Dummy host controller",
+ .hcd_priv_size = sizeof(struct dummy),
+
.flags = HCD_USB2,
.start = dummy_start,
.stop = dummy_stop,
- .hcd_alloc = dummy_alloc,
- .hcd_free = dummy_free,
-
.urb_enqueue = dummy_urb_enqueue,
.urb_dequeue = dummy_urb_dequeue,
.hub_status_data = dummy_hub_status,
.hub_control = dummy_hub_control,
+ .bus_suspend = dummy_bus_suspend,
+ .bus_resume = dummy_bus_resume,
+};
+
+static int dummy_hcd_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ int retval;
+
+ dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
+
+ hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd)
+ return -ENOMEM;
+ the_controller = hcd_to_dummy (hcd);
+
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval != 0) {
+ usb_put_hcd (hcd);
+ the_controller = NULL;
+ }
+ return retval;
+}
+
+static int dummy_hcd_remove (struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = platform_get_drvdata (pdev);
+ usb_remove_hcd (hcd);
+ usb_put_hcd (hcd);
+ the_controller = NULL;
+ return 0;
+}
+
+static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd;
+ struct dummy *dum;
+ int rc = 0;
+
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+
+ hcd = platform_get_drvdata (pdev);
+ dum = hcd_to_dummy (hcd);
+ if (dum->rh_state == DUMMY_RH_RUNNING) {
+ dev_warn(&pdev->dev, "Root hub isn't suspended!\n");
+ rc = -EBUSY;
+ } else
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ return rc;
+}
+
+static int dummy_hcd_resume (struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+
+ hcd = platform_get_drvdata (pdev);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ usb_hcd_poll_rh_status (hcd);
+ return 0;
+}
+
+static struct platform_driver dummy_hcd_driver = {
+ .probe = dummy_hcd_probe,
+ .remove = dummy_hcd_remove,
+ .suspend = dummy_hcd_suspend,
+ .resume = dummy_hcd_resume,
+ .driver = {
+ .name = (char *) driver_name,
+ .owner = THIS_MODULE,
+ },
};
/*-------------------------------------------------------------------------*/
+/* These don't need to do anything because the pdev structures are
+ * statically allocated. */
+static void
+dummy_udc_release (struct device *dev) {}
+
+static void
+dummy_hcd_release (struct device *dev) {}
+
+static struct platform_device the_udc_pdev = {
+ .name = (char *) gadget_name,
+ .id = -1,
+ .dev = {
+ .release = dummy_udc_release,
+ },
+};
+
+static struct platform_device the_hcd_pdev = {
+ .name = (char *) driver_name,
+ .id = -1,
+ .dev = {
+ .release = dummy_hcd_release,
+ },
+};
+
static int __init init (void)
{
- struct usb_hcd *hcd;
- int value;
+ int retval;
if (usb_disabled ())
return -ENODEV;
- if ((hcd = dummy_alloc ()) == 0)
- return -ENOMEM;
- the_controller = container_of (hcd, struct dummy, hcd);
- value = dummy_start (hcd);
+ retval = platform_driver_register (&dummy_hcd_driver);
+ if (retval < 0)
+ return retval;
- if (value != 0) {
- dummy_free (hcd);
- the_controller = 0;
- }
- return value;
+ retval = platform_driver_register (&dummy_udc_driver);
+ if (retval < 0)
+ goto err_register_udc_driver;
+
+ retval = platform_device_register (&the_hcd_pdev);
+ if (retval < 0)
+ goto err_register_hcd;
+
+ retval = platform_device_register (&the_udc_pdev);
+ if (retval < 0)
+ goto err_register_udc;
+ return retval;
+
+err_register_udc:
+ platform_device_unregister (&the_hcd_pdev);
+err_register_hcd:
+ platform_driver_unregister (&dummy_udc_driver);
+err_register_udc_driver:
+ platform_driver_unregister (&dummy_hcd_driver);
+ return retval;
}
module_init (init);
static void __exit cleanup (void)
{
- dummy_stop (&the_controller->hcd);
- dummy_free (&the_controller->hcd);
- the_controller = 0;
+ platform_device_unregister (&the_udc_pdev);
+ platform_device_unregister (&the_hcd_pdev);
+ platform_driver_unregister (&dummy_udc_driver);
+ platform_driver_unregister (&dummy_hcd_driver);
}
module_exit (cleanup);
-