#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
-#include <linux/usb_gadget.h>
+#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#define DRIVER_DESC "USB Host+Gadget Emulator"
#define DRIVER_VERSION "02 May 2005"
+#define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */
+
static const char driver_name [] = "dummy_hcd";
static const char driver_desc [] = "USB Host+Gadget Emulator";
case USB_SPEED_HIGH:
if (max == 512)
break;
- /* conserve return statements */
- default:
- switch (max) {
- case 8: case 16: case 32: case 64:
+ goto done;
+ case USB_SPEED_FULL:
+ if (max == 8 || max == 16 || max == 32 || max == 64)
/* we'll fake any legal size */
break;
- default:
- case USB_SPEED_LOW:
- goto done;
- }
+ /* save a return statement */
+ default:
+ goto done;
}
break;
case USB_ENDPOINT_XFER_INT:
list_del_init (&dum->ep [0].ep.ep_list);
INIT_LIST_HEAD(&dum->fifo_req.queue);
+ driver->driver.bus = NULL;
dum->driver = driver;
dum->gadget.dev.driver = &driver->driver;
dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
- if ((retval = driver->bind (&dum->gadget)) != 0)
- goto err_bind_gadget;
-
- driver->driver.bus = dum->gadget.dev.parent->bus;
- if ((retval = driver_register (&driver->driver)) != 0)
- goto err_register;
- if ((retval = device_bind_driver (&dum->gadget.dev)) != 0)
- goto err_bind_driver;
+ retval = driver->bind(&dum->gadget);
+ if (retval) {
+ dum->driver = NULL;
+ dum->gadget.dev.driver = NULL;
+ return retval;
+ }
/* khubd will enumerate this in a while */
spin_lock_irq (&dum->lock);
usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
-
-err_bind_driver:
- driver_unregister (&driver->driver);
-err_register:
- if (driver->unbind)
- driver->unbind (&dum->gadget);
- spin_lock_irq (&dum->lock);
- dum->pullup = 0;
- set_link_state (dum);
- spin_unlock_irq (&dum->lock);
-err_bind_gadget:
- dum->driver = NULL;
- dum->gadget.dev.driver = NULL;
- return retval;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
spin_unlock_irqrestore (&dum->lock, flags);
driver->unbind (&dum->gadget);
+ dum->gadget.dev.driver = NULL;
dum->driver = NULL;
- device_release_driver (&dum->gadget.dev);
- driver_unregister (&driver->driver);
-
spin_lock_irqsave (&dum->lock, flags);
dum->pullup = 0;
set_link_state (dum);
{
struct dummy *dum = platform_get_drvdata(pdev);
- dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&pdev->dev, "%s\n", __func__);
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;
}
{
struct dummy *dum = platform_get_drvdata(pdev);
- dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&pdev->dev, "%s\n", __func__);
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 int dummy_urb_enqueue (
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;
+ int rc;
if (!urb->transfer_buffer && urb->transfer_buffer_length)
return -EINVAL;
dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
+ rc = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (rc) {
+ kfree(urbp);
+ goto done;
+ }
if (!dum->udev) {
dum->udev = urb->dev;
if (!timer_pending (&dum->timer))
mod_timer (&dum->timer, jiffies + 1);
- spin_unlock_irqrestore (&dum->lock, flags);
- return 0;
+ done:
+ spin_unlock_irqrestore(&dum->lock, flags);
+ return rc;
}
-static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
struct dummy *dum;
unsigned long flags;
+ int rc;
/* 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))
+
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (!rc && dum->rh_state != DUMMY_RH_RUNNING &&
+ !list_empty(&dum->urbp_list))
mod_timer (&dum->timer, jiffies);
- spin_unlock_irqrestore (&dum->lock, flags);
- return 0;
-}
-static void maybe_set_status (struct urb *urb, int status)
-{
- spin_lock (&urb->lock);
- if (urb->status == -EINPROGRESS)
- urb->status = status;
- spin_unlock (&urb->lock);
+ spin_unlock_irqrestore (&dum->lock, flags);
+ return rc;
}
/* transfer up to a frame's worth; caller must own lock */
static int
-transfer (struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit)
+transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit,
+ int *status)
{
struct dummy_request *req;
*
* partially filling a buffer optionally blocks queue advances
* (so completion handlers can clean up the queue) but we don't
- * need to emulate such data-in-flight. so we only show part
- * of the URB_SHORT_NOT_OK effect: completion status.
+ * need to emulate such data-in-flight.
*/
if (is_short) {
if (host_len == dev_len) {
req->req.status = 0;
- maybe_set_status (urb, 0);
+ *status = 0;
} else if (to_host) {
req->req.status = 0;
if (dev_len > host_len)
- maybe_set_status (urb, -EOVERFLOW);
+ *status = -EOVERFLOW;
else
- maybe_set_status (urb,
- (urb->transfer_flags
- & URB_SHORT_NOT_OK)
- ? -EREMOTEIO : 0);
+ *status = 0;
} else if (!to_host) {
- maybe_set_status (urb, 0);
+ *status = 0;
if (host_len > dev_len)
req->req.status = -EOVERFLOW;
else
req->req.status = 0;
if (urb->transfer_buffer_length == urb->actual_length
&& !(urb->transfer_flags
- & URB_ZERO_PACKET)) {
- maybe_set_status (urb, 0);
- }
+ & URB_ZERO_PACKET))
+ *status = 0;
}
/* device side completion --> continuable */
}
/* host side completion --> terminate */
- if (urb->status != -EINPROGRESS)
+ if (*status != -EINPROGRESS)
break;
/* rescan to continue with any other queued i/o */
u8 address;
struct dummy_ep *ep = NULL;
int type;
+ int status = -EINPROGRESS;
urb = urbp->urb;
- if (urb->status != -EINPROGRESS) {
- /* likely it was just unlinked */
+ if (urb->unlinked)
goto return_urb;
- } else if (dum->rh_state != DUMMY_RH_RUNNING)
+ else if (dum->rh_state != DUMMY_RH_RUNNING)
continue;
type = usb_pipetype (urb->pipe);
dev_dbg (dummy_dev(dum),
"no ep configured for urb %p\n",
urb);
- maybe_set_status (urb, -EPROTO);
+ status = -EPROTO;
goto return_urb;
}
/* NOTE: must not be iso! */
dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n",
ep->ep.name, urb);
- maybe_set_status (urb, -EPIPE);
+ status = -EPIPE;
goto return_urb;
}
/* FIXME make sure both ends agree on maxpacket */
w_value = le16_to_cpu(setup.wValue);
if (le16_to_cpu(setup.wLength) !=
urb->transfer_buffer_length) {
- maybe_set_status (urb, -EOVERFLOW);
+ status = -EOVERFLOW;
goto return_urb;
}
if (setup.bRequestType != Dev_Request)
break;
dum->address = w_value;
- maybe_set_status (urb, 0);
+ status = 0;
dev_dbg (udc_dev(dum), "set_address = %d\n",
w_value);
value = 0;
if (value == 0) {
dum->devstatus |=
(1 << w_value);
- maybe_set_status (urb, 0);
+ status = 0;
}
} else if (setup.bRequestType == Ep_Request) {
}
ep2->halted = 1;
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
}
break;
case USB_REQ_CLEAR_FEATURE:
dum->devstatus &= ~(1 <<
USB_DEVICE_REMOTE_WAKEUP);
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
break;
default:
value = -EOPNOTSUPP;
}
ep2->halted = 0;
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
}
break;
case USB_REQ_GET_STATUS:
urb->actual_length = min (2,
urb->transfer_buffer_length);
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
}
break;
}
dev_dbg (udc_dev(dum),
"setup --> %d\n",
value);
- maybe_set_status (urb, -EPIPE);
+ status = -EPIPE;
urb->actual_length = 0;
}
* report random errors, to debug drivers.
*/
limit = max (limit, periodic_bytes (dum, ep));
- maybe_set_status (urb, -ENOSYS);
+ status = -ENOSYS;
break;
case PIPE_INTERRUPT:
default:
treat_control_like_bulk:
ep->last_io = jiffies;
- total = transfer (dum, urb, ep, limit);
+ total = transfer(dum, urb, ep, limit, &status);
break;
}
/* incomplete transfer? */
- if (urb->status == -EINPROGRESS)
+ if (status == -EINPROGRESS)
continue;
return_urb:
- urb->hcpriv = NULL;
list_del (&urbp->urbp_list);
kfree (urbp);
if (ep)
ep->already_seen = ep->setup_stage = 0;
+ usb_hcd_unlink_urb_from_ep(dummy_to_hcd(dum), urb);
spin_unlock (&dum->lock);
- usb_hcd_giveback_urb (dummy_to_hcd(dum), urb);
+ usb_hcd_giveback_urb(dummy_to_hcd(dum), urb, status);
spin_lock (&dum->lock);
goto restart;
memset (desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
- desc->wHubCharacteristics = (__force __u16)
- (__constant_cpu_to_le16 (0x0001));
+ desc->wHubCharacteristics = cpu_to_le16(0x0001);
desc->bNbrPorts = 1;
desc->bitmap [0] = 0xff;
desc->bitmap [1] = 0xff;
{
struct dummy *dum = hcd_to_dummy (hcd);
- dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
spin_lock_irq (&dum->lock);
dum->rh_state = DUMMY_RH_SUSPENDED;
struct dummy *dum = hcd_to_dummy (hcd);
int rc = 0;
- dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
spin_lock_irq (&dum->lock);
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
INIT_LIST_HEAD (&dum->urbp_list);
- /* only show a low-power port: just 8mA */
- hcd->power_budget = 8;
+ hcd->power_budget = POWER_BUDGET;
hcd->state = HC_STATE_RUNNING;
hcd->uses_new_polling = 1;
struct dummy *dum;
int rc = 0;
- dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&pdev->dev, "%s\n", __func__);
hcd = platform_get_drvdata (pdev);
dum = hcd_to_dummy (hcd);
{
struct usb_hcd *hcd;
- dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&pdev->dev, "%s\n", __func__);
hcd = platform_get_drvdata (pdev);
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/*-------------------------------------------------------------------------*/
-/* 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 struct platform_device *the_udc_pdev;
+static struct platform_device *the_hcd_pdev;
static int __init init (void)
{
- int retval;
+ int retval = -ENOMEM;
if (usb_disabled ())
return -ENODEV;
- retval = platform_driver_register (&dummy_hcd_driver);
- if (retval < 0)
+ the_hcd_pdev = platform_device_alloc(driver_name, -1);
+ if (!the_hcd_pdev)
return retval;
+ the_udc_pdev = platform_device_alloc(gadget_name, -1);
+ if (!the_udc_pdev)
+ goto err_alloc_udc;
- retval = platform_driver_register (&dummy_udc_driver);
+ retval = platform_driver_register(&dummy_hcd_driver);
+ if (retval < 0)
+ goto err_register_hcd_driver;
+ retval = platform_driver_register(&dummy_udc_driver);
if (retval < 0)
goto err_register_udc_driver;
- retval = platform_device_register (&the_hcd_pdev);
+ retval = platform_device_add(the_hcd_pdev);
if (retval < 0)
- goto err_register_hcd;
-
- retval = platform_device_register (&the_udc_pdev);
+ goto err_add_hcd;
+ retval = platform_device_add(the_udc_pdev);
if (retval < 0)
- goto err_register_udc;
+ goto err_add_udc;
return retval;
-err_register_udc:
- platform_device_unregister (&the_hcd_pdev);
-err_register_hcd:
- platform_driver_unregister (&dummy_udc_driver);
+err_add_udc:
+ platform_device_del(the_hcd_pdev);
+err_add_hcd:
+ platform_driver_unregister(&dummy_udc_driver);
err_register_udc_driver:
- platform_driver_unregister (&dummy_hcd_driver);
+ platform_driver_unregister(&dummy_hcd_driver);
+err_register_hcd_driver:
+ platform_device_put(the_udc_pdev);
+err_alloc_udc:
+ platform_device_put(the_hcd_pdev);
return retval;
}
module_init (init);
static void __exit cleanup (void)
{
- platform_device_unregister (&the_udc_pdev);
- platform_device_unregister (&the_hcd_pdev);
- platform_driver_unregister (&dummy_udc_driver);
- platform_driver_unregister (&dummy_hcd_driver);
+ 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);