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