X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fgadget%2Fdummy_hcd.c;h=7d1c22c34957bfa94ebc5dfe067a6b98ffa86e28;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=5fd8ea6f644f2cd83c227cf298f12ea2fb72bba4;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 5fd8ea6f6..7d1c22c34 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -4,7 +4,7 @@ * Maintainer: Alan Stern * * 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 @@ -36,7 +36,6 @@ #define DEBUG -#include #include #include #include @@ -49,8 +48,7 @@ #include #include #include -#include - +#include #include #include @@ -65,7 +63,7 @@ #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"; @@ -95,6 +93,17 @@ struct dummy_request { 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); +} + /*-------------------------------------------------------------------------*/ /* @@ -128,10 +137,24 @@ static const char *const ep_name [] = { /* 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; @@ -145,46 +168,156 @@ struct dummy { 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; +} /*-------------------------------------------------------------------------*/ @@ -195,8 +328,8 @@ static struct device_driver dummy_driver = { * 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) @@ -206,12 +339,14 @@ 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 @@ -221,7 +356,6 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * 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: @@ -290,7 +424,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) _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", @@ -311,22 +445,6 @@ done: 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; @@ -334,35 +452,34 @@ static int dummy_disable (struct usb_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); + req = kzalloc(sizeof(*req), mem_flags); if (!req) - return 0; - memset (req, 0, sizeof *req); + return NULL; INIT_LIST_HEAD (&req->queue); return &req->req; } @@ -373,11 +490,11 @@ dummy_free_request (struct usb_ep *_ep, struct usb_request *_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); } @@ -387,12 +504,17 @@ dummy_alloc_buffer ( 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; @@ -412,34 +534,31 @@ dummy_free_buffer ( 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 @@ -480,17 +599,18 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) 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); - spin_lock_irqsave (&dum->lock, flags); + if (!dum->driver) + return -ESHUTDOWN; + + local_irq_save (flags); + spin_lock (&dum->lock); list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) { list_del_init (&req->queue); @@ -499,14 +619,15 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) break; } } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock (&dum->lock); 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); } + local_irq_restore (flags); return retval; } @@ -514,12 +635,14 @@ static int 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) && @@ -563,14 +686,22 @@ static int dummy_wakeup (struct usb_gadget *_gadget) { 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; } @@ -578,7 +709,7 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) { 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 @@ -586,26 +717,41 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) 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); /*-------------------------------------------------------------------------*/ @@ -623,46 +769,6 @@ 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) { @@ -681,12 +787,8 @@ 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++) { @@ -701,7 +803,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) ep->ep.maxpacket = ~0; ep->last_io = jiffies; ep->gadget = &dum->gadget; - ep->desc = 0; + ep->desc = NULL; INIT_LIST_HEAD (&ep->queue); } @@ -712,51 +814,29 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) 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) { @@ -768,35 +848,133 @@ 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 @@ -812,22 +990,34 @@ EXPORT_SYMBOL (net2280_set_fifo_mode); */ 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); - if (!dum->hdev) - 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 */ @@ -841,7 +1031,16 @@ static int dummy_urb_enqueue ( 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; } @@ -985,8 +1184,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) 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; @@ -994,10 +1192,17 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) return limit; } +#define is_active(dum) ((dum->port_status & \ + (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \ + USB_PORT_STAT_SUSPEND)) \ + == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) + static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) { int i; + if (!is_active (dum)) + return NULL; if ((address & ~USB_DIR_IN) == 0) return &dum->ep [0]; for (i = 1; i < DUMMY_ENDPOINTS; i++) { @@ -1011,6 +1216,8 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) return NULL; } +#undef is_active + #define Dev_Request (USB_TYPE_STANDARD | USB_RECIP_DEVICE) #define Dev_InRequest (Dev_Request | USB_DIR_IN) #define Intf_Request (USB_TYPE_STANDARD | USB_RECIP_INTERFACE) @@ -1024,17 +1231,11 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) 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: @@ -1047,7 +1248,7 @@ static void dummy_timer (unsigned long _dum) 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; } @@ -1055,6 +1256,14 @@ static void dummy_timer (unsigned long _dum) /* 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; @@ -1062,18 +1271,19 @@ static void dummy_timer (unsigned long _dum) } 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? @@ -1090,10 +1300,10 @@ restart: 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; } @@ -1106,7 +1316,7 @@ restart: } 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; @@ -1118,12 +1328,14 @@ restart: 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; } @@ -1132,7 +1344,8 @@ restart: 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); @@ -1152,36 +1365,40 @@ restart: case USB_REQ_SET_ADDRESS: if (setup.bRequestType != Dev_Request) break; - if (dum->address != 0) { - maybe_set_status (urb, -ETIMEDOUT); - urb->actual_length = 0; - goto return_urb; - } - 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; @@ -1193,7 +1410,7 @@ restart: 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); @@ -1206,8 +1423,7 @@ restart: } } 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; @@ -1233,7 +1449,7 @@ restart: 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; @@ -1275,7 +1491,7 @@ restart: if (value < 0) { if (value != -EOPNOTSUPP) - dev_dbg (hardware, + dev_dbg (udc_dev(dum), "setup --> %d\n", value); maybe_set_status (urb, -EPIPE); @@ -1318,20 +1534,26 @@ restart: 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); } @@ -1339,29 +1561,39 @@ return_urb: /*-------------------------------------------------------------------------*/ #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; } @@ -1372,7 +1604,8 @@ hub_descriptor (struct usb_hub_descriptor *desc) 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; @@ -1390,7 +1623,10 @@ static int dummy_hub_control ( 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: @@ -1398,25 +1634,27 @@ static int dummy_hub_control ( 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->address = 0; - dum->hdev = 0; - dum->resuming = 0; - 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) @@ -1425,23 +1663,16 @@ static int dummy_hub_control ( /* 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; @@ -1462,8 +1693,9 @@ static int dummy_hub_control ( } } } - ((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; @@ -1471,36 +1703,42 @@ static int dummy_hub_control ( 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); @@ -1508,30 +1746,46 @@ static int dummy_hub_control ( 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; } /*-------------------------------------------------------------------------*/ @@ -1563,22 +1817,21 @@ show_urb (char *buf, size_t size, struct urb *urb) } 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); @@ -1586,17 +1839,11 @@ show_urbs (struct device *dev, char *buf) } 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 @@ -1604,139 +1851,55 @@ static int dummy_start (struct usb_hcd *hcd) * 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; + INIT_LIST_HEAD (&dum->urbp_list); - /* 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; - } + /* only show a low-power port: just 8mA */ + hcd->power_budget = 8; + hcd->state = HC_STATE_RUNNING; + hcd->uses_new_polling = 1; - INIT_LIST_HEAD (&hcd->dev_list); - usb_register_bus (bus); - - bus->root_hub = 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 (&dum->hcd)) != 0) { - bus->root_hub = 0; - usb_put_dev (root); -clean2: - dum->hcd.state = USB_STATE_QUIESCING; - goto clean1; - } - - 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, @@ -1744,36 +1907,147 @@ static const struct hc_driver dummy_hcd = { .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); -