Edit online

Loopback Function

31 Jan 2024
Read time: 3 minute(s)

Loopback Function 提供的功能更为简单,它分配了两个 bulk endpoint,所做的就是把 out_ep 接收到的数据 转发到 in_ep

主要流程如下:
drivers\usb\gadget\function\f_loopback.c:

loopback_bind():

static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
    /* (1) 从 gadget 中分配 2 个 bulk endpoint */
    /* allocate endpoints */
    loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);

    loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
}

loopback_set_alt() → enable_loopback() → alloc_requests():

static int alloc_requests(struct usb_composite_dev *cdev,
            struct f_loopback *loop)
{

    for (i = 0; i < loop->qlen && result == 0; i++) {
        result = -ENOMEM;

        in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC);
        if (!in_req)
            goto fail;

        out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen);
        if (!out_req)
            goto fail_in;

        in_req->complete = loopback_complete;
        out_req->complete = loopback_complete;

        in_req->buf = out_req->buf;
        /* length will be set in complete routine */
        in_req->context = out_req;
        out_req->context = in_req;

        /* (2) 先启动 OUT endpoint */
        result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC);
        if (result) {
            ERROR(cdev, "%s queue req --> %d\n",
                    loop->out_ep->name, result);
            goto fail_out;
        }
    }

}

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) {
            /*
            * We received some data from the host so let's
            * queue it so host can read the from our in ep
            */
            struct usb_request *in_req = req->context;

            in_req->zero = (req->actual < req->length);
            in_req->length = req->actual;
            ep = loop->in_ep;
            req = in_req;
        } else {
            /*
            * We have just looped back a bunch of data
            * to host. Now let's wait for some more data.
            */
            req = req->context;
            ep = loop->out_ep;
        }

        /* (3) 环回的关键:
                OUT endpoint 接收到的数据 转发到 IN endpoint
                IN endpoint 数据发送完成后 req 重新挂载到 OUT endpoint
        */
        /* queue the buffer back to host or for next bunch of data */
        status = usb_ep_queue(ep, req, GFP_ATOMIC);

}
也支持一些参数调整:
# ls functions/Loopback.0/
bulk_buflen  qlen