usb鼠标
谨以此文纪念过往的岁月。
一.前言
众所周知,usb子系统是个庞然大物,这个里面涉及的东西很多,网上也有很好的资料,如复旦ABC的,堪称经典。不过这儿仅以此文来记录学习的历程,不能和那些大牛比啊,各位不要见笑。
二.usb设备描述符
usb子系统很大,从哪儿下手呢。以前学习过usb hub的注册,现在主要看一个设备插入hub后的种种特征。
在这个之前需要理解部分usb协议,关于usb的电气等等都不管,只关注usb驱动软件部分。
1.usb描述符
标准usb设备有四层描述符。这个也许很突兀,但是仍想先介绍一下。关于其具体使用,下面在讲驱动具体实现时再说。
下面的四个描述符,是有包含关系的,设备描述符包括一个或多个配置描述符,配置描述符包括一个或多个接口描述符,而接口描述符包括一个或多个端点描述符。
对于下面的描述符均采用linux中的具体实现的结构体来说明。
1.1 Device Descriptor
struct usb_device_descriptor {
__u8 bLength; --描述符长度
__u8 bDescriptorType; --描述符类型:设备描述符0x01
__le16 bcdUSB; --usb规范版本号
__u8 bDeviceClass; --类代码
__u8 bDeviceSubClass; --子类代码
__u8 bDeviceProtocol; --协议代码
__u8 bMaxPacketSize0; --端点0支持最大数
__le16 idVendor; --供应商ID
__le16 idProduct; --产品ID
__le16 bcdDevice; --设备版本号
__u8 iManufacturer; --供应商字符串描述符的索引值
__u8 iProduct; --产品字符串描述符的索引值
__u8 iSerialNumber; --设备序列号
__u8 bNumConfigurations; --所支持的配置数
} __attribute__ ((packed)); --结构体字符类型对齐
1.2 Configuration Descriptor
struct usb_config_descriptor {
__u8 bLength; --描述符长度
__u8 bDescriptorType; --描述符类型
__le16 wTotalLength; --配置信息的总长度
__u8 bNumInterfaces; --所支持的接口数
__u8 bConfigurationValue; --配置值
__u8 iConfiguration; --字符串描述符的索引值
__u8 bmAttributes; --配置特征
__u8 bMaxPower; --所需最大的总线电流
} __attribute__ ((packed));
1.3 Interface Descriptor
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber; --接口编号
__u8 bAlternateSetting; --备用接口标号
__u8 bNumEndpoints; --接口数目
__u8 bInterfaceClass; --接口类型
__u8 bInterfaceSubClass; --接口子类型
__u8 bInterfaceProtocol; --接口所用协议
__u8 iInterface; --接口索引字符串数值
} __attribute__ ((packed));
1.4 Endpoint Descriptor
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress; --端点号包括传输方向
__u8 bmAttributes; --端点属性
__le16 wMaxPacketSize; --最大数据包长度
__u8 bInterval; --访问间隔
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
1.5 usb 控制传输
struct usb_ctrlrequest {
__u8 bRequestType; --指明控制请求的特征
__u8 bRequest; --指明控制请求的请求号
__le16 wValue; --控制请求的参数
__le16 wIndex; --控制请求的参数,主要是传输索引值或偏移量
__le16 wLength; --控制事务数据阶段需传输的字节数
} __attribute__ ((packed));
上面的设备描述符主要在设备加载进系统时有用,因为系统需要根据这些设备描述符来匹配不同驱动。在usb子系统中usb设备的mattch规则则是根据设备的usb_device_id来匹配。
三.usb驱动
对于usb设备驱动而言比较重要的是在设备驱动加载时
1. usb_register(struct usb_driver *)
2. usb_unregister(struct usb_driver *)
而在usb_driver中需要初始化成员的一般是
static struct usb_driver usb_xxxx_driver = {
.name = "xxxx",
.probe = usb_xxxx_probe,
.disconnect = usb_xxxx_disconnect,
.id_table = usb_xxxx_id_table,
};
在usb_ xxxx _id_table中是该驱动支持的设备ID。其初始化格式一般为(以usb键盘为例)
static struct usb_device_id usb_kbd_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },
{ }
};
在usb_ xxxx _probe函数中一般会申请一个urb 采用usb_alloc_urb(int iso_pockets, GFP_KERNEL)其中第一个参数用于表示该urb包含多少个等时数据包,如果不用等时urb,则设为0.在创建一个urb后,需要调用以下4个函数中一个来正确初始化一个urb
1.usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,void *transter_buffer,
int buffer_length,usb_complete_t compete,void *context,int interval)
Pipe 该urb所发送的目标usb设备的特定端点,采用usb_sndintpipe或usb_rcvintpipe创建
Compete 该urb结束后所调用的函数
Interval 该urb应该被调度的间隔
2. usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,
void *transter_buffer Int buffer_length,usb_complete_t compete,void *context)
Pipe 该urb所发送的目标usb设备的特定端点,采用usb_sndbulkpipe或usb_rcvbulkpipe创建
3.usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_pocket,
void *transter_buffer, Int buffer_length,usb_complete_t compete,void *context)
setup_pocket 指向即将被发送端点的设置数据包的数据
Pipe 该urb所发送的目标usb设备的特定端点,采用usb_sndctlpipe或usb_rcvctlpipe创建
4. 等时urb需要自己手工初始化
5. 在创建和初始化urb完成后需要提交urb
usb_subnit_urb(struct urb *urb,int mem_floags)
如果就简单的usb驱动可以说就可以了,注意在usb_ xxxx _disconnect中将所申请以及开辟的空间要及时释放。而usb通过驱动获取到数据后会通过其他的系统将数据传输到应用层,其中如usb鼠标及usb键盘会通过input子系统传输到应用层。
如果是普通的usb设备会通过cdev系统传输,可以直接通过usb_register_dev简单调用就可以了。
四.usb 鼠标驱动
说什么都是虚的,还是来看源码吧。以usb mouse为例,来看usb设备的驱动。
在usb插入hub时,usb core会为设备创建usb device,而我们仅仅需要的是将usb设备的驱动添加入usb系统。如何去注册一个usb driver呢,与以前的设备驱动模型是
一样的,调用usb_register来注册设备接口驱动。
4.1 usb 驱动注册
其函数原型如下,猛然一看,感觉像是注册一个usb设备。而这函数说白了就是usb_register_driver的马甲,还是调用usb_register_driver。
static inline int usb_register(struct usb_driver *driver)
{
return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}
那看usb_register_driver是如何实现的。
记住这个函数并不是注册整个设备的驱动,而是设备接口的驱动。这个很好去理解,例如usb扬声器,其就需要两个驱动,一个是usb键盘的按键一个是usb音频流。那在这种情况下一个设备就需要两个usb驱动,一个是usb键盘驱动一个是usb音频流驱动。其中usb_register_driver是为其中的一个接口提供驱动。
struct usbdrv_wrap {
struct device_driver driver; --设备驱动
int for_devices; --如果非零时设备驱动,而零则代表是接口驱动。
};
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = (char *) new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface; --在usb系统中很多的任务都由usb core完成,想这种interface探测驱动都是usb core完成的。
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name; --上面的一段就是对标准driver的初始化。
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver); --注册驱动。
if (!retval) {
pr_info("%s: registered new interface driver %s\n",usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver);
} else {
printk(KERN_ERR "%s: error %d registering interface "" driver %s\n",usbcore_name, retval, new_driver->name);
}
return retval;
}
蓦然回首,你会发现所有的一切一切都是浮云,只有driver_register才是真的,几乎所有的驱动最终都会调用该函数来注册驱动,说白了,几乎所有的驱动模型都是该函数的一种分支,在其基础上进行适合于各种设备的驱动模型。
所谓的usb_register完了。那看一下其传入的参数,就是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 (*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;
};
这个结构体是就是一些函数指针的结合体,几乎所有的driver都是这个鸟样。id_table是用去区别各种驱动的识别码,也是device查询适合的驱动和驱动查询适合的device的标志。
那来看这个结构体
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 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) },
{ }
};
那这个就需要讲述一下各个宏定义的含义了。
宏定义USB_INTERFACE_INFO用于描述根据接口类来match的规则。
#define USB_INTERFACE_INFO(cl, sc, pr) \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
.bInterfaceClass = (cl), \
.bInterfaceSubClass = (sc), \
.bInterfaceProtocol = (pr)
而宏定义USB_DEVICE_ID_MATCH_INT_INFO说明match的flags,如下面宏定义说明为根据接口的类,子类以及协议来匹配。
#define USB_DEVICE_ID_MATCH_INT_INFO \
(USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_SUBCLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL)
match_flags一般有下面五种。
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 --根据设备制造信息
而宏定义usb_device_id的方法根据上述五种区别以及部分组合有六种方法进行赋值。这个可以具体参考/drivers/usb/core/usb.h。
4.2 usb mouse探测函数
何所谓probe,都应该知道的。主要来看probe中是如何操作的。
在usb core调用驱动probe时,usb core已经将intf和驱动捆绑在一起,会将intf的信息同时传入,那这里就来看怎么使用的。其实这个会根据不同的设备其probe函数是不同的这里仅仅是为了更好的理解usb驱动才来看看的,这里将关于input子系统的程序删除。
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf); --根据intf获取usb_device信息。
struct usb_host_interface *interface; --这个会指向当前的设置
struct usb_endpoint_descriptor *endpoint; --端点描述符
struct usb_mouse *umouse;
struct input_dev *input_dev;
int pipe,maxp;
interface = intf->cur_altsetting;
if(interface->desc.bNumEndpoints != 1) --usb mouse的端点应该只有两个一个是0端点专门用于控制,而另一个则是中断端点。
return -ENODEV;
endpoint = &(interface->endpoint[0].desc);
if(!usb_endpoint_is_int_in(endpoint)) --判断端点是否为中断in类型。
return -ENODEV;
pipe = usb_rcvintpipe(udev,endpoint->bEndpointAddress); --创建一个接受中断类型pipe
maxp = usb_maxpacket(udev,pipe,usb_pipeout(pipe)); --usb最大包数据数
umouse->data = usb_buffer_alloc(udev,8,GFP_ATOMIC,&(umouse->data_dma)); --分配DMA空间
umouse->irq = usb_alloc_urb(0,GFP_ATOMIC); --申请一个urb
usb_fill_int_urb(umouse->irq,udev,pipe,umouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq,umouse,endpoint->bInterval); --填充urb
umouse->irq->transfer_dma = umouse->data_dma; --传输dma的地址
umouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; --传输标志符
usb_set_intfdata(intf, umouse); --设置私有数据
}