25.2.3 设备驱动结构
USB协议规定了许多种USB设备类型。Linux内核实现了音频设备、通信设备、人机接口、存储设备、电源设备、打印设备等几种USB设备类。
1.基本概念
Linux内核实现的USB设备类驱动都是针对通用的设备类型设计的。如存储设备类,只要USB存储设备是按照标准的USB存储设备规范实现的,就可以直接被内核USB存储设备驱动程序驱动。如果一个USB设备是非标准的,则需要编写对应设备的驱动程序。
Linux内核为不同的USB设备分配了设备号。在内核中还提供了一个usbfs文件系统,通过usbfs文件系统,用户可以方便地使用USB设备。为了使用usbfs,使用root权限在控制台输入“mount –t usbfs none /proc/bus/usb”,可以加载USB文件系统到内核。在插入一个USB设备的时候,内核会试图加载对应的驱动程序。
2.设备驱动结构
内核使用usb_driver结构体描述USB设备驱动,定义如下:
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id); // 探测函数
void (*disconnect) (struct usb_interface *intf); // 断开连接函数
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf); // I/O控制函数
int (*suspend) (struct usb_interface *intf, pm_message_t message); // 挂起函数
int (*resume) (struct usb_interface *intf); // 恢复函数
void (*pre_reset) (struct usb_interface *intf);
void (*post_reset) (struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct device_driver driver;
unsigned int no_dynamic_id:1;
};
实现一个USB设备的驱动主要是实现probe()和disconnect()函数接口。probe()函数在插入USB设备的时候被调用,disconnect()函数在拔出USB设备的时候被调用。在25.2.4节中详细讲解USB设备驱动程序框架。
3.USB请求块
USB请求块(USB request block,urb)的功能类似于网络设备中的sk_buff,用于描述USB设备与主机通信的基本数据结构。urb结构在内核中定义如下:
struct urb
{
/* private: usb core and host controller only fields in the urb */ // 私有数据,仅供USB核心和主机控制器使用
struct kref kref; /* reference count of the URB */ // urb引用计数
spinlock_t lock; /* lock for the URB */ // urb锁
void *hcpriv; /* private data for host controller */ // 主机控制器私有数据
int bandwidth; /* bandwidth for INT/ISO request */ // 请求带宽
atomic_t use_count; /* concurrent submissions counter */ // 并发传输计数
u8 reject; /* submissions will fail */ // 传输即将失败标志
/* public: documented fields in the urb that can be used by drivers */ // 公有数据,可以被驱动使用
struct list_head urb_list; /* list head for use by the urb's // 链表头
* current owner */
struct usb_device *dev; /* (in) pointer to associated device */ // 关联的USB设备
unsigned int pipe; /* (in) pipe information */ // 管道信息
int status; /* (return) non-ISO status */ // 当前状态
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */ // 数据缓冲区
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ // DMA使用的缓冲区
int transfer_buffer_length; /* (in) data buffer length */ // 缓冲区大小
int actual_length; /* (return) actual transfer length */ // 实际接收或发送数据的长度
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */ // 设置数据包缓冲区
int start_frame; /* (modify) start frame (ISO) */ // 等时传输中返回初始帧
int number_of_packets; /* (in) number of ISO packets */ // 等时传输中缓冲区数据
int interval; /* (modify) transfer interval // 轮询的时间间隔
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */ // 出错次数
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
内核提供了一组函数操作urb类型的结构变量。urb的使用流程如下:
(1)创建urb。在使用之前,USB设备驱动需要调用usb_alloc_urb()函数创建一个urb,函数定义如下:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
iso_packets参数是urb包含的等时数据包数目,如果为0表示不创建等时数据包。mem_flags参数是分配内存标志。如果分配urb成功,函数返回一个urb结构类型的指针,否则返回0。
内核还提供了释放urb的函数,定义如下:
void usb_free_urb(struct urb *urb)
在不使用urb的时候(退出驱动程序或者挂起驱动),需要使用usb_free_urb()函数释放urb。
(2)初始化urb,设置USB设备的端点。使用内核提供的usb_int_urb()函数设置urb初始结构,定义如下:
void usb_init_urb(struct urb *urb);
(3)提交urb到USB核心。在分配并设置urb完毕后,使用usb_submit_urb()函数把新的urb提交到USB核心,函数定义如下:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
参数urb指向被提交的urb结构,mem_flags是传递给USB核心的内存选项,用于告知USB核心如何分配内存缓冲区。如果函数执行成功,urb的控制权被USB核心接管,否则函数返回错误。