前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >USB总线-Linux内核USB3.0设备控制器驱动框架分析(四)

USB总线-Linux内核USB3.0设备控制器驱动框架分析(四)

作者头像
233333
发布2022-12-13 17:04:55
9.4K0
发布2022-12-13 17:04:55
举报

1.概述

如下图所示,USB控制器可以呈现出两种不同的状态。USB控制器作为Host时,称为USB主机控制器,使用USB主机控制器驱动。USB控制器作为Device时,称为USB设备控制器,使用UDC(usb device controller)驱动。本节只分析USB控制器作为Device时的驱动框架。

USB控制器作为Device时,驱动框架可分为5层。最上层的是Gadget Function驱动,代表了具体设备的驱动,如大容量存储设备驱动(U盘、移动硬盘等)、通讯类设备驱动(USB串口、USB虚拟网卡等)、UAC驱动(USB麦克风、USB声卡等USB音频类设备)。接下来是Gadget Funcation API层,该层是一个抽象层,向上和向下提供统一的API,屏蔽了差异,提高了驱动的兼容性。Composite层是一个可选的中间层,可通过一种配置或多种配置高效的支持多种功能的设备,简化了USB复合设备驱动的开发。目前最流行的是使用基于Composite和configfs实现的USB gadget configfs,可在用户空间灵活的配置USB设备。UDC驱动直接访问硬件,控制USB设备与USB主机之间的通信。USB设备控制器通过USB线缆连接USB主机控制器,负责USB数据的发送和接收。

image
image

2.Gadget Function驱动

Linux内核的USB Gadget Function驱动都在drivers/usb/gadget/function/目录下,有通讯设备类(Communication Device Class)驱动(f_acm.c、f_ecm、f_serial.c等)、USB音频设备类驱动(f_uac1.c、f_uac2.c、u_audio.c)、大容量存储设备驱动(f_mass_storage.c)、USB视频设备类驱动(f_uvc.c)等。

Gadget Function驱动的入口使用usb_function_driver数据结构描述,驱动需要实现alloc_inst和alloc_func函数。alloc_inst创建usb_function_instance数据结构并初始化。alloc_func创建usb_function并初始化,重点是设置里面的回调函数,通常情况下,不直接使用usb_function数据结构,而是嵌入到驱动的数据结构中使用。Composite驱动会通过Gadget Function API回调alloc_inst和alloc_func函数。usb_function了描述Gadget Function驱动,Gadget Function驱动的重点是实现这些回调函数。

代码语言:javascript
复制
[include/linux/usb/composite.h]
struct usb_function_driver {
    const char *name;
    struct module *mod;
    struct list_head list;
    // 创建usb_function_instance并初始化
    struct usb_function_instance *(*alloc_inst)(void);
    // 创建usb_function并初始化
    struct usb_function *(*alloc_func)(struct usb_function_instance *inst);
};
struct usb_function {  // 描述了一个gadget Function驱动
    const char			*name;  // gadget Function驱动名称
    struct usb_gadget_strings	**strings;  // 字符串表,由bind分配和控制请求提供的语言IDs
    struct usb_descriptor_header	**fs_descriptors; // full speed描述符
    struct usb_descriptor_header	**hs_descriptors; // high speed描述符
    struct usb_descriptor_header	**ss_descriptors; // super speed描述符
    struct usb_configuration	*config;  // usb_add_function函数添加的配置
    // 驱动的bind回调函数,分配驱动所需的资源,如配置、端点、I/O缓冲区等
    int	(*bind)(struct usb_configuration *, struct usb_function *);
    // 释放bind分配的资源
    void (*unbind)(struct usb_configuration *, struct usb_function *);
    void (*free_func)(struct usb_function *f);  // 释放usb_function
    // 设置可选的配置,有时候驱动可能有多个配置,需要使用set_alt进行切换
    int (*set_alt)(struct usb_function *, unsigned interface, unsigned alt);
    // 获取当前的设置的可选配置,如果没有多个配置,则默认使用配置0,则返回0
    int (*get_alt)(struct usb_function *, unsigned interface);
    // disable gadget function驱动,主机复位、主机重新配置gadget、断开连接时使用
    void (*disable)(struct usb_function *);
    // 用于特殊接口的控制请求
    int	(*setup)(struct usb_function *, const struct usb_ctrlrequest *);
    // 测试某些设备类请求能否被处理
    bool (*req_match)(struct usb_function *, const struct usb_ctrlrequest *);
    void (*suspend)(struct usb_function *);
    void (*resume)(struct usb_function *);
    /* USB 3.0 additions */
    // 向GetStatus请求返回当前gadget Function驱动的状态
    int (*get_status)(struct usb_function *);  
    // 当接收到SetFeature(FUNCTION_SUSPEND)时,回调该函数
    int (*func_suspend)(struct usb_function *, u8 suspend_opt);
    /* private: internals */
    struct list_head		list;
    DECLARE_BITMAP(endpoints, 32);  // 端点位图
    const struct usb_function_instance *fi;
    unsigned int		bind_deactivated:1;
};

