一、USB驱动框架分析
USB控制器作为device的驱动框架分为:gadget Function驱动、gadget Function API、Composite以及UDC驱动。
- gadget Function 驱动:
- 解释:是针对 USB 设备特定功能的驱动程序。
- 功能:负责实现 USB 设备对外提供的具体功能,比如一个 USB 存储设备的 gadget Function 驱动,就负责处理数据的读写操作等与存储功能相关的任务 。
- gadget Function API:
- 解释:为 gadget Function 驱动提供的一组应用程序接口。
- 功能:方便开发者在编写 gadget Function 驱动时调用,使驱动能够更好地与上层应用以及下层的其他组件交互,实现不同功能模块之间的通信和数据传递。
- Composite:
- 解释:用于组合多个 gadget Function,使一个 USB 设备可以同时提供多种功能。
- 功能:比如一个多功能的 USB 设备,既可以作为存储设备,又可以作为音频设备,Composite 就负责将这两种功能整合起来,让设备以一个整体的形式被主机识别和使用。
- UDC 驱动:
- 解释:USB Device Controller 驱动,即 USB 设备控制器驱动。
- 功能:负责管理 USB 设备控制器硬件,处理与硬件相关的底层操作,比如数据的传输控制、设备状态的监测和管理等,是连接硬件和上层软件的关键桥梁 。
1.USB控制器作为Host的时候(USB主机控制器),使用USB主机控制器驱动。USB控制器作为Device时(USB设备控制器)。使用UDC(usb device controller)驱动。
USB控制器作为Device时,驱动架构为:
上层 gadget function 驱动(代表具体设备驱动),比如存储设备:移动硬盘、U 盘设备;通讯设备:USB 串口、USB 虚拟网卡等;UAC 驱动(USB 声卡、USB 音频设备)。
gadget function API(抽象层),向上和向下提供统一的标准接口 API。
composite 中间层(支持多种功能的设备与 USB 复合设备驱动的开发)。
USB控制器是计算机系统中用于管理USB通信的硬件组件。它可以作为Host(主机)或Device(设备)。
- 作为Host(主机控制器) :当USB控制器作为Host时,它负责管理和控制连接到它的所有USB设备。例如,电脑的USB端口就是一个Host控制器,它可以连接键盘、鼠标、U盘等设备。
- 作为Device(设备控制器) :当USB控制器作为Device时,它是一个被连接到Host的设备。例如,U盘、鼠标、键盘等都是Device。
2.gadget function驱动使用usb_function_driver数据结构进行描述:
usb_function_driver
结构体这个结构体定义了一个 USB 功能驱动程序的基本信息和操作方法。
重要成员:
const char *name
:
- 功能: 用于标识该功能驱动程序的名称。
- 使用: 在注册功能驱动程序时,需要提供一个唯一的名称,以便在系统中识别和引用。
struct module *mod
:
- 功能: 指向该功能驱动程序所属的模块。
- 使用: 用于模块管理,确保在模块卸载时能够正确地清理资源。
struct list_head list
:
- 功能: 用于将该功能驱动程序链接到一个全局的链表中。
- 使用: 内核通过这个链表来管理和查找所有已注册的功能驱动程序。
struct usb_function_instance *(*alloc_inst)(void)
:
- 功能: 用于创建并初始化一个
usb_function_instance
实例。- 使用: 当需要使用该功能时,调用这个函数来创建一个实例,并进行必要的初始化操作。
struct usb_function *(*alloc_func)(struct usb_function_instance *inst)
:
- 功能: 用于创建并初始化一个
usb_function
实例。- 使用: 在创建
usb_function_instance
实例之后,调用这个函数来创建一个具体的usb_function
实例,并进行进一步的初始化。
usb_function结构体:
结构体描述了一个具体的 USB 功能模块,它包含了功能模块的名称、描述符、配置信息等。一个 USB 设备可以包含多个功能模块,每个功能模块都对应一个
usb_functionusb_function
实例。重要成员及其功能:
const char *name
:
- 功能: 用于标识该功能模块的名称。
- 使用: 在注册功能模块时,需要提供一个唯一的名称,以便在系统中识别和引用。
struct usb_gadget_strings**strings
:
- 功能: 用于存储该功能模块的字符串描述符。
- 使用: 当 USB 主机请求字符串描述符时,通过这个成员来提供相应的字符串信息。
struct usb_descriptor_header**fs_descriptors
:
- 功能: 用于存储该功能模块的全速描述符。
- 使用: 当 USB 设备工作在全速模式时,通过这个成员来提供相应的描述符信息。
3. gadget function api(抽象层):上层为function 驱动,使用function api注册和注销,下层为composite驱动使用function api和function驱动绑定和匹配。function驱动要实现usb_function_driver数据结构并向function api层注册,具体api如下:
1.
usb_function_register
函数功能:
usb_function_register
函数用于向USB gadget框架注册一个新的USB功能驱动。注册后,该功能驱动可以被USB gadget设备使用,以提供特定的USB功能(如串口、网络、存储等)。参数:
struct usb_function_driver *newf
:指向要注册的新USB功能驱动的结构体指针。这个结构体包含了功能驱动的名称、功能实现函数等信息。使用案例:
假设我们正在开发一个USB gadget设备,该设备需要提供一个虚拟串口功能。我们可以编写一个虚拟串口功能驱动,并使用
usb_function_register
函数将其注册到USB gadget框架中。// 定义虚拟串口功能驱动结构体 static struct usb_function_driver my_serial_driver = {.name = "my_serial",.fops = &my_serial_fops,.bind = my_serial_bind,.unbind = my_serial_unbind,// 其他功能实现函数... };// 在设备初始化时注册虚拟串口功能驱动 static int __init my_gadget_init(void) {int ret;// 注册虚拟串口功能驱动ret = usb_function_register(&my_serial_driver);if (ret < 0) {printk(KERN_ERR "Failed to register my_serial driver\n");return ret;}printk(KERN_INFO "my_serial driver registered successfully\n");return 0; }module_init(my_gadget_init);
2.
usb_function_unregister
函数功能:
usb_function_unregister
函数用于从USB gadget框架中注销一个已注册的USB功能驱动。注销后,该功能驱动将不再被USB gadget设备使用。参数:
struct usb_function_driver *fd
:指向要注销的USB功能驱动的结构体指针。使用案例:
假设我们之前注册了一个虚拟串口功能驱动,现在由于某种原因(如设备配置变更、驱动更新等),需要将该功能驱动从USB gadget框架中注销。
// 在设备配置变更时注销虚拟串口功能驱动 static void my_gadget_config_change(void) {// 注销虚拟串口功能驱动usb_function_unregister(&my_serial_driver);printk(KERN_INFO "my_serial driver unregistered successfully\n"); }module_init(my_gadget_init);
1.
usb_get_function_instance
函数参数和功能
- 参数:
const char *name
: 一个字符串,表示要获取的USB功能实例的名称。- 功能:
该函数从gadget function API层获取一个指定名称的usb_function_instance
结构体实例。这个实例代表了一个特定的USB功能(如Mass Storage、Ethernet等),并且可以被USB gadget设备使用。使用案例
假设你正在开发一个USB gadget设备,该设备需要提供一个Mass Storage功能,以便其他设备可以通过USB接口访问存储在设备上的文件。
// 获取名为"mass_storage"的USB功能实例 struct usb_function_instance *fi = usb_get_function_instance("mass_storage");if (!fi) {// 处理获取实例失败的情况printk(KERN_ERR "Failed to get mass storage function instance\n");return -ENODEV; }// 使用获取到的实例进行进一步的配置和使用 // ...
2.
usb_put_function_instance
函数参数和功能
- 参数:
struct usb_function_instance *fi
: 一个指向usb_function_instance
结构体的指针,表示要释放的USB功能实例。- 功能:
该函数用于释放一个之前通过usb_get_function_instance
获取的USB功能实例。这通常在不再需要使用该功能实例时调用,以释放相关的资源。使用案例
假设你在之前获取了一个USB功能实例,并且在完成相关操作后,不再需要使用这个实例了。
// 假设fi是之前通过usb_get_function_instance获取的实例 struct usb_function_instance *fi;// ... 使用fi进行相关操作 ...// 完成操作后,释放USB功能实例 usb_put_function_instance(fi);
usb_get_function
- 参数:
struct usb_function_instance *fi
: 这是一个指向usb_function_instance
结构体的指针。usb_function_instance
代表一个USB功能实例,它包含了配置和初始化USB功能所需的信息。- 功能:
- 该函数从
usb_function_instance
中获取一个usb_function
结构体指针。usb_function
结构体描述了具体的USB功能,例如一个USB设备的特定功能模块(如USB存储、USB网络等)。使用案例:假设你正在开发一个USB设备驱动程序,该设备需要支持USB存储功能。你需要从USB功能实例中获取USB存储功能的描述信息,以便进一步配置和使用该功能。
// 假设fi是一个已经初始化好的usb_function_instance结构体指针 struct usb_function_instance *fi = ...;// 从fi中获取USB存储功能的描述信息 struct usb_function *storage_func = usb_get_function(fi);if (storage_func) {// 配置和使用USB存储功能// 例如,设置存储容量、文件系统等configure_storage_function(storage_func);use_storage_function(storage_func); } else {// 处理获取功能失败的情况printk(KERN_ERR "Failed to get USB storage function\n"); }
usb_put_function
- 参数:
struct usb_function *f
: 这是一个指向usb_function
结构体的指针。usb_function
结构体描述了具体的USB功能。- 功能:
- 该函数用于释放一个
usb_function
结构体。它通过调用f->free_func(f)
来执行释放操作,确保资源被正确清理。使用案例:假设你已经使用完了一个USB功能(例如USB存储功能),现在需要释放该功能所占用的资源,以避免内存泄漏和资源浪费。
// 假设storage_func是一个已经使用完的usb_function结构体指针 struct usb_function *storage_func = ...;// 释放USB存储功能 usb_put_function(storage_func);// 此时storage_func所指向的资源已经被正确释放,可以安全地将其置为NULL storage_func = NULL;
4.在Linux内核中直接使用composite层的USB gadget legacy驱动(如USB音频设备驱动文件audio.c USB虚拟以太网设备驱动文件ether.c等等)。USB gadget configfs属于文件系统,可以在用户空间直接控制内核对象。主要适用于对象很多配置的模块。
一、Legacy驱动(如audio.c、ether.c)
1. 基本概念
- 定位:Legacy驱动是早期Linux内核中USB Gadget的配置方式,通过直接编写内核代码实现功能()。
- 特点:
- 静态配置:需要在内核源码中定义功能和参数(如VID/PID),重新编译内核才能生效。
- 单一功能为主:每个驱动文件(如
audio.c
、ether.c
)通常只实现单一功能(如音频设备或虚拟网卡)。- 依赖Composite框架:通过
module_usb_composite_driver
宏注册驱动,将多个功能(Function)组合成一个复合设备()。2. 示例:Legacy驱动的使用
例如,若要将设备配置为USB音频设备:
- 在内核配置中启用
CONFIG_USB_G_AUDIO
。- 加载模块:
modprobe g_audio
。- 设备会被主机识别为一个USB音频设备。
缺点:灵活性差,修改配置需重新编译内核,无法动态组合多个功能。
二、USB Gadget ConfigFS
1. 基本概念
- 定位:ConfigFS是一种基于内存的虚拟文件系统,允许用户空间通过目录和文件动态配置内核对象。
- 特点:
- 动态配置:无需修改内核代码,通过脚本或命令行动态组合功能。
- 模块化:内核提供独立的功能模块(如
uac2
、mass_storage
、ecm
等),用户按需组合。- 用户空间控制:通过创建目录、写入属性文件、建立符号链接完成配置。
2. 核心原理
- ConfigFS挂载:将ConfigFS挂载到
/sys/kernel/config
目录,生成usb_gadget
子目录。- 配置步骤:
- 创建Gadget模板:在
usb_gadget
下新建目录(如g1
),设置VID/PID、字符串描述符等。- 添加功能(Function) :在
functions
子目录下创建功能实例(如uac2.0
、ecm
)。- 绑定功能到配置:在
configs
目录下创建配置,通过符号链接将功能关联到配置。- 绑定UDC控制器:将Gadget绑定到USB设备控制器(UDC),激活设备。
3. 示例:通过ConfigFS配置USB音频+网卡
# 挂载ConfigFS mount -t configfs none /sys/kernel/config cd /sys/kernel/config/usb_gadget# 创建Gadget模板 mkdir g1 cd g1 echo 0x1d6b > idVendor # Linux Foundation的VID echo 0x0104 > idProduct # 复合设备PID mkdir strings/0x409 # 英文语言ID echo "123456" > strings/0x409/serialnumber echo "My Company" > strings/0x409/manufacturer echo "Composite Device" > strings/0x409/product# 添加音频功能(uac2) mkdir functions/uac2.0# 添加网卡功能(ecm) mkdir functions/ecm.usb0# 创建配置并绑定功能 mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo "Config 1" > configs/c.1/strings/0x409/configuration ln -s functions/uac2.0 configs/c.1/ ln -s functions/ecm.usb0 configs/c.1/# 绑定UDC控制器(假设UDC为fe200000.dwc3) echo "fe200000.dwc3" > UDC
三、Legacy驱动与ConfigFS的对比
四、实际应用场景
Legacy驱动:
- 嵌入式设备中功能固定的场景(如仅需作为U盘)。
- 示例:加载
g_ether
模块,直接生成虚拟网卡(。ConfigFS:
- 开发调试:快速测试不同功能组合。
- 复杂设备:如手机通过USB同时提供MTP、ADB、RNDIS等功能(。
- 动态切换配置:通过脚本切换设备模式(如从U盘模式切换到声卡模式)。
5.UDC驱动函数接口:内核初始化和模块加载初始化
1.
usb_udc_init
函数功能:
usb_udc_init
函数用于初始化 USB UDC(USB Device Controller)驱动。它创建了一个名为 "udc" 的设备类,并设置了一个设备事件处理函数usb_udc_uevent
。如果创建设备类失败,函数会返回相应的错误码。使用案例:
假设你正在开发一个基于 Linux 内核的 USB 设备驱动程序,需要初始化 UDC 驱动。在驱动程序的初始化代码中,你可以调用
usb_udc_init
函数来完成 UDC 驱动的初始化工作。
static int __init my_usb_driver_init(void) {int ret;ret = usb_udc_init();if (ret) {pr_err("Failed to initialize UDC driver\n");return ret;}// 其他初始化代码return 0; }
2.
usb_add_gadget_udc
函数功能:
usb_add_gadget_udc
函数用于将一个 USB gadget 设备添加到 UDC 驱动中。它接收一个struct device *parent
和一个struct usb_gadget *gadget
作为参数,并将gadget
添加到 UDC 链表中。使用案例:
假设你有一个 USB gadget 设备需要添加到 UDC 驱动中,可以在设备的初始化代码中调用
usb_add_gadget_udc
函数。static int __init my_usb_gadget_init(void) {struct device *parent = /* 获取 parent 设备 */;struct usb_gadget *gadget = /* 初始化 gadget 设备 */;int ret = usb_add_gadget_udc(parent, gadget);if (ret) {pr_err("Failed to add gadget to UDC\n");return ret;}// 其他初始化代码return 0; }
3.
usb_del_gadget_udc
函数功能:
usb_del_gadget_udc
函数用于从 UDC 驱动中删除一个 USB gadget 设备。它接收一个struct usb_gadget *gadget
作为参数,并从 UDC 链表中移除该gadget
。使用案例:
假设你需要在驱动程序的退出代码中删除一个 USB gadget 设备,可以在退出函数中调用
usb_del_gadget_udc
函数。static void __exit my_usb_gadget_exit(void) {struct usb_gadget *gadget = /* 获取 gadget 设备 */;usb_del_gadget_udc(gadget);// 其他清理代码 }
6.了解UDC驱动使用usb_udc数据结构进行描述,我们注册所有的usb_udc都会挂接到udc_list链表。usb_gadget_ops是USB设备控制器的硬件操作函数(包含开启USB设备控制器、停止USB设备控制器,vbus电源等功能)