📄 dummy_hcd.c
字号:
/*-------------------------------------------------------------------------*//* "function" sysfs attribute */static ssize_tshow_function (struct device *_dev, char *buf){ struct dummy *dum = the_controller; if (!dum->driver->function || strlen (dum->driver->function) > PAGE_SIZE) return 0; return snprintf (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){ struct dummy *dum = gadget_dev_to_dummy (dev); complete (&dum->released);}static voiddummy_hc_release (struct device *dev){ struct dummy *dum = dev_get_drvdata (dev); complete (&dum->released);}static intdummy_register_udc (struct dummy *dum){ int rc; strcpy (dum->gadget.dev.bus_id, "udc"); dum->gadget.dev.parent = &dum->pdev.dev; 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); init_completion (&dum->released); device_unregister (&dum->gadget.dev); wait_for_completion (&dum->released);}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 = 0; 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 (hardware, "binding gadget driver '%s'\n", driver->driver.name); if ((retval = driver->bind (&dum->gadget)) != 0) { dum->driver = 0; dum->gadget.dev.driver = 0; return retval; } // FIXME: Check these calls for errors and re-order driver->driver.bus = dum->pdev.dev.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->hdev = 0; dum->address = 0; /* this might not succeed ... */ del_timer (&dum->timer); /* 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 (hardware, "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; dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); spin_unlock_irqrestore (&dum->lock, flags); driver->unbind (&dum->gadget); dum->driver = 0; device_release_driver (&dum->gadget.dev); driver_unregister (&driver->driver); del_timer_sync (&dum->timer); 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 urb *urb, int mem_flags) { struct dummy *dum; unsigned long flags; /* patch to usb_sg_init() is in 2.5.60 */ BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length); dum = container_of (hcd, struct dummy, hcd); spin_lock_irqsave (&dum->lock, flags); if (!dum->hdev) dum->hdev = urb->dev->hcpriv; urb->hcpriv = dum; 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 = ep->desc->wMaxPacketSize; tmp = le16_to_cpu (tmp); tmp = (tmp >> 11) & 0x03; tmp *= 8 /* applies to entire frame */; limit += limit * tmp; } return limit;}static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address){ int i; 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;}#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 hcd_dev *hdev = dum->hdev; struct list_head *entry, *tmp; unsigned long flags; int limit, total; int i; if (!hdev) { dev_err (hardware, "timer fired with device gone?\n"); return; } /* 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 (hardware, "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); for (i = 0; i < DUMMY_ENDPOINTS; i++) { if (!ep_name [i]) break; dum->ep [i].already_seen = 0; }restart: list_for_each_safe (entry, tmp, &hdev->urb_list) { struct urb *urb; struct dummy_request *req; u8 address; struct dummy_ep *ep = 0; int type; urb = list_entry (entry, struct urb, urb_list); 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_err (hardware, "no ep configured for urb %p\n", urb); maybe_set_status (urb, -ETIMEDOUT); 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 (hardware, "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 (hardware, "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; if (dum->address != 0) { maybe_set_status (urb, -ETIMEDOUT); urb->actual_length = 0; goto return_urb; } dum->address = setup.wValue; maybe_set_status (urb, 0); dev_dbg (hardware, "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); } } else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, setup.wIndex); if (!ep2) { value = -EOPNOTSUPP; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -