* bypassing some hardware (and driver) issues. UML could help too.
*/
-#define DEBUG
-
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
-#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#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>
+#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#define DRIVER_DESC "USB Host+Gadget Emulator"
-#define DRIVER_VERSION "17 Dec 2004"
+#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";
/* 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)
/*-------------------------------------------------------------------------*/
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;
+ unsigned udc_suspended:1;
unsigned pullup:1;
unsigned active:1;
unsigned old_active:1;
/*
* MASTER/HOST side support
*/
+ enum dummy_rh_state rh_state;
struct timer_list timer;
u32 port_status;
u32 old_status;
dum->active = 0;
if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
dum->port_status = 0;
- else if (!dum->pullup) {
+
+ /* 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 |
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)
+ else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+ dum->rh_state != DUMMY_RH_SUSPENDED)
dum->active = 1;
}
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:
}
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;
return NULL;
ep = usb_ep_to_dummy_ep (_ep);
- req = kmalloc (sizeof *req, mem_flags);
+ req = kzalloc(sizeof(*req), mem_flags);
if (!req)
return NULL;
- memset (req, 0, sizeof *req);
INIT_LIST_HEAD (&req->queue);
return &req->req;
}
kfree (req);
}
-static void *
-dummy_alloc_buffer (
- struct usb_ep *_ep,
- unsigned bytes,
- dma_addr_t *dma,
- int mem_flags
-) {
- char *retval;
- struct dummy_ep *ep;
- struct dummy *dum;
-
- 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
-dummy_free_buffer (
- struct usb_ep *_ep,
- void *buf,
- dma_addr_t dma,
- unsigned bytes
-) {
- if (bytes)
- kfree (buf);
-}
-
static void
fifo_complete (struct usb_ep *ep, struct usb_request *req)
{
}
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;
if (!dum->driver)
return -ESHUTDOWN;
- spin_lock_irqsave (&dum->lock, flags);
+ 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);
break;
}
}
- spin_unlock_irqrestore (&dum->lock, flags);
+ spin_unlock (&dum->lock);
if (retval == 0) {
dev_dbg (udc_dev(dum),
req, _ep->name, _req->length, _req->buf);
_req->complete (_ep, _req);
}
+ local_irq_restore (flags);
return retval;
}
.alloc_request = dummy_alloc_request,
.free_request = dummy_free_request,
- .alloc_buffer = dummy_alloc_buffer,
- .free_buffer = dummy_free_buffer,
- /* map, unmap, ... eventually hook the "generic" dma calls */
-
.queue = dummy_queue,
.dequeue = dummy_dequeue,
struct dummy *dum;
dum = gadget_to_dummy (_gadget);
- if (!(dum->port_status & USB_PORT_STAT_SUSPEND)
- || !(dum->devstatus &
- ( (1 << USB_DEVICE_B_HNP_ENABLE)
+ 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;
return 0;
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);
/*-------------------------------------------------------------------------*/
return -EINVAL;
if (dum->driver)
return -EBUSY;
- if (!driver->bind || !driver->unbind || !driver->setup
+ if (!driver->bind || !driver->setup
|| driver->speed == USB_SPEED_UNKNOWN)
return -EINVAL;
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) {
+ retval = driver->bind(&dum->gadget);
+ if (retval) {
dum->driver = NULL;
dum->gadget.dev.driver = NULL;
return retval;
}
- 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 */
spin_lock_irq (&dum->lock);
dum->pullup = 1;
if (!dum)
return -ENODEV;
- if (!driver || driver != dum->driver)
+ if (!driver || driver != dum->driver || !driver->unbind)
return -EINVAL;
dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n",
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);
#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;
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
+ usb_put_hcd (dummy_to_hcd (dum));
}
-static int dummy_udc_probe (struct device *dev)
+static int dummy_udc_probe (struct platform_device *pdev)
{
struct dummy *dum = the_controller;
int rc;
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.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
+ usb_get_hcd (dummy_to_hcd (dum));
- dev_set_drvdata (dev, dum);
- device_create_file (&dum->gadget.dev, &dev_attr_function);
+ platform_set_drvdata (pdev, dum);
+ rc = device_create_file (&dum->gadget.dev, &dev_attr_function);
+ if (rc < 0)
+ device_unregister (&dum->gadget.dev);
return rc;
}
-static int dummy_udc_remove (struct device *dev)
+static int dummy_udc_remove (struct platform_device *pdev)
{
- struct dummy *dum = dev_get_drvdata (dev);
+ struct dummy *dum = platform_get_drvdata (pdev);
- dev_set_drvdata (dev, NULL);
+ platform_set_drvdata (pdev, 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,
+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", __func__);
+ spin_lock_irq (&dum->lock);
+ dum->udc_suspended = 1;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ 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", __func__);
+ spin_lock_irq (&dum->lock);
+ dum->udc_suspended = 0;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ 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,
+ },
};
/*-------------------------------------------------------------------------*/
static int dummy_urb_enqueue (
struct usb_hcd *hcd,
- struct usb_host_endpoint *ep,
struct urb *urb,
- int mem_flags
+ 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)
{
- /* giveback happens automatically in timer callback */
- return 0;
-}
+ struct dummy *dum;
+ unsigned long flags;
+ int rc;
-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);
+ /* giveback happens automatically in timer callback,
+ * so make sure the callback happens */
+ dum = hcd_to_dummy (hcd);
+ spin_lock_irqsave (&dum->lock, flags);
+
+ 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 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 */
/* high bandwidth mode */
tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
- tmp = le16_to_cpu (tmp);
tmp = (tmp >> 11) & 0x03;
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
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)
+ continue;
type = usb_pipetype (urb->pipe);
/* used up this frame's non-periodic bandwidth?
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 */
struct usb_ctrlrequest setup;
int value = 1;
struct dummy_ep *ep2;
+ unsigned w_index;
+ unsigned w_value;
setup = *(struct usb_ctrlrequest*) urb->setup_packet;
- if (setup.wLength != urb->transfer_buffer_length) {
- maybe_set_status (urb, -EOVERFLOW);
+ w_index = le16_to_cpu(setup.wIndex);
+ w_value = le16_to_cpu(setup.wValue);
+ if (le16_to_cpu(setup.wLength) !=
+ urb->transfer_buffer_length) {
+ status = -EOVERFLOW;
goto return_urb;
}
case USB_REQ_SET_ADDRESS:
if (setup.bRequestType != Dev_Request)
break;
- dum->address = setup.wValue;
- maybe_set_status (urb, 0);
+ dum->address = w_value;
+ status = 0;
dev_dbg (udc_dev(dum), "set_address = %d\n",
- setup.wValue);
+ 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:
}
if (value == 0) {
dum->devstatus |=
- (1 << setup.wValue);
- maybe_set_status (urb, 0);
+ (1 << w_value);
+ status = 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;
}
ep2->halted = 1;
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
}
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);
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
break;
default:
value = -EOPNOTSUPP;
}
} 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;
}
ep2->halted = 0;
value = 0;
- maybe_set_status (urb, 0);
+ status = 0;
}
break;
case USB_REQ_GET_STATUS:
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;
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, NULL);
+ usb_hcd_giveback_urb(dummy_to_hcd(dum), urb, status);
spin_lock (&dum->lock);
goto restart;
}
- /* want a 1 msec delay here */
- if (!list_empty (&dum->urbp_list))
- mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
- else {
+ 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);
{
struct dummy *dum;
unsigned long flags;
- int retval;
+ int retval = 0;
dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
+ 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);
set_link_state (dum);
}
- if (!(dum->port_status & PORT_C_MASK))
- retval = 0;
- else {
+ if ((dum->port_status & PORT_C_MASK) != 0) {
*buf = (1 << 1);
dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
- dum->port_status);
+ 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 = cpu_to_le16(0x0001);
desc->bNbrPorts = 1;
desc->bitmap [0] = 0xff;
desc->bitmap [1] = 0xff;
int retval = 0;
unsigned long flags;
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ return -ETIMEDOUT;
+
dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
switch (typeReq) {
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)
}
}
set_link_state (dum);
- ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
- ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
+ ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
+ ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
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 + msecs_to_jiffies(50);
/* FALLS THROUGH */
return retval;
}
+static int dummy_bus_suspend (struct usb_hcd *hcd)
+{
+ struct dummy *dum = hcd_to_dummy (hcd);
+
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
+
+ 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 int dummy_bus_resume (struct usb_hcd *hcd)
+{
+ struct dummy *dum = hcd_to_dummy (hcd);
+ int rc = 0;
+
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
+
+ spin_lock_irq (&dum->lock);
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ rc = -ESHUTDOWN;
+ } 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;
+}
/*-------------------------------------------------------------------------*/
init_timer (&dum->timer);
dum->timer.function = dummy_timer;
dum->timer.data = (unsigned long) dum;
+ dum->rh_state = DUMMY_RH_RUNNING;
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;
#endif
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
- device_create_file (dummy_dev(dum), &dev_attr_urbs);
- return 0;
+ return device_create_file (dummy_dev(dum), &dev_attr_urbs);
}
static void dummy_stop (struct usb_hcd *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 device *dev)
+static int dummy_hcd_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
int retval;
- dev_info (dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
+ dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
- hcd = usb_create_hcd (&dummy_hcd, dev, dev->bus_id);
+ hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id);
if (!hcd)
return -ENOMEM;
the_controller = hcd_to_dummy (hcd);
return retval;
}
-static int dummy_hcd_remove (struct device *dev)
+static int dummy_hcd_remove (struct platform_device *pdev)
{
struct usb_hcd *hcd;
- hcd = dev_get_drvdata (dev);
+ hcd = platform_get_drvdata (pdev);
usb_remove_hcd (hcd);
usb_put_hcd (hcd);
the_controller = NULL;
return 0;
}
-static struct device_driver dummy_hcd_driver = {
- .name = (char *) driver_name,
- .bus = &platform_bus_type,
- .probe = dummy_hcd_probe,
- .remove = dummy_hcd_remove,
-};
+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", __func__);
-/* These don't need to do anything because the pdev structures are
- * statically allocated. */
-static void
-dummy_udc_release (struct device *dev) {}
+ 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 void
-dummy_hcd_release (struct device *dev) {}
+static int dummy_hcd_resume (struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
-static struct platform_device the_udc_pdev = {
- .name = (char *) gadget_name,
- .id = -1,
- .dev = {
- .release = dummy_udc_release,
- },
-};
+ dev_dbg (&pdev->dev, "%s\n", __func__);
+
+ hcd = platform_get_drvdata (pdev);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ usb_hcd_poll_rh_status (hcd);
+ return 0;
+}
-static struct platform_device the_hcd_pdev = {
- .name = (char *) driver_name,
- .id = -1,
- .dev = {
- .release = dummy_hcd_release,
+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,
},
};
+/*-------------------------------------------------------------------------*/
+
+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 = 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 = 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:
- 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:
- 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);
- driver_unregister (&dummy_udc_driver);
- 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);