Gadget驱动程序框架
文章目录
- Gadget驱动程序框架
- 参考资料:
- 一、 怎样理解Gadget框架
- 二、从硬件软件角度理解Gadget框架
- 2.1 底层硬件操作_UDC驱动
- 2.2 上层软件操作
- 三、 从构造描述符的角度理解Gadget框架
- 致谢
参考资料:
- Linux下USB gadget设备详解
- Linux usb gadget框架概述
- USB设备驱动程序-USB Gadget Driver(二)
- usb gadge驱动设计之我是zero
- Linux-USB驱动笔记(四)–USB整体框架
- USB gadget设备驱动解析
- 调试软件:USB view 、bushound 、及一些硬件USB信号分析仪
- 可以用wireshark+usbmon捕捉usb协议数据包。
一、 怎样理解Gadget框架
USB协议是主从结构:
我们编写USB设备驱动程序时,主要是:
- 读取设备的各类描述符,比如endpoint描述符,得到端点号
- 使用底层USB Host Controller驱动程序提供的API函数,从endpoint上读写数据
那么要基于Gadget驱动框架模拟一个USB设备时,endpoint的数据传输能力是底层的USB Device Controller驱动提供的,我们要做的就是:
- 提供各类设备描述符
- 使用底层USB Device Controller驱动程序提供的API函数,从endpoint得到数据、反馈数据
Gadget的含义是"小器件",在Linux的USB系统中,它表示"usb device"。Gadget驱动程序,就是用来模拟USB Device。对于真实的USB Device,它有两大要素:
- 怎么表示自己?
- 每个USB Device都有1个设备描述符
- 都1个或多个配置描述符
- 每个配置描述符里面有1个或多个接口描述符
- 每个接口描述符里面有0个多个端点描述符
- 怎么进行数据传输?
- 通过端点进行传输
- 有端点的操作函数
在学习过程中,记住这几个要点非常有帮助:
- 各类描述符的构造
- USB Host获得Gadget各类描述符的过程
- 数据传输的流程
二、从硬件软件角度理解Gadget框架
USB传输的核心是endpoint,使用endpoint可以收发数据。在endpoint之上,就可以模拟USB串口、USB触碰屏、USB摄像头。基于这个角度,Gadget框架可以分为两层:
- 底层endpoint操作
- 上层模拟各类USB设备
2.1 底层硬件操作_UDC驱动
对于底层endpoint的代码,需要从UDC驱动开始分析:
-
IMX6ULL的代码:
Linux-4.9.88\drivers\usb\chipidea\ci_hdrc_imx.c
ci_hdrc_imx_probe ci_hdrc_add_device pdev = platform_device_alloc("ci_hdrc", id); // Linux-4.9.88\drivers\usb\chipidea\core.c static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, .remove = ci_hdrc_remove, .driver = { .name = "ci_hdrc", .pm = &ci_pm_ops, }, }; ci_hdrc_probe ret = ci_hdrc_gadget_init(ci); udc_start
2.2 上层软件操作
模拟各类USB设备时,软件怎么分层?以访问设备、获取描述符为例:
- Host要分配地址、把地址发送给设备:不管要模拟什么设备,Gadget都必须接收地址,这部分由usb_gadget(硬件相关的驱动程序)实现
- Host要读取各类描述符,这些描述符是由上层的驱动程序提供的
- 怎么把上层的描述符通过底层的usb_gadget传回给Host?还需要一个中间层。Host获取描述符时,方法是固定、通用的,这些方法可以由内核统一提供,这就是:usb_gadget_driver。
所以,从获取描述符的角度看看,上层软件至少分为2层:
- usb_gadget_driver:实现一些通用的USB访问方法,比如Host访问描述符时,由usb_gadget_driver提供
- 在这上面提供各类描述符,实际上,描述符的提供还可以分为两层:
- 设备描述符、配置描述符:由程序员决定,由usb_composite_driver提供
- 接口描述符、endpoint描述符:由内核事先实现的、常用的function driver提供
软件层次可以进一步细化,如下图:
这涉及2个结构体:
-
usb_composite_dev:它里面汇集有各类描述符、有一个usb_funciton链表(实现数据传输)
struct usb_composite_dev { struct usb_gadget *gadget; struct usb_request *req; struct usb_request *os_desc_req; struct usb_configuration *config; /* OS String is a custom (yet popular) extension to the USB standard. */ u8 qw_sign[OS_STRING_QW_SIGN_LEN]; u8 b_vendor_code; struct usb_configuration *os_desc_config; unsigned int use_os_string:1; /* private: */ /* internals */ unsigned int suspended:1; struct usb_device_descriptor desc; struct list_head configs; struct list_head gstrings; struct usb_composite_driver *driver; u8 next_string_id; char *def_manufacturer; /* the gadget driver won't enable the data pullup * while the deactivation count is nonzero. */ unsigned deactivations; /* the composite driver won't complete the control transfer's * data/status stages till delayed_status is zero. */ int delayed_status; /* protects deactivations and delayed_status counts*/ spinlock_t lock; /* public: */ unsigned int setup_pending:1; unsigned int os_desc_pending:1; };
-
usb_udc:UDC的本意是"usb device controller",usb_udc结构体里面有usb_gadget(表示UDC本身)、usb_gadget_driver()
struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; struct device dev; struct list_head list; bool vbus; };
三、 从构造描述符的角度理解Gadget框架
假设你要模拟一个USB设备,
- 这个USB设备含有厂家信息:它记录在设备描述符里,所以设备描述符应该由你提供
- 这个芯片可能有多种配置,这也是由你决定,所以配置描述符应该由你提供
- 某个配置下多个接口,接口就是功能,Linux内核里事先提供了很多功能的驱动程序,所以:接口描述符是内核提供的
- 某个接口下需要什么端点,也是内核里各类功能的驱动程序提供的
以zero.c为例:
- 配置1:loopback,Host写数据给它,就可以读出原样的数据
- 配置2:sourcesink,Host写数据给它(它只是记录下数据),Host还可以读数据(读到的都是0)
从下到上涉及这些文件:
阅读源码时,入口函数是
usb_composite_probe(&zero_driver)
:
函数调用过程中主要的函数如下,重点关注"xxx_bind"函数,可以认为bind就是初始化的意思:
- usb_composite_probe
- composite_bind
- zero_bind
- sourcesink_bind/loopback_bind
深入解读描述符的构造过程,可以得到下面的图:
- 构造出一个usb_composite_dev结构体
- 它把各层串联起来,里面构造有设备描述符、配置描述符、接口描述符、端点描述符
致谢
以上笔记源自
韦东山
老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!
在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!