这节分析loopback具体功能是怎么实现的。
loopback功能的入口在这里。
首先,在枚举的时候,有这样一段:
switch (ctrl->bRequest) { case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) break; if (w_value && !f->set_alt) break; value = f->set_alt(f, w_index, w_value); break;
在set interface阶段,会调用f->set_alt函数。对应于loopback的function,调用的就是loopback_set_alt,在这个函数中会调用enable_loopback。也就是说loopback是在set interface 时候使能的。
static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
int result = 0;
const struct usb_endpoint_descriptor *src, *sink;
struct usb_ep *ep;
struct usb_request *req;
unsigned i;
src = ep_choose(cdev->gadget,
&hs_loop_source_desc, &fs_loop_source_desc);
sink = ep_choose(cdev->gadget,
&hs_loop_sink_desc, &fs_loop_sink_desc);
/* one endpoint writes data back IN to the host */
ep = loop->in_ep;
result = usb_ep_enable(ep, src);
if (result < 0)
return result;
ep->driver_data = loop;
/* one endpoint just reads OUT packets */
ep = loop->out_ep;
result = usb_ep_enable(ep, sink);
ep->driver_data = loop;
/* 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.
*/
for (i = 0; i < qlen && result == 0; i++) {
req = alloc_ep_req(ep);
if (req) {
req->complete = loopback_complete;
result = usb_ep_queue(ep, req, GFP_ATOMIC);
if (result)
ERROR(cdev, "%s queue req --> %d\n",
ep->name, result);
} else {
usb_ep_disable(ep);
ep->driver_data = NULL;
result = -ENOMEM;
goto fail0;
}
}
DBG(cdev, "%s enabled\n", loop->function.name);
return result;
}
static void loopback_complete(struct usb_ep *ep, struct usb_request *req) { struct f_loopback *loop = ep->driver_data; struct usb_composite_dev *cdev = loop->function.config->cdev; int status = req->status; switch (status) { case 0: /* normal completion? */ if (ep == loop->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(loop->in_ep, req, GFP_ATOMIC); if (status == 0) return; } /* queue the buffer for some later OUT packet */ req->length = buflen; status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); if (status == 0) return; /* "should never get here" */ /* FALLTHROUGH */ default: ERROR(cdev, "%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; } }
在enable_loopback函数中,使能了in_ep和out_ep。并且调用alloc_ep_req(ep)。调用这个的功能相当于是在out_ep上面添加了qlen个读请求。当host端发送消息时,device就会收到消息,并调用回调函数loopback_complete。按照我们的逻辑,既然这个device的功能是收到信息,并将数据在返回给host,那么返回信息的功能就应该在回调函数中进行实现。下面我们分析回调函数。
我们重点关注这个complete函数的前半部分。这个函数if(ep == loop->out_ep),也就是收到了数据,那么就usb_ep_queue(loop->in_ep...),这个就相当于是发送数据,req的数据部分没有发生改动,通过这里就将数据返回给了host。
另外,这个回调函数也负责处理 发送完毕的情况, 因此, if(ep == loop->in_ep),那么就需要 在添加一个读请求到 out_ep,用于继续从host端进行接收。这里就是上面这部分函数的逻辑。
至此,device部分的实现就分析完了。我们接下来分析host部分的实现。