天天看点

Linux下USB驱动框架分析​

1.1 USB简介​

1.1.1 USB总线介绍​

USB是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,USB就是简写,中文叫通用串行总线。最早出现在1995年,伴随着奔腾机发展而来。自微软在Windows 98中加入对USB接口的支持后,USB接口才推广开来,USB设备也日渐增多,如数码相机、摄像头、扫描仪、游戏杆、打印机、键盘、鼠标等等,其中应用最广的就是摄像头和U盘了。​

USB包括老旧的USB 1.1标准和时下正流行的USB 2.0标准。传统的USB 1.1最高传输速率为12Mbps,一般厂商将其符合USB 1.1标准的产品称为“全速USB”。而高速USB 2.0最初推出时最高传输速率仅为240Mbps,后来USB2.0推广组(USB Promoter Group)在1999年10月将该速率提高到480Mbps,比传统的USB 1.1快40倍。​

USB2.0向下兼容USB 1.1,当然USB1.1设备也“向上兼容”USB 2.0,但是无法实现USB2.0的传输能力,并自动以低速传输。USB 2.0连接线的最大长度为5米,但如果用五个USB适配器,则最大长度可达30米。​

最新一代是USB 3.1,传输速度为10Gbit/s,三段式电压5V/12V/20V,最大供电100W ,新型Type C插型不再分正反。​

Linux下USB驱动框架分析​
Linux下USB驱动框架分析​

USB采用四线电缆,其中两根是用来传送数据的串行通道,另两根为下游(Downstream)设备提供电源,对于高速且需要高带宽的外设,USB以全速12Mbps的传输数据;对于低速外设,USB则以1.5Mbps的传输速率来传输数据。USB总线会根据外设情况在两种传输模式中自动地动态转换。USB是基于令牌的总线。类似于令牌环网络或FDDI基于令牌的总线。USB主控制器广播令牌,总线上设备检测令牌中的地址是否与自身相符,通过接收或发送数据给主机来响应。USB通过支持悬挂/恢复操作来管理USB总线电源。USB系统采用级联星型拓扑,该拓扑由三个基本部分组成:主机(Host),集线器(Hub)和功能设备。​

主机,也称为根,根结或根Hub,它做在主板上或作为适配卡安装在计算机上,主机包含有主控制器和根集线器(Root Hub),控制着USB总线上的数据和控制信息的流动,每个USB系统只能有一个根集线器,它连接在主控制器上。​

集线器是USB结构中的特定成分,它提供叫做端口(Port)的点将设备连接到USB总线上,同时检测连接在总线上的设备,并为这些设备提供电源管理,负责总线的故障检测和恢复。集线可为总线提供能源,亦可为自身提供能源(从外部得到电源),自身提供能源的设备可插入总线提供能源的集线器中,但总线提供能源的设备不能插入自身提供能源的集线器或支持超过四个的下游端口中,如总线提供能源设备的需要超过100mA电源时,不能同总线提供电源的集线器连接。​

USB介绍: ​​http://www.usb.org/home​​​

  • USB设备主要具有以下优点:​1. 可以热插拔。就是用户在使用外接设备时,不需要关机再开机等动作,而是在电脑工作时,直接将USB插上使用。​

    2. 携带方便。USB设备大多以“小、轻、薄”见长,对用户来说,随身携带大量数据时,很方便。当然USB硬盘是首要之选了。​

    3. 标准统一。大家常见的是IDE接口的硬盘,串口的鼠标键盘,并口的打印机扫描仪,可是有了USB之后,这些应用外设统统可以用同样的标准与个人电脑连接,这时就有了USB硬盘、USB鼠标、USB打印机等等。​

    4. 可以连接多个设备。USB在个人电脑上往往具有多个接口,可以同时连接几个设备,如果接上一个有四个端口的USB HUB时,就可以再连上;四个USB设备,以此类推,尽可以连下去,将你家的设备都同时连在一台个人电脑上而不会有任何问题(注:最高可连接至127个设备)​

  • USB接口定义颜色一般的排列方式是:红白绿黑从左到右

    定义:

  1. 红色-USB电源: 标有-VCC、Power、5V、5VSB字样​
  2. 白色-USB数据线:(负)-DATA-、USBD-、PD-、USBDT-​
  3. 绿色-USB数据线:(正)-DATA+、USBD+、PD+、USBDT+​
  4. 黑色-地线: GND、Ground​

1.1.2 USB接口HID设备​

HID(Human Interface Device,人机接口设备)是USB设备中常用的设备类型,是直接与人交互的USB设备,例如键盘、鼠标与游戏杆等。在USB设备中,HID设备的成本较低。另外,HID设备并不一定要有人机交互功能,只要符合HID类别规范的设备都是HID设备。​

  • HID设备的特点​

