Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48700
  • 博文数量: 26
  • 博客积分: 1175
  • 博客等级: 少尉
  • 技术积分: 300
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-14 19:16
文章分类
文章存档

2011年(1)

2010年(25)

我的朋友

分类: LINUX

2010-04-22 19:57:51

20.1.1 主机侧与设备侧USB驱动

 

第20章 USB主机与设备驱动

在Linux系统中,提供了主机侧和设备侧视角的USB驱动框架,本章主要讲解从主机侧角度看到的USB主机控制器驱动和设备驱动。

20.1节给出了Linux系统中USB驱动的整体视图,讲解了Linux中主机侧和设备侧角度的USB驱动层次。

从主机侧的角度而言,需要编写的USB驱动程序包括主机控制器驱动和设备驱动两类,USB主机控制器驱动程序控制插入其中的USB设备,而USB设 备驱动程序控制该设备如何作为从设备与主机通信。本章20.2节分析了USB主机控制器驱动的结构并给出实例,20.3节讲解了USB设备驱动的结构及其 设备请求块处理过程,并分析了USB设备驱动的骨架程序,20.4节则给出了Linux设备驱动的实例。

20.1节与20.2~20.4节是整体与部分的关系,20.2节与20.3~20.4节是并列关系。

20.1 Linux USB驱动层次

20.1.1  主机侧与设备侧USB驱动

USB采用树形拓扑结构,主机侧和设备侧的USB控制器分别称为主机控制器(Host Controller)和USB设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备间的通信,而设备不能主动向主机发送任何消息。 如图20.1所示,在Linux系统中,USB驱动可以从两个角度去观察,一个角度是主机侧,一个角度是设备侧。

图20.1 Linux USB 驱动总体结构

如图20.1的左侧所示,从主机侧的观念去看,在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控 制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。因此,在主机侧的层次结构中, 要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。Linux内核 USB核心负责USB驱动管理和协议处理的主要工作。主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函 数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;通过全局变量维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传 输控制等。

如图20.1的右侧所示,Linux内核中USB设备侧驱动程序分为3个层次:UDC驱动程序、Gadget API和Gadget驱动程序。UDC驱动程序直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与硬件相关操作的回调函数。当前Gadget API是UDC驱动程序回调函数的简单包装。Gadget驱动程序具体控制USB设备功能的实现,使设备表现出“网络连接”、“打印机”或“USB Mass Storage”等特性,它使用Gadget API控制UDC实现上述功能。Gadget API把下层的UDC驱动程序和上层的Gadget驱动程序隔离开,使得在Linux系统中编写USB设备侧驱动程序时能够把功能的实现和底层通信分离。

本章将重点讲解从主机侧角度看到的USB主机控制器驱动与USB设备驱动,关于设备侧的Linux驱动,将不会详细讲解。

20.1.2 设备、配置、接口、端点

20.1.2  设备、配置、接口、端点

在USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次。

每个USB设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一 个),配置由多个接口组成。

在USB协议中,接口由多个端点组成,代表一个基本的功能,是USB设备驱动程序控制的对象,一个功能复杂的USB设备可以具有多个接口。每个配置 中可以有多个接口,而设备接口是端点的汇集(collection)。例如USB扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接 口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数。

端点是USB通信的最基本形式,每一个USB设备接口在主机看来就是一个端点的集合。主机只能通过端点与设备进行通信,以使用设备的功能。在USB 系统中每一个端点都有惟一的地址,这是由设备地址和端点号给出的。每个端点都有一定的属性,其中包括传输方式、总线访问频率、带宽、端点号和数据包的最大 容量等。一个USB端点只能在一个方向承载数据,或者从主机到设备(称为输出端点),或者从设备到主机(称为输入端点),因此端点可看作一个单向的管道。 端点0通常为控制端点,用于设备初始化参数等。只要设备连接到USB上并且上电端点0就可以被访问。端点1、2等一般用作数据端点,存放主机与设备间往来 的数据。\

总体而言,USB设备非常复杂,由许多不同的逻辑单元组成,如图20.2所示,这些单元之间的关系如下:

图20.2 USB设备、配置、接口和端点

l 设备通常有一个或多个配置;

l 配置通常有一个或多个接口;

l 接口通常有一个或多个设置;

l 接口有零或多个端点。

这种层次化配置信息在设备中通过一组标准的描述符来描述,如下所示。

l 设备描述符:关于设备的通用信息,如供应商ID、产品ID和修订ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。在 Linux内核中,USB设备用usb_device结构体来描述,USB设备描述符定义为usb_device_descriptor结构体,如代码清 单20.1所示。

代码清单20.1  usb_device_descriptor结构体

1  struct usb_device_descriptor 
2 {
3 _ _u8 bLength; //描述符长度
4 _ _u8 bDescriptorType; //描述符类型编号
5
6 _ _le16 bcdUSB; //USB版本号
7 _ _u8 bDeviceClass; //USB分配的设备类code
8 _ _u8 bDeviceSubClass;// USB分配的子类code
9 _ _u8 bDeviceProtocol; //USB分配的协议code
10 _ _u8 bMaxPacketSize0; //endpoint0最大包大小
11 _ _le16 idVendor; //厂商编号
12 _ _le16 idProduct; //产品编号
13 _ _le16 bcdDevice; //设备出厂编号
14 _ _u8 iManufacturer; //描述厂商字符串的索引
15 _ _u8 iProduct; //描述产品字符串的索引
16 _ _u8 iSerialNumber; //描述设备序列号字符串的索引
17 _ _u8 bNumConfigurations; //可能的配置数量
18 } _ _attribute_ _ ((packed));

l 配置描述符:此配置中的接口数、支持的挂起和恢复能力以及功率要求。USB配置在内核中使用usb_host_config结构体描述,USB 配置描述符定义为结构体usb_config_descriptor,如代码清单20.2所示。

代码清单20.2  usb_config_descriptor结构体

1  struct usb_config_descriptor 
2 {
3 _ _u8 bLength; //描述符长度
4 _ _u8 bDescriptorType; //描述符类型编号
5
6 _ _le16 wTotalLength; //配置所返回的所有数据的大小
7 _ _u8 bNumInterfaces; // 配置所支持的接口数
8 _ _u8 bConfigurationValue; //Set_Configuration命令需要的参数值
9 _ _u8 iConfiguration; //描述该配置的字符串的索引值
10 _ _u8 bmAttributes; //供电模式的选择
11 _ _u8 bMaxPower; //设备从总线提取的最大电流
12 } _ _attribute_ _ ((packed));

l 接口描述符:接口类、子类和适用的协议,接口备用配置的数目和端点数目。USB接口在内核中使用usb_interface结构体描述,USB 接口描述符定义为结构体usb_interface_descriptor,如代码清单20.3所示。

代码清单20.3  usb_interface_descriptor结构体

1  struct usb_interface_descriptor 
2 {
3 _ _u8 bLength; //描述符长度
4 _ _u8 bDescriptorType; //描述符类型
5
6 _ _u8 bInterfaceNumber; // 接口的编号
7 _ _u8 bAlternateSetting; //备用的接口描述符编号
8 _ _u8 bNumEndpoints; //该接口使用的端点数,不包括端点0
9 _ _u8 bInterfaceClass; //接口类型
10 _ _u8 bInterfaceSubClass; //接口子类型
11 _ _u8 bInterfaceProtocol; //接口所遵循的协议
12 _ _u8 iInterface; //描述该接口的字符串索引值
13 } _ _attribute_ _ ((packed));

l 端点描述符:端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。在Linux内核中,USB端点使用 usb_host_endpoint结构体来描述,USB端点描述符定义为usb_endpoint_descriptor结构体,如代码清单20.4所 示。

代码清单20.4  usb_endpoint_descriptor结构体

1  struct usb_endpoint_descriptor 
2 {
3 _ _u8 bLength; //描述符长度
4 _ _u8 bDescriptorType; //描述符类型
5 _ _u8 bEndpointAddress; //端点地址:0~3位是端点号,第7位是方向(0-OUT,1-IN)
6 _ _u8 bmAttributes; //端点属性:bit[0:1] 的值为00表示控制,为01表示同步,为02表示批量,为03表示中断
7 _ _le16 wMaxPacketSize; //// 本端点接收或发送的最大信息包的大小
8 _ _u8 bInterval;//轮询数据传送端点的时间间隔
9 //对于批量传送的端点以及控制传送的端点,此域忽略
10 //对于同步传送的端点,此域必须为1
11 //对于中断传送的端点,此域值的范围为1~255
12 _ _u8 bRefresh;
13 _ _u8 bSynchAddress;
14 } _ _attribute_ _ ((packed));

l 字符串描述符:在其他描述符中会为某些字段提供字符串索引,它们可被用来检索描述性字符串,可以以多种语言形式提供。字符串描述符是可选的,有 的设备有,有的设备没有,字符串描述符对应于usb_string_descriptor结构体,如代码清单20.5所示。

代码清单20.5  usb_string_descriptor结构体

1 struct usb_string_descriptor 
2 {
3 _ _u8 bLength; //描述符长度
4 _ _u8 bDescriptorType; //描述符类型
5
6 _ _le16 wData[1];/* 以UTF-16LE编码 */
7 } _ _attribute_ _ ((packed));

例如,笔者在运行Linux 2.6.15.5的系统上插入一个SanDisk U盘后,通过lsusb命令得到这个U盘相关的描述符,从中可以显示这个U盘包含了一个设备描述符、一个配置描述符、一个接口描述符以及批量输入和批量输 出两个端点描述符。呈现出来的信息内容直接对应于usb_device_descriptor、usb_config_descriptor、 usb_interface_descriptor、usb_endpoint_descriptor、usb_string_descriptor结构 体,如下所示:

Bus 001 Device 004: ID 0781:5151 SanDisk Corp. 
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0 Interface
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0781 SanDisk Corp.
idProduct 0x5151
bcdDevice 0.10
iManufacturer 1 SanDisk Corporation
iProduct 2 Cruzer Micro
iSerial 3 20060877500A1BE1FDE1
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 32
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
MaxPower 200mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk (Zip)
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type none
wMaxPacketSize 512
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type none
wMaxPacketSize 512
bInterval 1
Language IDs: (length=4)
0409 English(US)

20.2.1 USB主机驱动的整体结构

20.2 USB主机驱动

20.2.1  USB主机驱动的整体结构

USB主机控制器有3种规格:OHCI (Open Host Controller Interface)、UHCI (Universal Host Controller Interface) 和EHCI (Enhanced Host Controller Interface)。OHCI驱动程序用来为非PC 系统上以及带有SiS和ALi芯片组的 PC 主板上的USB芯片提供支持。UHCI驱动程序多用来为大多数其他PC主板(包括 Intel和Via)上的USB芯片提供支持。EHCI由USB 2.0规范所提出,它兼容于OHCI 和UHCI。UHCI的硬件线路比OHCI简单,所以成本较低,但需要较复杂的驱动程序,CPU负荷稍重。本节将重点介绍嵌入式系统中常用的OHCI主机 控制器驱动。

1.主机控制器驱动

在Linux内核中,用usb_hcd结构体描述USB主机控制器驱动,它包含USB主机控制器的“家务”信息、硬件资源、状态描述和用于操作主机 控制器的hc_driver等,其定义如代码清单20.6所示。

代码清单20.6  usb_hcd结构体

1  struct usb_hcd
2 {
3 /* 管理“家务” */
4 struct usb_bus self;
5 const char *product_desc; /* 产品/厂商字符串 */
6 char irq_descr[24]; /* 驱动 + 总线 # */
7
8 struct timer_list rh_timer; /* 根Hub轮询 */
9 struct urb *status_urb; /* 目前的状态urb */
10
11 /* 硬件信息/状态 */
12 const struct hc_driver *driver; /* 硬件特定的钩子函数 */
13
14 /* 需要维护的标志 */
15 unsigned long flags;
16 #define HCD_FLAG_HW_ACCESSIBLE 0x00000001
17 #define HCD_FLAG_SAW_IRQ 0x00000002
18
19 unsigned rh_registered: 1; /* 根Hub注册? */
20
21 /* 下一个标志的采用只是“权益之计”,当所有HCDs支持新的根Hub轮询机制后将移除 */
22 unsigned uses_new_polling: 1;
23 unsigned poll_rh: 1; /* 轮询根Hub状态? */
24 unsigned poll_pending: 1; /* 状态已经改变? */
25
26 int irq; /* 被分配的irq */
27 void _ _iomem *regs; /* 设备内存和I/O */
28 u64 rsrc_start; /* 内存和I/O资源开始位置 */
29 u64 rsrc_len; /* 内存和I/O资源长度 */
30 unsigned power_budget; /* mA, 0 = 无限制 */
31
32 #define HCD_BUFFER_POOLS 4
33 struct dma_pool *pool[HCD_BUFFER_POOLS];
34
35 int state;
36 #define _ _ACTIVE 0x01
37 #define _ _SUSPEND 0x04
38 #define _ _TRANSIENT 0x80
39
40 #define HC_STATE_HALT 0
41 #define HC_STATE_RUNNING (_ _ACTIVE)
42 #define HC_STATE_QUIESCING (_ _SUSPEND|_ _TRANSIENT|_ _ACTIVE)
43 #define HC_STATE_RESUMING (_ _SUSPEND|_ _TRANSIENT)
44 #define HC_STATE_SUSPENDED (_ _SUSPEND)
45
46 #define HC_IS_RUNNING(state) ((state) & _ _ACTIVE)
47 #define HC_IS_SUSPENDED(state) ((state) & _ _SUSPEND)
48 /* 主机控制器驱动的私有数据 */
49 unsigned long hcd_priv[0]_ _attribute_ _((aligned(sizeof(unsigned long))));
50 };

usb_hcd中的hc_driver成员非常重要,它包含具体的用于操作主机控制器的钩子函数,其定义如代码清单20.7所示。

代码清单20.7  hc_driver结构体

1  struct hc_driver
2 {
3 const char *description; /* "ehci-hcd" 等 */
4 const char *product_desc; /* 产品/厂商字符串 */
5 size_t hcd_priv_size; /* 私有数据的大小 */
6
7 /* 中断处理函数 */
8 irqreturn_t(*irq)(struct usb_hcd *hcd, struct pt_regs *regs);
9
10 int flags;
11 #define HCD_MEMORY 0x0001 /* HC寄存器使用的内存和I/O */
12 #define HCD_USB11 0x0010 /* USB 1.1 */
13 #define HCD_USB2 0x0020 /* USB 2.0 */
14
15 /* 被调用以初始化HCD和根Hub */
16 int(*reset)(struct usb_hcd *hcd);
17 int(*start)(struct usb_hcd *hcd);
18
19 /* 挂起Hub后,进入D3(etc)前被调用 */
20 int(*suspend)(struct usb_hcd *hcd, pm_message_t message);
21
22 /* 在进入D0(etc)后,恢复Hub前调用 */
23 int(*resume)(struct usb_hcd *hcd);
24
25 /* 使HCD停止写内存和进行I/O操作 */
26 void(*stop)(struct usb_hcd *hcd);
27
28 /* 返回目前的帧数 */
29 int(*get_frame_number)(struct usb_hcd *hcd);
30
31 /* 管理I/O请求和设备状态 */
32 int(*urb_enqueue)(struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct
33 urb *urb, gfp_t mem_flags);
34 int(*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb);
35
36 /* 释放endpoint资源 */
37 void(*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
38
39 /* 根Hub支持 */
40 int(*hub_status_data)(struct usb_hcd *hcd, char *buf);
41 int(*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
42 char *buf, u16 wLength);
43 int(*bus_suspend)(struct usb_hcd*);
44 int(*bus_resume)(struct usb_hcd*);
45 int(*start_port_reset)(struct usb_hcd *, unsigned port_num);
46 void(*hub_irq_enable)(struct usb_hcd*);
47 };

在Linux内核中,使用如下函数来创建HCD:

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name);

如下函数被用来增加和移除HCD:

int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags);
void usb_remove_hcd(struct usb_hcd *hcd);

2.OHCI主机控制器驱动

OHCI HCD驱动属于HCD驱动的实例,它定义了一个ohci_hcd结构体,作为代码清单20.6给出的usb_hcd结构体的私有数据,这个结构体的定义如 代码清单20.8所示。

代码清单20.8  ohci_hcd结构体

1  struct ohci_hcd
2 {
3 spinlock_t lock;
4
5 /* 与主机控制器通信的I/O内存(DMA一致) */
6 struct ohci_regs _ _iomem *regs;
7
8 /* 与主机控制器通信的主存(DMA一致) */
9 struct ohci_hcca *hcca;
10 dma_addr_t hcca_dma;
11
12 struct ed *ed_rm_list; /* 将被移除 */
13 struct ed *ed_bulktail; /* 批量队列尾 */
14 struct ed *ed_controltail; /* 控制队列尾 */
15 struct ed *periodic[NUM_INTS]; /* int_table“影子” */
16
17 /* OTG控制器和收发器需要软件交互,其他的外部收发器应该是软件透明的 */
18 struct otg_transceiver *transceiver;
19
20 /* 队列数据的内存管理 */
21 struct dma_pool *td_cache;
22 struct dma_pool *ed_cache;
23 struct td *td_hash[TD_HASH_SIZE];
24 struct list_head pending;
25
26 /* driver状态 */
27 int num_ports;
28 int load[NUM_INTS];
29 u32 hc_control; /* 主机控制器控制寄存器的复制 */
30 unsigned long next_statechange; /* 挂起/恢复 */
31 u32 fminterval; /* 被保存的寄存器 */
32
33 struct notifier_block reboot_notifier;
34 unsigned long flags;
35 };

使用如下内联函数可实现usb_hcd和ohci_hcd的相互转换:

struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd);
struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci);

从usb_hcd得到ohci_hcd只是取得“私有”数据,而从ohci_hcd得到usb_hcd则是通过container_of()从结构 体成员获得结构体指针。

使用如下函数可初始化OHCI主机控制器:

int ohci_init (struct ohci_hcd *ohci);

如下函数分别用于开启、停止及复位OHCI控制器:

int ohci_run (struct ohci_hcd *ohci);
void ohci_stop (struct usb_hcd *hcd);
void ohci_usb_reset (struct ohci_hcd *ohci);

OHCI主机控制器驱动的主机工作仍然是实现代码清单20.7给出的hc_driver结构体中的成员函数。

20.2.2 实例:S3C2410 USB 主机驱动

20.2.2  实例:S3C2410 USB 主机驱动

S3C2410内部集成了一个USB主机控制器,完全兼容OCHI 1.0、USB 1.1标准,支持低速和全速USB设备,从基地址0x49000000开始分别提供了OHCI的HcRevision、HcControl、 HcCommonStatus、HcInterruptStatus、HcInterruptEnable、HcInterruptDisable、 HcHCCA、HcPeriodCuttentED、HcControlHeadED、HcControlCurrentED、 HcBulkHeadED、HcBulk CurrentED、HcDoneHead、HcRmInterval、HcFmRemaining、HcFmNumber、 HcPeriodicStart、HcLSThreshold、HcRhDescriptorA、HcRhDescriptorB、 HcRhStatus、HcRhPortStatus1、HcRhPortStatus2寄存器。

S3C2410主机控制器驱动hc_driver结构体中的大多数成员函数都是通用的ohci_xxx()函数,而start()、 hub_status_data()、hub_control()函数则针对S3C2410而编写的,如代码清单20.9所示。

代码清单20.9  S3C2410主机控制器驱动的hc_driver结构体

1  static const struct hc_driver ohci_s3c2410_hc_driver = 
2 {
3 .description =hcd_name,
4 .product_desc ="S3C24XX OHCI",
5 .hcd_priv_size =sizeof(struct ohci_hcd),
6
7 /* 通用硬件联接 */
8 .irq =ohci_irq,
9 .flags =HCD_USB11 | HCD_MEMORY, //USB 1.1标准,hc寄存器位于内存
10
11 /* 基本的生命周期操作 */
12 .start =ohci_s3c2410_start,
13 .stop =ohci_stop,
14
15 /* 管理I/O请求和相关的设备资源 */
16 .urb_enqueue =ohci_urb_enqueue,
17 .urb_dequeue =ohci_urb_dequeue,
18 .endpoint_disable =ohci_endpoint_disable,
19
20 /* 调度支持 */
21 .get_frame_number =ohci_get_frame,
22
23 /* 根Hub支持 */
24 .hub_status_data =ohci_s3c2410_hub_status_data,
25 .hub_control =ohci_s3c2410_hub_control,
26 #ifdefCONFIG_PM
27 .bus_suspend =ohci_bus_suspend,
28 .bus_resume =ohci_bus_resume,
29 #endif
30 .start_port_reset =ohci_start_port_reset,
31 };

hc_driver的start()成员函数用于初始化OHCI并启动主机控制器,如代码清单20.10所示。

代码清单20.10  S3C2410主机控制器驱动的start()函数

1  static int ohci_s3c2410_start (struct usb_hcd *hcd)
2 {
3 struct ohci_hcd*ohci = hcd_to_ohci (hcd);
4 int ret;
5
6 if ((ret = ohci_init(ohci)) < 0) //初始化ohci_hcd
7 return ret;
8
9 if ((ret = ohci_run (ohci)) < 0) { //启动ohci_hcd
10 err ("can't start %s", hcd->self.bus_name);
11 ohci_stop (hcd);
12 return ret;
13 }
14
15 return 0;
16 }

hc_driver的hub_control()成员函数ohci_s3c2410_hub_control()中的主体是调用通用的ohci_ hub_control()函数,hub_status_data()成员函数ohci_s3c2410_hub_status_data()的主体是调 用通用的ohci_hub_status_data()函数。

20.3.1 USB设备驱动整体结构

20.3 USB设备驱动

20.3.1  USB设备驱动整体结构

Linux系统实现了几类通用的USB设备驱动,划分为如下几个设备类。

l 音频设备类。
l 通信设备类。
l HID(人机接口)设备类。
l 显示设备类。
l 海量存储设备类。
l 电 源设备类。
l 打印设备类。
l 集线器设备类。

一般的通用的Linux设备(如U盘、USB鼠标、USB键盘等)都不需要工程师再编写驱动,需要编写的是特定厂商、特定芯片的驱动,而且往往也可 以参考内核中已提供的驱动的模板。

这里所说的USB设备驱动指的是从主机角度观察,怎样访问被插入的USB设备,而不是指USB设备内部本身运行的固件程序。USB设备内的固件称为 “设备用户固件”,“设备用户固件”完成设备内部的控制任务,并在USB传输中对接收到的设备请求做出解释并予以正确响应。

Linux内核为各类USB设备分配了相应的设备号,如ACM USB调制解调器的主设备号为166(默认设备名/dev/ttyACMn)、USB打印机的主设备号为180,次设备号为0~15(默认设备名/dev /lpn)、USB串口的主设备号为188(默认设备名/dev/ttyUSBn)等。

内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的。通过在/etc/fstab文件中添加如下一行:

none /proc/bus/usb usbfs defaults

或者输入命令:

mount -t usbfs none /proc/bus/usb

可以实现USB设备文件系统的挂载。

一个典型的/proc/bus/usb/devices文件的结构如下(笔者在VmWare上运行的Linux 2.6.15.5内核上的机器上插入了一个SanDisk U盘):
T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12  MxCh= 2
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 1.10 Cls=09(hub ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0000 ProdID=0000 Rev= 2.06
S: Manufacturer=Linux 2.6.15.5 uhci_hcd
S: Product=UHCI Host Controller
S: SerialNumber=0000:00:07.2
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=255ms

T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0781 ProdID=5151 Rev= 0.10
S: Manufacturer=SanDisk Corporation
S: Product=Cruzer Micro
S: SerialNumber=20060877500A1BE1FDE1
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA
I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=(none)
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms

通过分析usbfs中记录的信息,可以得到系统中USB完整的信息,例如,usbview可以以图形化的方式显示系统中的USB设备。

当然,在编译Linux内核时,应该包括“USB device filesystem”,如图20.3所示。usbfs动态跟踪总线上插入和移除的设备,通过它可以查看系统中USB设备的信息,包括拓扑、带宽、设备描 述符信息、产品ID、字符串描述符、配置描述符、接口描述符、端点描述符等。

图20.3 USB设备文件系统编译

此外,在sysfs文件系统中,同样包含了USB相关信息的描述,但只限于接口级别。USB设备和USB接口在sysfs中均表示为单独的USB设 备,其目录命名规则如下:

根集线器-集线器端口号(-集线器端口号-...):配置.接口。

下面讲解/sys/bus/usb目录的树形结构实例,其中的多数文件都是到/sys/devices及/sys/drivers中相应文件的链 接。

usb
|-- devices
| |-- 1-0:1.0 -> ../../../devices/pci0000:00/0000:00:07.2/usb1/1-0:1.0
| |-- 1-1 -> ../../../devices/pci0000:00/0000:00:07.2/usb1/1-1
| |-- 1-1:1.0 -> ../../../devices/pci0000:00/0000:00:07.2/usb1/1-1/1-1:1.0
| '-- usb1 -> ../../../devices/pci0000:00/0000:00:07.2/usb1
'-- drivers
|-- hub
| |-- 1-0:1.0 -> ../../../../devices/pci0000:00/0000:00:07.2/usb1/1-0:1.0
| |-- bind
| |-- module -> ../../../../module/usbcore
| '-- unbind
|-- usb
| |-- 1-1 -> ../../../../devices/pci0000:00/0000:00:07.2/usb1/1-1
| |-- bind
| |-- module -> ../../../../module/usbcore
| |-- unbind
| '-- usb1 -> ../../../../devices/pci0000:00/0000:00:07.2/usb1
'-- usbfs
|-- bind
|-- module -> ../../../../module/usbcore
'-- unbind

正如tty_driver、pci_driver等,在Linux内核中,使用usb_driver结构体描述一个USB设备驱 动,usb_driver结构体的定义如代码清单20.11所示。

代码清单20.11  usb_driver结构体

1  struct usb_driver {
2 const char *name; /* 驱动名称 */
4 int (*probe) (struct usb_interface *intf,
5 const struct usb_device_id *id); /*探测函数*/
7 void (*disconnect) (struct usb_interface *intf); /*断开函数*/
9 int (*ioctl) (struct usb_interface *intf, unsigned int code,
10 void *buf); /* I/O控制函数 */
12 int (*suspend) (struct usb_interface *intf, pm_message_t message);/*挂起函数*/
13 int (*resume) (struct usb_interface *intf); /* 恢复函数 */
15 void (*pre_reset) (struct usb_interface *intf);
16 void (*post_reset) (struct usb_interface *intf);
18 const struct usb_device_id *id_table;/* usb_device_id表指针 */
20 struct usb_dynids dynids;
21 struct usbdrv_wrap drvwrap;
22 unsigned int no_dynamic_id:1;
23 unsigned int supports_autosuspend:1;
24 };

在编写新的USB设备驱动时,主要应该完成的工作是probe()和disconnect()函数,即探测和断开函数,它们分别在设备被插入和拔出 的时候被调用,用于初始化和释放软硬件资源。对usb_driver的注册和注销通过这两个函数完成:

int usb_register(struct usb_driver *new_driver)
void usb_deregister(struct usb_driver *driver);

usb_driver结构体中的id_table成员描述了这个USB驱动所支持的USB设备列表,它指向一个usb_device_id数 组,usb_device_id结构体用于包含USB设备的制造商ID、产品ID、产品版本、设备类、接口类等信息及其要匹配标志成员match- Flash(标明要与哪些成员匹配)。可以借助下面一组宏来生成usb_device_id结构体的实例:

USB_DEVICE(vendor, product)

该宏根据制造商ID和产品ID生成一个usb_device_id结构体的实例,在数组中增加该元素将意味着该驱动可支持匹配制造商ID、产品ID 的设备。

USB_DEVICE_VER(vendor, product, lo, hi)

该宏根据制造商ID、产品ID、产品版本的最小值和最大值生成一个usb_device_id结构体的实例,在数组中增加该元素将意味着该驱动可支 持匹配制造商ID、产品ID和lo~hi范围内版本的设备。

USB_DEVICE_INFO(class, subclass, protocol)

该宏用于创建一个匹配设备指定类型的usb_device_id结构体实例。

USB_INTERFACE_INFO(class, subclass, protocol)

该宏用于创建一个匹配接口指定类型的usb_device_id结构体实例。

代码清单20.12所示为两个用于描述某USB驱动所支持的USB设备的usb_device_id结构体数组实例。

代码清单20.12  usb_device_id结构体数组实例

1  /* 本驱动支持的USB设备列表 */
2
3 /* 实例1 */
4 static struct usb_device_id id_table [] = {
5 { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
6 { },
7 };
8 MODULE_DEVICE_TABLE (usb, id_table);
9
10 /* 实例2 */
11 static struct usb_device_id id_table [] = {
12 { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
13 { },
14 };
15 MODULE_DEVICE_TABLE (usb, id_table);

上述usb_driver结构体中的函数是USB设备驱动中USB相关的部分,而USB只是一个总线,真正的USB设备驱动的主体工作仍然是USB 设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。因此USB设备驱动包含其作为总线上挂在设备的驱动和本身所属设备类型的驱动两部 分。

与platform_driver类似,usb_driver起到了“牵线”的作用,即在probe()里注册相应的字符、tty等设备,在 disconnect()注销相应的字符、tty等设备,而原先对设备的注册和注销一般直接发生在模块加载和卸载函数中。

尽管USB本身所属设备驱动的结构与其不挂在USB总线上时完全相同,但是在访问方式上却发生了很大的变化,例如,对于字符设备而言,尽管仍然是 write()、read()、ioctl()这些函数,但是在这些函数中,与USB设备通信时不再是I/O内存和I/O端口的访问,而贯穿始终的是称为 URB的USB请求块。

如图20.4所示,在这棵树里,我们把树根比作主机控制器,树叶比作具体的USB设备,树干和树枝就是USB总线。树叶本身与树枝通过 usb_driver连接,而树叶本身的驱动(读写、控制)则需要通过其树叶设备本身所属类设备驱动来完成。树根和树叶之间的“通信”依靠在树干和树枝里 “流淌”的URB来完成。

由此可见,usb_driver本身只是起到了找到USB设备、管理USB设备连接和断开的作用,也就是说,它是公司入口处的“打卡机”,可以获得 员工(USB设备)的上/下班情况。树叶和员工一样,可以是研发工程师也可以是销售工程师,而作为USB设备的树叶可以是字符树叶、网络树叶或块树叶,因 此必须实现相应设备类的驱动。

图20.4 USB设备驱动结构

20.3.2 USB请求块(URB)

20.3.2  USB请求块(URB)

1.urb结构体

USB请求块(USB request block,urb)是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体,是 USB主机与设备通信的“电波”。

代码清单20.13  urb结构体

1  struct urb
2 {
3 /* 私有的:只能由USB核心和主机控制器访问的字段 */
4 struct kref kref; /*urb引用计数 */
5 spinlock_t lock; /* urb锁 */
6 void *hcpriv; /* 主机控制器私有数据 */
7 int bandwidth; /* INT/ISO请求的带宽 */
8 atomic_t use_count; /* 并发传输计数 */
9 u8 reject; /* 传输将失败*/
10
11 /* 公共的: 可以被驱动使用的字段 */
12 struct list_head urb_list; /* 链表头*/
13 struct usb_device *dev; /* 关联的USB设备 */
14 unsigned int pipe; /* 管道信息 */
15 int status; /* URB的当前状态 */
16 unsigned int transfer_flags; /* URB_SHORT_NOT_OK | ...*/
17 void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */
18 dma_addr_t transfer_dma; /*用来以DMA方式向设备传输数据的缓冲区 */
19 int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向缓冲区的大小 */
20
21 int actual_length; /* URB结束后,发送或接收数据的实际长度 */
22 unsigned char *setup_packet; /* 指向控制URB的设置数据包的指针*/
23 dma_addr_t setup_dma; /*控制URB的设置数据包的DMA缓冲区*/
24 int start_frame; /*等时传输中用于设置或返回初始帧*/
25 int number_of_packets; /*等时传输中等时缓冲区数据 */
26 int interval; /* URB被轮询到的时间间隔(对中断和等时urb有效) */
27 int error_count; /* 等时传输错误数量 */
28 void *context; /* completion函数上下文 */
29 usb_complete_t complete; /* 当URB被完全传输或发生错误时,被调用 */
30 struct usb_iso_packet_descriptor iso_frame_desc[0];
31 /*单个URB一次可定义多个等时传输时,描述各个等时传输 */
32 };

当transfer_flags标志中的URB_NO_TRANSFER_DMA_MAP被置位时,USB核心将使用transfer_dma指向 的缓冲区而非transfer_buffer指向的缓冲区,意味着即将传输DMA缓冲区。

当transfer_flags标志中的URB_NO_SETUP_DMA_MAP被置位时,对于有DMA缓冲区的控制urb而言,USB核心将使 用setup_dma指向的缓冲区而非setup_packet指向的缓冲区。

2.urb处理流程

USB设备中的每个端点都处理一个urb队列,在队列被清空之前,一个urb的典型生命周期如下:

(1)被一个 USB 设备驱动创建。

创建urb结构体的函数为:

struct urb *usb_alloc_urb(int iso_packets, int mem_flags);

iso_packets是这个urb应当包含的等时数据包的数目,若为0表示不创建等时数据包。 mem_flags参数是分配内存的标志,和kmalloc()函数的分配标志参数含义相同。如果分配成功,该函数返回一个urb结构体指针,否则返回 0。

urb结构体在驱动中不能静态创建,因为这可能破坏USB核心给urb使用的引用计数方法。

usb_alloc_urb()的“反函数”为:

void usb_free_urb(struct urb *urb);

该函数用于释放由usb_alloc_urb()分配的urb结构体。

(2)初始化,被安排给一个特定USB设备的特定端点。

对于中断urb,使用usb_fill_int_urb()函数来初始化urb,如下所示:

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,
void *context, int interval);

urb参数指向要被初始化的urb的指针;dev指向这个urb要被发送到的USB设备;pipe是这个urb要被发送到的USB设备的特定端 点;transfer_buffer是指向发送数据或接收数据的缓冲区的指针,和urb一样,它也不能是静态缓冲区,必须使用kmalloc()来分 配;buffer_length是transfer_buffer指针所指向缓冲区的大小;complete指针指向当这个 urb完成时被调用的完成处理函数;context是完成处理函数的“上下文”;interval是这个urb应当被调度的间隔。

上述函数参数中的pipe使用usb_sndintpipe()或usb_rcvintpipe()创建。

对于批量urb,使用usb_fill_bulk_urb()函数来初始化urb,如下所示:

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete,
void *context);

除了没有对应于调度间隔的interval参数以外,该函数的参数和usb_fill_int_urb()函数的参数含义相同。

上述函数参数中的pipe使用usb_sndbulkpipe()或者usb_rcvbulkpipe()函数来创建。

对于控制 urb,使用usb_fill_control_urb()函数来初始化urb,如下所示:

void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
unsigned int pipe, unsigned char *setup_packet,
void *transfer_buffer, int buffer_length,
usb_complete_t complete, void *context);

除了增加了新的setup_packet参数以外,该函数的参数和usb_fill_bulk_urb()函数的参数含义相同。 setup_packet参数指向即将被发送到端点的设置数据包。

上述函数参数中的pipe使用usb_sndctrlpipe()或usb_rcvictrlpipe()函数来创建。

等时urb没有像中断、控制和批量urb的初始化函数,我们只能手动地初始化urb,而后才能提交给USB核心。代码清单20.14给出了初始化等 时urb的例子,它来自drivers/usb/media/usbvideo.c文件。

代码清单20.14  初始化等时urb

1  for (i = 0; i < USBVIDEO_NUMSBUF; i++)
2 {
3 int j, k;
4 struct urb *urb = uvd->sbuf[i].urb;
5 urb->dev = dev;
6 urb->context = uvd;
7 urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);/*端口*/
8 urb->interval = 1;
9 urb->transfer_flags = URB_ISO_ASAP; /*urb被调度*/
10 urb->transfer_buffer = uvd->sbuf[i].data;/*传输buffer*/
11 urb->complete = usbvideo_IsocIrq; /* 完成函数 */
12 urb->number_of_packets = FRAMES_PER_DESC; /*urb中的等时传输数量*/
13 urb->transfer_buffer_length = uvd->iso_packet_len *FRAMES_PER_DESC;
14 for (j = k = 0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len)
15 {
16 urb->iso_frame_desc[j].offset = k;
17 urb->iso_frame_desc[j].length = uvd->iso_packet_len;
18 }
19 }

(3)被USB设备驱动提交给USB 核心。

在完成第(1)、(2)步的创建和初始化urb后,urb便可以提交给USB核心,通过usb_submit_urb()函数来完成,如下所示:

int usb_submit_urb(struct urb *urb, int mem_flags);

urb参数是指向urb的指针,mem_flags参数与传递给kmalloc()函数参数的意义相同,它用于告知USB核心如何在此时分配内存缓 冲区。

在提交urb到USB核心后,直到完成函数被调用之前,不要访问urb中的任何成员。

usb_submit_urb()在原子上下文和进程上下文中都可以被调用,mem_flags变量需根据调用环境进行相应的设置,如下所示。

l GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及urb完成函数中,在调用者持有自旋锁或者读写锁时以及当 驱动将current->state修改为非 TASK_ RUNNING时,应使用此标志。

l GFP_NOIO:在存储设备的块I/O和错误处理路径中,应使用此标志;

l GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。

如果usb_submit_urb()调用成功,即urb的控制权被移交给USB核心,该函数返回0;否则,返回错误号。

(4)提交由USB核心指定的USB主机控制器驱动。

(5)被USB主机控制器处理,进行一次到USB设备的传送。

第(4)~(5)步由USB核心和主机控制器完成,不受USB设备驱动的控制。

(6)当urb完成,USB主机控制器驱动通知USB设备驱动。

在如下3种情况下,urb将结束,urb完成函数将被调用。

l urb 被成功发送给设备,并且设备返回正确的确认。如果urb->status为0,意味着对于一个输出urb,数据被成功发送;对于一个输入urb,请 求的数据被成功收到。

l 如果发送数据到设备或从设备接收数据时发生了错误,urb->status将记录错误值。

l urb 被从USB 核心“去除连接”,这发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb虽已提交,而USB设备被 拔出的情况下。

usb_unlink_urb()和usb_kill_urb()这两个函数用于取消已提交的urb,其参数为要被取消的urb指针。对 usb_unlink_urb()而言,如果urb结构体中的URB_ASYNC_UNLINK(即异步unlink)的标志被置位,则对该urb的 usb_unlink_urb()调用将立即返回,具体的unlink动作将在后台进行。否则,此函数一直等到urb被解开链接或结束时才返回。 usb_kill_urb()会彻底终止urb的生命周期,它通常在设备的disconnect()函数中被调用。

当urb生命结束时(处理完成或被解除链接),通过urb结构体的status成员可以获知其原因,如0表示传输成功,-ENOENT表示被 usb_kill_urb()杀死,-ECONNRESET表示被usb_unlink_urb()杀死,-EPROTO表示传输中发生了 bitstuff错误或者硬件未能及时收到响应数据包,-ENODEV表示USB设备已被移除,-EXDEV表示等时传输仅完成了一部分等。

对以上urb的处理步骤进行一个总结,图20.5给出了一个urb的整个处理流程,虚线框的usb_unlink_urb()和 usb_kill_urb()并非一定会发生,它只是在urb正在被USB核心和主机控制器处理时,被驱动程序取消的情况下才发生。

3.简单的批量与控制URB

有时USB驱动程序只是从USB设备上接收或向USB设备发送一些简单的数据,这时候,没有必要将urb创建、初始化、提交、完成处理的整个流程走 一遍,而可以使用两个更简单的函数,如下所示。

(1)usb_bulk_msg()。

usb_bulk_msg()函数创建一个USB批量urb 并将它发送到特定设备,这个函数是同步的,它一直等待urb完成后才返回。usb_bulk_msg()函数的原型为:

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);

图20.5 urb处理流程

usb_dev参数为批量消息要发送的USB 设备的指针,pipe为批量消息要发送到的USB设备的端点,data参数为指向要发送或接收的数据缓冲区的指针,len参数为data参数所指向的缓冲 区的长度,actual_length用于返回实际发送或接收的字节数,timeout是发送超时,以jiffies为单位,0意味着永远等待。

如果函数调用成功,返回0;否则,返回1个负的错误值。

(2)usb_control_msg()函数。

usb_control_msg()函数与usb_bulk_msg()函数类似,不过它提供驱动发送和结束USB控制信息而非批量信息的能力,该 函数的原型为:

int usb_control_msg(struct usb_device *dev, unsigned int pipe, _ _u8 request, 
_ _u8 requesttype, _ _u16 value, _ _u16 index, void *data, _ _u16 size, int timeout);

dev指向控制消息发往的USB设备,pipe是控制消息要发往的USB设备的端点,request是这个控制消息的USB请求 值,requesttype是这个控制消息的USB请求类型,value是这个控制消息的USB消息值,index是这个控制消息的USB消息索引 值,data指向要发送或接收的数据缓冲区,size是data参数所指向的缓冲区的大小,timeout是发送超时,以jiffies为单位,0意味着 永远等待。

参数request、requesttype、value和index与USB规范中定义的USB控制消息直接对应。

如果函数调用成功,该函数返回发送到设备或从设备接收到的字节数;否则,返回一个负的错误值。

对usb_bulk_msg()和usb_control_msg()函数的使用要特别慎重,由于它们是同步的,因此不能在中断上下文和持有自旋锁 的情况下使用。而且,该函数也不能被任何其他函数取消,因此,务必要使得驱动程序的disconnect()函数掌握足够的信息,以判断和等待该调用的结 束。

20.3.3 探测和断开函数

20.3.3  探测和断开函数

在USB设备驱动usb_driver结构体的探测函数中,应该完成如下工作。

l 探测设备的端点地址、缓冲区大小,初始化任何可能用于控制USB设备的数据结构。

l 把已初始化数据结构的指针保存到接口设备中。

usb_set_intfdata()函数可以设置usb_interface的私有数据,这个函数的原型为:

void usb_set_intfdata (struct usb_interface *intf, void *data);

这个函数的“反函数”用于得到usb_interface的私有数据,其原型为:

void *usb_get_intfdata (struct usb_interface *intf);

l 注册USB设备。

如果是简单的字符设备,调用usb_register_dev(),这个函数的原型为:

int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);

上述函数中第二个参数为usb_class_driver结构体,这个结构体的定义如代码清单20.15所示。

代码清单20.15  usb_class_driver结构体

1 struct usb_class_driver
2 {
3 char *name; /*sysfs中用来描述设备名*/
4 struct file_operations *fops;/*文件操作结构体指针*/
5 int minor_base; /*开始次设备号*/
6 };

对于字符设备而言,usb_class_driver结构体的fops成员中的write()、read()、ioctl()等函数的地位完全等同 于本书第6章中的file_operations成员函数。

如果是其他类型的设备,如tty设备,则调用对应设备的注册函数。

在USB设备驱动usb_driver结构体的探测函数中,应该完成如下工作。

l 释放所有为设备分配的资源。
l 设置接口设备的数据指针为NULL。
l 注销USB设备。

对于字符设备,可以直接调用usb_register_dev()函数的“反函数”,如下所示:

void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);

对于其他类型的设备,如tty设备,则调用对应设备的注销函数。

对探测函数的调用发生在USB设备被安装且USB核心认为该驱动程序与安装的USB设备对应时(usb_driver的id_table成员在此时 发挥作用),而对断开函数的调用则发生在驱动因为种种原因不再控制该设备的时候。对这两个函数的调用都是在内核线程中进行的,因此,可以在其中进行任何可 能引起睡眠的操作。但是,由于USB核心对所有探测和断开函数的调用都发生在单一线程内,因此,太慢的probe()或disconnect()将拖延其 他USB设备的探测时间。

20.4.1 USB串口驱动

20.4 USB设备驱动实例

20.4.1  USB串口驱动

在Linux内核中,串口属于tty设备,对于一个USB串口设备而言,其驱动主要由两部分组成:usb_driver的成员函数和tty设备的 tty_operations结构体成员函数。

在USB串口设备驱动的模块加载函数中,将注册对应于USB串口的usb_driver,并初始化和注册tty驱动,如代码清单20.28所示。

代码清单20.28  USB串口设备驱动的模块加载函数

1  static int __init usb_serial_init(void)
2 {
3 int i;
4 int result;
5
6 /* 分配tty_driver */
7 usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
8 if (!usb_serial_tty_driver)
9 return - ENOMEM;
10
11 /* 初始化全局数据 */
12 for (i = 0; i < SERIAL_TTY_MINORS; ++i)
13 {
14 serial_table[i] = NULL;
15 }
16
17 /* 注册总线 */
18 result = bus_register(&usb_serial_bus_type);
19 if (result)
20 {
21 err("%s - registering bus driver failed", _ _FUNCTION_ _);
22 goto exit_bus;
23 }
24
25 /* 初始化tty_driver */
26 usb_serial_tty_driver->owner = THIS_MODULE;
27 usb_serial_tty_driver->driver_name = "usbserial";
28 usb_serial_tty_driver->devfs_name = "usb/tts/";
29 usb_serial_tty_driver->name = "ttyUSB";
30 usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
31 usb_serial_tty_driver->minor_start = 0;
32 usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
33 usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
34 usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
35 usb_serial_tty_driver->init_termios = tty_std_termios;
36 usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
37 CLOCAL;
38 tty_set_operations(usb_serial_tty_driver, &serial_ops);
39 /* 注册tty_driver */
40 result = tty_register_driver(usb_serial_tty_driver);
41 if (result)
42 {
43 err("%s - tty_register_driver failed", _ _FUNCTION_ _);
44 goto exit_reg_driver;
45 }
46
47 /* 注册USB驱动 */
48 result = usb_register(&usb_serial_driver);
49 if (result < 0)
50 {
51 err("%s - usb_register failed", _ _FUNCTION_ _);
52 goto exit_tty;
53 }
54
55 result = usb_serial_generic_register(debug);
56 if (result < 0)
57 {
58 err("%s - registering generic driver failed", _ _FUNCTION_ _);
59 goto exit_generic;
60 }
61
62 info(DRIVER_DESC);
63
64 return result;
65
66 exit_generic: usb_deregister(&usb_serial_driver);
67 exit_tty: tty_unregister_driver(usb_serial_tty_driver);
68 exit_reg_driver: bus_unregister(&usb_serial_bus_type);
69 exit_bus: err("%s - returning with error %d", _ _FUNCTION_ _, result);
70 put_tty_driver(usb_serial_tty_driver);
71 return result;
72 }

在USB串口设备驱动的模块卸载函数中,将注销对应于USB串口的usb_driver,并注销tty驱动,如代码清单20.29所示。

代码清单20.29  USB串口设备驱动的模块卸载函数

1 static void _ _exit usb_serial_exit(void)
2 {
3 usb_serial_console_exit();
4 usb_serial_generic_deregister();
5 usb_deregister(&usb_serial_driver);//注销usb_driver
6 tty_unregister_driver(usb_serial_tty_driver);//注销tty_driver
7 put_tty_driver(usb_serial_tty_driver);//减少引用计数
8 bus_unregister(&usb_serial_bus_type);//注销bus
9 }

在usb_driver的探测成员函数usb_serial_probe()中,将初始化USB端点等信息,并通过 usb_set_intfdata()设置接口私有数据,它也将初始化urb。相反地,在断开成员函数usb_serial_disconnect()中 将设置接口私有数据为NULL,并释放引用计数。

USB串口驱动的tty_operations结构体实例serial_ops定义如代码清单20.30所示,它封装了USB串口设备驱动中的串口 驱动成分。

代码清单20.30  USB串口驱动的tty_operations结构体

1  static struct tty_operations serial_ops = 
2 {
3 .open =serial_open,
4 .close =serial_close,
5 .write =serial_write,
6 .write_room =serial_write_room,
7 .ioctl =serial_ioctl,
8 .set_termios =serial_set_termios,
9 .throttle =serial_throttle,
10 .unthrottle =serial_unthrottle,
11 .break_ctl =serial_break,
12 .chars_in_buffer =serial_chars_in_buffer,
13 .read_proc =serial_read_proc,
14 .tiocmget =serial_tiocmget,
15 .tiocmset =serial_tiocmset,
16 };

在tty_operations的各write()、read()等成员函数中,将调用usb_serial_driver结构体中的相应函 数,usb_serial_driver结构体中封装了串口的各函数(读写、读写中断端点完成函数、读写批量端点完成函数等),其定义如代码清单 20.31所示。

代码清单20.31  usb_serial_driver结构体

1  struct usb_serial_driver
2 {
3 const char *description; //用于描述该驱动的字符串
4 const struct usb_device_id *id_table; //usb_device_id数组
5 char num_interrupt_in; //中断输入端点数量
6 char num_interrupt_out; //中断输出端点数量
7 char num_bulk_in; //批量输入端点数量
8 char num_bulk_out; //批量输出端点数量
9 char num_ports; //设备包含的端口数量
10
11 struct list_head driver_list;
12 struct device_driver driver;
13
14 int(*probe)(struct usb_serial *serial, const struct usb_device_id *id);
15 int(*attach)(struct usb_serial *serial);
16 int(*calc_num_ports)(struct usb_serial *serial);
17
18 void(*shutdown)(struct usb_serial *serial);
19
20 int(*port_probe)(struct usb_serial_port *port);
21 int(*port_remove)(struct usb_serial_port *port);
22
23 /* 串口函数 */
24 int(*open)(struct usb_serial_port *port, struct file *filp);
25 void(*close)(struct usb_serial_port *port, struct file *filp);
26 int(*write)(struct usb_serial_port *port, const unsigned char *buf, int count)
27 ;
28 int(*write_room)(struct usb_serial_port *port);
29 int(*ioctl)(struct usb_serial_port *port, struct file *file, unsigned int cmd,
30 unsigned long arg);
31 void(*set_termios)(struct usb_serial_port *port, struct termios *old);
32 void(*break_ctl)(struct usb_serial_port *port, int break_state);
33 int(*chars_in_buffer)(struct usb_serial_port *port);
34 void(*throttle)(struct usb_serial_port *port);
35 void(*unthrottle)(struct usb_serial_port *port);
36 int(*tiocmget)(struct usb_serial_port *port, struct file *file);
37 int(*tiocmset)(struct usb_serial_port *port, struct file *file, unsigned int
38 set, unsigned int clear);
39 /* urb完成回调函数 */
40 void(*read_int_callback)(struct urb *urb, struct pt_regs *regs);
41 void(*write_int_callback)(struct urb *urb, struct pt_regs *regs);
42 void(*read_bulk_callback)(struct urb *urb, struct pt_regs *regs);
43 void(*write_bulk_callback)(struct urb *urb, struct pt_regs *regs);
44 };

文件drivers/usb/serial/Generic.c文件中提供了USB串口驱动的通用打开/关闭、写函数、批量urb完成函数,如 usb_serial_generic_write()、usb_serial_generic_write_bulk_callback()、 usb_serial_
generic_read_bulk_callback()等。代码清单20.32列举了通用写函数及输出批量urb完成 回调函数。

代码清单20.32  USB串口通用写函数及批量写urb完成函数

1  int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char
2 *buf, int count)
3 {
4 struct usb_serial *serial = port->serial;
5 int result;
6 unsigned char *data;
7
8 if (count == 0)
9 {
10 dbg("%s - write request of 0 bytes", _ _FUNCTION_ _);
11 return (0);
12 }
13
14 /* 如果有批量输出端点 */
15 if (serial->num_bulk_out)
16 {
17 spin_lock(&port->lock);
18 if (port->write_urb_busy)
19 {
20 spin_unlock(&port->lock);
21 dbg("%s - already writing", _ _FUNCTION_ _);
22 return 0;
23 }
24 port->write_urb_busy = 1;
25 spin_unlock(&port->lock);
26
27 count = (count > port->bulk_out_size) ? port->bulk_out_size: count;
28
29 memcpy(port->write_urb->transfer_buffer, buf, count);
30 data = port->write_urb->transfer_buffer;
31 usb_serial_debug_data(debug, &port->dev, _ _FUNCTION_ _, count, data);
32
33 /* 设置urb */
34 usb_fill_bulk_urb(port->write_urb, serial->dev, usb_sndbulkpipe(serial->dev,
35 port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, count,
36 ((serial->type->write_bulk_callback) ? serial->type->write_bulk_callback:
37 usb_serial_generic_write_bulk_callback), port);
38
39 /* 将数据送出给批量端口 */
40 port->write_urb_busy = 1;
41 result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
42 if (result)
43 {
44 port->write_urb_busy = 0;
45 }
46 else
47 result = count;
48
49 return result;
50 }
51
52 return 0;
53 }
54
55 void usb_serial_generic_write_bulk_callback(struct urb *urb, struct pt_regs
56 *regs)
57 {
58 struct usb_serial_port *port = (struct usb_serial_port*)urb->context;
59
60 port->write_urb_busy = 0;
61 if (urb->status) //不成功
62 {
63 dbg("%s - nonzero write bulk status received: %d", _ _FUNCTION_ _, urb
64 ->status);
65 return ;
66 }
67
68 usb_serial_port_softint((void*)port);//tty_wakeup
69 schedule_work(&port->work);//调度底半部
70 }

 

20.4.2 USB键盘驱动

20.4.2  USB键盘驱动

在Linux系统中,键盘被认定为标准输入设备,对于一个USB键盘而言,其驱动主要由两部分组成:usb_driver的成员函数和输入设备的打 开、关闭、中断处理等函数。

在USB键盘设备驱动的模块加载和卸载函数中,将分别注册和注销对应于USB键盘的usb_driver结构体usb_kbd_driver,代码 清单20.33所示为模块加载与卸载函数以及usb_kbd_driver结构体的定义。

代码清单20.33  USB键盘设备驱动的模块加载与卸载函数及usb_driver结构体

1  static int _ _init usb_kbd_init(void)
2 {
3 int result = usb_register(&usb_kbd_driver); //注册USB设备驱动
4 if (result == 0)
5 info(DRIVER_VERSION ":" DRIVER_DESC);
6 return result;
7 }
8
9 static void _ _exit usb_kbd_exit(void)
10 {
11 usb_deregister(&usb_kbd_driver); //注销USB设备驱动
12 }
13
14 //usb_driver结构体
15 static struct usb_driver usb_kbd_driver =
16 {
17 .name = "usbkbd",
18 .probe = usb_kbd_probe,
19 .disconnect = usb_kbd_disconnect,
20 .id_table = usb_kbd_id_table,
21 };
22
23 //支持的设备列表
24 static struct usb_device_id usb_kbd_id_table [] = {
25 { USB_INTERFACE_INFO(3, 1, 1) },
26 { }
27 };
28 MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);

在usb_driver的探测函数中,将进行input设备的初始化和注册,USB键盘要使用的中断urb和控制urb的初始化,并设置接口的私有 数据,如代码清单20.34所示。

代码清单20.34  USB键盘设备驱动的探测函数

1  static int usb_kbd_probe(struct usb_interface *iface, const struct
2 usb_device_id *id)
3 {
4 struct usb_device *dev = interface_to_usbdev(iface);
5 struct usb_host_interface *interface;
6 struct usb_endpoint_descriptor *endpoint;
7 struct usb_kbd *kbd;
8 struct input_dev *input_dev;
9 int i, pipe, maxp;
10
11 interface = iface->cur_altsetting;
12 /* 设备是否适合本驱动?*/
13 if (interface->desc.bNumEndpoints != 1)
14 return - ENODEV;
15
16 endpoint = &interface->endpoint[0].desc;
17 if (!(endpoint->bEndpointAddress &USB_DIR_IN))
18 return - ENODEV;
19 if ((endpoint->bmAttributes &USB_ENDPOINT_XFERTYPE_MASK) !=
20 USB_ENDPOINT_XFER_INT)
21 return - ENODEV;
22
23 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//创建端点的管道
24 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
25
26 kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
27 input_dev = input_allocate_device();//分配input_dev结构体
28 if (!kbd || !input_dev) //分配
29 goto fail1;
30
31 if (usb_kbd_alloc_mem(dev, kbd))
32 goto fail2;
33
34 kbd->usbdev = dev;
35 kbd->dev = input_dev;
36
37 if (dev->manufacturer)//制造商名非空
38 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
39
40 if (dev->product)//产品名非空
41 {
42 if (dev->manufacturer)
43 strlcat(kbd->name, " ", sizeof(kbd->name));
44 strlcat(kbd->name, dev->product, sizeof(kbd->name));
45 }
46
47 if (!strlen(kbd->name))
48 snprintf(kbd->name, sizeof(kbd->name), "USB HIDBP Keyboard
49 %04x:%04x",le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev
50 ->descriptor.idProduct));
51
52 usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
53 strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
54 /* 输入设备初始化 */
55 input_dev->name = kbd->name;
56 input_dev->phys = kbd->phys;
57 usb_to_input_id(dev, &input_dev->id);
58 input_dev->cdev.dev = &iface->dev;
59 input_dev->private = kbd;
60
61 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
62 input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) |
63 BIT(LED_SCROLLL) |BIT(LED_COMPOSE) | BIT(LED_KANA);
64
65 for (i = 0; i < 255; i++)
66 set_bit(usb_kbd_keycode[i], input_dev->keybit);
67 clear_bit(0, input_dev->keybit);
68
69 input_dev->event = usb_kbd_event;
70 input_dev->open = usb_kbd_open;
71 input_dev->close = usb_kbd_close;
72 /* 初始化中断urb */
73 usb_fill_int_urb(kbd->irq, dev, pipe, kbd->new, (maxp > 8 ? 8 : maxp),
74 usb_kbd_irq, kbd, endpoint->bInterval);
75 kbd->irq->transfer_dma = kbd->new_dma;
76 kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
77
78 kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
79 kbd->cr->bRequest = 0x09;
80 kbd->cr->wValue = cpu_to_le16(0x200);
81 kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
82 kbd->cr->wLength = cpu_to_le16(1);
83 /* 初始化控制urb */
84 usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0), (void*)kbd->cr,
85 kbd->leds, 1, usb_kbd_led, kbd);
86 kbd->led->setup_dma = kbd->cr_dma;
87 kbd->led->transfer_dma = kbd->leds_dma;
88 kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP |
89 URB_NO_SETUP_DMA_MAP);
90 input_register_device(kbd->dev); //注册输入设备
91
92 usb_set_intfdata(iface, kbd); //设置接口私有数据
93 return 0;
94
95 fail2: usb_kbd_free_mem(dev, kbd);
96 fail1: input_free_device(input_dev);
97 kfree(kbd);
98 return - ENOMEM;
99 }

在usb_driver的断开函数中,将设置接口私有数据为NULL、终止已提交的urb并注销输入设备,如代码清单20.35所示。

代码清单20.35  USB键盘设备驱动的断开函数

1  static void usb_kbd_disconnect(struct usb_interface *intf)
2 {
3 struct usb_kbd *kbd = usb_get_intfdata(intf);
4
5 usb_set_intfdata(intf, NULL);//设置接口私有数据为NULL
6 if (kbd)
7 {
8 usb_kill_urb(kbd->irq);//终止urb
9 input_unregister_device(kbd->dev); //注销输入设备
10 usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
11 kfree(kbd);
12 }
13 }

在键盘中断处理函数,也就是中断urb的完成函数中,将会通过input_report_key()报告按键事件,通过input_sync()报 告同步事件,并发起一次新的控制urb传输,如代码清单20.36所示。

代码清单20.36  USB键盘设备驱动的中断urb完成函数

1  static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)
2 {
3 struct usb_kbd *kbd = urb->context;
4 int i;
5
6 switch (urb->status)
7 {
8 case 0: /* 成功 */
9 break;
10 case - ECONNRESET: /* unlink */
11 case - ENOENT:
12 case - ESHUTDOWN:
13 return ;
14 default: /* 错误 */
15 goto resubmit;
16 }
17 /*获得键盘扫描码并报告按键事件*/
18 input_regs(kbd->dev, regs);
19
20 for (i = 0; i < 8; i++)
21 input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) &1);
22
23 for (i = 2; i < 8; i++)
24 {
25 if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) ==
26 kbd->new +8)
27 {
28 if (usb_kbd_keycode[kbd->old[i]])
29 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
30 else
31 info("Unknown key (scancode %#x) released.", kbd->old[i]);
32 }
33
34 if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) ==
35 kbd->old +8)
36 {
37 if (usb_kbd_keycode[kbd->new[i]])
38 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
39 else
40 info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
41 }
42 }
43
44 input_sync(kbd->dev);报告同步事件
45
46 memcpy(kbd->old, kbd->new, 8);
47
48 resubmit: i = usb_submit_urb(urb, SLAB_ATOMIC);
49 if (i)
50 err("can't resubmit intr, %s-%s/input0, status %d", kbd->usbdev->bus
51 ->bus_name, kbd->usbdev->devpath, i);
52 }

20.5 总结

 
 
 
 20.5 总结

USB驱动分为USB主机驱动和USB设备驱动,如果系统的USB主机控制器符合OHCI等标准,这主机驱动的绝大部分工作都可以沿用通用的代码。

对于一个USB设备而言,它至少具备两重身份:首先它是“USB”的,其次它是“自己”的。USB设备是“USB”的,指它挂接在USB总线上,其 必须完成usb_driver的初始化和注册;USB设备是“自己”的,意味着本身可能是一个字符设备、tty设备、网络设备等,因此,USB设备驱动中 也必须实现符合相应框架的代码。

USB设备驱动的自身设备驱动部分的读写等操作流程有其特殊性,即以URB来贯穿始终,一个URB的生命周期通常包含创建、初始化、提交,和被 USB核心及USB主机传递及完成后回调函数被调用的过程,当然,在URB被驱动提交后,也可以被取消。此外,简单的控制及批量消息传递可以用同步的 usb_bulk_msg()、usb_control_msg()函数完成。

阅读(1209) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~