linux gadget 驱动

介绍USB系统框架,只关注框架部分,不涉及细节

这里的USB设备控制器(UDC)驱动指作为其他usb主机控制器外设的usb硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个usb设备依附于一个usb主机控制器上。

在usb设备控制器于gadget驱动中,我们主要关心几个核心的数据结构。描述一个usb设备控制器的usb_gadget,描述一个gadget驱动的usb_gadget_driver,表示一个传输请求的usb_request,描述一个端点的usb_ep,描述端点操作的usb_ep_ops结构体

研究时使用9x07平台

初始化流程

## 添加udc设备

以ci3xxx_msm举例

  1. 初始化gadget

    ci13xxx_msm_probe->udc_probe

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
    if (udc == NULL)
    return -ENOMEM;
    udc->lock = &udc_lock;
    udc->regs = regs;
    udc->udc_driver = driver;

    udc->gadget.ops = &usb_gadget_ops;
    udc->gadget.speed = USB_SPEED_UNKNOWN;
    udc->gadget.max_speed = USB_SPEED_HIGH;
    udc->gadget.is_otg = 0;
    udc->gadget.name = driver->name;

udc->gadget.usb_core_id没有初始化,默认值为0

  1. 创建&添加udc设备

    retval = usb_add_gadget_udc(dev, &udc->gadget);
    usb_add_gadget_udc_release(parent, gadget, NULL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
udc = kzalloc(sizeof(*udc), GFP_KERNEL)
dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work);

device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
udc->dev.class = udc_class;
udc->dev.groups = usb_udc_attr_groups;
udc->dev.parent = parent;
ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
udc->gadget = gadget;

list_add_tail(&udc->list, &udc_list);
ret = device_add(&udc->dev);

添加android设备

  1. 从android_probe开始

​ 从设备树中获取usb_core_id,默认值为0,创建出来的设备是android0

​ android_dev = kzalloc(sizeof(*android_dev), GFP_KERNEL);

​ android_create_device(struct android_dev *dev, u8 usb_core_id)

1
2
3
snprintf(device_node_name, ANDROID_DEVICE_NODE_NAME_LENGTH,
"android%d", usb_core_id);
dev->dev = device_create(android_class, NULL, MKDEV(0, usb_core_id),

​ 创建一个android设备,其中usb_core_id默认为0

  1. 初始化设备(android0)默认支持的功能(function)

    1
    2
    3
    4
    android_dev->name = pdev->name;
    android_dev->disable_depth = 1;
    android_dev->functions =
    supported_list ? supported_list : default_functions;

其中supported_list从设备树获取,默认为空,既使用default_functions

  1. 绑定udc设备
    usb_composite_probe(&android_usb_driver)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    int usb_composite_probe(struct usb_composite_driver *driver)
    {
    struct usb_gadget_driver *gadget_driver;
    u8 core_id;


    core_id = driver->gadget_driver.usb_core_id;
    driver->gadget_driver = composite_driver_template;
    gadget_driver = &driver->gadget_driver;

    gadget_driver->function = (char *) driver->name;
    gadget_driver->driver.name = driver->name;
    gadget_driver->max_speed = driver->max_speed;

    if (core_id)
    gadget_driver->usb_core_id = core_id;
    return usb_gadget_probe_driver(gadget_driver);
    }

    usb_gadget_probe_driver(struct usb_gadget_driver *driver)

    根据usb_core_id找到udc(在ci13xxx_msm_probe中添加的)
    将udc和driver绑定:udc_bind_to_driver
    
  2. udc_bind_to_driver
    ret = driver->bind(udc->gadget, driver);
    执行composite_bind

  3. composite_bind
    创建 usb_composite_dev设备

    1
    2
    cdev = kzalloc(sizeof *cdev, GFP_KERNEL)
    cdev->gadget = gadget;

    执行usb_composite_driver的bind,此处是android_bind

  4. android_bind
    初始化产品信息(可以通过应用层修改,修改/sys/class/android_usb/android0目录下的文档)
    初始化&创建function(可以通过应用层修改,修改/sys/class/android_usb/android0/functions)

应用层修改&使能

参数&配置修改:修改修改/sys/class/android_usb/android0下的文档,暂不关注系统

使能

应用层修改/sys/class/android_usb/android0/enable文档触发android_enable

  1. 添加配置
    usb_add_config(cdev, &conf->usb_config, android_bind_config);
    android_bind_config —> android_bind_enabled_functions
    遍历配置里的所有function,执行相应的bind_config

  2. 连接,触发host端的连接请求
    usb_gadget_connect(cdev->gadget);

    gadget->ops->pullup(gadget, 1)
    

gadget配置

host端请求代码:USB_REQ_GET_DESCRIPTOR –> USB_DT_DEVICE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
{
struct usb_device_descriptor *desc;
int ret;

if (size > sizeof(*desc))
return -EINVAL;
desc = kmalloc(sizeof(*desc), GFP_NOIO);
if (!desc)
return -ENOMEM;

ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
if (ret >= 0)
memcpy(&dev->descriptor, desc, size);
kfree(desc);
return ret;
}

int usb_get_descriptor(struct usb_device *dev, unsigned char type,
unsigned char index, void *buf, int size)
{
int i;
int result;

memset(buf, 0, size); /* Make sure we parse really received data */

for (i = 0; i < 3; ++i) {
/* retry on length 0 or error; some devices are flakey */
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
(type << 8) + index, 0, buf, size,
USB_CTRL_GET_TIMEOUT);
if (result <= 0 && result != -ETIMEDOUT)
continue;
if (result > 1 && ((u8 *)buf)[1] != type) {
result = -ENODATA;
continue;
}
break;
}
return result;
}

device端请求:入口:composite_setup

1
2
3
4
5
6
switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */
case USB_REQ_GET_DESCRIPTOR:
switch (w_value >> 8) {
case USB_DT_CONFIG:
value = config_desc(cdev, w_value);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
{
...//根据w_value(配置索引号,一般为0)获取配置
return config_buf(c, speed, cdev->req->buf, type);
}


static int config_buf(struct usb_configuration *config,
enum usb_device_speed speed, void *buf, u8 type)
{
c = buf;
c->bLength = USB_DT_CONFIG_SIZE;
c->bDescriptorType = type;
/* wTotalLength is written later */
c->bNumInterfaces = config->next_interface_id;
c->bConfigurationValue = config->bConfigurationValue;
c->iConfiguration = config->iConfiguration;
c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
c->bMaxPower = encode_bMaxPower(speed, config);
}

接口数量

c->bNumInterfaces = config->next_interface_id

next_interface_id在usb_interface_id中修改,每调用一次usb_interface_id,next_interface_id加1

usb_interface_id在各个function的bind_config函数中调用

f_rndis的interface数量为2,包含控制接口和数据接口

1
2
3
4
5
6
7
8
9
10
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
rndis->ctrl_id = status;
...
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
rndis->data_id = status;

f_rmnet的interface数量可变,具体如下:

  1. 使能android接口之前需要设置rmnet_transports(f_rmnet/transports文档)
  2. rmnet_function_bind_config 遍历rmnet_transports配置,执行frmnet_init_port
  3. frmnet_init_port初始化rmnet_port,记录port总数量
  4. bind:每个端口执行一次frmnet_bind_config , frmnet_bind —> usb_interface_id,为每个端口添加一个interface

​ rmnet用为拨号接口有更复杂的其它功能,不在此描述

f_diag的interface数量可变,具体如下:

  1. 使能android接口之前需要设置diag_clients(f_diag/clients文档)
  2. diag_function_bind_config遍历diag_clients配置,执行diag_function_add
  3. bind:diag_function_bind —> usb_interface_id,为每个配置添加一个interface

f_serail的interface数量可变,具体如下:

  1. 使能android接口之前需要设置serial_transports(f_serial/transports文档)

  2. serial_function_bind_config遍历serial_transports配置,执行gserial_init_port

  3. gserial_init_port初始化gserial_ports,记录port总数量

  4. bind: 每个端口执行一次gser_alloc,gser_bind —> usb_interface_id,为每个端口添加一个interface

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    for (i = 0; i < ports; i++) {
    config->f_serial_inst[i] = usb_get_function_instance("gser");
    if (IS_ERR(config->f_serial_inst[i])) {
    err = PTR_ERR(config->f_serial_inst[i]);
    goto err_gser_usb_get_function_instance;
    }
    config->f_serial[i] = usb_get_function(config->f_serial_inst[i]);
    if (IS_ERR(config->f_serial[i])) {
    err = PTR_ERR(config->f_serial[i]);
    goto err_gser_usb_get_function;
    }
    }

    serial_initialized = 1;

    bind_config:
    for (i = 0; i < ports; i++) {
    err = usb_add_function(c, config->f_serial[i]);
    if (err) {
    pr_err("Could not bind gser%u confign", i);
    goto err_gser_usb_add_function;
    }
    }

接口编号

host端的驱动根据device端的接口编号来匹配

接口编号按照注册顺序生成(遍历functions),比如:

1
2
3
4
echo diag > f_diag/clients
echo tty,smd,smd > f_serial/transports
echo QTI,BAM_DMUX > f_rmnet/transports
echo diag,serial,rmnet > functions

编号0:diag

编号1: tty

编号2:smd

编号3:smd

编号4:rmnet

endpoint

从usb 主机到设备称为 out 端点,从设备到主机称为in 端点。

创建endpoint

udc初始化时会创建endpoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
for (i = 0; i < hw_ep_max/2; i++) {
for (j = RX; j <= TX; j++) {
int k = i + j * hw_ep_max/2;
struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k];

scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i,
(j == TX) ? "in" : "out");

mEp->lock = udc->lock;
mEp->device = &udc->gadget.dev;
mEp->td_pool = udc->td_pool;

mEp->ep.name = mEp->name;
mEp->ep.ops = &usb_ep_ops;
usb_ep_set_maxpacket_limit(&mEp->ep,
k ? USHRT_MAX : CTRL_PAYLOAD_MAX);

INIT_LIST_HEAD(&mEp->qh.queue);
mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL,
&mEp->qh.dma);
if (mEp->qh.ptr == NULL)
retval = -ENOMEM;
else
memset(mEp->qh.ptr, 0, sizeof(*mEp->qh.ptr));

/* skip ep0 out and in endpoints */
if (i == 0)
continue;

list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list);
}
}

创建的endpoint由adget.ep_list管理

1
2
3
4
5
6
7
8
9
10
11
static const struct usb_ep_ops usb_ep_ops = {
.enable = ep_enable,
.disable = ep_disable,
.alloc_request = ep_alloc_request,
.free_request = ep_free_request,
.queue = ep_queue,
.dequeue = ep_dequeue,
.set_halt = ep_set_halt,
.set_wedge = ep_set_wedge,
.fifo_flush = ep_fifo_flush,
};

重点关注usb_ep_ops

申请endpoint

每个接口(interface)bind时会申请endpoint,比如:

1
2
3
4
5
ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
if (!ep)
goto fail;
gser->port.in = ep;
ep->driver_data = cdev; /* claim */

数据通讯

host端的控制请求响应
udc_irq —> isr_tr_complete_handler —> udc->driver->setup
composite_setup(struct usb_gadget gadget, const struct usb_ctrlrequest ctrl)

​ composite_setup实现通用的控制命令,function可以扩展实现更多的控制命令

host端数据传输 -> device端

​ udc_irq —> isr_tr_complete_low —> mReq->req.complete

usb的数据通讯基于endpoint,每个endpoint都一个地址,双向通过这个地址通讯

传输数据之前,需要申请usb_request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
{
struct usb_request *req;

req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req) {
req->length = len ?: default_len;
req->buf = kmalloc(req->length, GFP_ATOMIC);
if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
}
}
return req;
}

发送数据(in端点)

填充req的数据,举例:smd_read(pi->ch, req->buf, avail);

调用usb_ep_queue发送

1
2
3
4
5
static inline int usb_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags)
{
return ep->ops->queue(ep, req, gfp_flags);
}

发送完成:执行req->complete

接收数据(out端点)

执行req->complete

参考

usb gadget usb host数据传输

USB中的端点详细了解