Commit d9b76251 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB dummy_hcd: Use separate pdevs for HC and UDC

This patch makes the dummy_hcd driver create separate platform devices for
the emulated host controller and emulated device controller.  This gives a
more accurate simulation and will permit testing of situations where only
one of the two devices is suspended.

This also changes the name of the host controller platform device to match
the name of the driver.  That way the normal platform bus probe mechanism
will handle binding the driver to the device.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c2db8b5e
......@@ -141,6 +141,8 @@ static const char *const ep_name [] = {
};
#define DUMMY_ENDPOINTS (sizeof(ep_name)/sizeof(char *))
/*-------------------------------------------------------------------------*/
#define FIFO_SIZE 64
struct urbp {
......@@ -189,6 +191,11 @@ 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);
......@@ -208,19 +215,6 @@ static struct dummy *the_controller;
/*-------------------------------------------------------------------------*/
/*
* 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.
*/
static struct platform_device the_pdev;
static struct device_driver dummy_driver = {
.name = (char *) driver_name,
.bus = &platform_bus_type,
};
/*-------------------------------------------------------------------------*/
/* SLAVE/GADGET SIDE DRIVER
*
* This only tracks gadget state. All the work is done when the host
......@@ -324,7 +318,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
_ep->maxpacket = max;
ep->desc = desc;
dev_dbg (dummy_dev(dum), "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",
......@@ -379,7 +373,7 @@ static int dummy_disable (struct usb_ep *_ep)
nuke (dum, ep);
spin_unlock_irqrestore (&dum->lock, flags);
dev_dbg (dummy_dev(dum), "disabled %s\n", _ep->name);
dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name);
return retval;
}
......@@ -474,7 +468,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
return -ESHUTDOWN;
#if 0
dev_dbg (dummy_dev(dum), "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
......@@ -537,7 +531,7 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req)
spin_unlock_irqrestore (&dum->lock, flags);
if (retval == 0) {
dev_dbg (dummy_dev(dum),
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);
......@@ -661,38 +655,6 @@ DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
* for each driver that registers: just add to a big root hub.
*/
/* This doesn't need to do anything because the udc device structure is
* stored inside the hcd and will be deallocated along with it. */
static void
dummy_udc_release (struct device *dev) {}
/* This doesn't need to do anything because the pdev structure is
* statically allocated. */
static void
dummy_pdev_release (struct device *dev) {}
static int
dummy_register_udc (struct dummy *dum)
{
int rc;
strcpy (dum->gadget.dev.bus_id, "udc");
dum->gadget.dev.parent = dummy_dev(dum);
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);
device_unregister (&dum->gadget.dev);
}
int
usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
......@@ -711,12 +673,6 @@ 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;
/* maybe claim OTG support, though we won't complete HNP */
dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
dum->devstatus = 0;
dum->resuming = 0;
......@@ -745,7 +701,7 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
dum->driver = driver;
dum->gadget.dev.driver = &driver->driver;
dev_dbg (dummy_dev(dum), "binding gadget driver '%s'\n",
dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
if ((retval = driver->bind (&dum->gadget)) != 0) {
dum->driver = NULL;
......@@ -798,7 +754,7 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
if (!driver || driver != dum->driver)
return -EINVAL;
dev_dbg (dummy_dev(dum), "unregister gadget driver '%s'\n",
dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
driver->driver.name);
spin_lock_irqsave (&dum->lock, flags);
......@@ -826,6 +782,64 @@ int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
}
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 device *dev)
{
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 = 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
dev_set_drvdata (dev, dum);
device_create_file (&dum->gadget.dev, &dev_attr_function);
return rc;
}
static int dummy_udc_remove (struct device *dev)
{
struct dummy *dum = dev_get_drvdata (dev);
dev_set_drvdata (dev, NULL);
device_remove_file (&dum->gadget.dev, &dev_attr_function);
device_unregister (&dum->gadget.dev);
return 0;
}
static struct device_driver dummy_udc_driver = {
.name = (char *) gadget_name,
.bus = &platform_bus_type,
.probe = dummy_udc_probe,
.remove = dummy_udc_remove,
};
/*-------------------------------------------------------------------------*/
/* MASTER/HOST SIDE DRIVER
......@@ -1184,7 +1198,7 @@ restart:
list_for_each_entry (req, &ep->queue, queue) {
list_del_init (&req->queue);
req->req.status = -EOVERFLOW;
dev_dbg (dummy_dev(dum), "stale req = %p\n",
dev_dbg (udc_dev(dum), "stale req = %p\n",
req);
spin_unlock (&dum->lock);
......@@ -1207,7 +1221,7 @@ restart:
break;
dum->address = setup.wValue;
maybe_set_status (urb, 0);
dev_dbg (dummy_dev(dum), "set_address = %d\n",
dev_dbg (udc_dev(dum), "set_address = %d\n",
setup.wValue);
value = 0;
break;
......@@ -1333,7 +1347,7 @@ restart:
if (value < 0) {
if (value != -EOPNOTSUPP)
dev_dbg (dummy_dev(dum),
dev_dbg (udc_dev(dum),
"setup --> %d\n",
value);
maybe_set_status (urb, -EPIPE);
......@@ -1561,7 +1575,7 @@ static int dummy_hub_control (
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
if (dum->driver) {
dev_dbg (dummy_dev(dum),
dev_dbg (udc_dev(dum),
"disconnect\n");
stop_activity (dum, dum->driver);
}
......@@ -1643,7 +1657,6 @@ static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
static int dummy_start (struct usb_hcd *hcd)
{
struct dummy *dum;
int retval;
dum = hcd_to_dummy (hcd);
......@@ -1659,9 +1672,6 @@ static int dummy_start (struct usb_hcd *hcd)
INIT_LIST_HEAD (&dum->urbp_list);
if ((retval = dummy_register_udc (dum)) != 0)
return retval;
/* only show a low-power port: just 8mA */
hcd->power_budget = 8;
hcd->state = HC_STATE_RUNNING;
......@@ -1682,10 +1692,7 @@ static void dummy_stop (struct usb_hcd *hcd)
dum = hcd_to_dummy (hcd);
device_remove_file (dummy_dev(dum), &dev_attr_urbs);
usb_gadget_unregister_driver (dum->driver);
dummy_unregister_udc (dum);
dev_info (dummy_dev(dum), "stopped\n");
}
......@@ -1715,7 +1722,7 @@ static const struct hc_driver dummy_hcd = {
.hub_control = dummy_hub_control,
};
static int dummy_probe (struct device *dev)
static int dummy_hcd_probe (struct device *dev)
{
struct usb_hcd *hcd;
int retval;
......@@ -1735,7 +1742,7 @@ static int dummy_probe (struct device *dev)
return retval;
}
static void dummy_remove (struct device *dev)
static int dummy_hcd_remove (struct device *dev)
{
struct usb_hcd *hcd;
......@@ -1743,35 +1750,41 @@ static void dummy_remove (struct device *dev)
usb_remove_hcd (hcd);
usb_put_hcd (hcd);
the_controller = NULL;
return 0;
}
/*-------------------------------------------------------------------------*/
static int dummy_pdev_detect (void)
{
int retval;
retval = driver_register (&dummy_driver);
if (retval < 0)
return retval;
static struct device_driver dummy_hcd_driver = {
.name = (char *) driver_name,
.bus = &platform_bus_type,
.probe = dummy_hcd_probe,
.remove = dummy_hcd_remove,
};
the_pdev.name = "hc";
the_pdev.dev.driver = &dummy_driver;
the_pdev.dev.release = dummy_pdev_release;
/*-------------------------------------------------------------------------*/
retval = platform_device_register (&the_pdev);
if (retval < 0)
driver_unregister (&dummy_driver);
return retval;
}
/* 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_pdev_remove (void)
{
platform_device_unregister (&the_pdev);
driver_unregister (&dummy_driver);
}
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)
{
......@@ -1779,17 +1792,39 @@ static int __init init (void)
if (usb_disabled ())
return -ENODEV;
if ((retval = dummy_pdev_detect ()) != 0)
retval = driver_register (&dummy_hcd_driver);
if (retval < 0)
return retval;
if ((retval = dummy_probe (&the_pdev.dev)) != 0)
dummy_pdev_remove ();
retval = 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:
driver_unregister (&dummy_udc_driver);
err_register_udc_driver:
driver_unregister (&dummy_hcd_driver);
return retval;
}
module_init (init);
static void __exit cleanup (void)
{
dummy_remove (&the_pdev.dev);
dummy_pdev_remove ();
platform_device_unregister (&the_udc_pdev);
platform_device_unregister (&the_hcd_pdev);
driver_unregister (&dummy_udc_driver);
driver_unregister (&dummy_hcd_driver);
}
module_exit (cleanup);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment