文章目录
USB驱动程序简介
USB设备驱动程序(层次划分)
USB设备驱动程序位于不同的内核子系统和USB主控制器之间,USB核心为USB驱动程序提供一个用于访问和控制USB硬件的软件接口,使得USB设备驱动程序不必考虑USB硬件控制器。
USB设备层次:
从上图可以看出,USB设备逻辑层次可以分为:设备、配置、接口、端点
划重点:
- USB设备驱动程序对应的是接口层次,也就是说每一个接口都要对应一个USB设备驱动程序
USB驱动程序描述
在Linux内核中,使用 struct usb_driver 结构描述一个 USB驱动。
struct usb_driver
{
const char *name; //驱动程序名
/*当USB核心发现了该驱动能够处理USB接口时,调用次函数*/
int (*probe)(struct usb_interface *intf,const struct usb_device_id *id);
/*当相应的USB接口被移除时,调用此函数*/
void (*disconnect)(struct usb_interface *intf);
/*USB驱动能够处理的设备列表*/
const struct usb_device_id *id_table;
};
USB设备列表
一种设备:
Linux内核提供了宏USB_DEVICE来定义一种USB设备的
USB_DEVICE(vend,prod)
参数:
- vend:USB Vendor ID
- prod:USB Product ID
例(USB OV511摄像头):
USB_DEVICE(VEND_OMNIVISION,PROD_OV511)
#define VEND_OMNIVISION 0x05A9
#define PROD_OV511 0x0511
struct usb_device_id
一类设备:
Linux内核提供了宏USB_INTERFACE_INFO来协助定义一类USB设备
USB_INTERFACE_INFO(cl,sc,pr)
参数:
- cl:bInterfaceClass value
- sc:bInterfaceSubClass value
- pr:bInterfaceProtocol value
例(USB鼠标)
USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE)
注册驱动
Linux内核提供了函数usb_register来注册USB驱动程序。
static inline int usb_register(struct usb_driver *driver)
Linux USB 描述符
设备描述
linux内核使用 struct usb_device 来描述一个USB 设备
struct usb_device
{
int devnum; //USB设备号
char devpath[16]; //设备ID字符串
enum usb_device_state state; //设备状态:未连接,已配置
enum usb_device_speed speed; //高速;全速;低速
struct usb_device_descriptor descriptor; //USB设备描述符
struct usb_host_config *config;
char *product; //产品号
char *manufacturer; //厂商名
char *serial; //设备串号
};
设备描述
上面结构体中包含的结构体:usb_device_descriptor 来对应USB描述符中的 设备描述符
struct usb_device_descriptor
{
__u8 bLength;
__u8 bDescriptorTypr;
__le16 bcdUSB;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0;
__le16 idVendor;
__le16 idProduct;
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__((packed)); //按字节对齐
严格按照USB协议的USB设备描述符保持一致;
配置描述:
上面结构体 struct usb_device 中包含的结构体:usb_device_host_config 来描述USB配置:
struct usb_host_config
{
struct usb_config_descriptor desc; //配置描述符
char *string; //配置的字符串(如果存在)
//接口链表
struct usb_interface *interface[USB_MAXINTERFACES];
};
上述结构体中的 struct usb_config_descriptor
接口描述
linux内核使用 struct usb_interface 来描述USB接口。
struct usb_interface
{
struct usb_host_interface *altsetting; //接口设置数组
struct usb_host_interface *cur_altsetting; //当前设置
unsigned num_altsetting; //设置数
};
配置 VS 设置
- 一个配置包含一个或多个接口
- 一个接口包含一个或多个设置
举例:
接口设置
linux内核使用 struct usb_host_interface 来描述USB接口设置
struct usb_host_interface
{
struct usb_interface_descriptor desc;//接口描述符
struct usb_host_endpoint *endpoint; //接口包含的端点
};
接口描述符
linux内核使用 struct usb_interface_descriptor 来对应USB描述符中的接口描述符:
端点描述
linux内核使用 struct usb_host_endpoint 来描述USB端点
struct usb_host_endpoint
{
struct usb_endpoint_descriptor desc; //端点描述符
struct list_head usb_list; //urb
.........
.........
};
USB URB
URB定义
USB请求块(USB request block,urb)是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的 sk_buff 结构体,是USB主机与设备通信的“”电波“”。
URB处理流程
- USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb,并提交USB core;
- USB core提交该urb到USB主控制器驱动程序;
- USB主控制器驱动程序根据该urb描述的信息,来访问USB设备;
- 当设备访问结束后,USB主控制器驱动程序通知USB设备驱动程序。
创建URB
创建URB函数为:
struct *usb_alloc_urb(int iso_packets,gtp_t mem_flags)
参数:
- iso_packets:urb所包含的等时数据包个数
- mem_flags:内存分配标识(如GFP_KERNEL),参考kmalloc
初始化URB
- 对于中断urb,使用 usb_fill_int_urb 函数来初始化:
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)
参数:
-
urb:要初始化的urb指针;
-
dev:urb所要访问的设备;
-
pipe:要访问的端点所对应的管道,使用usb_sndintpipe()或usb_rcvintpipe()创建
-
transfer_buffer:要传输的数据缓冲区
-
buffer_length:transfer_buffer所指缓冲区长度
-
complete_fn:当完成urb所请求的操作时,要调用的回调函数;
-
context:complet_fn 函数所需的上下文,通常取值为dev;
-
interval:urb被调度的时间间隔
-
对于批量urb,使用usb_fill_bulk_urb 函数来初始化
-
对于控制urb,使用usb_fill_control_urb 函数来初始化
-
等时urb没有像中断、控制和批量urb那样的初始化函数,我们只能手动地初始化urb
提交URB
在完成urb的创建和初始化后,urb便可以通过usb_submit_urb函数来提交后USB核心
int usb_submit_urb(struct urb *urb,gfp_t mem_flags)
参数:
- urb:执行urb的指针
- mem_flags:内存分配标识,它用于告知USB核心如何分配内存缓冲区
处理URB
URB被提交后,USB核心指定主控制器驱动程序来处理该urb,在3种情况下,urb会被认为为处理完成
- urb 被成功发送给设备,并且设备返回正确的确认。如果 urb->status 为0,意味着对于一个输出urb,数据被成功发送;对于一个输入的urb,请求的数据被成功收到。
- 如果发送数据到设备或从设备接受数据时出现错误,urb->status将记录该错误值。
- urb 被“”取消“”,这发生在驱动通过 usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb虽已提交,而USB设备被拔出的情况下。
当urb处理完成后,urb完成函数将被调用。
USB鼠标驱动程序分析设计
代码部分 mouse.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/slab.h>
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR("silence");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
struct usb_mouse
{
char name[128];
char phys[64];
struct usb_device *usbdev;
struct input_dev *dev;
struct urb *irq;
signed char *data;
dma_addr_t data_dma;
};
static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
//检测urb传输是否成功
switch(urb->status)
{
case 0: //success
break;
case -ECONNRESET: //unlink
case -ENOENT:
case -ESHUTDOWN:
return;
//-EPIPE:should clear the halt
default: //error
goto resubmit;
}
//报告按键状态
input_report_key(dev,BTN_LEFT,data[0] & 0x01);
input_report_key(dev,BTN_RIGHT,data[0] & 0x02);
input_report_key(dev,BTN_MIDDLE,data[0] & 0x04);
input_report_key(dev,BTN_SIDE,data[0] & 0x08);
input_report_key(dev,BTN_EXTRA,data[0] & 0x10);
input_report_rel(dev,REL_X,data[1]);
input_report_rel(dev,REL_Y,data[2]);
input_report_rel(dev,REL_WHEEL,data[3]);
input_sync(dev);
resubmit:
//提交下次传输
status = usb_submit_urb(urb,GFP_ATOMIC);
if(status)
err("can't resubmit intr,%s-%s/input0,status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath,status);
}
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev);
mouse->irq->dev = mouse->usbdev;
if(usb_submit_urb(mouse->irq,GFP_KERNEL))
return -EIO;
return 0;
}
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev);
usb_kill_urb(mouse->irq);
}
static int usb_mouse_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
//设备描述usb_device
//接口描述usb_interface
struct usb_device *dev = interface_to_usbdev(intf);
//接口设置描述
struct usb_host_interface *interface;
//端点描述符
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe,maxp;
int error = -ENOMEM;
//获取当前接口设置
interface = intf->cur_altsetting;
//根据HID规范,鼠标只有一个端点(不包含0号控制端点)
if(interface->desc.bNumEndpoints != 1)
return -ENODEV;
//获取端点0描述符
endpoint = &interface->endpoint[0].desc;
//根据HID规范,鼠标唯一的端点应为中断端点
if(!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
//生成中断管道
pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);
//返回该端点能够传输的最大包长度,鼠标的返回最大数据包为4个字节
maxp = usb_maxpacket(dev,pipe,usb_pipeout(pipe));
//创建input设备
mouse = kzalloc(sizeof(struct usb_mouse),GFP_KERNEL);
input_dev = input_allocate_device();
if(!mouse || !input_dev)
goto fail1;
//申请内存空间用于数据传输,data为指向该空间地址
mouse->data = usb_alloc_coherent(dev,8,GFP_ATOMIC,&mouse->data_dma);
if(!mouse->data)
goto fail1;
//分配URB
mouse->irq = usb_alloc_urb(0,GFP_KERNEL);
if(!mouse->irq)
goto fail2;
mouse->usbdev = dev;
mouse->dev = input_dev;
if(dev->manufacturer)
strlcpy(mouse->name,dev->manufacturer,sizeof(mouse->name));
if(dev->product)
{
if(dev->manufacturer)
strlcat(mouse->name," ",sizeof(mouse->name));
strlcat(mouse->name,dev->product,sizeof(mouse->name));
}
if(!strlen(mouse->name))
snprintf(mouse->name,sizeof(mouse->name),"USB HIDBP Mouse %04x:%04x",le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));
//usb_make_path 用来获取 USB 设备在 Sysfs 中的路径
usb_make_path(dev,mouse->phys,sizeof(mouse->phys));
strlcat(mouse->phys,"/input0",sizeof(mouse->phys));
//字符设备初始化
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
usb_to_input_id(dev,&input_dev->id);
input_dev->dev.parent = &intf->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
input_set_drvdata(input_dev,mouse);
input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close;
//初始化中断URB
usb_fill_int_urb(mouse->irq,dev,pipe,mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq,mouse,endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
error = input_register_device(mouse->dev);
if(error)
goto fail3;
//将mouse指针保存在intf的dev成员中
usb_set_intfdata(intf,mouse);
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev,8,mouse->data,mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata(intf);
usb_set_intfdata(intf,NULL);
if(mouse)
{
usb_kill_urb(mouse->irq);
input_unregister_device(mouse->dev);
usb_free_urb(mouse->irq);
usb_free_coherent(interface_to_usbdev(intf),8,mouse->data,mouse->data_dma);
kfree(mouse);
}
}
static struct usb_device_id usb_mouse_id_table [] =
{
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },
{ } //Terminating entry
};
MODULE_DEVICE_TABLE(usb,usb_mouse_id_table);
static struct usb_driver usb_mouse_driver =
{
.name = "usbmouse", //驱动名
.probe = usb_mouse_probe, //捕获函数
.disconnect = usb_mouse_disconnect, //卸载函数
.id_table = usb_mouse_id_table, //设备列表
};
static int __init usb_mouse_init(void)
{
//注册鼠标驱动程序
int retval = usb_register(&usb_mouse_driver);
if(retval == 0)
//printk(KERN_INFO KBUILD_MODNAME ":" DRIVER_VERSION ":" DRIVER_DESC "\n");
printk("register success\n");
return retval;
}
static void __exit usb_mouse_exit(void)
{
usb_deregister(&usb_mouse_driver);
}
module_init(usb_mouse_init);
module_exit(usb_mouse_exit);