Edit online

Gadget Device

31 Jan 2024
Read time: 10 minute(s)
上一节说过 Gadget Device 由 UDC Driver 创建。
dwc2_driver_probe() → usb_add_gadget_udc() → usb_add_gadget_udc_release() → usb_add_gadget()

Gadget Device 的主要作用是提供了 Endpoint 资源,供 Function Layer 使用标准的 Gadget API 来进行访问。

Endpoint Alloc

UDC Driver 在调用 usb_add_gadget_udc() 注册 Gadget Device 之前,初始化了 Gadget 的 Endpoint 资源链表:

dwc2_driver_probe() → dwc2_gadget_init():

int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{

    /* (1) 初始化 Gadget Device 的 Endpoint 资源链表为空  */
    INIT_LIST_HEAD(&hsotg->gadget.ep_list);
    hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;


    /* initialise the endpoints now the core has been initialised */
    /* (2) 初始化 UDC 拥有的 Endpoint,加入到 Gadget Device 的 Endpoint 资源链表中 */
    for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
        if (hsotg->eps_in[epnum])
            dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum],
                    epnum, 1);
        if (hsotg->eps_out[epnum])
            dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum],
                    epnum, 0);
    }

}

↓

static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
                struct dwc2_hsotg_ep *hs_ep,
                    int epnum,
                    bool dir_in)
{


    INIT_LIST_HEAD(&hs_ep->queue);
    INIT_LIST_HEAD(&hs_ep->ep.ep_list);

    /* add to the list of endpoints known by the gadget driver */
    /* (2.1) UDC 中除了 endpoint0 以外,其他的 endpoint 都加入到 Device  的 Endpoint 资源链表 `gadget.ep_list` 中
            endpoint0 的操作由 UDC 驱动自己来处理
    */
    if (epnum)
        list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);

    /* (2.2) 初始化 endpoint 的结构体成员 */
    hs_ep->parent = hsotg;
    hs_ep->ep.name = hs_ep->name;

    if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW)
        usb_ep_set_maxpacket_limit(&hs_ep->ep, 8);
    else
        usb_ep_set_maxpacket_limit(&hs_ep->ep,
                    epnum ? 1024 : EP0_MPS_LIMIT);

    /* (2.3) endpoint 最重要的结构体成员,endpoint 操作函数集
        endpoint 的相关操作最后调用到这些函数上
    */
    hs_ep->ep.ops = &dwc2_hsotg_ep_ops;

    if (epnum == 0) {
        hs_ep->ep.caps.type_control = true;
    } else {
        if (hsotg->params.speed != DWC2_SPEED_PARAM_LOW) {
            hs_ep->ep.caps.type_iso = true;
            hs_ep->ep.caps.type_bulk = true;
        }
        hs_ep->ep.caps.type_int = true;
    }

    if (dir_in)
        hs_ep->ep.caps.dir_in = true;
    else
        hs_ep->ep.caps.dir_out = true;

}

Gadget Device 准备好了 Endpoint 资源链表以后,通过 usb_add_gadget_udc() 注册。这样就可以 Function Layer 就可以通过调用 Gadget Api 来动态分配 Endpoint 了。例如:

static int
acm_bind(struct usb_configuration *c, struct usb_function *f)
{

    /* allocate instance-specific endpoints */
    /* (1) 从 Gadget Device 中分配一个 in endpoint */
    ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
    if (!ep)
        goto fail;
    acm->port.in = ep;

    /* (2) 从 Gadget Device 中分配一个 out endpoint */
    ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
    if (!ep)
        goto fail;
    acm->port.out = ep;

    /* (3) 从 Gadget Device 中分配一个 notify endpoint */
    ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
    if (!ep)
        goto fail;
    acm->notify = ep;

}

其中通过 usb_ep_autoconfig() 函数从 Gadget Device 的 Endpoint 资源链表中分配空闲的 endpoint:

drivers\usb\gadget\function\f_acm.c:

usb_ep_autoconfig() → usb_ep_autoconfig_ss():

struct usb_ep *usb_ep_autoconfig_ss(
    struct usb_gadget               *gadget,
    struct usb_endpoint_descriptor  *desc,
    struct usb_ss_ep_comp_descriptor *ep_comp
)
{
    struct usb_ep   *ep;

    if (gadget->ops->match_ep) {
        ep = gadget->ops->match_ep(gadget, desc, ep_comp);
        if (ep)
            goto found_ep;
    }

    /* Second, look at endpoints until an unclaimed one looks usable */
    /* (1) 从 Gadget Device 的 Endpoint 资源链表中查找一个空闲的(ep->claimed 为空) 且符合要求的 endpoint  */
    list_for_each_entry (ep, &gadget->ep_list, ep_list) {
        if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp))
            goto found_ep;
    }

    /* Fail */
    return NULL;
found_ep:

    ...

    ep->address = desc->bEndpointAddress;
    ep->desc = NULL;
    ep->comp_desc = NULL;
    /* (2) 设置 endpoint 为已分配 */
    ep->claimed = true;
    return ep;
}

EndPoint Access

Gadget Device 不仅仅为 Gadget Api 提供了分配 endpoint 的支持,还支持对 endpoint 收发数据的底层支持。在上一节的 endpoint 初始化时,就已经设置 endpoint 的操作函数集 dwc2_hsotg_ep_ops

dwc2_driver_probe() → dwc2_gadget_init() → dwc2_hsotg_initep():

static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
                struct dwc2_hsotg_ep *hs_ep,
                    int epnum,
                    bool dir_in)
{

    /* (2.3) endpoint 最重要的结构体成员,endpoint 操作函数集
        endpoint 的相关操作最后调用到这些函数上
    */
    hs_ep->ep.ops = &dwc2_hsotg_ep_ops;

}

↓

static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
    .enable         = dwc2_hsotg_ep_enable,
    .disable        = dwc2_hsotg_ep_disable_lock,
    .alloc_request  = dwc2_hsotg_ep_alloc_request,
    .free_request   = dwc2_hsotg_ep_free_request,
    .queue          = dwc2_hsotg_ep_queue_lock,
    .dequeue        = dwc2_hsotg_ep_dequeue,
    .set_halt       = dwc2_hsotg_ep_sethalt_lock,
    /* note, don't believe we have any call for the fifo routines */
};

Gadget Api 提供了以下接口来操作 endpoint 读写数据。在 Host 侧对 endpoint 进行一次操作请求的数据结构是 struct urb ,而在 Device 侧也有类似的数据结构称为 struct usb_request ,对 endpoint 的数据读写就是围绕 struct usb_request 展开的:

drivers\usb\gadget\function\f_acm.c:

static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
        void *data, unsigned length)
{
    struct usb_ep                   *ep = acm->notify;
    struct usb_request              *req;
    struct usb_cdc_notification     *notify;
    const unsigned                  len = sizeof(*notify) + length;
    void                            *buf;
    int                             status;

    /* (1) 初始化 `struct usb_request` 数据结构 */
    req = acm->notify_req;
    acm->notify_req = NULL;
    acm->pending = false;

    req->length = len;
    notify = req->buf;
    buf = notify + 1;

    notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
            | USB_RECIP_INTERFACE;
    notify->bNotificationType = type;
    notify->wValue = cpu_to_le16(value);
    notify->wIndex = cpu_to_le16(acm->ctrl_id);
    notify->wLength = cpu_to_le16(length);
    memcpy(buf, data, length);

    /* ep_queue() can complete immediately if it fills the fifo... */
    spin_unlock(&acm->lock);
    /* (2) 提交 `usb_request` 请求到 endpoint 处理队列中 */
    status = usb_ep_queue(ep, req, GFP_ATOMIC);
    spin_lock(&acm->lock);

}

其中 usb_ep_queue() 函数就会调用 endpoint 的操作函数集 dwc2_hsotg_ep_ops 中的 .queue 函数:

int usb_ep_queue(struct usb_ep *ep,
                struct usb_request *req, gfp_t gfp_flags)
{
    int ret = 0;

    if (WARN_ON_ONCE(!ep->enabled && ep->address)) {
        ret = -ESHUTDOWN;
        goto out;
    }

    /* (1) 实际调用 dwc2_hsotg_ep_queue_lock() */
    ret = ep->ops->queue(ep, req, gfp_flags);

out:
    trace_usb_ep_queue(ep, req, ret);

    return ret;
}

UDC Control

Gadget Device 还提供了 UDC 层级的一些操作函数,UDC Driver 在调用 usb_add_gadget_udc() 注册 Gadget Device 之前,初始化了 Gadget 的 操作函数集:

dwc2_driver_probe() → dwc2_gadget_init():

int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{

    hsotg->gadget.max_speed = USB_SPEED_HIGH;
    /* (1) 初始化 Gadget Device 的操作函数集  */
    hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
    hsotg->gadget.name = dev_name(dev);
    hsotg->remote_wakeup_allowed = 0;

}

↓

static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
    .get_frame      = dwc2_hsotg_gadget_getframe,
    .set_selfpowered        = dwc2_hsotg_set_selfpowered,
    .udc_start              = dwc2_hsotg_udc_start,
    .udc_stop               = dwc2_hsotg_udc_stop,
    .pullup                 = dwc2_hsotg_pullup,
    .vbus_session           = dwc2_hsotg_vbus_session,
    .vbus_draw              = dwc2_hsotg_vbus_draw,
};

Gadget Api 提供了一些内部函数来调用:

static inline int usb_gadget_udc_start(struct usb_udc *udc)
{
    return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
}

static inline void usb_gadget_udc_stop(struct usb_udc *udc)
{
    udc->gadget->ops->udc_stop(udc->gadget);
}

static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
                        enum usb_device_speed speed)
{
    if (udc->gadget->ops->udc_set_speed) {
        enum usb_device_speed s;

        s = min(speed, udc->gadget->max_speed);
        udc->gadget->ops->udc_set_speed(udc->gadget, s);
    }
}

int usb_gadget_connect(struct usb_gadget *gadget)
{
    int ret = 0;

    if (!gadget->ops->pullup) {
        ret = -EOPNOTSUPP;
        goto out;
    }

    if (gadget->deactivated) {
        /*
        * If gadget is deactivated we only save new state.
        * Gadget will be connected automatically after activation.
        */
        gadget->connected = true;
        goto out;
    }

    ret = gadget->ops->pullup(gadget, 1);
    if (!ret)
        gadget->connected = 1;

out:
    trace_usb_gadget_connect(gadget, ret);

    return ret;
}

int usb_gadget_disconnect(struct usb_gadget *gadget)
{
    int ret = 0;

    if (!gadget->ops->pullup) {
        ret = -EOPNOTSUPP;
        goto out;
    }

    if (!gadget->connected)
        goto out;

    if (gadget->deactivated) {
        /*
        * If gadget is deactivated we only save new state.
        * Gadget will stay disconnected after activation.
        */
        gadget->connected = false;
        goto out;
    }

    ret = gadget->ops->pullup(gadget, 0);
    if (!ret) {
        gadget->connected = 0;
        gadget->udc->driver->disconnect(gadget);
    }

out:
    trace_usb_gadget_disconnect(gadget, ret);

    return ret;
}