USB总线-Linux内核USB3.0设备控制器复合设备之legacy方式分析(八)
1.概述
在usb gadget configfs引入到内核之前,内核都使用硬编码的方式实现复合设备,无法在用户空间动态修改和绑定不同的function驱动,若要修改,则需要修改内核代码,重新编码,非常不方便。目前这部分代码在被放到drivers/usb/gadget/legacy/目录下。被编译成内核模块时,名称以g开头,如音频设备g_audio.ko、串口设备g_serial.ko、CDC设备及大容量存储设备g_multi.ko。USB gadget configfs和legacy相比只是实现复合设备的形式不同而已,设备的功能最终还是要通过function驱动实现。下面以音频复合设备为例,分析g_audio驱动的工作过程。
2.音频复合设备驱动
从前面的分析中可以看出,复合设备驱动围绕usb_composite_driver和usb_composite_dev两个数据结构展开,legacy方式的复合设备驱动也不例外。
2.1.定义
音频复合设备的实现在drivers/usb/gadget/legacy/audio.c文件中,其usb_composite_driver数据结构定义如下。最重要的是audio_bind和audio_unbind两个回调函数,在g_audio驱动绑定时和解除绑定时调用。g_audio驱动使用module_usb_composite_driver宏注册到内核中,初始化函数为usb_composite_probe,卸载函数为usb_composite_unregister。
[drivers/usb/gadget/legacy/audio.c]
static struct usb_composite_driver audio_driver = {
.name = "g_audio", // 驱动名称
.dev = &device_desc, // USB设备描述符,USB复合设备只有一个设备描述符
.strings = audio_strings, // 字符串
.max_speed = USB_SPEED_HIGH, // USB设备最大速度,USB2.0
.bind = audio_bind, // audio composite驱动bind回调函数
.unbind = audio_unbind, // audio composite驱动unbind回调函数
};
module_usb_composite_driver(audio_driver); // 注册audio_driver
[include/linux/usb/composite.h]
#define module_usb_composite_driver(__usb_composite_driver) \
module_driver(__usb_composite_driver, usb_composite_probe, \
usb_composite_unregister)
module_driver宏定义如下,最终还是通过module_init初始化,module_exit宏卸载。
[include/linux/device.h]
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
2.2.参数
g_audio驱动将音频参数定义为模块参数,如下所示,加载模块的时候可以指定选项修改这些参数的默认值。
[drivers/usb/gadget/legacy/audio.c]
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC2_DEF_PCHMASK;
module_param(p_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
static int p_srate = UAC2_DEF_PSRATE;
module_param(p_srate, uint, S_IRUGO);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
module_param(p_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC2_DEF_CCHMASK;
module_param(c_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
static int c_srate = UAC2_DEF_CSRATE;
module_param(c_srate, uint, S_IRUGO);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
module_param(c_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
驱动模块加载成功后,这些参数会被导出到用户空间,目录为/sys/module/g_audio/parameters。
2.3.初始化
g_audio驱动调用usb_composite_probe函数初始化,执行流程如下图所示。和USB gadget configfs定义的复合设备驱动初始化流程相似,只是设置的usb_gadget_driver不同。USB gadget configfs定义的usb_gadget_driver为configfs_driver_template,而legacy方式定义的usb_gadget_driver为composite_driver_template。composite_driver_template是function驱动和UDC驱动沟通的桥梁,在适当的时机会被UDC驱动回调。
[drivers/usb/gadget/composite.c]
static const struct usb_gadget_driver composite_driver_template = {
.bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
.reset = composite_disconnect,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
usb_composite_probe的工作流程如下:
- 设置usb_gadget_driver为composite_driver_template。
- 将audio_driver设置的最大速度设置到composite_driver_template,表明该复合设备支持的最大速度。
- 调用UDC驱动接口,找到合适的USB设备控制器绑定composite_driver_template。
- 遍历udc_list链表,查找第一个USB控制器。USB gadget configfs根据名称查找USB设备控制器,而legacy方式只匹配第一个USB设备控制器,无法匹配指定的USB设备控制器。
- 找到USB设备控制器后,UDC数据结构保存composite_driver_template,此时就完成了UDC绑定composite_driver_template。
- 回调composite_driver_template定义的composite_bind函数,将复合设备和function驱动绑定。
- 分配端点0的usb_request、分配USB请求的缓冲区、设置usb_request的回调函数、复位所有端点,并将gadget的端点数量清零。
- 回调复合设备驱动g_audio定义的audio_bind函数。复合设备和function驱动绑定在这里完成,主要是添加配置、调用f_uac2驱动的afunc_alloc_inst和afunc_alloc函数创建usb_function_instance和usb_function、调用f_uac2驱动的 afunc_bind 函数。
- 如果使用os_string,则需要分配os_string requset、分配USB请求的缓冲区、设置USB请求的回调函数。
- 将audio_driver中定义的设备描述符更新到usb_composite_dev中。
- 将usb_composite_driver中定义的设备描述符更新到usb_composite_dev中。
3.总结
从上面可以看出,legacy方式定义的复合设备很不灵活,使用者无法在用户空间动态配置复合设备和绑定的function驱动。若要使用音频设备,则只能通过g_audio驱动构造复合设备,若使用USB虚拟网卡,则只能通过g_ether驱动构造复合设备,若需要多个功能的USB设备,则需要重新构造复合设备,编码定义usb_composite_driver。USB gadget configfs不需要在内核中预先定义好复合设备,使用者在用户空间配置,内核会自动生成所需的复合设备,并和对应的function驱动绑定。
相关文章
- linux抓包命令到文件,Linux下抓包命令tcpdump详解「建议收藏」
- Linux内核编写_全志linux驱动写寄存器
- 程序的开发Linux驱动程序开发的新技术(基于linux驱动)
- 修改Linux用户目录: 快速又有效的方法!(修改linux用户目录)
- Linux下新增用户权限管理(linux添加用户权限)
- 抢占Linux数码相框时尚荣耀!(linux数码相框)
- 用户权限Linux下限制用户权限的方法(linux限制)
- 系统探索Linux分支系统:从何处开始?(linux的分支)
- 设备Linux下查看PCI设备的简单指南(linux查看pci)
- 深入理解Linux键值表:掌握内核架构设计的关键技能。(linux键值表)
- Linux二进制安装:快捷简单的安装方式(linux二进制安装)
- Linux计划任务:轻松定时执行任务(linux计划任务)
- Linux下运行Redis:实现高速计算数据存储(linux运行redis)
- Linux 上键翻页技巧精彩演示(linux往上翻页)
- Linux移植过程中的指南:一步一步更新你的系统(linux移植手册)
- 命令的使用 掌握Linux下的Cat命令,实现文件操作的高效率(linux下cat)
- 在 Linux 中替换文本的方法(linux替换文本)
- Linux校验和:保护系统安全的屏障(linux校验和)
- 探究Linux如何支持中文:深入了解Linux中文内核(linux中文内核)
- 探索Linux下调整显示分辨率的奥秘(linux设置dpi)
- Linux安装显卡驱动程序:步步高升(linux装显卡驱动)
- 利用Linux挂载设备,实现更高效的数据管理(linux挂载设备)
- 本深入了解Linux新版本,加快科技发展(linux 版)
- 深入学习Linux系统内核(linux系统内核学习)
- 夹Linux建立文件夹:创建一个满足你需求的空间(linux 建立文件)