usb_function_driver通常使用DECLARE_USB_FUNCTION_INIT宏定义并初始化。将宏展开后,其定义了usb_function_driver结构体实例,主要设置alloc_inst和alloc_func成员,前置用于创建usb_function_instance,表示一个Gadget Function实例,后者用于创建usb_function并初始化。usb_function中的方法实现了具体的Gadget Function驱动。usb_function_register和usb_function_unregister函数完成usb_function_driver结构体的注册和注销。

代码语言:javascript
复制
[include/linux/usb/composite.h]
#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);	\  // 注册UAC设备驱动
    }								\
    static void __exit _name ## mod_exit(void)			\
    {								\
        usb_function_unregister(&_name ## usb_func);		\  // 注销UAC设备驱动
    }								\
    module_init(_name ## mod_init);					\  // 模块初始化
    module_exit(_name ## mod_exit)                     // 模块卸载

#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)		\
    // 定义UAC2.0的Gadget Function驱动,名称为uac2_usb_func
    static struct usb_function_driver _name ## usb_func = {		\
        .name = __stringify(_name),				\  // 驱动名称为uac2
        .mod  = THIS_MODULE,					\
        .alloc_inst = _inst_alloc,				\
        .alloc_func = _func_alloc,				\
    };								\
    MODULE_ALIAS("usbfunc:"__stringify(_name));

3.Gadget Function API

Gadget Funcation API是一个抽象层,上层的Gadget Function驱动使用Gadget Funcation API注册和注销,下层的Composite驱动使用Gadget Funcation API和Gadget Function驱动绑定和匹配。Gadget Function驱动需要实现usb_function_driver数据结构并向Gadget Funcation API层注册。

Gadget Function API的主要API如下。usb_function_register将注册的usb_function_driver挂到func_list链表中。usb_function_instance函数会遍历func_list链表,将参数name和usb_function_driver的name进行对比,若名称一致,则匹配成功,接着调用匹配成功的usb_function_driver中的alloc_inst回调函数获取usb_function_instance,然后将usb_function_driver的指针设置到usb_function_instance中,最后返回usb_function_instance的指针。usb_get_function函数通过回调alloc_func函数获取并初始化usb_function。其他API可参考源代码。

代码语言:javascript
复制
[drivers/usb/gadget/functions.c]
// 向Gadget Function API层注册Gadget Function驱动
int usb_function_register(struct usb_function_driver *newf)
// 注销Gadget Function驱动
void usb_function_unregister(struct usb_function_driver *fd)
// 从Gadget Function API层获取usb_function_instance
struct usb_function_instance *usb_get_function_instance(const char *name)
// 回调free_func_inst销毁usb_function_instance
void usb_put_function_instance(struct usb_function_instance *fi)
// 从Gadget Function API层获取usb_function_instance
struct usb_function *usb_get_function(struct usb_function_instance *fi)
// 回调free_func销毁usb_function
void usb_put_function(struct usb_function *f)

4.Composite层

USB Composite的核心数据结构为usb_composite_driver。Composite驱动必须实现设备描述符dev和bind回调函数。

Composite(复合)设备使用usb_composite_dev数据结构描述,该数据结构在Composite驱动注册的时候内核会在驱动bind函数调用之前自动创建,不需要驱动创建。

gadget指向dwc3结构体中的usb_gadget。req在Composite驱动注册的时候就提前分配好,用于响应主机发送的控制请求。config指向当前使用的usb配置。desc是当前设备的描述符,在Composite驱动注册的时候设置。driver指向对应的usb_composite_driver。usb_composite_driver结构体包含了usb_gadget_driver数据结构,用来表示usb设备驱动。

代码语言:javascript
复制
[include/linux/usb/composite.h]
struct usb_composite_driver {
    const char  *name;  // 驱动名称
    const struct usb_device_descriptor *dev;  // 设备描述符,必须定义
    struct usb_gadget_strings  **strings;
    enum usb_device_speed  max_speed;  // 设备支持的最大速度
    unsigned  needs_serial:1;
    // 用于分配整个设备共享的资源,使用usb_add_config添加配置,必须实现
    int  (*bind)(struct usb_composite_dev *cdev);  
    int  (*unbind)(struct usb_composite_dev *);       // 销毁资源
    void  (*disconnect)(struct usb_composite_dev *);  // 可选的驱动disconnect method
    /* global suspend hooks */
    void  (*suspend)(struct usb_composite_dev *);
    void  (*resume)(struct usb_composite_dev *);
    // composite驱动层提供了默认的实现,即composite_driver_template
    struct usb_gadget_driver  gadget_driver;  
};
struct usb_composite_dev {  // 复合设备
    // 只读,usb设备控制器的抽象,指向dwc3结构体中的usb_gadget
	struct usb_gadget		*gadget;  
	struct usb_request		*req;     // 用于响应控制请求,缓冲区提前分配好
	struct usb_request		*os_desc_req;  // 用于响应OS描述符,缓冲区提前分配
	struct usb_configuration	*config;  // 当前使用配置
    //  qwSignature part of the OS string
	u8		qw_sign[OS_STRING_QW_SIGN_LEN]; 
	u8		b_vendor_code; // bMS_VendorCode part of the OS string
	struct usb_configuration *os_desc_config;  // OS描述符使用的配置
	unsigned int			use_os_string:1;
	unsigned int			suspended:1;
	struct usb_device_descriptor desc; // 设备描述符
	struct list_head		configs;
	struct list_head		gstrings;
	struct usb_composite_driver	*driver;  // 指向Composite驱动
    ......
};

4.1.legacy

inux内核中直接使用Composite层的USB gadget legacy驱动大多都在drivers/usb/gadget/legacy/目录下,如USB音频设备驱动文件audio.c,USB虚拟以太网设备驱动文件ether.c,HID设备驱动文件hid.c。legacy驱动可以直接使用内核提供的module_usb_composite_driver宏,方便定义Composite驱动。参数为usb_composite_driver结构体。使用usb_composite_probe注册Composite驱动。使用usb_composite_unregister函数注销Composite驱动。

代码语言:javascript
复制
[include/linux/usb/composite.h]
#define module_usb_composite_driver(__usb_composite_driver) \
	module_driver(__usb_composite_driver, usb_composite_probe, \
		       usb_composite_unregister)

[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);

usb_composite_probe和usb_composite_unregister函数的定义如下。usb_composite_probe初始化复合设备驱动,usb_composite_unregister卸载复合设备驱动。

代码语言:javascript
复制
[include/linux/usb/composite.h]
/**
 * usb_composite_probe() - register a composite driver
 * @driver: the driver to register
 *
 * Context: single threaded during gadget setup
 *
 * This function is used to register drivers using the composite driver
 * framework.  The return value is zero, or a negative errno value.
 * Those values normally come from the driver's @bind method, which does
 * all the work of setting up the driver to match the hardware.
 *
 * On successful return, the gadget is ready to respond to requests from
 * the host, unless one of its components invokes usb_gadget_disconnect()
 * while it was binding.  That would usually be done in order to wait for
 * some userspace participation.
 */
int usb_composite_probe(struct usb_composite_driver *driver)
/**
 * usb_composite_unregister() - unregister a composite driver
 * @driver: the driver to unregister
 *
 * This function is used to unregister drivers using the composite
 * driver framework.
 */
void usb_composite_unregister(struct usb_composite_driver *driver)

内核在Composite驱动层实现了usb_gadget_driver,即composite_driver_template变量,所有复合设备都使用该数据结构,无需驱动实现。

Composite驱动使用usb_composite_probe注册时,内核会将composite_driver_template中的数据拷贝到usb_composite_driver的gadget_driver成员。

代码语言:javascript
复制
[drivers/usb/gadget/composite.c]
static const struct usb_gadget_driver composite_driver_template = {  // 内核实现的usb设备驱动
	.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,
	},
};

4.2.USB Gadget Configfs

Configfs是一种基于ram的文件系统,可以在用户空间直接控制内核对象,主要适用于内核对象有众多配置的模块,比如USB复合设备。Linux 3.11版本引入了USB Gadget Configfs。在用户层可以通过暴漏出来的API定义USB Gadget设备的任意功能和配置,极大的方便了USB复合设备的配置和使用。该部分内容后面将会详细介绍原理和使用方法。USB Gadget Configfs在drivers/usb/gadget/configfs.c文件中实现。

5.UDC驱动

5.1.函数接口

UDC驱动模块定义如下,内核初始化或模块加载时初始化,创建udc_class,设置uevent的回调函数为usb_udc_uevent。

代码语言:javascript
复制
[drivers/usb/gadget/udc/core.c]
static struct class *udc_class;
static int __init usb_udc_init(void)
{
	udc_class = class_create(THIS_MODULE, "udc");
    ......
    udc_class->dev_uevent = usb_udc_uevent;
    return 0;
}
subsys_initcall(usb_udc_init);

static void __exit usb_udc_exit(void)
{
	class_destroy(udc_class);
}
module_exit(usb_udc_exit);

使用usb_add_gadget_udc注册UDC驱动,首先分配一个usb_udc数据结构,初始化相关成员,最后将usb_udc挂到udc_list链表中,注册成功后UDC的状态为USB_STATE_NOTATTACHED。使用usb_del_gadget_udc删除UDC驱动,首先回调pullup断开连接,然后回调udc_stop停止USB设备控制器,最后从udc_list链表中删除usb_udc。

代码语言:javascript
复制
[drivers/usb/gadget/udc/core.c]
/**
 * usb_add_gadget_udc - adds a new gadget to the udc class driver list
 * @parent: the parent device to this udc. Usually the controller
 * driver's device.
 * @gadget: the gadget to be added to the list
 *
 * Returns zero on success, negative errno otherwise.
 */
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
/**
 * usb_del_gadget_udc - deletes @udc from udc_list
 * @gadget: the gadget to be removed.
 *
 * This, will call usb_gadget_unregister_driver() if
 * the @udc is still busy.
 */
void usb_del_gadget_udc(struct usb_gadget *gadget)

Composite驱动调用usb_gadget_probe_driver和UDC驱动匹配,首先遍历udc_list链表,若有usb_udc的driver成员为空,则表示匹配成功,接着Composite驱动和UDC驱动绑定,通过将Composite驱动的usb_composite_driver.gadget_driver的地址设置到usb_udc.driver成员中完成绑定,最后回调udc_start启动USB设备控制器。调用usb_gadget_unregister_driver解除Composite驱动和UDC驱动的绑定关系。

代码语言:javascript
复制
[drivers/usb/gadget/udc/core.c]
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)

UDC层还向USB devcie function驱动提供了一些的接口,用来开启和关闭USB设备控制器、使能和禁止端点、queues/dequeues I/O请求、分配和释放usb_request、匹配端点等。这些函数内部会调用具体的USB设备控制器的UDC驱动。RK3399平台上,就会调用dwc3实现的UDC驱动。至于具体内容,后面章节在分析dwc3的UDC驱动时会详细说明。

代码语言:javascript
复制
[drivers/usb/gadget/udc/core.c]
int usb_ep_enable(struct usb_ep *ep)   // 使能端点
int usb_ep_disable(struct usb_ep *ep)  // 禁止端点
int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags)  // queues usb_request 
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)  // dequeue usb_request
struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)  // 分配usb_request 
void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req)  // 释放usb_request 
// 根据描述符,匹配要使用的端点
int usb_gadget_ep_match_desc(struct usb_gadget *gadget, struct usb_ep *ep,
		struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *ep_comp)

5.2.数据结构

UDC驱动使用usb_udc数据结构描述,注册的所有usb_udc数据结构都会挂到udc_list链表上。UDC驱动的功能主要由成员gadget实现,即usb_gadget数据结构。struct usb_gadget_driver由composite层实现,用于连接USB Function驱动和UDC驱动。usb_gadget_ops是USB设备控制器的硬件操作函数,包含启动USB设备控制器、停止USB设备控制器、vbus电源等功能。ep0表示端点0,驱动注册时会提前分配好,用于响应控制请求。除端点0外,USB设备驱动还会使用其他的端点,这些端点数据结构挂到ep_list链表中。speed表示USB设备控制器当前的速度。max_speed表示USB设备控制器最大的速度。

