USB驱动程序代码分析——鼠标用作键盘

首先我们贴出代码:

/*
 * drivers\hid\usbhid\usbmouse.c
 */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

static struct input_dev *uk_dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;

static struct usb_device_id usbmouse_as_key_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{USB_DEVICE(0x1234,0x5678)},
{ } /* Terminating entry */
};

static void usbmouse_as_key_irq(struct urb *urb)
{
static unsigned char pre_val;
#if 0
int i;
static int cnt = 0;
printk("data cnt %d: ", ++cnt);
for (i = 0; i < len; i++)
{
printk("%02x ", usb_buf[i]);
}
printk("\n");
#endif
/* USB鼠标数据含义
* data[0]: bit0-左键, 1-按下, 0-松开
*          bit1-右键, 1-按下, 0-松开
*          bit2-中键, 1-按下, 0-松开 
*
     */
if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))//设备端点会将设备的数据写入驱动缓冲区 usb_buf
{
/* 左键发生了变化 */
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);
}

/* 匹配时调用这个函数 */
static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);//获取usb接口结构体中的usb设备结构体
struct usb_host_interface *interface;                      
struct usb_endpoint_descriptor *endpoint;
int pipe;
interface = intf->cur_altsetting;  //获取usb接口结构体中的usb host接口结构体
endpoint = &interface->endpoint[0].desc;//获取 usb  host接口结构体中的端点描述结构体

/* 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);//能产生L类事件
set_bit(KEY_S, uk_dev->keybit);//能产生S类事件
set_bit(KEY_ENTER, uk_dev->keybit);//能产生ENTER
/* c. 注册 */
input_register_device(uk_dev);
/* 在向下分析之前我们有必要先补充一些关于urb的知识,请看注释1 */

/* d. 硬件相关操作 */
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//创建管道,用于连接驱动程序缓冲区和设备端口

/* 长度: */
len = endpoint->wMaxPacketSize;

/* 目的: */
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);//这就是驱动程序缓冲区了,缓冲区物理地                                                                                                                                                  //址在 usb_buf_phys中

/* 使用"3要素" */
/* 分配usb request block */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 使用"3要素设置urb" */
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_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);//提交urb给usb主控制器驱动
return 0;
}

static void usbmouse_as_key_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);
}

/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse_as_key_",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,
};


static int usbmouse_as_key_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}

static void usbmouse_as_key_exit(void)
{
usb_deregister(&usbmouse_as_key_driver);
}

module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);

MODULE_LICENSE("GPL");

注释1: 
1、定义:USB请求块(USB request block,urb)在USB设备驱动程序中用来描述与USB设备通信所用的基本载体和核心数据结构。非常类似于网络设备驱动程序中的sk_buff结构体,是USB主机与设备通信的电波。
2、URB处理流程:
(1)USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb,并提交给USB core
(2)USR core提交该urb到到USB主控制器驱动程序。
(3)USB主控制器驱动程序根据该urb描述的信息,来访问usb设备
(4)当设备访问结束后,USB主的控制器驱动程序通知USB设备驱动程序。
3、urb相关函数
(1)创建urb的函数:
原型:struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
参数: iso_packets:urb所包含的等时数据包的个数
            mem_flags  :内存分配标志(如GFP_KERNEL)
(2)初始化urb(以中断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                      :要访问的设备
           pipe                     :要访问的端点所对应的管道,使用usb_andintpipe()或usb_rcvintpipe()创建(管道:驱动程序的数据缓冲区和一个端点的连接,它代表了一种在两者之间移动数据的能力) 
           transfer_buffer    :要传输的数据的缓冲区。
           buffer_length       : transfer_buffer所指缓冲区的长度
           complete_fn        :当完成urb所请求的操作时,要调用的回调函数
           context                :complet_fn函数所需的上下文,通常取值为dev
           interval                 :urb被调度的时间间隔 
(3)提交urb:在完成urb的创建和初始化后,urb便可以通过usb_submit_urb函数来提交给USB核心:
原型:int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
参数:urb              :指向urb的指针
           mem_flags :内存分配标志,它用于告诉USB核心如何分配内存缓冲区
处理:urb被提交到USB核心后,usb核心指定usb主控制器驱动程序来处理该urb,在3中情况下,urb会被认为处理完成
(1)urb被成功发送给设备,并且设备返回正确的确认。如果urb->status为0,意味着对于一个输出urb,数据被成功发送,对于一个输入urb,数据被成功接收。
(2)如果发送数据或接收数据发生了错误, urb->status会记录错误值
(3)urb被“取消”,这发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消urb,或ur虽已提交,而usb设备被拔出的情况下。urb完成,函数将被调用。

总结:
我们来总结一下工作流程:当插入usb鼠标时,会根据驱动程序注册的id.table来遍历驱动并判断驱动与设备是否匹配。一旦匹配就会调用probe函数,所以我们的主要工作放在了probe函数里面。在probe函数中我们首先获得usb设备端口信息,这些信息在设置urb的时候会用到。接着设置能产生哪类事件以及这类事件里的哪些事件。然后就是重点了,我们来设置urb请求块,可以通过设置它来控制驱动程序与设备端点之间的通信。设置并提交之后,USB主控制器就会根据它以一定的间隔去查询usb设备是否发送数据到usb驱动程序缓冲区,一旦发现了usb设备发送数据过来就会向cpu发出中断请求,并执行相应的中断函数。在中断函数里面,我们根据usb设备发送过来的数据,判断发生了什么事情,然后上报相应的事件,最后不要忘记重新提交urb。


测试:
1. insmod usbmouse_as_key.ko
2. ls /dev/event*
3. 接上USB鼠标
4. ls /dev/event*
5. cat /dev/tty1    然后按鼠标键

你可能感兴趣的:(USB驱动程序代码分析——鼠标用作键盘)