首页 > 代码库 > Linux USB 鼠标输入驱动详解

Linux USB 鼠标输入驱动详解

平台:mini2440

内核:linux 2.6.32.2

USB设备插入时,内核会读取设备信息,接着就把id_table里的信息与读取到的信息做比较,看是否匹配,如果匹配,就调用probe函数。USB设备拔出时会调用disconnect函数。URB在USB设备驱动程序中用来描述与USB设备通信时用到的基本载体和核心数据结构。

URB(usb request block)处理流程:

    ①USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb并提交给USB core。

    ②USB core把这个urb提交到USB主控制器驱动程序。

    ③USB主控制器驱动程序根据该urb描述的信息来访问usb设备。

    ④当设备访问结束后,USB主控制器驱动程序通知USB设备驱动程序。

USB鼠标数据格式:

    ①bit0 ->左键,1->按下,0->松开

    ②bit1 ->右键,1->按下,0->松开

    ③bit2 ->中键,1->按下,0->松开

驱动代码清单:

usb_mouse_input_test.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 urb *uk_urb;
static char       *usb_buf;
static int        len;
static struct     input_dev *uk_dev;
static dma_addr_t usb_buf_phys;

static struct usb_device_id usb_mouse_input_test_id_table [] = 
{
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, 
			             USB_INTERFACE_SUBCLASS_BOOT,
		                 USB_INTERFACE_PROTOCOL_MOUSE) 
	}
};

static void usb_mouse_input_test_irq(struct urb *urb)
{
	static unsigned char pre_val;//USB鼠标将它的数据写到驱动缓冲区usb_buf
	if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
	{
		//状态变化
		printk("left !\n");
	}

	if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
	{
		//状态变化
		printk("right !\n");
	}

	if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
	{
		//状态变化
		printk("middle !\n");
	}
	
	pre_val = usb_buf[0];
	usb_submit_urb(uk_urb, GFP_KERNEL);
}

static int usb_mouse_input_test_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接口结构体中的端点描述结构体
	uk_dev = input_allocate_device();

	set_bit(EV_KEY, uk_dev->evbit);//设置
	set_bit(EV_REP, uk_dev->evbit);
	set_bit(KEY_L, uk_dev->keybit);
	set_bit(KEY_S, uk_dev->keybit);
	set_bit(KEY_ENTER, uk_dev->keybit);
	
	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);
	uk_urb = usb_alloc_urb(0, GFP_KERNEL);//分配usb request block
	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usb_mouse_input_test_irq, NULL, endpoint->bInterval);
	uk_urb->transfer_dma = usb_buf_phys;  //源,目的,长度,设置URB
	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	usb_submit_urb(uk_urb, GFP_KERNEL);//把URB提交到USB主控制器驱动
	
	return 0;
}

static void usb_mouse_input_test_disconnect(struct usb_interface *intf)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	printk("disconnect mouse!\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);
}

static struct usb_driver usb_mouse_input_test_driver = {
	.name		= "usb_mouse_input_test_",
	.probe		= usb_mouse_input_test_probe,
	.disconnect	= usb_mouse_input_test_disconnect,
	.id_table	= usb_mouse_input_test_id_table,
};

static int usb_mouse_input_test_init(void)
{
	usb_register(&usb_mouse_input_test_driver);
	return 0;
}

static void usb_mouse_input_test_exit(void)
{
	usb_deregister(&usb_mouse_input_test_driver);	
}

MODULE_LICENSE("GPL");
module_init(usb_mouse_input_test_init);
module_exit(usb_mouse_input_test_exit);

Makefile:

obj-m	+= usb_mouse_input_test.o

KERN_DIR = /home/***/linux-2.6.32.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

测试前去掉linux内核中鼠标功能:



insmod usb_mouse_input_test.ko

插入USB鼠标

点击鼠标三个按键

终端可见打印信息:

left !

right !

middle !