您的位置:首页 > 其它

简单的usb鼠标驱动分析

2012-09-19 19:53 375 查看
如果没有看过 :
驱动usb鼠标识别 建议先看看

下面主要是分析了usb识别后的操作

#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>
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "zhutoubenben"
#define DRIVER_DESC "u s b ..."

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
#define USB_MOUSE_VENDOR_ID     0x1c4f 
#define USB_MOUSE_PRODUCT_ID    0x03 
struct usb_device_id my_usb_table[] = {
#if 0 
	{		USB_DEVICE(USB_MOUSE_VENDOR_ID,USB_MOUSE_PRODUCT_ID),	 }, //  已经注释 无效
#else
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
	  USB_INTERFACE_PROTOCOL_MOUSE) },
#endif
	{}
};

struct my_mouse_dev {
	struct usb_device* usb_dev;
	struct input_dev*  input_dev;
	struct urb *       urb;
	signed char *      data;     
	dma_addr_t         data_dma;	
};

int my_usb_mouse_open(struct input_dev *dev){
	struct my_mouse_dev* mouse_dev = input_get_drvdata( dev);
	mouse_dev->urb->dev = mouse_dev->usb_dev;
	if(usb_submit_urb(mouse_dev->urb,GFP_ATOMIC)){
		return -EIO;
	}	
	return 0;
}

void my_usb_mouse_close(struct input_dev *dev){
	struct my_mouse_dev *mouse = input_get_drvdata(dev);
	usb_kill_urb(mouse->urb);
}

/*******  鼠标事件发生 调用的函数
  由下方的probe中的 usb_fill_int_urb()决定
  ******/
static void usb_mouse_irq(struct urb *urb){
	struct my_mouse_dev* mouse = urb->context;
	struct input_dev*    dev   = mouse->input_dev;
	signed char *        data  = mouse->data;
	//printk("irq now ..... \n");
	
	switch(urb->status){
	case 0:
		break;
	case -ECONNRESET:	/* unlink */
	case -ENOENT:
	case -ESHUTDOWN:
		return;
	default:		/* error */
		goto resubmit;
	}
	input_report_key(dev, BTN_LEFT,   data[0] & 0x01);//data[0]: bit0-左键, 1-按下, 0-松开
	input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);//         bit1-右键, 1-按下, 0-松开
	input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);//         bit2-中键, 1-按下, 0-松开 
	input_report_key(dev, BTN_SIDE,   data[0] & 0x08);// 鼠标的side 键
	input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);// 鼠标的extra键
	input_report_rel(dev, REL_X,      data[1]);       //  表示鼠标的水平位移
	input_report_rel(dev, REL_Y,      data[2]);       //  表示鼠标的垂直位移
	input_report_rel(dev, REL_WHEEL,  data[3]);       //  REL_WHEEL滚轮位移
	input_sync(dev);  
resubmit:
	usb_submit_urb (urb, GFP_ATOMIC);//  提交
        /*   URB处理流程
            (1)USB设备驱动程序创建并初始化一个访问特定USB设备特定端点的urb,并提交给USB core
            (2)USR core提交该urb到到USB主控制器驱动程序。
            (3)USB主控制器驱动程序根据该urb描述的信息,来访问usb设备
            (4)当设备访问结束后,USB主的控制器驱动程序通知USB设备驱动程序。          
          */
        // 此次应加上判断返回的状态是否成功....
}

