📄 dummy_hcd.c
字号:
) { 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 voiddummy_free_buffer ( struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) { if (bytes) kfree (buf);}static voidfifo_complete (struct usb_ep *ep, struct usb_request *req){}static intdummy_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t mem_flags){ struct dummy_ep *ep; struct dummy_request *req; struct dummy *dum; unsigned long flags; req = usb_request_to_dummy_request (_req); if (!_req || !list_empty (&req->queue) || !_req->complete) return -EINVAL; ep = usb_ep_to_dummy_ep (_ep); if (!_ep || (!ep->desc && _ep->name != ep0name)) return -EINVAL; dum = ep_to_dummy (ep); if (!dum->driver || !is_enabled (dum)) return -ESHUTDOWN;#if 0 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 _req->status = -EINPROGRESS; _req->actual = 0; spin_lock_irqsave (&dum->lock, flags); /* implement an emulated single-request FIFO */ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && list_empty (&dum->fifo_req.queue) && list_empty (&ep->queue) && _req->length <= FIFO_SIZE) { req = &dum->fifo_req; req->req = *_req; req->req.buf = dum->fifo_buf; memcpy (dum->fifo_buf, _req->buf, _req->length); req->req.context = dum; req->req.complete = fifo_complete; spin_unlock (&dum->lock); _req->actual = _req->length; _req->status = 0; _req->complete (_ep, _req); spin_lock (&dum->lock); } list_add_tail (&req->queue, &ep->queue); spin_unlock_irqrestore (&dum->lock, flags); /* real hardware would likely enable transfers here, in case * it'd been left NAKing. */ return 0;}static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req){ struct dummy_ep *ep; struct dummy *dum; int retval = -EINVAL; unsigned long flags; struct dummy_request *req = NULL; if (!_ep || !_req) return retval; ep = usb_ep_to_dummy_ep (_ep); dum = ep_to_dummy (ep); if (!dum->driver) return -ESHUTDOWN; spin_lock_irqsave (&dum->lock, flags); list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) { list_del_init (&req->queue); _req->status = -ECONNRESET; retval = 0; break; } } spin_unlock_irqrestore (&dum->lock, flags); if (retval == 0) { 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); } return retval;}static intdummy_set_halt (struct usb_ep *_ep, int value){ struct dummy_ep *ep; struct dummy *dum; if (!_ep) return -EINVAL; ep = usb_ep_to_dummy_ep (_ep); dum = ep_to_dummy (ep); if (!dum->driver) return -ESHUTDOWN; if (!value) ep->halted = 0; else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && !list_empty (&ep->queue)) return -EAGAIN; else ep->halted = 1; /* FIXME clear emulated data toggle too */ return 0;}static const struct usb_ep_ops dummy_ep_ops = { .enable = dummy_enable, .disable = dummy_disable, .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, .set_halt = dummy_set_halt,};/*-------------------------------------------------------------------------*//* there are both host and device side versions of this call ... */static int dummy_g_get_frame (struct usb_gadget *_gadget){ struct timeval tv; do_gettimeofday (&tv); return tv.tv_usec / 1000;}static int dummy_wakeup (struct usb_gadget *_gadget){ struct dummy *dum; dum = gadget_to_dummy (_gadget); 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; dum->re_timeout = jiffies + msecs_to_jiffies(20); mod_timer (&dummy_to_hcd (dum)->rh_timer, dum->re_timeout); return 0;}static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value){ struct dummy *dum; dum = gadget_to_dummy (_gadget); if (value) dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else dum->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); return 0;}static int dummy_pullup (struct usb_gadget *_gadget, int value){ struct dummy *dum; unsigned long flags; dum = gadget_to_dummy (_gadget); spin_lock_irqsave (&dum->lock, flags); dum->pullup = (value != 0); set_link_state (dum); spin_unlock_irqrestore (&dum->lock, flags); usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0;}static const struct usb_gadget_ops dummy_ops = { .get_frame = dummy_g_get_frame, .wakeup = dummy_wakeup, .set_selfpowered = dummy_set_selfpowered, .pullup = dummy_pullup,};/*-------------------------------------------------------------------------*//* "function" sysfs attribute */static ssize_tshow_function (struct device *dev, struct device_attribute *attr, char *buf){ struct dummy *dum = gadget_dev_to_dummy (dev); if (!dum->driver || !dum->driver->function) return 0; return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function);}static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);/*-------------------------------------------------------------------------*//* * Driver registration/unregistration. * * This is basically hardware-specific; there's usually only one real USB * device (not host) controller since that's how USB devices are intended * to work. So most implementations of these api calls will rely on the * fact that only one driver will ever bind to the hardware. But curious * hardware can be built with discrete components, so the gadget API doesn't * require that assumption. * * For this emulator, it might be convenient to create a usb slave device * for each driver that registers: just add to a big root hub. */intusb_gadget_register_driver (struct usb_gadget_driver *driver){ struct dummy *dum = the_controller; int retval, i; if (!dum) return -EINVAL; if (dum->driver) return -EBUSY; if (!driver->bind || !driver->unbind || !driver->setup || driver->speed == USB_SPEED_UNKNOWN) return -EINVAL; /* * SLAVE side init ... the layer above hardware, which * can't enumerate without help from the driver we're binding. */ dum->devstatus = 0; INIT_LIST_HEAD (&dum->gadget.ep_list); for (i = 0; i < DUMMY_ENDPOINTS; i++) { struct dummy_ep *ep = &dum->ep [i]; if (!ep_name [i]) break; ep->ep.name = ep_name [i]; ep->ep.ops = &dummy_ep_ops; list_add_tail (&ep->ep.ep_list, &dum->gadget.ep_list); ep->halted = ep->already_seen = ep->setup_stage = 0; ep->ep.maxpacket = ~0; ep->last_io = jiffies; ep->gadget = &dum->gadget; ep->desc = NULL; INIT_LIST_HEAD (&ep->queue); } dum->gadget.ep0 = &dum->ep [0].ep; dum->ep [0].ep.maxpacket = 64; list_del_init (&dum->ep [0].ep.ep_list); INIT_LIST_HEAD(&dum->fifo_req.queue); 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) { 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; set_link_state (dum); spin_unlock_irq (&dum->lock); usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0;}EXPORT_SYMBOL (usb_gadget_register_driver);intusb_gadget_unregister_driver (struct usb_gadget_driver *driver){ struct dummy *dum = the_controller; unsigned long flags; if (!dum) return -ENODEV; if (!driver || driver != dum->driver) return -EINVAL; dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n", driver->driver.name); spin_lock_irqsave (&dum->lock, flags); dum->pullup = 0; set_link_state (dum); spin_unlock_irqrestore (&dum->lock, flags); driver->unbind (&dum->gadget); 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); spin_unlock_irqrestore (&dum->lock, flags); usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0;}EXPORT_SYMBOL (usb_gadget_unregister_driver);#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;}EXPORT_SYMBOL (net2280_set_fifo_mode);/* The gadget structure is stored inside the hcd structure and will be * released along with it. */static voiddummy_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 platform_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->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 platform_set_drvdata (dev, dum); device_create_file (&dum->gadget.dev, &dev_attr_function); return rc;}static int dummy_udc_remove (struct platform_device *dev){ struct dummy *dum = platform_get_drvdata (dev); platform_set_drvdata (dev, NULL); device_remove_file (&dum->gadget.dev, &dev_attr_function); device_unregister (&dum->gadget.dev); return 0;}static int dummy_udc_suspend (struct platform_device *dev, pm_message_t state){ struct dummy *dum = platform_get_drvdata(dev); dev_dbg (&dev->dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); dum->udc_suspended = 1; set_link_state (dum); spin_unlock_irq (&dum->lock); dev->dev.power.power_state = state; usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0;}static int dummy_udc_resume (struct platform_device *dev){ struct dummy *dum = platform_get_drvdata(dev); dev_dbg (&dev->dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); dum->udc_suspended = 0; set_link_state (dum); spin_unlock_irq (&dum->lock); dev->dev.power.power_state = PMSG_ON; 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, },};/*-------------------------------------------------------------------------*//* MASTER/HOST SIDE DRIVER * * this uses the hcd framework to hook up to host side drivers. * its root hub will only have one device, otherwise it acts like * a normal host controller. * * when urbs are queued, they're just stuck on a list that we * scan in a timer callback. that callback connects writes from * the host with reads from the device, and so on, based on the * usb 2.0 rules. */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; if (!urb->transfer_buffer && urb->transfer_buffer_length) return -EINVAL; urbp = kmalloc (sizeof *urbp, mem_flags); if (!urbp) return -ENOMEM; urbp->urb = urb; dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); if (!dum->udev) { dum->udev = urb->dev; usb_get_dev (dum->udev); } else if (unlikely (dum->udev != urb->dev)) dev_err (dummy_dev(dum), "usb_device address has changed!\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -