staticint get_frame_limit(enum usb_device_speed speed)
{ switch (speed) { case USB_SPEED_LOW: return 8 /*bytes*/ * 12 /*packets*/; case USB_SPEED_FULL: return 64 /*bytes*/ * 19 /*packets*/; case USB_SPEED_HIGH: return 512 /*bytes*/ * 13 /*packets*/ * 8 /*uframes*/; case USB_SPEED_SUPER: /* Bus speed is 500000 bytes/ms, so use a little less */ return 490000; default: /* error */ return -1;
}
}
/* * handle_control_request() - handles all control transfers * @udc: pointer to vudc * @urb: the urb request to handle * @setup: pointer to the setup data for a USB device control * request * @status: pointer to request handling status * * Return 0 - if the request was handled * 1 - if the request wasn't handles * error code on error * * Adapted from drivers/usb/gadget/udc/dummy_hcd.c
*/ staticint handle_control_request(struct vudc *udc, struct urb *urb, struct usb_ctrlrequest *setup, int *status)
{ struct vep *ep2; int ret_val = 1; unsignedint w_index; unsignedint w_value;
w_index = le16_to_cpu(setup->wIndex);
w_value = le16_to_cpu(setup->wValue); switch (setup->bRequest) { case USB_REQ_SET_ADDRESS: if (setup->bRequestType != DEV_REQUEST) break;
udc->address = w_value;
ret_val = 0;
*status = 0; break; case USB_REQ_SET_FEATURE: if (setup->bRequestType == DEV_REQUEST) {
ret_val = 0; switch (w_value) { case USB_DEVICE_REMOTE_WAKEUP: break; case USB_DEVICE_B_HNP_ENABLE:
udc->gadget.b_hnp_enable = 1; break; case USB_DEVICE_A_HNP_SUPPORT:
udc->gadget.a_hnp_support = 1; break; case USB_DEVICE_A_ALT_HNP_SUPPORT:
udc->gadget.a_alt_hnp_support = 1; break; default:
ret_val = -EOPNOTSUPP;
} if (ret_val == 0) {
udc->devstatus |= (1 << w_value);
*status = 0;
}
} elseif (setup->bRequestType == EP_REQUEST) { /* endpoint halt */
ep2 = vudc_find_endpoint(udc, w_index); if (!ep2 || ep2->ep.name == udc->ep[0].ep.name) {
ret_val = -EOPNOTSUPP; break;
}
ep2->halted = 1;
ret_val = 0;
*status = 0;
} break; case USB_REQ_CLEAR_FEATURE: if (setup->bRequestType == DEV_REQUEST) {
ret_val = 0; switch (w_value) { case USB_DEVICE_REMOTE_WAKEUP:
w_value = USB_DEVICE_REMOTE_WAKEUP; break;
/* Adapted from dummy_hcd.c ; caller must hold lock */ staticint transfer(struct vudc *udc, struct urb *urb, struct vep *ep, int limit)
{ struct vrequest *req; int sent = 0;
top: /* if there's no request queued, the device is NAKing; return */
list_for_each_entry(req, &ep->req_queue, req_entry) { unsignedint host_len, dev_len, len; void *ubuf_pos, *rbuf_pos; 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);
to_host = usb_pipein(urb->pipe); if (unlikely(len == 0))
is_short = 1; else { /* send multiple of maxpacket first, then remainder */ if (len >= ep->ep.maxpacket) {
is_short = 0; if (len % ep->ep.maxpacket > 0)
rescan = 1;
len -= len % ep->ep.maxpacket;
} else {
is_short = 1;
}
urb->actual_length += len;
req->req.actual += len;
sent += 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.
*/ if (is_short) { if (host_len == dev_len) {
req->req.status = 0;
urb->status = 0;
} elseif (to_host) {
req->req.status = 0; if (dev_len > host_len)
urb->status = -EOVERFLOW; else
urb->status = 0;
} else {
urb->status = 0; if (host_len > dev_len)
req->req.status = -EOVERFLOW; else
req->req.status = 0;
}
/* many requests terminate without a short packet */ /* also check if we need to send zlp */
} else { if (req->req.length == req->req.actual) { if (req->req.zero && to_host)
rescan = 1; else
req->req.status = 0;
} if (urb->transfer_buffer_length == urb->actual_length) { if (urb->transfer_flags & URB_ZERO_PACKET &&
!to_host)
rescan = 1; else
urb->status = 0;
}
}
/* device side completion --> continuable */ if (req->req.status != -EINPROGRESS) {
total = get_frame_limit(udc->gadget.speed); if (total < 0) { /* unknown speed, or not set yet */
timer->state = VUDC_TR_IDLE;
spin_unlock_irqrestore(&udc->lock, flags); return;
} /* is it next frame now? */ if (time_after(jiffies, timer->frame_start + msecs_to_jiffies(1))) {
timer->frame_limit = total; /* FIXME: how to make it accurate? */
timer->frame_start = jiffies;
} else {
total = timer->frame_limit;
}
/* We have to clear ep0 flags separately as it's not on the list */
udc->ep[0].already_seen = 0;
list_for_each_entry(_ep, &udc->gadget.ep_list, ep_list) {
ep = to_vep(_ep);
ep->already_seen = 0;
}
/* TODO - also wait on empty usb_request queues? */ if (list_empty(&udc->urb_queue))
timer->state = VUDC_TR_IDLE; else
mod_timer(&timer->timer,
timer->frame_start + msecs_to_jiffies(1));
spin_unlock_irqrestore(&udc->lock, flags);
}
/* All timer functions are run with udc->lock held */
dev_dbg(&udc->pdev->dev, "timer kick"); switch (t->state) { case VUDC_TR_RUNNING: return; case VUDC_TR_IDLE:
t->state = VUDC_TR_RUNNING;
fallthrough; case VUDC_TR_STOPPED: /* we may want to kick timer to unqueue urbs */
mod_timer(&t->timer, time);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.