📄 dummy_hcd.c
字号:
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 int
transfer (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;
total = transfer (dum, urb, ep, limit);
break;
}
/* incomplete transfer? */
if (urb->status == -EINPROGRESS)
continue;
return_urb:
urb->hcpriv = NULL;
list_del (&urbp->urbp_list);
kfree (urbp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -