Gadget Device
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; }