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 !

Linux USB 鼠标输入驱动详解

时间: 2024-12-28 16:51:23

Linux USB 鼠标输入驱动详解的相关文章

Linux的i2c驱动详解

目录(?)[-] 简介 架构 设备注册 I2C关键数据结构和详细注册流程 关键数据结构 详细注册流程 使用I2C子系统资源函数操作I2C设备 Gpio模拟i2c总线的通用传输算法 总结 理清i2c中的个结构体关系 i2c驱动的编写建议 1 简介 I2C 总线仅仅使用 SCL . SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用.因此, I2C 总线被非常广泛地应用在 EEPROM .实时钟.小型 LCD 等设备与 CPU 的接口中. Linux I2

linux串口编程参数配置详解

1.linux串口编程需要的头文件 #include <stdio.h>         //标准输入输出定义 #include <stdlib.h>        //标准函数库定义 #include <unistd.h>       //Unix标准函数定义 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>          //文件控制定义 #incl

linux cat命令使用方法详解--把档案串连接后传到基本输出

linux cat命令使用方法详解--把档案串连接后传到基本输出 日期:2011-12-05 点击:2324 来源: 未知 分享至: 使用方式:cat [-AbeEnstTuv] [--help] [--version] fileName说明:把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案)参数:-n 或 --number 由 1 开始对所有输出的行数编号-b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号-s 或 --squeeze-b

Android4.0 input事件输入流程详解(中间层到应用层)

在Android系统中,类似于键盘按键.触摸屏等事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序进行处理.系统启动时,窗口管理服务也会启动,该服务启动过程中,会通过系统输入管理器InputManager来负责监控键盘消息.当某一个Activity激活时,会在该Service下注册一个接收消息的通道,表明可以处理具体的消息,然后当有消息时,InputManager就会分发给当前处于激活状态下的Activity进行处理. InputManager的启动

LINux网络的NAPI机制详解一

在查看NAPI机制的时候发现一篇介绍NAPI引入初衷的文章写的很好,通俗易懂,就想要分享下,重要的是博主还做了可以在他基础上任意修改,而并不用注明出处的声明,着实令我敬佩,不过还是附上原文链接! http://blog.csdn.net/dog250/article/details/5302853 处理外部事件是cpu必须要做的事,因为cpu和外设的不平等性导致外设的事件被cpu 当作是外部事件,其实它们是平等的,只不过冯氏机器不这么认为罢了,既然要处理外部事件,那么就需要一定的方法,方法不止一

Linux基础知识之挂载详解(mount,umount及开机自动挂载)

Linux基础知识之挂载详解(mount,umount及开机自动挂载) 转载自:http://www.linuxidc.com/Linux/2016-08/134666.htm 挂载概念简述: 根文件系统之外的其他文件要想能够被访问,都必须通过"关联"至根文件系统上的某个目录来实现,此关联操作即为"挂载",此目录即为"挂载点",解除此关联关系的过程称之为"卸载" 1.挂载:根文件系统外通过关联至根文件系统上的某个目录来实现访问

16.Linux-LCD驱动(详解)

在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构体: framebuffer_alloc(); 2) 设置fb_info 3) 设置硬件相关的操作 4) 使能LCD,并注册fb_info: register_framebuffer() 本节需要用到的函数: void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

Linux CAT与ECHO命令详解 &lt;&lt;EOF EOF

Linux CAT与ECHO命令详解 cat命令是Linux下的一个文本输出命令,通常是用于观看某个文件的内容的: cat主要有三大功能: 1.一次显示整个文件. $ cat filename 2.从键盘创建一个文件. $ cat > filename 只能创建新文件,不能编辑已有文件. 3.将几个文件合并为一个文件. $cat file1 file2 > file cat具体命令格式为 : cat [-AbeEnstTuv] [--help] [--version] fileName 说明:

Linux逻辑卷管理LVM2详解

一. 前言LVM是逻辑卷管理(Logical Volume Manager)的简称,它是建立在物理存储设备之上的一个抽象层,允许你生成逻辑存储卷,与直接使用物理存储在管理上相比,提供了更好灵活性.LVM将存储虚拟化,使用逻辑卷,你不会受限于物理磁盘的大小,另外,与硬件相关的存储设置被其隐藏,你可以不用停止应用或卸载文件系统来调整卷大小或数据迁移.这样可以减少操作成本.LVM与直接使用物理存储相比,有以下优点:1. 灵活的容量.当使用逻辑卷时,文件系统可以扩展到多个磁盘上,你可以聚合多个磁盘或磁盘