HID设备交换的数据储存在称为报表(Report)的结构内,设备的固件必须支持HlD报表的格式。主机通过控制和中断传输中的传送和请求报表来传送和接收数据,报表的格式非常灵活。每一笔事务可以携带小量或中量的数据。低速设备每一笔事务最大是8B,全速设备每一笔事务最大是64B,高速设备每一笔事务最大是1024B。一个报表可以使用多笔事务。设备可以在未预期的时间传送信息给主机,例如键盘的按键或是鼠标的移动。所以 主机会定时轮询设备,以取得最新的数据。 Ÿ​

HID设备的最大传输速度有限制。主机可以保证低速的中断端点每10ms内最多1 笔事务,每一秒最多是800B。保证全速端点每lms一笔事务,每一秒最多是64000B。保证高速端点每125 us三笔事务,每一秒最多是24.576MB。HID设备没有保证的传输速率。如果设备是设置在10ms的时距,事务之间的时间可能等于或小于10ms。除非设备是设置在全速时在每个帧传输数据,或是在高速时在每个微帧传输数据。这是最快的轮询速率,所以端点可以保证有正确的带宽可供使用。HID设备除了传送数据给主机外,它也会从主机接收数据。只要能够符合HlD类别规范的设备都可以是HID设备。 ​

设备除了HlD接口之外,它可能同时还包含有其他的USB接口。例如:影像显示设备可能使用HID接口来做亮度、对比度的软件控制,而使用传统的影像接口来传送要显示的数据。USB扩音器可以使用实时传输来播放语音,同时使用HID接口来控制音量、低音等。 ​

HID类别设备的规范文件主要是以下两份: Ÿ ​

  1. Device Class Definition for Human interface Devices Ÿ ​
  2. HID Usage Tables ​

其中前者是HID的基本规范文件,后者可以是前者的附件,为开发人员提供实际的控制类型的描述。文件是用来定义让主机了解以及使用HID数据的数值。这两份文件是由 USB Device Working Group制定的,可以在网址http://www.usb.org/developers/hidpage/ #Class _Definition下载。​

鼠标驱动源码路径:\drivers\hid\usbhid\usbmouse.c ​

键盘驱动源码路径: \drivers/usb/input/usbkbd.c​

USB转串口源码:/drivers/usb/serial/​

USB蓝牙源码:/drivers/bluetooth/​

USB摄像头源码:/drivers/media/video/uvc/​

USB总线定义: \drivers\usb\core\ driver.c​

USB总线核心结构定义文件:\include\linux\usb.h​

USB注册: \drivers\usb\core\usb.c​

1.1.1 USB 构造​

1.1.2 USB设备断点描述符​

USB 通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。​

一个 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:​

  • 控制CONTROL ​控制端点被用来控制对 USB 设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为"端点 0", 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.​
  • 中断INTERRUPT ​每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到 USB 设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.​
  • 批量BULK​批量端点用以传送大量数据。这些端点常比中断端点大得多. 它们普遍用于不能有任何数据丢失的数据。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。​
  • 等时ISOCHRONOUS 等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。

    控制和批量端点用于异步数据传送,而中断和同步端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。

    端点在内核中使用结构 struct usb_host_endpoint 来描述,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。

1.1.3 USB总线的注册​

在\drivers\usb\core\ driver.c文件的最后几行:​

struct bus_type usb_bus_type = {​

.name ="usb",​

.match =usb_device_match, //用来匹配USB设备端和驱动端的设备ID和类型​

.uevent =usb_uevent,​

};​

1.2 USB驱动端的注册​

1.2.2 USB驱动的注册​

#define usb_register(driver) \​

usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)​

功能:注册USB设备驱动端。注册成功后,USB设备端就可以与驱动端进行匹配了。​

涉及到的头文件:usb.h​

  • struct usb_driver原型如下:

struct usb_driver {​

const char *name; //USB设备的名字,名称可以随便填写。​

int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);资源探索函数,当usB驱动端与设备端匹配成功的时候调用。​

void (*disconnect) (struct usb_interface *intf); //USB设备断开时调用。​

int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,void *buf); ​

int (*suspend) (struct usb_interface *intf, pm_message_t message);​

int (*resume) (struct usb_interface *intf);​

int (*reset_resume)(struct usb_interface *intf);​

int (*pre_reset)(struct usb_interface *intf);​

int (*post_reset)(struct usb_interface *intf);​

const struct usb_device_id *id_table; //USB设备的匹配ID列表​

struct usb_dynids dynids;​

struct usbdrv_wrap drvwrap;​

unsigned int no_dynamic_id:1;​

unsigned int supports_autosuspend:1;​

unsigned int disable_hub_initiated_lpm:1;​

unsigned int soft_unbind:1;​

};​

红色部分为必填成员。​

  • struct usb_device_id 设备ID结构原型如下:

