📄 dummy_hcd.c
字号:
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 const struct usb_gadget_ops dummy_ops = { .get_frame = dummy_g_get_frame, .wakeup = dummy_wakeup, .set_selfpowered = dummy_set_selfpowered,};/*-------------------------------------------------------------------------*//* "function" sysfs attribute */static ssize_tshow_function (struct device *dev, 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);}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. */static voiddummy_udc_release (struct device *dev){}static voiddummy_pdev_release (struct device *dev){}static intdummy_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 voiddummy_unregister_udc (struct dummy *dum){ device_remove_file (&dum->gadget.dev, &dev_attr_function); device_unregister (&dum->gadget.dev);}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->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.is_dualspeed = 1; dum->devstatus = 0; dum->resuming = 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 (dummy_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; } // FIXME: Check these calls for errors and re-order 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 */ dum->port_status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION); return 0;}EXPORT_SYMBOL (usb_gadget_register_driver);/* caller must hold lock */static voidstop_activity (struct dummy *dum, struct usb_gadget_driver *driver){ struct dummy_ep *ep; /* prevent any more requests */ dum->address = 0; /* The timer is left running so that outstanding URBs can fail */ /* nuke any pending requests first, so driver i/o is quiesced */ list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) nuke (dum, ep); /* driver now does any non-usb quiescing necessary */ if (driver) { spin_unlock (&dum->lock); driver->disconnect (&dum->gadget); spin_lock (&dum->lock); }}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 (dummy_dev(dum), "unregister gadget driver '%s'\n", driver->driver.name); spin_lock_irqsave (&dum->lock, flags); stop_activity (dum, driver); dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); spin_unlock_irqrestore (&dum->lock, flags); driver->unbind (&dum->gadget); dum->driver = NULL; device_release_driver (&dum->gadget.dev); driver_unregister (&driver->driver); return 0;}EXPORT_SYMBOL (usb_gadget_unregister_driver);#undef is_enabledint net2280_set_fifo_mode (struct usb_gadget *gadget, int mode){ return -ENOSYS;}EXPORT_SYMBOL (net2280_set_fifo_mode);/*-------------------------------------------------------------------------*//* 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, int 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"); list_add_tail (&urbp->urbp_list, &dum->urbp_list); urb->hcpriv = urbp; if (usb_pipetype (urb->pipe) == PIPE_CONTROL) urb->error_count = 1; /* mark as a new urb */ /* kick the scheduler, it'll do the rest */ if (!timer_pending (&dum->timer)) mod_timer (&dum->timer, jiffies + 1); spin_unlock_irqrestore (&dum->lock, flags); return 0;}static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb){ /* giveback happens automatically in timer callback */ return 0;}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);}/* transfer up to a frame's worth; caller must own lock */static inttransfer (struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit){ struct dummy_request *req;top: /* if there's no request queued, the device is NAKing; return */ list_for_each_entry (req, &ep->queue, queue) { unsigned host_len, dev_len, len; int is_short, to_host; int rescan = 0; /* 1..N packets of ep->ep.maxpacket each ... the last one * may be short (including zero length). * * writer can send a zlp explicitly (length 0) or implicitly * (length mod maxpacket zero, and 'zero' flag); they always * terminate reads. */ host_len = urb->transfer_buffer_length - urb->actual_length; dev_len = req->req.length - req->req.actual; len = min (host_len, dev_len); /* FIXME update emulated data toggle too */ to_host = usb_pipein (urb->pipe); if (unlikely (len == 0)) is_short = 1; else { char *ubuf, *rbuf; /* not enough bandwidth left? */ if (limit < ep->ep.maxpacket && limit < len) break; len = min (len, (unsigned) limit); if (len == 0) break; /* use an extra pass for the final short packet */ if (len > ep->ep.maxpacket) { rescan = 1; len -= (len % ep->ep.maxpacket); } is_short = (len % ep->ep.maxpacket) != 0; /* else transfer packet(s) */ ubuf = urb->transfer_buffer + urb->actual_length; rbuf = req->req.buf + req->req.actual; if (to_host) memcpy (ubuf, rbuf, len); else memcpy (rbuf, ubuf, len); ep->last_io = jiffies; limit -= len; urb->actual_length += len; req->req.actual += len; } /* short packets terminate, maybe with overflow/underflow. * it's only really an error to write too much. * * 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. */ if (is_short) { if (host_len == dev_len) { req->req.status = 0; maybe_set_status (urb, 0); } else if (to_host) { req->req.status = 0; if (dev_len > host_len) maybe_set_status (urb, -EOVERFLOW); else maybe_set_status (urb, (urb->transfer_flags & URB_SHORT_NOT_OK) ? -EREMOTEIO : 0); } else if (!to_host) { maybe_set_status (urb, 0); if (host_len > dev_len) req->req.status = -EOVERFLOW; else req->req.status = 0; } /* many requests terminate without a short packet */ } else { if (req->req.length == req->req.actual && !req->req.zero) req->req.status = 0; if (urb->transfer_buffer_length == urb->actual_length && !(urb->transfer_flags & URB_ZERO_PACKET)) { maybe_set_status (urb, 0); } } /* device side completion --> continuable */ if (req->req.status != -EINPROGRESS) { list_del_init (&req->queue); spin_unlock (&dum->lock); req->req.complete (&ep->ep, &req->req); spin_lock (&dum->lock); /* requests might have been unlinked... */ rescan = 1; } /* host side completion --> terminate */ if (urb->status != -EINPROGRESS) break; /* rescan to continue with any other queued i/o */ if (rescan) goto top; } return limit;}static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep){ int limit = ep->ep.maxpacket; if (dum->gadget.speed == USB_SPEED_HIGH) { int tmp; /* 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; } return limit;}#define is_active(dum) ((dum->port_status & \ (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \ USB_PORT_STAT_SUSPEND)) \ == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address){ int i; if (!is_active (dum)) return NULL; if ((address & ~USB_DIR_IN) == 0) return &dum->ep [0]; for (i = 1; i < DUMMY_ENDPOINTS; i++) { struct dummy_ep *ep = &dum->ep [i]; if (!ep->desc) continue; if (ep->desc->bEndpointAddress == address) return ep; } return NULL;}#undef is_active#define Dev_Request (USB_TYPE_STANDARD | USB_RECIP_DEVICE)#define Dev_InRequest (Dev_Request | USB_DIR_IN)#define Intf_Request (USB_TYPE_STANDARD | USB_RECIP_INTERFACE)#define Intf_InRequest (Intf_Request | USB_DIR_IN)#define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)#define Ep_InRequest (Ep_Request | USB_DIR_IN)/* drive both sides of the transfers; looks like irq handlers to * both drivers except the callbacks aren't in_irq(). */static void dummy_timer (unsigned long _dum){ struct dummy *dum = (struct dummy *) _dum; struct urbp *urbp, *tmp; unsigned long flags; int limit, total; int i; /* simplistic model for one frame's bandwidth */ switch (dum->gadget.speed) { case USB_SPEED_LOW: total = 8/*bytes*/ * 12/*packets*/; break; case USB_SPEED_FULL: total = 64/*bytes*/ * 19/*packets*/; break; case USB_SPEED_HIGH: total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/; break; default: dev_err (dummy_dev(dum), "bogus device speed\n"); return; } /* FIXME if HZ != 1000 this will probably misbehave ... */ /* look at each urb queued by the host side driver */ spin_lock_irqsave (&dum->lock, flags); if (!dum->udev) { dev_err (dummy_dev(dum), "timer fired with no URBs pending?\n"); spin_unlock_irqrestore (&dum->lock, flags); return; } for (i = 0; i < DUMMY_ENDPOINTS; i++) { if (!ep_name [i]) break; dum->ep [i].already_seen = 0; }restart: list_for_each_entry_safe (urbp, tmp, &dum->urbp_list, urbp_list) { struct urb *urb; struct dummy_request *req; u8 address; struct dummy_ep *ep = NULL; int type; urb = urbp->urb; if (urb->status != -EINPROGRESS) { /* likely it was just unlinked */ goto return_urb; } type = usb_pipetype (urb->pipe); /* used up this frame's non-periodic bandwidth? * FIXME there's infinite bandwidth for control and * periodic transfers ... unrealistic. */ if (total <= 0 && type == PIPE_BULK) continue; /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); if (usb_pipein (urb->pipe)) address |= USB_DIR_IN; ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */ dev_dbg (dummy_dev(dum), "no ep configured for urb %p\n", urb); maybe_set_status (urb, -EPROTO); goto return_urb; } if (ep->already_seen) continue; ep->already_seen = 1; if (ep == &dum->ep [0] && urb->error_count) { ep->setup_stage = 1; /* a new urb */ urb->error_count = 0; } if (ep->halted && !ep->setup_stage) { /* 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); goto return_urb; } /* FIXME make sure both ends agree on maxpacket */ /* handle control requests */ if (ep == &dum->ep [0] && ep->setup_stage) { struct usb_ctrlrequest setup; int value = 1; struct dummy_ep *ep2; 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) { maybe_set_status (urb, -EOVERFLOW); goto return_urb; } /* paranoia, in case of stale queued data */ 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", req); spin_unlock (&dum->lock); req->req.complete (&ep->ep, &req->req); spin_lock (&dum->lock); ep->already_seen = 0; goto restart; } /* gadget driver never sees set_address or operations * on standard feature flags. some hardware doesn't * even expose them. */ ep->last_io = jiffies; ep->setup_stage = 0; ep->halted = 0; switch (setup.bRequest) { case USB_REQ_SET_ADDRESS: if (setup.bRequestType != Dev_Request) break; dum->address = setup.wValue; maybe_set_status (urb, 0); dev_dbg (dummy_dev(dum), "set_address = %d\n", setup.wValue); value = 0; break; case USB_REQ_SET_FEATURE: if (setup.bRequestType == Dev_Request) { value = 0; switch (setup.wValue) { case USB_DEVICE_REMOTE_WAKEUP: break; default: value = -EOPNOTSUPP; } if (value == 0) { dum->devstatus |= (1 << setup.wValue); maybe_set_status (urb, 0); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -