usb协议由于考虑通用性,既可以支持大容量存储设备,音频设备,HID设备,CDC协议,DFU等等等。。。在设计整套协议的时候非常复杂,比如相关包类型,事务,端点等这些概念内容非常多,如果读者向深入分析整套协议栈,可以阅读<<圈圈教你玩usb>>,内容理论与实例相结合,基于51单片机和一块usb桥接芯片实现了usb众多设备。本篇不详细分析总线驱动程序,而着重分析设备驱动程序。
Linux系统中实现了如下以下设备类
- 音频通信类 ,比如USB声卡,USB麦克风
- 通信设备类,比如USB转虚拟串口
- HID类,比如鼠标,键盘,游戏手柄
- 打印设备类,比如打印机
- 海量存储类,比如U盘
在Linux内核中,使用usb_driver结构体描述USB设备驱动,usb_driver结构体定义如下所示:
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 (*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;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int soft_unbind:1;
}
usb总线驱动程序,在接入USB设备,会构造了一个新的usb_dev结构注册到usb_bus_type,这部分内容内核已经帮我们实现好了,设备驱动程序需要做的就是构造好usb_driver结构体,注册到usb_bus_type,usb_driver结构体中有一项id_table,表示能够支持哪些设备,当usb能匹配到id_table的某个设备时,就会调用usb_driver结构体的probe探测函数,当拔掉usb设备时,就会调用用usb_driver结构体的emove注销函数。
usb_device_id结构体定义如下所示,usb_device_id结构体包含有USB设备的PID,VID,版本,设备类,设备子类,接口类等以及匹配标志成员match_flags,以表明匹配的种类
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* not matched against */
kernel_ulong_t driver_info;
};
可以借助一些宏来生成usb_device_id结构体,比如USB_DEVICE(vendor,product),USB_DEIVICE_INFO(class,subclass,protocol),USB_INTERFACE_INFO(class,subclass,protocal)等
USB请求块是USB设备驱动中用来描述与USB设备通信所用的基本载体。非常类似于I2C设备驱动中的i2c_msg结构体,SPI设备驱动程序中的spi_message结构体。
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* 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 list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
unsigned int stream_id; /* (in) stream ID */
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 */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 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结构体
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
//iso_packets=NULL表示不创建等时数据包
//开辟urb空间的mem标志,一般为GFP_KERNEL
- 设置创建好的URB结构体,把这个URB分配给USB设备的特定端点
中断URB
static inline void usb_fill_int_urb(struct urb *urb, //指向该URB
struct usb_device *dev, //指向该请求块要发送的usb设备,设备可以利用interface_to_usbdev函数根据probe中的 intf形参转换而来
unsigned int pipe, //特定端点,通过usb_rcvintpipe创建
void *transfer_buffer, //指向数据缓冲区
int buffer_length, //指向数据缓冲区长度
usb_complete_t complete_fn, //完成数据传输的回调函数
void *context,
int interval) //调度间隔
批量URB,批量URB没有调度间隔
static inline void usb_fill_bulk_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe, //特定端点,通过usb_rcvbulkpipe创建
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
控制URB
static inline void usb_fill_control_urb(struct urb *urb,
struct usb_device *dev,
unsigned int pipe, //特定端点,通过usb_rcvctrlpipe创建
unsigned char *setup_packet, //指向将发送到端点的设置数据包
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context)
等时URB没有以上三种传输的初始化函数,只能手动设置,然后提交给USB核心层
- 提交URB给USB核心
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
//struct urb *urb 指向该URB
//gfp_t mem_flags 开辟urb空间的mem标志,一般为GFP_KERNEL
简单的URB使用流程
在一些批量与控制传输的场合,有时设备驱动只是从usb上传输一些简单的控制消息,可以不需要使用urb,而使用一些更简单的函数来完成usb数据的传输
批量传输
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
控制传输
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)
实例:USB鼠标(linux2.6 测试环境s3c2440)
Linux系统中,鼠标属于标准输入设备,其驱动包含两部分:usb_driver结构体的设置以及输入设备驱动input_dev的设置。
该驱动程序模块加载与卸载中,分别注册与注销usb_driver结构体
static struct usb_device_id usbmouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
/* 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse",
.probe = usbmouse_probe,
.disconnect = usbmouse_disconnect,
.id_table = usbmouse_id_table,
};
static int usbmouse_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_driver);
return 0;
}
static void usbmouse_exit(void)
{
usb_deregister(&usbmouse_driver);
}
在usb_driver中的probe()函数中,对输入设备进行初始化并注册,设置创建好的URB结构体并提交给USB核心层
static int usbmouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); //获取usb设备
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
/* a. 分配一个input_dev */
uk_dev = input_allocate_device();
/* b. 设置 */
/* b.1 能产生哪类事件 */
set_bit(EV_KEY, uk_dev->evbit);
set_bit(EV_REP, uk_dev->evbit);
/* b.2 能产生哪些事件 */
set_bit(KEY_L, uk_dev->keybit);
set_bit(KEY_S, uk_dev->keybit);
set_bit(KEY_ENTER, uk_dev->keybit);
/* c. 注册 */
input_register_device(uk_dev);
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //创建中断输入端点
len = endpoint->wMaxPacketSize; //端点的包长
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);//分配usb缓冲区
/* 分配URB */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
//设置URB
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* 向核心层提交URB */
usb_submit_urb(uk_urb, GFP_KERNEL);
return 0;
}
在usb_driver断开函数中,终止已提交的URB,并注销输入设备
static void usbmouse_disconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
//printk("disconnect usbmouse!\n");
usb_kill_urb(uk_urb);
usb_free_urb(uk_urb);
usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}
usb鼠标数据传输属于中断传输,在鼠标中断的URB完成函数,会通过input_report_key()报告按键时间
static void usbmouse_as_key_irq(struct urb *urb)
{
static unsigned char pre_val;
/* USB鼠标数据含义
* data[0]: bit0-左键, 1-按下, 0-松开
* bit1-右键, 1-按下, 0-松开
* bit2-中键, 1-按下, 0-松开
*
*/
if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
{
/* 左键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);
input_sync(uk_dev);
}
if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
{
/* 右键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);
input_sync(uk_dev);
}
if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
{
/* 中键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);
input_sync(uk_dev);
}
pre_val = usb_buf[0];
/* 重新提交urb */
usb_submit_urb(uk_urb, GFP_KERNEL);
}
从USB鼠标驱动例子可以看出,usb_driver结构体本省只是起一个挂载总线的作用,具体设备类型的驱动模型根据具体设备类型额定,鼠标就是input子系统,USB声卡就是ALSA框架,USB摄像头就是V4L2框架,而这些具体设备的底层硬件操作时,都是调用的URB相关接口。USB核心层API的存在无需关系底层USB主机控制器的具体细节