struct usb_device_id {​

/* 确定设备信息去和结构体中哪几个字段匹配来判断驱动的适用性,比如是否是HID协议等*/​

__u16match_flags;​

/*用于特定于产品的匹配 */​

__u16idVendor; /*USB设备的制造商ID,须向www.usb.org申请*/​

__u16idProduct; // USB设备的产品ID,有制造商自定​

__u16bcdDevice_lo; /* USB设备的产品版本号最低值*/​

__u16bcdDevice_hi; /* 和最高值,以BCD码来表示。*/​

/* 分别定义设备的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中. ​

这些值指定这个设备的行为, 包括设备上所有的接口 */ ​

__u8bDeviceClass;​

__u8bDeviceSubClass;​

__u8bDeviceProtocol;​

/* 分别定义单个接口的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中 */​

__u8bInterfaceClass;​

__u8bInterfaceSubClass;​

__u8bInterfaceProtocol;​

/* 这个值不用来匹配驱动的, 驱动用它来在 USB 驱动的探测回调函数中区分不同的设备 ​

该成员一般来保存一个结构体指针,存放该设备特殊的数据​

*/​

kernel_ulong_tdriver_info;​

  • 填充struct usb_device_id结构体中的__u16match_flags成员用到的宏定义:

#define USB_DEVICE_ID_MATCH_VENDOR0x0001 /*供应商厂家ID*/​

#define USB_DEVICE_ID_MATCH_PRODUCT0x0002产品ID*/​

  • 快速填充usb_device_id结构体的相关宏:

USB_DEVICE_ID_MATCH_INT_INFO --根据接口信息​

USB_DEVICE_ID_MATCH_DEV_INFO --根据设备的信息​

USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION --根据设备制造信息和版本​

USB_DEVICE_ID_MATCH_DEV_RANGE --根据设备版本​

USB_DEVICE_ID_MATCH_DEVICE 根据设备制造信息​

  • struct usb_device_id结构体填充示例1—(摘自DM9620-USB网卡)

static const struct usb_device_id products[] = {​

{​

USB_DEVICE(0x07aa, 0x9601),/* Corega FEther USB-TXC */​

.driver_info = (unsigned long)&dm9620_info,​

}, {​

USB_DEVICE(0x0a46, 0x9601),/* Davicom USB-100 */​

.driver_info = (unsigned long)&dm9620_info,​

},​

}​

  • struct usb_device_id结构体填充示例2------->(摘自内核自带的鼠标驱动)​

static struct usb_device_id usb_mouse_id_table [] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_MOUSE) },​

{ }/* 结束 */​

};​

  • USB鼠标USB驱动端结构体填充示例:​

static struct usb_driver usb_mouse_driver = {​

.name= "usbmouse", /*鼠标的名称-不是匹配使用*/​

.probe= usb_mouse_probe,资源探测函数-匹配成功调用*/​

.disconnect= usb_mouse_disconnect,/*当鼠标断开连接调用*/​

.id_table= usb_mouse_id_table,匹配驱动使用*/​

};​

1.2.5 USB设备的注销

void usb_deregister(struct usb_driver *driver)​

1.2.6 USB注册框架示例

#include <linux/init.h>​

#include <linux/module.h>​

#include <linux/usb.h>​

//定义USB的IDTAB​

static const struct usb_device_id usbtest[] = {​

{//148f:760b​

USB_DEVICE(0x148f, 0x760b),/*360WIFI的制造商ID和产品ID */​

},​

};​

//USB设备信息与驱动端匹配成功的时候调用。​

static int test_probe(struct usb_interface *intf,const struct usb_device_id *id) //资源探索函数​

{​

printk("USB 驱动匹配成功!\n");​

return 0;​

}​

//USB断开的时候调用​

static void test_disconnect(struct usb_interface *intf)​

{​

printk("USB 设备释放成功!\n"); ​

}​

//定义USB驱动结构体 ​

static struct usb_driver usbtest_driver = {​

.name = "tiny4412_usbtest",​

.id_table = usbtest,​

.probe = test_probe,​

.disconnect = test_disconnect​

};​

static int __init usbtest_init(void)​

{​

//注册USB设备驱动​

usb_register(&usbtest_driver);​

return 0;​

}​

module_init(usbtest_init);​

static void __exit usbtest_exit(void)​

{​

//注销USB设备驱动​

usb_deregister(&usbtest_driver);​

}​

module_exit(usbtest_exit);​

MODULE_AUTHOR("xiaolong");​

MODULE_LICENSE("GPL");​

1.3 USB数据传输

USB系统的信息传输就是打成URB结构,然后再过行传送的,URB的全称叫USB request block。​

1.3.1 创建urb接口

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)​

这个函数有两个参数,一个是iso_packets.仅仅用于ISO传输.表示ISO数据包个数,如果用于其它类型的传输,此参数为0.另一个是mem_flags.是分配内存的参数。​

示例:​

struct urb *urb_1;​

urb_1= usb_alloc_urb(0, GFP_KERNEL);​

1.3.2 初始化USB中断

static inline void usb_fill_int_urb(struct urb *urb,​

struct usb_device *dev,​

unsigned int pipe, //管道端点​

void *transfer_buffer, //数据的缓冲区​

int buffer_length, //传输的长度​

usb_complete_t complete_fn, //回调函数指针​

void *context,​

int interval)​

1.3.3 分配数据缓冲区地址

void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,​

继续阅读