usb 是什么, usb 只是一种数据传输的方式, usb 的作用就是数据传输 , 我们接受到了数据时候做什么, 是我们自己决定, 在usb 中则通过描述符来确定这些数据要来干什么, 设备描述符用于描述当前是什么 usb 设备, 以及这个设备支持什么样的功能, 这个支持的功能(串口, uvc等)则由 function
来描述. 设备的描述符以及功能等由 usb_composite_dev
进行统一的管理维护.
configfs
则将这种管理进行动态化, 使得我们能够动态的去配置我们的 usb 为特定的 usb 设备. 当然我们也可以不用 configfs
自己去写死 usb 支持的功能, 甚至驱动不做处理仅仅导出 libusb
库, 应用程序自己实现 usb 设备支持的功能的各种描述符. 因为 usb 只是数据传输, 具体的功能实现可以是驱动也可以应用, 取决与开发的需求.本文将以 configfs
以及串口设备为例分析 usb
做 device
的驱动框架.
阅读这篇文章之前请先阅读我博客里面的 linux usb 驱动 - configfs 文件系统 这篇文章 , 因为后文都是默认已经知道了 configfs 文件系统的调用流程, 不会对 configfs 调用流程做过多解释.
一、 usb device 驱动框架
USB 有两种基本模式: Host 模式和 Device 模式.
在 Host 模式下,设备作为主机,负责控制 USB 总线并管理与外设的通信, 通常是电脑、智能手机等主控设备, 它们发起通信并配置外设.
在 Device 模式下, 设备作为外设, 等待主机发起通信请求并提供特定功能, 例如 USB 存储设备、键盘、鼠标等.
USB 还有一个 OTG(On-The-Go) 模式, 它允许设备根据需要在 Host 模式和 Device 模式之间切换. 模式的切换通常是通过 id 引脚来实现的, 在 usb otg 模式中, id 引脚的电平状态决定了设备是 Host 还是 Device 模式 . 一般情况 id 脚逻辑低表示 Host , id 脚逻辑高表示高电平. 具体由 usb 控制器的实现来决定.
usb device 的框架整体分为 3 层, gadget、compiste、function、用户空间通过 configs 对它们进行配置以及交互.
Gadget 实现了 usb 设备的底层数据传输和通信等功能
Composite 则通过 usb_composite_dev 数据结构用来描述一个完成的 usb 设备 , 当于 host 端的 usb device. 他是整体的描述.
function 层描述了具体的 usb 设备接口 , 如 usb 存储设备、网络适配器、音频设备等. 它对应 host 端的接口设备.
configfs 则是用户可以动态的对 usb 进行配置 , 它包括 usb_composite_dev 中对设备描述符的配置、function 中具体的功能的配置等, 同时将这些属性信息导出到用户空间.
二、 gadget
gadget 的分配和初始化流程如下图所示.
注册流程就是对核心的数据结构进行初始化, 核心调用接口如下.
1 2 3 4 5 6 7 8 9 10 11 12 dwc2_init () --> hsotg = devm_kzalloc (); --> dwc2_gadget_init () --> hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; --> hsotg->ctrl_buff = devm_kzalloc (); --> hsotg->ep0_buff = devm_kzalloc (); --> ret = devm_request_irq (); --> hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request (); --> for (epnum = 0 ; epnum < hsotg->num_of_eps; epnum++) --> dwc2_hsotg_initep (); usb_add_gadget_udc () --> usb_add_gadget_udc_release () -->
分配 hostg
结构, gadget
作为其成员变量在这里分配.
设置 gadget.ops
回调函数, 这个回调就是 hcd
相关的底层接口
分配 ctrl_buff
和 ep0_buff
用来做端点分配控制传输的 buffer
注册 gadget
中断, 为控制传输分配 ctrl_req
dwc2_hsotg_initep
为每一个端点分配内存, 即分配 eps_in[epnum]
数组. 有个小细节, ep0
并没有链接到 gadget->ep_list
.
分配 usb_udc
将其链接到 udc_list
链表, 注册对应设备到设备模型
更新 udc 电源状态, 设置 udc->gadget = gadget
和 adget->udc = udc
注册完成之后各个数据结构关系如下所示.
这个数据结构图很重要, 通过这个图我们可以总结出一下信息:
所有的端点公用 dwc2_hsotg_ep_ops
操作接口, 它提供了端点寄存器相关的底层操作
所有的端点由 dwc2_hsotg
进行统一管理, 统一被存放到 eps_in[]/eps_out[]
数组
gadget 向 udc 提供 dwc2_hsotg_gadget_ops
接口用于 udc 基本的初始化等操作.
这个阶段是自动创建的, 配置好 dts 之后, 开机的时候会自动加载 usb 的 udc 驱动.这部分配置每个平台都会有点区别, 具体需要找平台要配置手册.
三、配置 usb 串口
Linux3.11 版本配引入了 configfs 之后, usb 设备的配置就通过 configfs 配置. 本文以 usb 串口配置为了进行分析. 这个脚本完整的展示了一个串口 usb 设备的配置流程, 它分为以下步骤: 创建配置根目录 、配置设备描述符 、配置配置描述符 、配置功能描述符 、配置字符串描述符 、关联 compiste 和 gadget
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 43 echo "Creating the USB gadget..." mount -t configfs none /sys/kernel/config mkdir /sys/kernel/config/usb_gadget/serial_demo cd /sys/kernel/config/usb_gadget/serial_demo echo "Setting Device Descriptor..." echo "0x02" > bDeviceClass echo "0x00" > bDeviceSubClass echo "0x00" > bDeviceProtocol echo "0x0200" > bcdUSB echo "0x2400" > bcdDevice echo $VID > idVendor echo $PID > idProduct echo "Setting English strings..." mkdir strings/0x409 echo "INGENIC" > strings/0x409 /manufacturer echo "Gadget Serial v2.4" > strings/0x409 /product echo "ingenic-serial" > strings/0x409 /serialnumber echo "Creating Config..." mkdir configs/c.1 echo "120" > configs/c.1 /MaxPower echo "0x80" > configs/c.1 /bmAttributes mkdir configs/c.1 /strings/0x409 echo "INGENIC" > configs/c.1 /strings/0x409 /configuration echo "Creating functions..." mkdir functions/acm.0 ln -s functions/acm.0 configs/c.1 echo `ls /sys/class /udc/` > UDC
四、compiste
composite 的核心数据结构是 usb_composite_dev
它用来管理并描述具体的 usb 设备. 和 composite 相关的配置流程有, 创建配置根目录 、配置设备描述符 、配置字符串描述符 、配置配置描述符 .
1、创建配置根目录
1 mkdir /sys/kernel/config/usb_gadget/serial_demo
这个操作是创建一个 configfs 文件系根目录, 由 configfs 文件系统的特性可以知道会回调到 gadgets_make 接口. 调用流程如下.
1 2 3 4 5 6 7 mkdir /sys/kernel/config/usb_gadget/serial_demo --> syscall_common () --> sys_mkdirat () --> vfs_mkdir () --> dir->i_op->mkdir () --> configfs_mkdir () --> type->ct_group_ops->make_group () -->
gadgets_make 是一个关键接口, 我们看看他的实现.
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 static struct config_group *gadgets_make ( struct config_group *group, const char *name) { struct gadget_info *gi; printk ("%s\n" , __func__); gi = kzalloc (sizeof (*gi), GFP_KERNEL); if (!gi) return ERR_PTR (-ENOMEM); gi->group.default_groups = gi->default_groups; gi->group.default_groups[0 ] = &gi->functions_group; gi->group.default_groups[1 ] = &gi->configs_group; gi->group.default_groups[2 ] = &gi->strings_group; gi->group.default_groups[3 ] = &gi->os_desc_group; config_group_init_type_name (&gi->functions_group, "functions" , &functions_type); config_group_init_type_name (&gi->configs_group, "configs" , &config_desc_type); config_group_init_type_name (&gi->strings_group, "strings" , &gadget_strings_strings_type); config_group_init_type_name (&gi->os_desc_group, "os_desc" , &os_desc_type); gi->composite.bind = configfs_do_nothing; gi->composite.unbind = configfs_do_nothing; gi->composite.suspend = NULL ; gi->composite.resume = NULL ; gi->composite.max_speed = USB_SPEED_SUPER; spin_lock_init (&gi->spinlock); mutex_init (&gi->lock); INIT_LIST_HEAD (&gi->string_list); INIT_LIST_HEAD (&gi->available_func); composite_init_dev (&gi->cdev); gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE; gi->cdev.desc.bDescriptorType = USB_DT_DEVICE; gi->cdev.desc.bcdDevice = cpu_to_le16 (get_default_bcdDevice ()); gi->composite.gadget_driver = configfs_driver_template; gi->composite.gadget_driver.function = kstrdup (name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; if (!gi->composite.gadget_driver.function) goto err; if (android_device_set_drvdata (gi) < 0 ) goto err; config_group_init_type_name (&gi->group, name, &gadget_root_type); return &gi->group; err: kfree (gi); return ERR_PTR (-ENOMEM); }
函数调用完成之后将会创建以 usb_composite_dev 为中心的 compiste 层对应的数据结构关系.
总结一下函数的功能:
分配 gadget_info 数据结构, usb_composite_driver
和usb_composite_dev
作为其成员变量同时被分配
设置 functions
、configs
、strings
、os_desc
等默认的子目录配置项.即创建子目录与子目录的属性文件.
简单初始化设备描述符 cdev.desc
的基本信息
初始化 composite
并且填充 gadget_driver
2、配置设备描述符
1 2 3 4 5 6 7 8 9 echo "Setting Device Descriptor..." echo "0x02" > bDeviceClass echo "0x00" > bDeviceSubClass echo "0x00" > bDeviceProtocol echo "0x0200" > bcdUSB echo "0x2400" > bcdDevice echo $VID > idVendor echo $PID > idProduct
我们在内核中找到对应的属性接口的定义.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 GI_DEVICE_DESC_SIMPLE_R_u16 (bcdUSB);GI_DEVICE_DESC_SIMPLE_RW (bDeviceClass, u8);GI_DEVICE_DESC_SIMPLE_RW (bDeviceSubClass, u8);GI_DEVICE_DESC_SIMPLE_RW (bDeviceProtocol, u8);GI_DEVICE_DESC_SIMPLE_RW (bMaxPacketSize0, u8);GI_DEVICE_DESC_SIMPLE_RW (idVendor, u16);GI_DEVICE_DESC_SIMPLE_RW (idProduct, u16);GI_DEVICE_DESC_SIMPLE_R_u16 (bcdDevice);#define GI_DEVICE_DESC_SIMPLE_RW(_name, _type) \ GI_DEVICE_DESC_SIMPLE_R_##_type(_name) \ GI_DEVICE_DESC_SIMPLE_W_##_type(_name) #define GI_DEVICE_DESC_SIMPLE_R_u16(__name) \ static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \ char *page) \ { \ return sprintf(page, "0x%04x\n" , \ le16_to_cpup(&to_gadget_info(item)->cdev.desc.__name)); \ }
我们把这个展开之后发现, 应用配置配置配置描述符 , 就是通过属性文件直接读写 usb_composite_dev
的 usb_device_descriptor
设备描述符结构体的成员变量.
3、配置字符串描述符
1 2 3 4 5 6 echo "Setting English strings..." mkdir strings/0x409 echo "INGENIC" > strings/0x409 /manufacturer echo "Gadget Serial v2.4" > strings/0x409 /product echo "ingenic-serial" > strings/0x409 /serialnumber
1. strings
strings 目录在这里有点特殊单独讨论, strings 从名字可以知道是对字符串进行管理. configs 将字符串管理的 api 进行了统一, 对应的在 gadget_configfs.h
这头文件里面.
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 #ifndef __GADGET_CONFIGFS__ #define __GADGET_CONFIGFS__ #include <linux/configfs.h> int check_user_usb_string (const char *name, struct usb_gadget_strings *stringtab_dev) ;#define GS_STRINGS_W(__struct, __name) \ static ssize_t __struct##_##__name##_store(struct config_item *item, \ const char *page, size_t len) \ { \ struct __struct *gs = to_##__struct(item); \ int ret; \ \ ret = usb_string_copy(page, &gs->__name); \ if (ret) \ return ret; \ return len; \ } #define GS_STRINGS_R(__struct, __name) \ static ssize_t __struct##_##__name##_show(struct config_item *item, char *page) \ { \ struct __struct *gs = to_##__struct(item); \ return sprintf(page, "%s\n" , gs->__name ?: "" ); \ } #define GS_STRINGS_RW(struct_name, _name) \ GS_STRINGS_R(struct_name, _name) \ GS_STRINGS_W(struct_name, _name) \ CONFIGFS_ATTR(struct_name##_, _name) #define USB_CONFIG_STRING_RW_OPS(struct_in) \ static struct configfs_item_operations struct_in##_langid_item_ops = { \ .release = struct_in##_attr_release, \ }; \ \ static struct config_item_type struct_in##_langid_type = { \ .ct_item_ops = &struct_in##_langid_item_ops, \ .ct_attrs = struct_in##_langid_attrs, \ .ct_owner = THIS_MODULE, \ } #define USB_CONFIG_STRINGS_LANG(struct_in, struct_member) \ static struct config_group *struct_in##_strings_make( \ struct config_group *group, \ const char *name) \ { \ struct struct_member *gi; \ struct struct_in *gs; \ struct struct_in *new; \ int langs = 0; \ int ret; \ \ new = kzalloc(sizeof(*new), GFP_KERNEL); \ if (!new) \ return ERR_PTR(-ENOMEM); \ \ ret = check_user_usb_string(name, &new->stringtab_dev); \ if (ret) \ goto err; \ config_group_init_type_name(&new->group, name, \ &struct_in##_langid_type); \ \ gi = container_of(group, struct struct_member, strings_group); \ ret = -EEXIST; \ list_for_each_entry(gs, &gi->string_list, list) { \ if (gs->stringtab_dev.language == new->stringtab_dev.language) \ goto err; \ langs++; \ } \ ret = -EOVERFLOW; \ if (langs >= MAX_USB_STRING_LANGS) \ goto err; \ \ list_add_tail(&new->list, &gi->string_list); \ return &new->group; \ err: \ kfree(new); \ return ERR_PTR(ret); \ } \ \ static void struct_in##_strings_drop( \ struct config_group *group, \ struct config_item *item) \ { \ config_item_put(item); \ } \ \ static struct configfs_group_operations struct_in##_strings_ops = { \ .make_group = &struct_in##_strings_make, \ .drop_item = &struct_in##_strings_drop, \ }; \ \ static struct config_item_type struct_in##_strings_type = { \ .ct_group_ops = &struct_in##_strings_ops, \ .ct_owner = THIS_MODULE, \ } #endif
这个要配合 configfs 的机制来看, 直接看它的使用实例.
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 43 44 45 46 47 GS_STRINGS_RW (gadget_strings, manufacturer); GS_STRINGS_RW (gadget_strings, product); GS_STRINGS_RW (gadget_strings, serialnumber); static struct configfs_attribute *gadget_strings_langid_attrs[] = { &gadget_strings_attr_manufacturer, &gadget_strings_attr_product, &gadget_strings_attr_serialnumber, NULL , }; static void gadget_strings_attr_release (struct config_item *item) { struct gadget_strings *gs = to_gadget_strings (item); kfree (gs->manufacturer); kfree (gs->product); kfree (gs->serialnumber); list_del (&gs->list); kfree (gs); } USB_CONFIG_STRING_RW_OPS (gadget_strings);USB_CONFIG_STRINGS_LANG (gadget_strings, gadget_info);static struct config_group *gadgets_make (struct config_group *group, const char *name){ ...... config_group_init_type_name (&gi->strings_group, "strings" , &gadget_strings_strings_type); ...... }
这里的描述看不懂, 可以参考linux usb 驱动 - configfs 文件系统 这篇文章, 配合源码一起看,因为这里涉及到 configfs 的机制可能有点难以理解, 没关系我们直接给出总结.
2. strings 总结
configs 中的 strings 子目录下创建的子目录默认支持 manufacturer
, manufacturer
, manufacturer
这三个属性文件.
这三个属性文件对应的属性 在 gadget_strings
这个数据结构中, 它们链接到了gadget_info->string_list
链表.可以通过这个链表找到它.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct gadget_strings { struct usb_gadget_strings stringtab_dev; struct usb_string strings[USB_GADGET_FIRST_AVAIL_IDX]; char *manufacturer; char *product; char *serialnumber; struct config_group group; struct list_head list; }; struct usb_gadget_strings { u16 language; struct usb_string *strings; };
strings 目录的子目录的目录名用于表示支持的语言, 必须符合指定的格式 , 他会通过 check_user_usb_string
函数进行检查. 表格给出了一些常见的国家.
语言
语言标识符(Language ID)
英语(美国)
0x0409
中文(简体)
0x0804
法语
0x040c
德语
0x0407
西班牙语
0x0c0a
注: 这里说的 strings 目录是 /sys/kernel/config/usb_gadget/xxx/strings
目录, 不是 configs/c.1/strings/
目录.
strings 目录配置完之后的 composite 数据结构增加了 gadget_strings
.
4、配置配置描述符
1 2 3 4 5 6 7 echo "Creating Config..." mkdir configs/c.1 echo "120" > configs/c.1 /MaxPower echo "0x80" > configs/c.1 /bmAttributes
这里分为两步首先是创建 configs/c.1 这个目录, 最终回调 config_desc_make
这个接口
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 static struct config_group *config_desc_make ( struct config_group *group, const char *name) { struct gadget_info *gi; struct config_usb_cfg *cfg; char buf[MAX_NAME_LEN]; char *num_str; u8 num; int ret; gi = container_of (group, struct gadget_info, configs_group); ret = snprintf (buf, MAX_NAME_LEN, "%s" , name); if (ret >= MAX_NAME_LEN) return ERR_PTR (-ENAMETOOLONG); num_str = strchr (buf, '.' ); if (!num_str) { pr_err ("Unable to locate . in name.bConfigurationValue\n" ); return ERR_PTR (-EINVAL); } *num_str = '\0' ; num_str++; if (!strlen (buf)) return ERR_PTR (-EINVAL); ret = kstrtou8 (num_str, 0 , &num); if (ret) return ERR_PTR (ret); cfg = kzalloc (sizeof (*cfg), GFP_KERNEL); if (!cfg) return ERR_PTR (-ENOMEM); cfg->c.label = kstrdup (buf, GFP_KERNEL); if (!cfg->c.label) { ret = -ENOMEM; goto err; } cfg->c.bConfigurationValue = num; cfg->c.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW; cfg->c.bmAttributes = USB_CONFIG_ATT_ONE; INIT_LIST_HEAD (&cfg->string_list); INIT_LIST_HEAD (&cfg->func_list); cfg->group.default_groups = cfg->default_groups; cfg->default_groups[0 ] = &cfg->strings_group; config_group_init_type_name (&cfg->group, name, &gadget_config_type); config_group_init_type_name (&cfg->strings_group, "strings" , &gadget_config_name_strings_type); ret = usb_add_config_only (&gi->cdev, &cfg->c); if (ret) goto err; return &cfg->group; err: kfree (cfg->c.label); kfree (cfg); return ERR_PTR (ret); } static struct configfs_attribute *gadget_config_attrs[] = { &gadget_config_desc_attr_MaxPower, &gadget_config_desc_attr_bmAttributes, NULL , }; static struct config_item_type gadget_config_type = { .ct_item_ops = &gadget_config_item_ops, .ct_attrs = gadget_config_attrs, .ct_owner = THIS_MODULE, };
在 confgs 目录下创建的子目录的名称 c.1
中的数字(如 .1)表示配置编号,而前缀(如 c、a、xxx)可以随意更改, 只要确保每个配置编号唯一即可.
创建默认的属性文件 MaxPower
和 bmAttributes
创建子目录 strings, 以及其属性文件
ls 查看生成的目录
1 2 3 4 # pwd /sys/kernel/config/usb_gadget/serial_demo/configs/c.1 # ls MaxPower acm.0 bmAttributes strings
然后在 strings 目录下创建 0x409 目录表示配置支持英文.
1 2 mkdir configs/c.1 /strings/0x409 # 为配置创建语言描述符 echo "INGENIC" > configs/c.1 /strings/0x409 /configuration # 设置配置的描述符
它对应的内核代码如下.
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 43 44 45 struct gadget_config_name { struct usb_gadget_strings stringtab_dev; struct usb_string strings; char *configuration; struct config_group group; struct list_head list; }; struct config_usb_cfg { struct config_group group; struct config_group strings_group; struct config_group *default_groups[2 ]; struct list_head string_list; struct usb_configuration c; struct list_head func_list; struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1 ]; }; static struct configfs_attribute *gadget_config_name_langid_attrs[] = { &gadget_config_name_attr_configuration, NULL , }; static void gadget_config_name_attr_release (struct config_item *item) { struct gadget_config_name *cn = to_gadget_config_name (item); kfree (cn->configuration); list_del (&cn->list); kfree (cn); } USB_CONFIG_STRING_RW_OPS (gadget_config_name);USB_CONFIG_STRINGS_LANG (gadget_config_name, config_usb_cfg);static struct config_group *config_desc_make (struct config_group *group, const char *name){ ...... config_group_init_type_name (&cfg->strings_group, "strings" , &gadget_config_name_strings_type); ...... }
这个和前面是一样的, 也就是 configs
下的子目录 c.1
的语言描述目录, 它只支持 configuration
这个属性.配置完成之后数据结构关系如下所示.
这个涉及到具体的 function 配置, 后文会详述这里为了完整性先给出.
五、function
usb 做 device 的时候具体的功能描述, 相当于 host 端的接口设备.通过 configs 配置功能描述符 .
1 2 3 4 5 echo "Creating functions..." mkdir functions/acm.0 ln -s functions/acm.0 configs/c.1
首先是通过 make 创建目录配置项, 最终调用到 function_make 函数.
1、 function_make
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 static struct config_group *function_make ( struct config_group *group const char *name){ struct gadget_info *gi; struct usb_function_instance *fi; char buf[MAX_NAME_LEN]; char *func_name; char *instance_name; int ret; ret = snprintf (buf, MAX_NAME_LEN, "%s" , name); if (ret >= MAX_NAME_LEN) return ERR_PTR (-ENAMETOOLONG); func_name = buf; instance_name = strchr (func_name, '.' ); if (!instance_name) { pr_err ("Unable to locate . in FUNC.INSTANCE\n" ); return ERR_PTR (-EINVAL); } *instance_name = '\0' ; instance_name++; fi = usb_get_function_instance (func_name); if (IS_ERR (fi)) return ERR_CAST (fi); ret = config_item_set_name (&fi->group.cg_item, "%s" , name); if (ret) { usb_put_function_instance (fi); return ERR_PTR (ret); } if (fi->set_inst_name) { ret = fi->set_inst_name (fi, instance_name); if (ret) { usb_put_function_instance (fi); return ERR_PTR (ret); } } gi = container_of (group, struct gadget_info, functions_group); mutex_lock (&gi->lock); list_add_tail (&fi->cfs_list, &gi->available_func); mutex_unlock (&gi->lock); return &fi->group; }
这个函数解析传入的 “acm.0” , 然后以 “acm” 作为参数调用 usb_get_function_instance
, 这个函数用于返回 usb_function_instance
结构, 然后将这个数据结构链接到 gadget_info
.
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 struct usb_function_instance *usb_get_function_instance (const char *name){ struct usb_function_instance *fi; int ret; fi = try_get_usb_function_instance (name); if (!IS_ERR (fi)) return fi; ret = PTR_ERR (fi); if (ret != -ENOENT) return fi; ret = request_module ("usbfunc:%s" , name); if (ret < 0 ) return ERR_PTR (ret); return try_get_usb_function_instance (name); } EXPORT_SYMBOL_GPL (usb_get_function_instance);
usb_get_function_instance
先尝试使用已有的驱动, 在 func_list
中查找 name
为 acm
对应的 usb_function_driver
, 调用 name
为 acm
对应的 usb_function_driver->fd->alloc_inst()
创建一个 fi
.
从这里我们就可以知道, 我们需要提前往 func_list
中注册一个 usb_function_driver
用于分配我们需要的 usb_function_instance
.
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 43 44 45 46 47 #define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \ static struct usb_function_driver _name ## usb_func = { \ .name = __stringify(_name), \ .mod = THIS_MODULE, \ .alloc_inst = _inst_alloc, \ .alloc_func = _func_alloc, \ }; \ MODULE_ALIAS("usbfunc:" __stringify(_name)); #define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \ DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \ static int __init _name ## mod_init(void) \ { \ return usb_function_register(&_name ## usb_func); \ } \ static void __exit _name ## mod_exit(void) \ { \ usb_function_unregister(&_name ## usb_func); \ } \ module_init(_name ## mod_init); \ module_exit(_name ## mod_exit) int usb_function_register (struct usb_function_driver *newf) { struct usb_function_driver *fd; int ret; ret = -EEXIST; mutex_lock (&func_lock); list_for_each_entry (fd, &func_list, list) { if (!strcmp (fd->name, newf->name)) goto out; } ret = 0 ; list_add_tail (&newf->list, &func_list); out: mutex_unlock (&func_lock); return ret; } EXPORT_SYMBOL_GPL (usb_function_register);
展开之后可以知道 DECLARE_USB_FUNCTION
宏创建 usb_function_driver
结构体并设置 alloc_inst
和 alloc_func
回调函数, 然后将它注册到 func_list
链表中. 在 usb
串口驱动 f_acm.c
中注册.
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 static struct usb_function_instance *acm_alloc_instance (void ){ struct f_serial_opts *opts; int ret; opts = kzalloc (sizeof (*opts), GFP_KERNEL); if (!opts) return ERR_PTR (-ENOMEM); opts->func_inst.free_func_inst = acm_free_instance; ret = gserial_alloc_line (&opts->port_num); if (ret) { kfree (opts); return ERR_PTR (ret); } config_group_init_type_name (&opts->func_inst.group, "" , &acm_func_type); return &opts->func_inst; }
2、function_make 总结
这个函数解析传入的 “acm.0” , 然后以 “acm” 作为参数调用 usb_get_function_instance
, 这个函数在 func_list 中查找 “acm” 对应的 usb_function_driver
, 调用 usb_function_driver->fd->alloc_inst()
创建一个 fi 并返回, 然后将这个数据结构链接到 gadget_info
.
alloc_inst
分配 f_serial_opts
数据结构, usb_function_instance
作为其成员变量同时被分配.
设置 usb 串口通讯模式为 8n1, usb 使用 usb_cdc_line_coding
数据结构描述串口模式.
分配 1 个 gs_port
, 并存放于 ports 数组中. f_serial_opts->num
作为数组索引, tty_port
作为其成员变量同时分配.
初始化 gs_port->read_pool
, gs_port->read_queue
, gs_port->write_pool
, 链表分别用于读写.
始化 tasklet
软中断 gs_port->push
, 用于接受数据, 回调函数为 gs_rx_push
使用 tty_port
分配的和 gs_tty_driver
注册进 tty core
生成 /dev/ttyGS*
节点, 最后返回分配的端口号 port_num
. 这个 port_num
很重要作为 tty_port
的索引. acm
驱动可以通过端口号找到对应的数据结构.
配置功能描述符之后的数据结构关系图如下所示
3. 关联 function 和 configs
1 ln -s functions/acm.0 configs/c.1
这个配置将 function 绑定到对应的配置描述符, 调用流程如下
1 2 3 4 configfs_symlink () --> type->ct_item_ops->allow_link () --> config_usb_cfg_link () -->
最终调用到 config_usb_cfg_link
接口.
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 43 44 45 46 47 static int config_usb_cfg_link (struct config_item *usb_cfg_ci, struct config_item *usb_func_ci) { struct config_usb_cfg *cfg = to_config_usb_cfg (usb_cfg_ci); struct usb_composite_dev *cdev = cfg->c.cdev; struct gadget_info *gi = container_of (cdev, struct gadget_info, cdev); struct config_group *group = to_config_group (usb_func_ci); struct usb_function_instance *fi = container_of (group, struct usb_function_instance, group); struct usb_function_instance *a_fi; struct usb_function *f; int ret; mutex_lock (&gi->lock); list_for_each_entry (a_fi, &gi->available_func, cfs_list) { if (a_fi == fi) break ; } if (a_fi != fi) { ret = -EINVAL; goto out; } list_for_each_entry (f, &cfg->func_list, list) { if (f->fi == fi) { ret = -EEXIST; goto out; } } f = usb_get_function (fi); if (IS_ERR (f)) { ret = PTR_ERR (f); goto out; } list_add_tail (&f->list, &cfg->func_list); ret = 0 ; out: mutex_unlock (&gi->lock); return ret; }
这个函数最终调用到 acm_alloc_func
函数
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 static struct usb_function *acm_alloc_func (struct usb_function_instance *fi){ struct f_serial_opts *opts; struct f_acm *acm; acm = kzalloc (sizeof (*acm), GFP_KERNEL); if (!acm) return ERR_PTR (-ENOMEM); spin_lock_init (&acm->lock); acm->port.connect = acm_connect; acm->port.disconnect = acm_disconnect; acm->port.send_break = acm_send_break; acm->port.func.name = "acm" ; acm->port.func.strings = acm_strings; acm->port.func.bind = acm_bind; acm->port.func.set_alt = acm_set_alt; acm->port.func.setup = acm_setup; acm->port.func.disable = acm_disable; opts = container_of (fi, struct f_serial_opts, func_inst); acm->port_num = opts->port_num; acm->port.func.unbind = acm_unbind; acm->port.func.free_func = acm_free_func; return &acm->port.func; }
关联的总结如下:
分配 f_acm
, 同时 gserial
作为其成员变量被分配, usb_function
则作为 gserial
的成员变量同时被分配.
初始化 gserial
数据结构的 acm_connect
、 acm_disconnect
、acm_send_break
回调函数
初始化 usb_function
成员变量的各个回调函数等
设置 acm->port_num
这个是 ports[]
数组的索引
将 usb_function
链接到 config_usb_cfg
关联 function 和 configs 之后数据结构如下.
六、关联 compiste 和 gadget
1 echo `ls /sys/class /udc/` > UDC
这个操作会调用到 gadget_dev_desc_UDC_store
这个属性文件操作接口, 调用流程如下.
这个过程主要作了这些事情.
绑定 udc->driver
和 usb_gadget_driver
调用 gadget->ops
的 dc_set_speed
接口设置 usb 设备支持的速度, 这个不是必须的
调用 gadget->ops
的 bind
接口, 这个接口必须实现 , 这个接口会最终会调用到 acm_bind
acm_bind
设置输入端点 、输出端点 、notify 端点 , 设置接口描述符.
调用 gadget->ops
的 udc_start
接口, 用于初始化 udc 的硬件, 这个接口必须实现
调用 gadget->ops
的 pullup
接口, 启用 D+(或D-) 上的上拉电阻,通知主机它已准备好进行连接。当主机检测到这个信号后,它会启动枚举过程, 这个接口不是必须的.
绑定之后数据结构关系如下所示.
绑定 compiste
和 gadget
的过程就是 usb 设备初始化的过程 , 当完成关联结束, 我们的设备就准备好了, 接下来就会触发 set_up
枚举流程. 调用流程如下所示.
usb 串口枚举流程大概如下所示.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 USB_DT_DEVICE USB_DT_DEVICE USB_DT_CONFIG USB_DT_STRING USB_DT_STRING USB_DT_STRING USB_DT_DEVICE USB_DT_CONFIG USB_DT_CONFIG USB_DT_CONFIG USB_REQ_SET_CONFIGURATION USB_RECIP_INTERFACE USB_RECIP_INTERFACE USB_RECIP_INTERFACE USB_RECIP_INTERFACE USB_DT_STRING USB_DT_STRING USB_DT_STRING USB_RECIP_INTERFACE USB_DT_STRING USB_DT_STRING USB_DT_STRING
其中 USB_REQ_SET_CONFIGURATION
比较特殊, 用于设置 config
, 在 usb 串口中设置对应的配置.
1 2 3 4 5 6 composite_setup () --> set_config () --> f->set_alt (f, tmp, 0 ) --> acm_set_alt () --> gserial_connect () --> usb_ep_enable () -->
除此之外 USB_RECIP_INTERFACE
这用于枚举设备的接口信息, 会回调 acm_setup
这是对应的 function
的 setup
接口, 用于提供设备的接口类信息. usb 串口中用于获取以及设置串口的属性 8n1
等.
1 2 3 composite_setup () --> f->setup () --> acm_setup () -->
七、数据通讯
usb 串口的数据通信比较简单, usb 做设备的通讯是通过 usb_request
来进行, 简称 req. usb 通讯是通过端点进行通信, 为了隔离 function
和 udc 控制器之间的实现细节, 内核封装了 usb_ep_queue
用于 function
进行数据通信. 大致的发送流程.
分配 req
将要通讯的数据填入到对应的 req
调用 usb_ep_queue
填入要通讯的 ep 和 包含数据的 req
调用 gadget 提供的通用接口 dwc2_hsotg_ep_queue_lock
将数据填充到 fifo
或者 dma buffer
通信的流程都是主机发起的, 前面先把数据填充到 buffer
, 接下来等待主机发起通信请求.
usb 串口中首先通过 gs_open
分配通讯的 req 和对应的 buffer.
发送和接受数据都比较简单, 发送流程如下.
接收的流程如下所示.