📄 dummy_hcd.c
字号:
if (ep)
ep->already_seen = ep->setup_stage = 0;
spin_unlock (&dum->lock);
usb_hcd_giveback_urb (dummy_to_hcd(dum), urb, NULL);
spin_lock (&dum->lock);
goto restart;
}
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);
}
/*-------------------------------------------------------------------------*/
#define PORT_C_MASK \
((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 = 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);
dum->port_status &= ~USB_PORT_STAT_SUSPEND;
set_link_state (dum);
}
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);
retval = 1;
if (dum->rh_state == DUMMY_RH_SUSPENDED)
usb_hcd_resume_root_hub (hcd);
}
done:
spin_unlock_irqrestore (&dum->lock, flags);
return retval;
}
static inline void
hub_descriptor (struct usb_hub_descriptor *desc)
{
memset (desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
desc->wHubCharacteristics = (__force __u16)
(__constant_cpu_to_le16 (0x0001));
desc->bNbrPorts = 1;
desc->bitmap [0] = 0xff;
desc->bitmap [1] = 0xff;
}
static int dummy_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct dummy *dum;
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) {
case ClearHubFeature:
break;
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
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:
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:
*(__le32 *) buf = __constant_cpu_to_le32 (0);
break;
case GetPortStatus:
if (wIndex != 1)
retval = -EPIPE;
/* whoever resets or resumes must GetPortStatus to
* complete it!!
*/
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 & 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;
dum->gadget.ep0->maxpacket = 64;
switch (dum->gadget.speed) {
case USB_SPEED_HIGH:
dum->port_status |=
USB_PORT_STAT_HIGH_SPEED;
break;
case USB_SPEED_LOW:
dum->gadget.ep0->maxpacket = 8;
dum->port_status |=
USB_PORT_STAT_LOW_SPEED;
break;
default:
dum->gadget.speed = USB_SPEED_FULL;
break;
}
}
}
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;
break;
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
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 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 + msecs_to_jiffies(50);
/* FALLS THROUGH */
default:
if ((dum->port_status & USB_PORT_STAT_POWER) != 0) {
dum->port_status |= (1 << wValue);
set_link_state (dum);
}
}
break;
default:
dev_dbg (dummy_dev(dum),
"hub control req%04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
/* "protocol stall" on error */
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 int dummy_hub_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_hub_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;
}
/*-------------------------------------------------------------------------*/
static inline ssize_t
show_urb (char *buf, size_t size, struct urb *urb)
{
int ep = usb_pipeendpoint (urb->pipe);
return snprintf (buf, size,
"urb/%p %s ep%d%s%s len %d/%d\n",
urb,
({ char *s;
switch (urb->dev->speed) {
case USB_SPEED_LOW: s = "ls"; break;
case USB_SPEED_FULL: s = "fs"; break;
case USB_SPEED_HIGH: s = "hs"; break;
default: s = "?"; break;
}; s; }),
ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "",
({ char *s; \
switch (usb_pipetype (urb->pipe)) { \
case PIPE_CONTROL: s = ""; break; \
case PIPE_BULK: s = "-bulk"; break; \
case PIPE_INTERRUPT: s = "-int"; break; \
default: s = "-iso"; break; \
}; s;}),
urb->actual_length, urb->transfer_buffer_length);
}
static ssize_t
show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
{
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);
list_for_each_entry (urbp, &dum->urbp_list, urbp_list) {
size_t temp;
temp = show_urb (buf, PAGE_SIZE - size, urbp->urb);
buf += temp;
size += temp;
}
spin_unlock_irqrestore (&dum->lock, flags);
return size;
}
static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
static int dummy_start (struct usb_hcd *hcd)
{
struct dummy *dum;
dum = hcd_to_dummy (hcd);
/*
* MASTER side init ... we emulate a root hub that'll only ever
* talk to one device (the slave side). Also appears in sysfs,
* just like more familiar pci-based HCDs.
*/
spin_lock_init (&dum->lock);
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->state = HC_STATE_RUNNING;
hcd->uses_new_polling = 1;
#ifdef CONFIG_USB_OTG
hcd->self.otg_port = 1;
#endif
/* 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;
dum = hcd_to_dummy (hcd);
device_remove_file (dummy_dev(dum), &dev_attr_urbs);
usb_gadget_unregister_driver (dum->driver);
dev_info (dummy_dev(dum), "stopped\n");
}
/*-------------------------------------------------------------------------*/
static int dummy_h_get_frame (struct usb_hcd *hcd)
{
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,
.urb_enqueue = dummy_urb_enqueue,
.urb_dequeue = dummy_urb_dequeue,
.get_frame_number = dummy_h_get_frame,
.hub_status_data = dummy_hub_status,
.hub_control = dummy_hub_control,
.hub_suspend = dummy_hub_suspend,
.hub_resume = dummy_hub_resume,
};
static int dummy_hcd_probe (struct device *dev)
{
struct usb_hcd *hcd;
int retval;
dev_info (dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
hcd = usb_create_hcd (&dummy_hcd, dev, 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 device *dev)
{
struct usb_hcd *hcd;
hcd = dev_get_drvdata (dev);
usb_remove_hcd (hcd);
usb_put_hcd (hcd);
the_controller = NULL;
return 0;
}
static int dummy_hcd_suspend (struct device *dev, pm_message_t state,
u32 level)
{
struct usb_hcd *hcd;
if (level != SUSPEND_DISABLE)
return 0;
dev_dbg (dev, "%s\n", __FUNCTION__);
hcd = dev_get_drvdata (dev);
#ifndef CONFIG_USB_SUSPEND
/* Otherwise this would never happen */
usb_lock_device (hcd->self.root_hub);
dummy_hub_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
hcd->state = HC_STATE_SUSPENDED;
return 0;
}
static int dummy_hcd_resume (struct device *dev, u32 level)
{
struct usb_hcd *hcd;
if (level != RESUME_ENABLE)
return 0;
dev_dbg (dev, "%s\n", __FUNCTION__);
hcd = dev_get_drvdata (dev);
hcd->state = HC_STATE_RUNNING;
#ifndef CONFIG_USB_SUSPEND
/* Otherwise this would never happen */
usb_lock_device (hcd->self.root_hub);
dummy_hub_resume (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
usb_hcd_poll_rh_status (hcd);
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,
.suspend = dummy_hcd_suspend,
.resume = dummy_hcd_resume,
};
/*-------------------------------------------------------------------------*/
/* 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)
{
int retval;
if (usb_disabled ())
return -ENODEV;
retval = driver_register (&dummy_hcd_driver);
if (retval < 0)
return retval;
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)
{
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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -