📄 dummy_hcd.c
字号:
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){ 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;}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 = (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; } else if (dum->rh_state != DUMMY_RH_RUNNING) continue; 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; unsigned w_index; unsigned w_value; setup = *(struct usb_ctrlrequest*) urb->setup_packet; 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; } /* 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 (udc_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 = w_value; maybe_set_status (urb, 0); dev_dbg (udc_dev(dum), "set_address = %d\n", w_value); value = 0; break; case USB_REQ_SET_FEATURE: if (setup.bRequestType == Dev_Request) { value = 0; switch (w_value) { case USB_DEVICE_REMOTE_WAKEUP: break; case USB_DEVICE_B_HNP_ENABLE: dum->gadget.b_hnp_enable = 1; break; case USB_DEVICE_A_HNP_SUPPORT: dum->gadget.a_hnp_support = 1; break; case USB_DEVICE_A_ALT_HNP_SUPPORT: dum->gadget.a_alt_hnp_support = 1; break; default: value = -EOPNOTSUPP; } if (value == 0) { dum->devstatus |= (1 << w_value); maybe_set_status (urb, 0); } } else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, w_index); if (!ep2) { value = -EOPNOTSUPP; break; } ep2->halted = 1; value = 0; maybe_set_status (urb, 0); } break; case USB_REQ_CLEAR_FEATURE: if (setup.bRequestType == Dev_Request) { switch (w_value) { case USB_DEVICE_REMOTE_WAKEUP: dum->devstatus &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); value = 0; maybe_set_status (urb, 0); break; default: value = -EOPNOTSUPP; break; } } else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, w_index); if (!ep2) { value = -EOPNOTSUPP; break; } ep2->halted = 0; value = 0; maybe_set_status (urb, 0); } break; case USB_REQ_GET_STATUS: if (setup.bRequestType == Dev_InRequest || setup.bRequestType == Intf_InRequest || setup.bRequestType == Ep_InRequest ) { char *buf; // device: remote wakeup, selfpowered // interface: nothing // endpoint: halt buf = (char *)urb->transfer_buffer; if (urb->transfer_buffer_length > 0) { if (setup.bRequestType == Ep_InRequest) { ep2 = find_endpoint (dum, w_index); if (!ep2) { value = -EOPNOTSUPP; break; } buf [0] = ep2->halted; } else if (setup.bRequestType == Dev_InRequest) { buf [0] = (u8) dum->devstatus; } else buf [0] = 0; } if (urb->transfer_buffer_length > 1) buf [1] = 0; urb->actual_length = min (2, urb->transfer_buffer_length); value = 0; maybe_set_status (urb, 0); } break; } /* gadget driver handles all other requests. block * until setup() returns; no reentrancy issues etc. */ if (value > 0) { spin_unlock (&dum->lock); value = dum->driver->setup (&dum->gadget, &setup); spin_lock (&dum->lock); if (value >= 0) { /* no delays (max 64KB data stage) */ limit = 64*1024; goto treat_control_like_bulk; } /* error, see below */ } if (value < 0) { if (value != -EOPNOTSUPP) dev_dbg (udc_dev(dum), "setup --> %d\n", value); maybe_set_status (urb, -EPIPE); urb->actual_length = 0; } goto return_urb; } /* non-control requests */ limit = total; switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: /* FIXME is it urb->interval since the last xfer? * use urb->iso_frame_desc[i]. * complete whether or not ep has requests queued. * report random errors, to debug drivers. */ limit = max (limit, periodic_bytes (dum, ep)); maybe_set_status (urb, -ENOSYS); break; case PIPE_INTERRUPT: /* FIXME is it urb->interval since the last xfer? * this almost certainly polls too fast. */ limit = max (limit, periodic_bytes (dum, ep)); /* FALLTHROUGH */ // case PIPE_BULK: case PIPE_CONTROL: default: treat_control_like_bulk: ep->last_io = jiffies;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -