/* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral * controller drivers. * * This just sinks bulk packets OUT to the peripheral and sources them IN * to the host, optionally with specific data patterns for integrity tests. * As such it supports basic functionality and load tests. * * In terms of control messaging, this supports all the standard requests * plus two that support control-OUT tests. If the optional "autoresume" * mode is enabled, it provides good functional coverage for the "USBCV" * test harness from USB-IF.
*/ struct f_sourcesink { struct usb_function function;
ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); if (!ss->out_ep) goto autoconf_fail;
/* sanity check the isoc module parameters */ if (ss->isoc_interval < 1)
ss->isoc_interval = 1; if (ss->isoc_interval > 16)
ss->isoc_interval = 16; if (ss->isoc_mult > 2)
ss->isoc_mult = 2; if (ss->isoc_maxburst > 15)
ss->isoc_maxburst = 15;
/* fill in the FS isoc descriptors from the module parameters */
fs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ?
1023 : ss->isoc_maxpacket;
fs_iso_source_desc.bInterval = ss->isoc_interval;
fs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ?
1023 : ss->isoc_maxpacket;
fs_iso_sink_desc.bInterval = ss->isoc_interval;
/* allocate iso endpoints */
ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc); if (!ss->iso_in_ep) goto no_iso;
ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc); if (!ss->iso_out_ep) {
usb_ep_autoconfig_release(ss->iso_in_ep);
ss->iso_in_ep = NULL;
no_iso: /* * We still want to work even if the UDC doesn't have isoc * endpoints, so null out the alt interface that contains * them and continue.
*/
fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
}
if (ss->isoc_maxpacket > 1024)
ss->isoc_maxpacket = 1024;
/* support high speed hardware */
hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
/* * Fill in the HS isoc descriptors from the module parameters. * We assume that the user knows what they are doing and won't * give parameters that their UDC doesn't support.
*/
hs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
hs_iso_source_desc.wMaxPacketSize |= ss->isoc_mult << 11;
hs_iso_source_desc.bInterval = ss->isoc_interval;
hs_iso_source_desc.bEndpointAddress =
fs_iso_source_desc.bEndpointAddress;
/* support super speed hardware */
ss_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
ss_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
/* * Fill in the SS isoc descriptors from the module parameters. * We assume that the user knows what they are doing and won't * give parameters that their UDC doesn't support.
*/
ss_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket;
ss_iso_source_desc.bInterval = ss->isoc_interval;
ss_iso_source_comp_desc.bmAttributes = ss->isoc_mult;
ss_iso_source_comp_desc.bMaxBurst = ss->isoc_maxburst;
ss_iso_source_comp_desc.wBytesPerInterval = ss->isoc_maxpacket *
(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
ss_iso_source_desc.bEndpointAddress =
fs_iso_source_desc.bEndpointAddress;
for (i = 0; i < req->actual; i++, buf++) { switch (ss->pattern) {
/* all-zeroes has no synchronization issues */ case 0: if (*buf == 0) continue; break;
/* "mod63" stays in sync with short-terminated transfers, * OR otherwise when host and gadget agree on how large * each usb transfer request should be. Resync is done * with set_interface or set_config. (We *WANT* it to * get quickly out of sync if controllers or their drivers * stutter for any reason, including buffer duplication...)
*/ case 1: if (*buf == (u8)((i % max_packet_size) % 63)) continue; break;
}
ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
usb_ep_set_halt(ss->out_ep); return -EINVAL;
} return 0;
}
switch (ss->pattern) { case 0:
memset(req->buf, 0, req->length); break; case 1: for (i = 0; i < req->length; i++)
*buf++ = (u8) ((i % max_packet_size) % 63); break; case 2: break;
}
}
staticvoid source_sink_complete(struct usb_ep *ep, struct usb_request *req)
{ struct usb_composite_dev *cdev; struct f_sourcesink *ss = ep->driver_data; int status = req->status;
/* driver_data will be null if ep has been disabled */ if (!ss) return;
cdev = ss->function.config->cdev;
switch (status) {
case 0: /* normal completion? */ if (ep == ss->out_ep) {
check_read_data(ss, req); if (ss->pattern != 2)
memset(req->buf, 0x55, req->length);
} break;
/* this endpoint is normally active while we're configured */ case -ECONNABORTED: /* hardware forced ep reset */ case -ECONNRESET: /* request dequeued */ case -ESHUTDOWN: /* disconnect from host */
VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
req->actual, req->length); if (ep == ss->out_ep)
check_read_data(ss, req);
free_ep_req(ep, req); return;
case -EOVERFLOW: /* buffer overrun on read means that * we didn't provide a big enough * buffer.
*/ default: #if 1
DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length); break; #endif case -EREMOTEIO: /* short read */ break;
}
status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status) {
ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n",
ep->name, req->length, status);
usb_ep_set_halt(ep); /* FIXME recover later ... somehow */
}
}
staticint source_sink_start_ep(struct f_sourcesink *ss, bool is_in, bool is_iso, int speed)
{ struct usb_ep *ep; struct usb_request *req; int i, size, qlen, status = 0;
staticint
enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, int alt)
{ int result = 0; int speed = cdev->gadget->speed; struct usb_ep *ep;
/* one bulk endpoint writes (sources) zeroes IN (to the host) */
ep = ss->in_ep;
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); if (result) return result;
result = usb_ep_enable(ep); if (result < 0) return result;
ep->driver_data = ss;
result = source_sink_start_ep(ss, true, false, speed); if (result < 0) {
fail:
ep = ss->in_ep;
usb_ep_disable(ep); return result;
}
/* one bulk endpoint reads (sinks) anything OUT (from the host) */
ep = ss->out_ep;
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); if (result) goto fail;
result = usb_ep_enable(ep); if (result < 0) goto fail;
ep->driver_data = ss;
result = source_sink_start_ep(ss, false, false, speed); if (result < 0) {
fail2:
ep = ss->out_ep;
usb_ep_disable(ep); goto fail;
}
if (alt == 0) goto out;
/* one iso endpoint writes (sources) zeroes IN (to the host) */
ep = ss->iso_in_ep; if (ep) {
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); if (result) goto fail2;
result = usb_ep_enable(ep); if (result < 0) goto fail2;
ep->driver_data = ss;
result = source_sink_start_ep(ss, true, true, speed); if (result < 0) {
fail3:
ep = ss->iso_in_ep; if (ep)
usb_ep_disable(ep); goto fail2;
}
}
/* one iso endpoint reads (sinks) anything OUT (from the host) */
ep = ss->iso_out_ep; if (ep) {
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); if (result) goto fail3;
result = usb_ep_enable(ep); if (result < 0) goto fail3;
ep->driver_data = ss;
result = source_sink_start_ep(ss, false, true, speed); if (result < 0) {
usb_ep_disable(ep); goto fail3;
}
}
out:
ss->cur_alt = alt;
/* composite driver infrastructure handles everything except * the two control test requests.
*/ switch (ctrl->bRequest) {
/* * These are the same vendor-specific requests supported by * Intel's USB 2.0 compliance test devices. We exceed that * device spec by allowing multiple-packet requests. * * NOTE: the Control-OUT data stays in req->buf ... better * would be copying it into a scratch buffer, so that other * requests may safely intervene.
*/ case 0x5b: /* control WRITE test -- fill the buffer */ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) goto unknown; if (w_value || w_index) break; /* just read that many bytes into the buffer */ if (w_length > req->length) break;
value = w_length; break; case 0x5c: /* control READ test -- return the buffer */ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) goto unknown; if (w_value || w_index) break; /* expect those bytes are still in the buffer; send back */ if (w_length > req->length) break;
value = w_length; break;
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.