static int my_usb_probe(struct usb_interface *intf,const struct usb_device_id *id){
	
	int retval = -ENOMEM;
	struct my_mouse_dev *mouse;
	struct input_dev *input_dev;   
	
	struct usb_endpoint_descriptor *endpoint;  /* 端点描述符 */
	struct usb_host_interface *host_intf;      /* 接口设置描述 */
	struct usb_device *dev = interface_to_usbdev(intf);  /* 设备描述 usb_device */
	int pipe; // 管道
	int maxp;
	host_intf = intf->cur_altsetting;// 获取当前的接口配置
	if(1!=host_intf->desc.bNumEndpoints){
		return -ENODEV;
	}
	endpoint = &host_intf->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint)){
		return -ENODEV;/* 根据HID规范,鼠标唯一的端点应为中断端点 */
	}

	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);/* 生成中断接收管道 */
	
	/* 返回该端点能够传输的最大的包长度,鼠标的返回的最大数据包为4个字节。*/  
	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

	mouse = kzalloc(sizeof(struct my_mouse_dev),GFP_KERNEL);
	
	input_dev = input_allocate_device();
	if(!mouse || !input_dev){
		goto fail0;
	}
	mouse->data = usb_buffer_alloc(dev,8,GFP_ATOMIC,&mouse->data_dma);
	if(!mouse->data){
		printk("usb_buffer_alloc error \n ");
		goto fail1;
	}
	mouse->urb = usb_alloc_urb(0,GFP_KERNEL);//0表示不适用iso等时传输,GFP_KERNEL申请内存的一种方式
	if(!mouse->urb){
		printk("usb_alloc_urb error \n ");
		goto fail2;
	}
	mouse->usb_dev= dev;
	mouse->input_dev = input_dev;
        usb_fill_int_urb(mouse->urb,dev,pipe,mouse->data,(maxp > 8 ? 8 : maxp),
                                                      usb_mouse_irq, mouse, endpoint->bInterval);
        mouse->urb->transfer_dma = mouse->data_dma;
        mouse->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;// 当不使用DMA
        
	/*   input 子系统部分 */
	input_dev->dev.parent = &intf->dev; 
	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);	//EV_REL 相对坐标(轨迹球) ; EV_ABS 绝对坐标
	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 = my_usb_mouse_open;
	input_dev->close = my_usb_mouse_close;
	
	retval = input_register_device(mouse->input_dev);
	if (retval){
		printk("input_register_device error \n ");
		goto fail3;
	}	
	
	usb_set_intfdata(intf, mouse);//  设置私有数据
	return 0;

fail3:	
	usb_free_urb(mouse->urb);
fail2:	
	usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
fail1:	
	input_free_device(input_dev);
fail0:
	kfree(mouse);
	return retval;

}
/*
对于probe函数中的设置有两部分, 一部分是usb的,一部分是input子系统的
对于usb部分:
1.获得usb_host_interface即当前接口配置描述的指针,目的是
2.为了获取端点配置(可能有多个)
3.生成中断接收管道
4.设置包最大长度
5.分配usb_buffer这个用来存放获取的数据data
6.申请并设置urb
7.调用usb_fill_int_urb来填充中断
8.在适当的时机调用usb_submit_urb开始工作(这里是在open的时候调用的)
至于input子系统主要就是申请input_dev,设置其支持的事件和键,注册*/
static void my_usb_disconnect (struct usb_interface *intf){
	struct my_mouse_dev* mouse = usb_get_intfdata (intf);

	usb_set_intfdata(intf, NULL);
	if (mouse) {
		usb_kill_urb(mouse->urb);                   /* 结束 urb 生命周期 */
		input_unregister_device(mouse->input_dev);  /* 将鼠标设备从输入子系统中注销 */		
		input_free_device(mouse->input_dev);       /* 释放存放鼠标事件的存储空间 */
		usb_free_urb(mouse->urb);                   /* 释放 urb 存储空间 */
		usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);/* 释放存放鼠标事件的 data 存储空间 */
		kfree(mouse);
	}
	printk("device %x disconnect ... \n",interface_to_usbdev(intf)->descriptor.idVendor);
}

static struct usb_driver my_usb_driver = {
	.name =		"my_usb_driver",
	.probe =	my_usb_probe,
	.disconnect =	my_usb_disconnect,
	.id_table =	my_usb_table,
};

static int __init my_usb_mouse_init(void){
	int result;
	result = usb_register(&my_usb_driver);
	if (result)
		err("usb_register failed. Error number %d", result);
	return result;
}

static void __exit my_usb_mouse_exit(void){
	usb_deregister(&my_usb_driver);
}
module_init(my_usb_mouse_init);
module_exit(my_usb_mouse_exit);

insmod驱动后, 使用cat /dev/event? 然后移动鼠标或者按下鼠标就会有提示输出, 但是都是乱码, 主要的原因是他所提交的都是基于input子系统的结构体, 故这里是乱码, 如果要打印出具体的信息那么就需要我们去写一个input子系统的应用程序, 把具体的数据打印出来...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: