#define DRIVER_DESC "USB Host+Gadget Emulator"
-#define DRIVER_VERSION "17 Dec 2004"
+#define DRIVER_VERSION "02 May 2005"
static const char driver_name [] = "dummy_hcd";
static const char driver_desc [] = "USB Host+Gadget Emulator";
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;
}
}
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;
struct usb_ep *_ep,
unsigned bytes,
dma_addr_t *dma,
- int mem_flags
+ gfp_t mem_flags
) {
char *retval;
struct dummy_ep *ep;
}
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;
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);
/*-------------------------------------------------------------------------*/
#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;
return 0;
}
+static int dummy_udc_suspend (struct device *dev, pm_message_t state)
+{
+ struct dummy *dum = dev_get_drvdata(dev);
+
+ dev_dbg (dev, "%s\n", __FUNCTION__);
+ spin_lock_irq (&dum->lock);
+ dum->udc_suspended = 1;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ dev->power.power_state = state;
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ return 0;
+}
+
+static int dummy_udc_resume (struct device *dev)
+{
+ struct dummy *dum = dev_get_drvdata(dev);
+
+ dev_dbg (dev, "%s\n", __FUNCTION__);
+ spin_lock_irq (&dum->lock);
+ dum->udc_suspended = 0;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+
+ dev->power.power_state = PMSG_ON;
+ usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+ return 0;
+}
+
static struct device_driver dummy_udc_driver = {
.name = (char *) gadget_name,
+ .owner = THIS_MODULE,
.bus = &platform_bus_type,
.probe = dummy_udc_probe,
.remove = dummy_udc_remove,
+ .suspend = dummy_udc_suspend,
+ .resume = dummy_udc_resume,
};
/*-------------------------------------------------------------------------*/
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;
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;
}
/* 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;
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?
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;
}
case USB_REQ_SET_ADDRESS:
if (setup.bRequestType != Dev_Request)
break;
- dum->address = setup.wValue;
+ dum->address = w_value;
maybe_set_status (urb, 0);
dev_dbg (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);
+ (1 << w_value);
maybe_set_status (urb, 0);
}
} else if (setup.bRequestType == Ep_Request) {
// endpoint halt
- ep2 = find_endpoint (dum,
- setup.wIndex);
+ ep2 = find_endpoint (dum, w_index);
if (!ep2) {
value = -EOPNOTSUPP;
break;
break;
case USB_REQ_CLEAR_FEATURE:
if (setup.bRequestType == Dev_Request) {
- switch (setup.wValue) {
+ switch (w_value) {
case USB_DEVICE_REMOTE_WAKEUP:
dum->devstatus &= ~(1 <<
USB_DEVICE_REMOTE_WAKEUP);
}
} else if (setup.bRequestType == Ep_Request) {
// endpoint halt
- ep2 = find_endpoint (dum,
- setup.wIndex);
+ ep2 = find_endpoint (dum, w_index);
if (!ep2) {
value = -EOPNOTSUPP;
break;
if (urb->transfer_buffer_length > 0) {
if (setup.bRequestType ==
Ep_InRequest) {
- ep2 = find_endpoint (dum, setup.wIndex);
+ ep2 = find_endpoint (dum, w_index);
if (!ep2) {
value = -EOPNOTSUPP;
break;
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 (hcd->state != HC_STATE_RUNNING)
+ 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 = (__force __u16)
+ (__constant_cpu_to_le16 (0x0001));
desc->bNbrPorts = 1;
desc->bitmap [0] = 0xff;
desc->bitmap [1] = 0xff;
int retval = 0;
unsigned long flags;
+ if (hcd->state != HC_STATE_RUNNING)
+ 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);
+
+ spin_lock_irq (&dum->lock);
+ dum->rh_state = DUMMY_RH_SUSPENDED;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+ return 0;
+}
+
+static int dummy_bus_resume (struct usb_hcd *hcd)
+{
+ struct dummy *dum = hcd_to_dummy (hcd);
+
+ spin_lock_irq (&dum->lock);
+ dum->rh_state = DUMMY_RH_RUNNING;
+ set_link_state (dum);
+ if (!list_empty(&dum->urbp_list))
+ mod_timer (&dum->timer, jiffies);
+ spin_unlock_irq (&dum->lock);
+ return 0;
+}
/*-------------------------------------------------------------------------*/
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);
.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)
return 0;
}
+static int dummy_hcd_suspend (struct device *dev, pm_message_t state)
+{
+ struct usb_hcd *hcd;
+
+ dev_dbg (dev, "%s\n", __FUNCTION__);
+ hcd = dev_get_drvdata (dev);
+
+ hcd->state = HC_STATE_SUSPENDED;
+ return 0;
+}
+
+static int dummy_hcd_resume (struct device *dev)
+{
+ struct usb_hcd *hcd;
+
+ dev_dbg (dev, "%s\n", __FUNCTION__);
+ hcd = dev_get_drvdata (dev);
+ hcd->state = HC_STATE_RUNNING;
+
+ usb_hcd_poll_rh_status (hcd);
+ return 0;
+}
+
static struct device_driver dummy_hcd_driver = {
.name = (char *) driver_name,
+ .owner = THIS_MODULE,
.bus = &platform_bus_type,
.probe = dummy_hcd_probe,
.remove = dummy_hcd_remove,
+ .suspend = dummy_hcd_suspend,
+ .resume = dummy_hcd_resume,
};
/*-------------------------------------------------------------------------*/