参考资料:
《圈圈教你玩USB》 《STM32 USB-FS-Device development kit》 沁雪微电子-USB开发视频教程 零声教育-Linux内核-USB系统架构 正点原子实验例程
源于我的有道云笔记,图片我懒得上传了,博客上的格式可能没调好,推荐直接看有道云:
文档:USB驱动开发.note
链接:http://note.youdao.com/noteshare?id=c0a1bdf8d93e876af261194547c92caf&sub=87C2944A1AFC402985AD9F7AD0F1E508
汇报思路:
Linux的驱动层层封装我们不能直观的理解USB的工作流程和重要的协议,设备驱动的代码虽然很简单,但是不了解USB的工作流程根本看不懂代码。所以打算:
实验效果
重点:
一、USB简介
1、串行通信协议介绍
2、USB发展史
3、USB设备分类
二、USB的通信
1、Linux的USB主机-设备通信驱动框架
2、USB设备端的通信过程
3、USB设备的描述符
4、传输的构成:USB的传输协议(非重点)
三、USB通信分析(主从设备对比)
1、stm32的USB详细架构(主机)
2、LinuxUSB鼠标设备驱动
3、CH552设备端驱动
4、STM32设备端驱动
5、实验效果展示
附录:
一、USB通信抓包解析
二、INPUT设备驱动开发流程
一、USB简介
一个完整的硬件产品是由多种模块组合实现产品功能的,微控制器 MCU 充当大脑,外围的存储单元、显示单元、发声单元、传感器单元、运动单元结合起来,就离不开相互之间的数据通信,衍生出了繁多的协议,这些协议通常可以分为并行通信协议和串行通信协议。
在同步通讯系统中,两个设备通讯则需要同步信号,同步信号分为时钟同步信号和自同步信号两种,时钟同步方式在通讯链路上具有时钟信号(IIC、SPI),自同步方式在通讯链路中没有同步信号(PCIE、USB),自同步方式常常适用于高速通讯系统中。USB使用NRZI编码的方式同步信号。
1、串行通信协议介绍
SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线。
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
USB通用串行总线(英语:Universal Serial Bus,缩写:USB)是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,并扩展至摄影器材、数字电视(机顶盒)、游戏机等其它相关领域。
2、USB发展史
3、USB设备分类
这里列举从USB设备接口描述符里面获得的部分分类
二、USB的通信
1、Linux的USB主机-设备通信驱动框架
主机端(其他系统也是这种框架):
主机端工作过程:
设备端的驱动一般都是写成固件的形式(使用中断的方式实现),在Linux封装下这一些操作已经“看不到了”,后面使用51的例子讲解工作流程。Linux有专用的USB设备端驱动框架,这里只做简述。
设备端:
2、USB设备端的通信过程
USB主机控制器获取信息的过程通常称为枚举,对应USB的主机端工作过程,在设备端工作过程为:
简单了解一下USB设备端-鼠标的工作流程,后面再详细讲:
在流程图中右边主要是枚举过程,左边分支主要是数据传输过程。
开发USB设备端驱动,我们只需要提供设备描述符与部分事件的响应即可。
3、USB设备的描述符
USB的设备描述符一般固化在固件中,USB的设备架构:
如果USB主机要和USB设备通信,光有设备地址是不够的,还需要一个端点地址。有了设备地址和端点地址,就能准确地对端点发送和读取数据了。而配置和接口,是为了更方便地管理端点而抽象出来的概念。一个设备可以有多个配置,但是同一时间只能有一个配置有效,每个配置下又可以有多个接口。当我们需要不同的功能时,只要选择不同的配置即可。同一个端点号不能出现在同一配置下的两个或多个不同的接口中。同一个端点号可用在不同的配置中。一个端点在主机端对应一个管道。
在Linux中,使用struct usb_driver结构体来描述一个USB(接口)驱动,通过usb_register在USB驱动中注册。
4、传输的构成:USB的传输协议(非重点)
USB的传输协议有很多,鼠标、键盘等设备只需要只用最基础的传输协议即可,但是在一些复杂设备上就会涉及其他的协议(比如在Mass Storage类就会涉及BulkOnly)如下图。
在下面简单讲讲基础的传输协议及其构成。
a、传输:
b、事务
c、包:
根据不同类型的包,所包含的域是不一样的。但是不同的包有个共同的特点,就是都要以同步域开始,紧跟着一个包标识符PID(Packet Of Identifier),最终以包结束符EOP(End Of Packet)来结束这个包。
包标识符PID是用来标识一个包的类型的。USB协议规定了4类包,分别是:令牌包(toker packet,PID1~0为01),数据包(data packet,PID1~PID0为11),握手包(handshake packet,PID1~PID0位10)和特殊包(special packet,PID1~PID0为00)。
d、域:
USB是串行总线,所以数据是一位一位地在数据线上传递的。既然是一位一位地传递,就存在着一个数据位先后的问题。USB使用的是LSB在前的方式,即先出来的是最低位数据,接下来是次低位,最后是最高位(MSB)。一个包,又被分成了很多个域(field),而LSB、MSB就是以域为单位来划分的。包括同步域、地址域、端点域、帧号域、标识域、数据域、校验域。
三、USB通信分析(主从设备对比)
1、stm32的USB详细架构(主机)
代码分析一:STM32USB鼠标键盘(Host)实验(参考于正点原子的demo)
mian函数: 初始化USB主机,注册回调函数 USBH_Usr_cb_TypeDef USR_Callbacks = { USBH_USR_Init,//USB HOST 初始化 USBH_USR_DeInit,//重新初始化 USBH_USR_DeviceAttached,//检测到U盘插入 USBH_USR_ResetDevice,//复位从机 USBH_USR_DeviceDisconnected,//检测到U盘拔出 USBH_USR_OverCurrentDetected,//USB接口电流过载 USBH_USR_DeviceSpeedDetected,//检测到从机速度 USBH_USR_Device_DescAvailable,//检测到从机的设备描述符 USBH_USR_DeviceAddressAssigned,//从机地址分配成功 USBH_USR_Configuration_DescAvailable,//配置描述符获有效 USBH_USR_Manufacturer_String,//获取到设备Manufacturer String USBH_USR_Product_String,//获取到设备Product String USBH_USR_SerialNum_String,//获取到设备SerialNum String USBH_USR_EnumerationDone,//设备USB枚举完成 USBH_USR_UserInput,//等待用户输入按键,执行下一步操作 NULL, USBH_USR_DeviceNotSupported,//无法识别的USB设备 USBH_USR_UnrecoveredError//无法恢复的错误!! }; USBH_Process: 不断的检测USB的连接(会配置不同的状态字来处理不同的USB外设),监视USB的状态。并对注册的函数进行回调。 设备驱动程序: 当USBH识别设备为HID设备后,会调用USBH_HID_InterfaceInit识别设备的BOOT_CODE以此来判别鼠标 HID_cb_TypeDef HID_MOUSE_cb = { MOUSE_Init, MOUSE_Decode,//MOUSE_Decode(uint8_t *data) };
2、LinuxUSB鼠标设备驱动
我们在设备驱动里面只需要对urb进行处理
LinuxUSB-鼠标设备驱动:
代码分析二:LinuxUSB鼠标设备驱动
1、注册驱动模块 2、注册USB设备驱动:usb_register(&usb_mouse_driver) static struct usb_driver usb_mouse_driver = { .name = "hclusbmouse", .probe = usb_mouse_probe, .disconnect = usb_mouse_disconnect, .id_table = usb_mouse_id_table, }; usb_mouse_probe 获取设备信息 完成urb的初始化 注册信息上报设备驱动(input驱动) 注册urb_completion回调函数
通过hexdump /dev/input/eventX查看上报的信息
3、CH552设备端驱动
CH552是一款支持USB的单片机。
代码分析三:CH552鼠标固件程序
USBDeviceInit(); //USB设备模式初始化 设置USB工作模式:设备模式 设置端点数据传输地址 USB设备寄存器初始化 对USB中断进行初始化 UEP1_T_LEN = 0; //预使用发送长度一定要清空 UEP2_T_LEN = 0; //清空端点2发送长度 FLAG = 0;//清空USB中断传输完成标志 Ready = 0;//清空USB枚举完成标志 然后轮询按键状态 DeviceInterrupt中断处理函数(一长串的switch-case语句) 对请求的处理 枚举:我们可以在HIDDescriptor.h看到描述符信息 HID类命令 端点数据上传 应答 错误处理
当定义一个设备为HID设备时,其设备描述符应为:
接口描述符应该:bInterfaceClass=0x03
HID描述符用于识别HID设备中所包含的额外描述符,如下:
4、STM32设备端驱动
移植stm32官方提供的的USB库文件
重点关注:
对于繁多的描述符我选择用工具生成,然后进行改动。
如:
更改设备接口类型(usbd_hid.c改150行)
定义hid类型为鼠标(usbd_hid.c改152行)
然后通过USBD_HID_SendReport将数据提交到缓冲区,对中断的响应已经交给usb_core处理了.
5、实验效果展示
主机使用imx6ull(Linux操作系统)从机使用STM32F4(无操作系统)
当鼠标连接后每次鼠标上报数据后,我们在主机上都能看到上报的数据。
附录:
一、USB通信抓包解析
EOP:
setup:控制传输起始的令牌包
data:
setup:设置从机地址到0B
。。。。。。
setup:获取设备描述符、配置描述符等(因为分层的思想可能会要很多遍)
OUT:设置初始状态
IN:定时取数据
二、INPUT设备驱动开发流程
1、简介
input 子系统就是管理输入的子系统,是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等
等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。
2、input设备驱动的使用
struct input_dev *inputdev; //1、申请 input_dev inputdev = input_allocate_device(); //2、设置产生事件的类型和事件值:点击、相对坐标等 //按键事件和相对位移事件 inputdev ->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); //左、右、中键、位移、侧边键、滑轮 inputdev ->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); inputdev ->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); inputdev ->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); inputdev ->relbit[0] |= BIT_MASK(REL_WHEEL); //3、注册 input_dev input_register_device(inputdev); //4、上报事件:事件以及值 input_report_key(input, BTN_LEFT, buf); input_report_key(input, BTN_RIGHT, buf); input_report_key(input, BTN_MIDDLE, buf); input_report_key(input, BTN_SIDE, buf); input_report_key(input, BTN_EXTRA, buf); input_report_rel(input, REL_X, buf); input_report_rel(input, REL_Y, buf); input_report_rel(input, REL_WHEEL, buf); 用户空间的/dev/input/eventX就是对应的input设备文件
三、USB接口驱动
OHCI、UHCI都是USB1.1的接口标准,而EHCI是对应USB2.0的接口标准,最新的xHCI是USB3.0的接口标准。
1. OHCI(Open Host Controller Interface)是支持USB1.1的标准,但它不仅仅是针对USB,还支持其他的一些接口,比如它还支持Apple的火线(Firewire,IEEE 1394)接口。与UHCI相比,OHCI的硬件复杂,硬件做的事情更多,所以实现对应的软件驱动的任务,就相对较简单。主要用于非x86的USB,如扩展卡、嵌入式开发板的USB主控。
2. UHCI(Universal Host Controller Interface),是Intel主导的对USB1.0、1.1的接口标准,与OHCI不兼容。UHCI的软件驱动的任务重,需要做得比较复杂,但可以使用较便宜、较简单的硬件的USB控制器。Intel和VIA使用UHCI,而其余的硬件提供商使用OHCI。
3. EHCI(Enhanced Host Controller Interface),是Intel主导的USB2.0的接口标准。EHCI仅提供USB2.0的高速功能,而依靠UHCI或OHCI来提供对全速(full-speed)或低速(low-speed)设备的支持。
4. xHCI(eXtensible Host Controller Interface),是最新最火的USB3.0的接口标准,它在速度、节能、虚拟化等方面都比前面3中有了较大的提高。xHCI支持所有种类速度的USB设备(USB 3.0 SuperSpeed, USB 2.0 Low-, Full-, and High-speed, USB 1.1 Low- and Full-speed)。xHCI的目的是为了替换前面3中(UHCI/OHCI/EHCI)。
四、其他描述符
报表描述符由描述 HID 设备的数据 项目(Item ) 组成。
项目的 第一个字节 (项目前缀)由三部分构成:
五、USB枚举过程