📄 zero.c
字号:
/* two configurations will always be index 0 and index 1 */
if (index > 1)
return -EINVAL;
is_source_sink = loopdefault ? (index == 1) : (index == 0);
#ifdef CONFIG_USB_GADGET_DUALSPEED
if (type == USB_DT_OTHER_SPEED_CONFIG)
hs = !hs;
if (hs)
function = is_source_sink
? hs_source_sink_function
: hs_loopback_function;
else
#endif
function = is_source_sink
? fs_source_sink_function
: fs_loopback_function;
/* for now, don't advertise srp-only devices */
if (!gadget->is_otg)
function++;
len = usb_gadget_config_buf (is_source_sink
? &source_sink_config
: &loopback_config,
buf, USB_BUFSIZ, function);
if (len < 0)
return len;
((struct usb_config_descriptor *) buf)->bDescriptorType = type;
return len;
}
/*-------------------------------------------------------------------------*/
static struct usb_request *
alloc_ep_req (struct usb_ep *ep, unsigned length)
{
struct usb_request *req;
req = usb_ep_alloc_request (ep, GFP_ATOMIC);
if (req) {
req->length = length;
req->buf = usb_ep_alloc_buffer (ep, length,
&req->dma, GFP_ATOMIC);
if (!req->buf) {
usb_ep_free_request (ep, req);
req = NULL;
}
}
return req;
}
static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
{
if (req->buf)
usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
usb_ep_free_request (ep, req);
}
/*-------------------------------------------------------------------------*/
/* optionally require specific source/sink data patterns */
static int
check_read_data (
struct zero_dev *dev,
struct usb_ep *ep,
struct usb_request *req
)
{
unsigned i;
u8 *buf = req->buf;
for (i = 0; i < req->actual; i++, buf++) {
switch (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.
*/
case 1:
if (*buf == (u8)(i % 63))
continue;
break;
}
ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf);
usb_ep_set_halt (ep);
return -EINVAL;
}
return 0;
}
static void
reinit_write_data (
struct zero_dev *dev,
struct usb_ep *ep,
struct usb_request *req
)
{
unsigned i;
u8 *buf = req->buf;
switch (pattern) {
case 0:
memset (req->buf, 0, req->length);
break;
case 1:
for (i = 0; i < req->length; i++)
*buf++ = (u8) (i % 63);
break;
}
}
/* if there is only one request in the queue, there'll always be an
* irq delay between end of one request and start of the next.
* that prevents using hardware dma queues.
*/
static void source_sink_complete (struct usb_ep *ep, struct usb_request *req)
{
struct zero_dev *dev = ep->driver_data;
int status = req->status;
switch (status) {
case 0: /* normal completion? */
if (ep == dev->out_ep)
check_read_data (dev, ep, req);
else
reinit_write_data (dev, ep, req);
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 (dev, "%s gone (%d), %d/%d\n", ep->name, status,
req->actual, req->length);
if (ep == dev->out_ep)
check_read_data (dev, ep, 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 (dev, "%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
#endif
case -EREMOTEIO: /* short read */
break;
}
status = usb_ep_queue (ep, req, GFP_ATOMIC);
if (status) {
ERROR (dev, "kill %s: resubmit %d bytes --> %d\n",
ep->name, req->length, status);
usb_ep_set_halt (ep);
/* FIXME recover later ... somehow */
}
}
static struct usb_request *
source_sink_start_ep (struct usb_ep *ep, unsigned gfp_flags)
{
struct usb_request *req;
int status;
req = alloc_ep_req (ep, buflen);
if (!req)
return NULL;
memset (req->buf, 0, req->length);
req->complete = source_sink_complete;
if (strcmp (ep->name, EP_IN_NAME) == 0)
reinit_write_data (ep->driver_data, ep, req);
status = usb_ep_queue (ep, req, gfp_flags);
if (status) {
struct zero_dev *dev = ep->driver_data;
ERROR (dev, "start %s --> %d\n", ep->name, status);
free_ep_req (ep, req);
req = NULL;
}
return req;
}
static int
set_source_sink_config (struct zero_dev *dev, unsigned gfp_flags)
{
int result = 0;
struct usb_ep *ep;
struct usb_gadget *gadget = dev->gadget;
gadget_for_each_ep (ep, gadget) {
const struct usb_endpoint_descriptor *d;
/* one endpoint writes (sources) zeroes in (to the host) */
if (strcmp (ep->name, EP_IN_NAME) == 0) {
d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
result = usb_ep_enable (ep, d);
if (result == 0) {
ep->driver_data = dev;
if (source_sink_start_ep (ep, gfp_flags) != 0) {
dev->in_ep = ep;
continue;
}
usb_ep_disable (ep);
result = -EIO;
}
/* one endpoint reads (sinks) anything out (from the host) */
} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
result = usb_ep_enable (ep, d);
if (result == 0) {
ep->driver_data = dev;
if (source_sink_start_ep (ep, gfp_flags) != 0) {
dev->out_ep = ep;
continue;
}
usb_ep_disable (ep);
result = -EIO;
}
/* ignore any other endpoints */
} else
continue;
/* stop on error */
ERROR (dev, "can't start %s, result %d\n", ep->name, result);
break;
}
if (result == 0)
DBG (dev, "buflen %d\n", buflen);
/* caller is responsible for cleanup on error */
return result;
}
/*-------------------------------------------------------------------------*/
static void loopback_complete (struct usb_ep *ep, struct usb_request *req)
{
struct zero_dev *dev = ep->driver_data;
int status = req->status;
switch (status) {
case 0: /* normal completion? */
if (ep == dev->out_ep) {
/* loop this OUT packet back IN to the host */
req->zero = (req->actual < req->length);
req->length = req->actual;
status = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
ERROR (dev, "can't loop %s to %s: %d\n",
ep->name, dev->in_ep->name,
status);
}
/* queue the buffer for some later OUT packet */
req->length = buflen;
status = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
/* FALLTHROUGH */
default:
ERROR (dev, "%s loop complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
/* FALLTHROUGH */
/* NOTE: since this driver doesn't maintain an explicit record
* of requests it submitted (just maintains qlen count), we
* rely on the hardware driver to clean up on disconnect or
* endpoint disable.
*/
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
free_ep_req (ep, req);
return;
}
}
static int
set_loopback_config (struct zero_dev *dev, unsigned gfp_flags)
{
int result = 0;
struct usb_ep *ep;
struct usb_gadget *gadget = dev->gadget;
gadget_for_each_ep (ep, gadget) {
const struct usb_endpoint_descriptor *d;
/* one endpoint writes data back IN to the host */
if (strcmp (ep->name, EP_IN_NAME) == 0) {
d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
result = usb_ep_enable (ep, d);
if (result == 0) {
ep->driver_data = dev;
dev->in_ep = ep;
continue;
}
/* one endpoint just reads OUT packets */
} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
result = usb_ep_enable (ep, d);
if (result == 0) {
ep->driver_data = dev;
dev->out_ep = ep;
continue;
}
/* ignore any other endpoints */
} else
continue;
/* stop on error */
ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
break;
}
/* allocate a bunch of read buffers and queue them all at once.
* we buffer at most 'qlen' transfers; fewer if any need more
* than 'buflen' bytes each.
*/
if (result == 0) {
struct usb_request *req;
unsigned i;
ep = dev->out_ep;
for (i = 0; i < qlen && result == 0; i++) {
req = alloc_ep_req (ep, buflen);
if (req) {
req->complete = loopback_complete;
result = usb_ep_queue (ep, req, GFP_ATOMIC);
if (result)
DBG (dev, "%s queue req --> %d\n",
ep->name, result);
} else
result = -ENOMEM;
}
}
if (result == 0)
DBG (dev, "qlen %d, buflen %d\n", qlen, buflen);
/* caller is responsible for cleanup on error */
return result;
}
/*-------------------------------------------------------------------------*/
static void zero_reset_config (struct zero_dev *dev)
{
if (dev->config == 0)
return;
DBG (dev, "reset config\n");
/* just disable endpoints, forcing completion of pending i/o.
* all our completion handlers free their requests in this case.
*/
if (dev->in_ep) {
usb_ep_disable (dev->in_ep);
dev->in_ep = NULL;
}
if (dev->out_ep) {
usb_ep_disable (dev->out_ep);
dev->out_ep = NULL;
}
dev->config = 0;
del_timer (&dev->resume);
}
/* change our operational config. this code must agree with the code
* that returns config descriptors, and altsetting code.
*
* it's also responsible for power management interactions. some
* configurations might not work with our current power sources.
*
* note that some device controller hardware will constrain what this
* code can do, perhaps by disallowing more than one configuration or
* by limiting configuration choices (like the pxa2xx).
*/
static int
zero_set_config (struct zero_dev *dev, unsigned number, unsigned gfp_flags)
{
int result = 0;
struct usb_gadget *gadget = dev->gadget;
if (number == dev->config)
return 0;
if (gadget_is_sa1100 (gadget) && dev->config) {
/* tx fifo is full, but we can't clear it...*/
INFO (dev, "can't change configurations\n");
return -ESPIPE;
}
zero_reset_config (dev);
switch (number) {
case CONFIG_SOURCE_SINK:
result = set_source_sink_config (dev, gfp_flags);
break;
case CONFIG_LOOPBACK:
result = set_loopback_config (dev, gfp_flags);
break;
default:
result = -EINVAL;
/* FALL THROUGH */
case 0:
return result;
}
if (!result && (!dev->in_ep || !dev->out_ep))
result = -ENODEV;
if (result)
zero_reset_config (dev);
else {
char *speed;
switch (gadget->speed) {
case USB_SPEED_LOW: speed = "low"; break;
case USB_SPEED_FULL: speed = "full"; break;
case USB_SPEED_HIGH: speed = "high"; break;
default: speed = "?"; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -