8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
介绍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举例初始化gadget
ci13xxx_msm_probe->udc_probe1
2
3
4
5
6
7
8
9
10
11
12udc = 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创建&添加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
14udc = 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设备从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
3snprintf(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初始化设备(android0)默认支持的功能(function)1
2
3
4android_dev->name = pdev->name;
android_dev->disable_depth = 1;
android_dev->functions =
supported_list ? supported_list : default_functions;
其中supported_list从设备树获取,默认为空,既使用default_functions绑定udc设备
usb_composite_probe(&android_usb_driver)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18int 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
udc_bind_to_driver
ret = driver->bind(udc->gadget, driver);
执行composite_bind
composite_bind
创建 usb_composite_dev设备1
2cdev = kzalloc(sizeof *cdev, GFP_KERNEL)
cdev->gadget = gadget;
执行usb_composite_driver的bind,此处是android_bind
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添加配置
usb_add_config(cdev, &conf->usb_config, android_bind_config);
android_bind_config —> android_bind_enabled_functions
遍历配置里的所有function,执行相应的bind_config
连接,触发host端的连接请求
usb_gadget_connect(cdev->gadget);gadget->ops->pullup(gadget, 1)
gadget配置
host端请求代码:USB_REQ_GET_DESCRIPTOR –> USB_DT_DEVICE1
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
42int 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_setup1
2
3
4
5
6switch (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
20static 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数量可变,具体如下:使能android接口之前需要设置rmnet_transports(f_rmnet/transports文档)
rmnet_function_bind_config 遍历rmnet_transports配置,执行frmnet_init_port
frmnet_init_port初始化rmnet_port,记录port总数量
bind:每个端口执行一次frmnet_bind_config , frmnet_bind —> usb_interface_id,为每个端口添加一个interface
rmnet用为拨号接口有更复杂的其它功能,不在此描述
f_diag的interface数量可变,具体如下:使能android接口之前需要设置diag_clients(f_diag/clients文档)
diag_function_bind_config遍历diag_clients配置,执行diag_function_add
bind:diag_function_bind —> usb_interface_id,为每个配置添加一个interface
f_serail的interface数量可变,具体如下:使能android接口之前需要设置serial_transports(f_serial/transports文档)
serial_function_bind_config遍历serial_transports配置,执行gserial_init_port
gserial_init_port初始化gserial_ports,记录port总数量
bind: 每个端口执行一次gser_alloc,gser_bind —> usb_interface_id,为每个端口添加一个interface1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23for (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
4echo 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初始化时会创建endpoint1
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
32for (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
11static 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
5ep = 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_ctrlrequestctrl)
composite_setup实现通用的控制命令,function可以扩展实现更多的控制命令
host端数据传输 -> device端
udc_irq —> isr_tr_complete_low —> mReq->req.complete
usb的数据通讯基于endpoint,每个endpoint都一个地址,双向通过这个地址通讯
传输数据之前,需要申请usb_request1
2
3
4
5
6
7
8
9
10
11
12
13
14
15struct 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
5static 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
参考