代码语言:javascript
复制
[drivers/usb/gadget/udc/core.c]
static LIST_HEAD(udc_list);
struct usb_udc {  // 描述usb设备控制器
    // 指向Composite驱动中的usb_gadget_driver
	struct usb_gadget_driver *driver;
    // 实现udc驱动的结构体,包含usb设备控制器硬件操作函数
	struct usb_gadget		*gadget;  
	struct device			dev;
    // usb_udc结构体可以组成一个链表
	struct list_head		list;
	bool		vbus;  // 对于不关心vbus状态的udc,该值始终为true
};
[include/linux/usb/gadget.h]
struct usb_gadget {
    // 用于sysfs_notify的工作队列
	struct work_struct		work;
	struct usb_udc			*udc;  // 指向usb_udc
    // usb设备控制器硬件操作函数,不涉及io操作
	const struct usb_gadget_ops	*ops;  
	struct usb_ep *ep0;  // 端点0,用于响应控制读写请求
	struct list_head ep_list; // 该usb设备驱动所需的所有端点链表
	enum usb_device_speed speed;  // 当前连接usb主机的速度
	enum usb_device_speed max_speed;  // udc驱动支持的最大速度
	enum usb_device_state state;  // 当前的状态
	const char *name;  // udc驱动名称,用与确认控制器硬件类型
	struct device  dev;
	unsigned out_epnum;  // 最近使用的输出端点编号
	unsigned in_epnum;   // 最近使用的输入端点编号
	unsigned mA;         // 最近设置的mA值
	struct usb_otg_caps *otg_caps; // OTG的能力
	unsigned sg_supported:1;       // 是否支持聚合DMA
	unsigned is_otg:1;  // 是否支持OTG,支持OTG必须提供OTG描述符
	unsigned is_a_peripheral:1;    // 一般为false除非支持OTG
    // 输出端点的请求缓冲区大小按MaxPacketSize对齐
	unsigned quirk_ep_out_aligned_size:1;  
	unsigned is_selfpowered:1;  // 是否是自供电
	unsigned connected:1;       // 是否连接成功
	unsigned uvc_enabled:1;     // uvc功能是否使能
    ......
};
struct usb_gadget_driver {
	char *function;  // 描述usb_gadget_driver的字符串
	enum usb_device_speed	max_speed;  // 该驱动可处理的最大速度
    // 回调函数,可通过该函数绑定上层的gadget function驱动
	int	(*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
	void (*unbind)(struct usb_gadget *);
    // 端点0控制请求调用,用于描述符和配置的管理,通常在中断中调用,不可睡眠
	int	(*setup)(struct usb_gadget *, const struct usb_ctrlrequest *);
    // 当主机断开时,所有传输停止后调用,可能会在中断中调用,不可睡眠
	void (*disconnect)(struct usb_gadget *);
	void (*suspend)(struct usb_gadget *);
	void (*resume)(struct usb_gadget *);
    // usb总线复位时调用,必须实现,在中断中调用
	void (*reset)(struct usb_gadget *);
	struct device_driver	driver;
};
struct usb_gadget_ops {  // usb设备控制器硬件操作函数,不涉及端点和io
	int	(*get_frame)(struct usb_gadget *);
	int	(*wakeup)(struct usb_gadget *);
	int	(*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
	int	(*vbus_session) (struct usb_gadget *, int is_active);
	int	(*vbus_draw) (struct usb_gadget *, unsigned mA);
    // 下拉让usb主机感知到usb设备接入usb总线,usb主机会枚举usb设备
	int	(*pullup) (struct usb_gadget *, int is_on);  
	int	(*ioctl)(struct usb_gadget *, unsigned code, unsigned long param);
	void (*get_config_params)(struct usb_dcd_config_params *);
	int	(*udc_start)(struct usb_gadget *, struct usb_gadget_driver *); // 启动udc
	int	(*udc_stop)(struct usb_gadget *);  // 停止udc
    // 匹配usb端点
	struct usb_ep *(*match_ep)(struct usb_gadget *,
		struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *);
};
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.概述
  • 2.Gadget Function驱动
  • 3.Gadget Function API
  • 4.Composite层
    • 4.1.legacy
      • 4.2.USB Gadget Configfs
      • 5.UDC驱动
        • 5.1.函数接口
          • 5.2.数据结构
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档