USB3.0设备控制器驱动分析

一、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 功能驱动程序的基本信息和操作方法。

重要成员:

  1. const char *name:

    • 功能: 用于标识该功能驱动程序的名称。
    • 使用: 在注册功能驱动程序时,需要提供一个唯一的名称,以便在系统中识别和引用。
  2. struct module *mod:

    • 功能: 指向该功能驱动程序所属的模块。
    • 使用: 用于模块管理,确保在模块卸载时能够正确地清理资源。
  3. struct list_head list:

    • 功能: 用于将该功能驱动程序链接到一个全局的链表中。
    • 使用: 内核通过这个链表来管理和查找所有已注册的功能驱动程序。
  4. struct usb_function_instance *(*alloc_inst)(void):

    • 功能: 用于创建并初始化一个 usb_function_instance 实例。
    • 使用: 当需要使用该功能时,调用这个函数来创建一个实例,并进行必要的初始化操作。
  5. struct usb_function *(*alloc_func)(struct usb_function_instance *inst):

    • 功能: 用于创建并初始化一个 usb_function 实例。
    • 使用: 在创建 usb_function_instance 实例之后,调用这个函数来创建一个具体的 usb_function 实例,并进行进一步的初始化。

usb_function结构体:
usb_function
 结构体描述了一个具体的 USB 功能模块,它包含了功能模块的名称、描述符、配置信息等一个 USB 设备可以包含多个功能模块,每个功能模块都对应一个 usb_function 实例。

重要成员及其功能:

    1. const char *name:

      • 功能: 用于标识该功能模块的名称。
      • 使用: 在注册功能模块时,需要提供一个唯一的名称,以便在系统中识别和引用。
    2. struct usb_gadget_strings**strings:

      • 功能: 用于存储该功能模块的字符串描述符。
      • 使用: 当 USB 主机请求字符串描述符时,通过这个成员来提供相应的字符串信息。
    3. 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.cether.c)通常只实现单一功能(如音频设备或虚拟网卡)。
    • 依赖Composite框架:通过module_usb_composite_driver宏注册驱动,将多个功能(Function)组合成一个复合设备()。
2. 示例:Legacy驱动的使用

例如,若要将设备配置为USB音频设备:

  1. 在内核配置中启用CONFIG_USB_G_AUDIO
  2. 加载模块:modprobe g_audio
  3. 设备会被主机识别为一个USB音频设备。

缺点:灵活性差,修改配置需重新编译内核,无法动态组合多个功能。


二、USB Gadget ConfigFS

1. 基本概念
  • 定位:ConfigFS是一种基于内存的虚拟文件系统,允许用户空间通过目录和文件动态配置内核对象。
  • 特点
    • 动态配置:无需修改内核代码,通过脚本或命令行动态组合功能。
    • 模块化:内核提供独立的功能模块(如uac2mass_storageecm等),用户按需组合。
    • 用户空间控制:通过创建目录、写入属性文件、建立符号链接完成配置。
2. 核心原理
  • ConfigFS挂载:将ConfigFS挂载到/sys/kernel/config目录,生成usb_gadget子目录。
  • 配置步骤
    1. 创建Gadget模板:在usb_gadget下新建目录(如g1),设置VID/PID、字符串描述符等。
    2. 添加功能(Function) :在functions子目录下创建功能实例(如uac2.0ecm)。
    3. 绑定功能到配置:在configs目录下创建配置,通过符号链接将功能关联到配置。
    4. 绑定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的对比

四、实际应用场景

  1. Legacy驱动

    • 嵌入式设备中功能固定的场景(如仅需作为U盘)。
    • 示例:加载g_ether模块,直接生成虚拟网卡(。
  2. 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电源等功能)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值