linux驱动绑定设备 命令 bind,linux gadget 驱动
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
参考
linux驱动绑定设备 命令 bind,linux gadget 驱动相关推荐
- linux驱动绑定设备 命令 bind,Linux设备驱动节点里的bind与unbind
在Linux环境下,如果有两张网卡,假如A识别为eth0,B识别为eth1,而有些时候想对调设备结点,有如下办法可以解决该问题: 1.确认驱动对应的设备结点 网卡A: ls /sys/devices/ ...
- Linux 查看PCI设备命令---lspci
Linux 查看PCI设备命令-lspci 选项与参数: -v :显示更多的 PCI 接口装置的详细信息 -vv :比 -v 还要更详细的信息 -n :直接观察 PCI 的 ID 而不是厂商名称 查看 ...
- android设备安装kali,在Android设备上安装Kali Linux基于Android设备的Kali Linux渗透测试教程大学霸...
在Android设备上安装Kali Linux基于Android设备的Kali Linux渗透测试教程大学霸 基于Android设备的Kali Linux渗透测试教程2 Android是一种基于Lin ...
- STM32MP157驱动开发——设备树下的LED驱动
STM32MP157驱动开发--设备树下的LED驱动 主要内容:将之前章节中使用新设备设备驱动编写的LED驱动改成设备树形式 文章目录 STM32MP157驱动开发--设备树下的LED驱动 一.主要步 ...
- USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)
1.概述 USB设备控制器(UDC)驱动的框图如下图所示,由三部分组成.第一部分是UDC驱动核心层,在drivers/usb/gadget/udc/core.c文件中实现,该层是一个兼容层,将USB ...
- linux卸载cf卡命令,嵌入式Linux 中CF卡的驱动和管理技术研究
在嵌入式Linux系统中,为了在没有PCMCIA控制器的情况下仍然要利用CompactFlash存储卡(简称CF卡)作为存储设备,作者从CF卡的硬件特性入手,在系统层基于CF卡的memory寻址访问方 ...
- Linux下的硬件驱动——USB设备(上)(驱动配置部分)
USB设备越来越多,而Linux在硬件配置上仍然没有做到完全即插即用,对于Linux怎样配置和使用他们,也越来越成为困扰我们的一大问题.本文着力从Linux系统下设备驱动的架构,去阐述怎样去使用和配置 ...
- linux命令 bind,Linux bind命令
# Linux bind命令 [![Linux 命令大全](/images/1590082124592.gif "Linux 命令大全") Linux 命令大全](/queryGr ...
- linux重启网卡的命令行,linux系统重启网卡命令
关于win10系统开机总是要禁用重启网卡才能联网如何解决就为大家介绍到这边了,有遇到同样情况的用户们可以采取上面的方法步骤来解决.问题2已知win7下不需要删除qos协议,在跑无盘模式下以前最好不要安 ...
最新文章
- Webpy 0.3新手指南
- CSS的display:none与visible:hidden区别
- Spring框架—基础介绍
- C# WPF文本框TextEdit不以科学计数法显示
- 深入浅出分析MySQL常用存储引擎
- 各种常用排序算法的时间复杂度和空间复杂度
- ios 筛选_LOL手游开始筛选玩家对安卓和IOS有不同要求,不达标可以放弃了
- PSD分层模板,助你设计出有创意的圣诞海报
- 巧用vim+sed整理shell脚本文件
- 为了搞懂什么是区块链,我都快抑郁了(转)
- 内存泄露与内存溢出的区别
- 老实说,WPF对自由开发者与小微型团体来说就是个毒瘤!
- 使用jquery简化ajax开发
- JavaScript继承详解
- [转载] Python语言程序设计基础(第二版)嵩天等课后习题答案
- python3d立体相册代码_Python 30 行代码画各种 3D 图形
- 单相PWM整流器工作原理与调制方向判断
- Oracle数据库 | Oracle备份实例
- VS.Net 2005 下载地址
- 金蝶服务器组件无法正常工作,K3组件kdsvrmgr无法正常工作