编写USB鼠标驱动程序,并测试

news/2024/11/7 10:48:14/

转载自:https://www.cnblogs.com/lxl-lennie/p/10189188.html

8.1 编写USB鼠标驱动程序,并测试

学习目标:编写USB鼠标驱动程序,并测试(将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键).


一、怎么写USB设备驱动程序?步骤如下:

1. 首先先定义全局变量usb_driver结构体,并在入口函数中通过usb_register()函数进行注册

2. 分别写usb_driver结构体的成员函数:myusb_mouseprobe、myusb_mousedisconnect、myusb_mouseid_table

--> 2.1 usb_driver的probe函数

  1) 分配一个input_dev结构体

  2) 设置input_dev结构体,使它支持L、S、回车3个按键事件;

  3) 注册input_dev结构体

  4) 硬件相关的操作,即设置USB数据传输3要素和urb结构体:

   ->4.1) 通过usb_rcvintpipe()创建一个接收中断类型的端点管道,用来端点和数据缓冲区之间的连接

   ->4.2) 通过usb_buffer_alloc()申请USB缓冲区

   ->4.3) 申请urb结构体,并利用usb_fill_int_urb()初始化,用来传输数据

   ->4.4) 因为我们2440支持DMA,所以要告诉urb结构体,使用DMA缓冲区地址

   ->4.5) 使用usb_submit_urb()提交urb.

--> 2.2 编写probe函数调用的鼠标中断函数

  1)判断缓存区数据是否改变,若改变,则通过input_event上传鼠标事件

  2)使用usb_submit_urb()提交urb

--> 2.3 usb_driver的disconnect函数中

  1) 通过usb_kill_urb()杀掉提交到内核中的urb

  2) 释放urb结构体

  3 )释放USB缓存区

  4)注销并释放input_device结构体

3. 出口函数通过usb_deregister ()函数注销usb_driver结构体

二、程序源码

复制代码

  1 #include <linux/kernel.h>2 #include <linux/slab.h>3 #include <linux/module.h>4 #include <linux/init.h>5 #include <linux/usb/input.h>6 #include <linux/hid.h>7 8 static struct input_dev *uk_dev;        //input_dev9 static char *usb_buf;                   //虚拟地址缓存区10 static dma_addr_t usb_buf_phys;         //DMA缓存区11 static int len;                         //数据包长度12 static struct urb *uk_urb;              //urb数据传输所用结构体13 14 static struct usb_device_id myusb_mouseid_table [] = {15     { USB_INTERFACE_INFO(16                             USB_INTERFACE_CLASS_HID,         //接口类:hid类17                             USB_INTERFACE_SUBCLASS_BOOT,     //子类:启动设备类18                             USB_INTERFACE_PROTOCOL_MOUSE) }, // USB协议:鼠标协议19 };20 21 static void myusb_mouseirq(struct urb *urb)22 {23     static unsigned char pre_val;25     /* USB鼠标数据含义* data[0]:   bit0-左键, 1-按下, 0-松开26      *                           bit1-右键, 1-按下, 0-松开27      *                           bit2-中键, 1-按下, 0-松开  29      */30     if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))31     {32         /* 左键发生了变化 */33         input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);34         input_sync(uk_dev);35     }37     if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))38     {39         /* 右键发生了变化 */40         input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);41         input_sync(uk_dev);42     }44     if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))45     {46         /* 中键发生了变化 */47         input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);48         input_sync(uk_dev);49     }51     pre_val = usb_buf[0];53     /* 重新提交urb */54     usb_submit_urb(uk_urb, GFP_KERNEL);55 }57 static int myusb_mouseprobe(struct usb_interface *intf, const struct usb_device_id *id)58 {59     struct usb_device *dev = interface_to_usbdev(intf); // 设备,通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用60     struct usb_host_interface *interface;               // 当前接口61     struct usb_endpoint_descriptor *endpoint;62     int pipe;                                            // 端点管道63     64     interface = intf->cur_altsetting;65     endpoint = &interface->endpoint[0].desc;            // 当前接口下的端点描述符66 67     /* a. 分配一个input_dev */68     uk_dev = input_allocate_device(); 70     /* b. 设置 */71     /* b.1 能产生哪类事件 */ //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)72     set_bit(EV_KEY, uk_dev->evbit);
 73     set_bit(EV_REP, uk_dev->evbit);74     75     /* b.2 能产生哪些事件 */76     set_bit(KEY_L, uk_dev->keybit);77     set_bit(KEY_S, uk_dev->keybit);78     set_bit(KEY_ENTER, uk_dev->keybit);79     80     /* c. 注册 */81     input_register_device(uk_dev);82     83     /* d. 硬件相关操作 */84     /* 数据传输3要素: 源,目的,长度 */85     /* 源: USB设备的某个端点 */86     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);87 88     /* 长度: */89     len = endpoint->wMaxPacketSize;90 91     /* 目的: */92     usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);93 94     /* 使用"3要素" */95     /* 分配usb request block */96     uk_urb = usb_alloc_urb(0, GFP_KERNEL);//usb传输素具的urb结构体97     /* 使用"3要素设置urb" */98     usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, myusb_mouseirq, NULL, endpoint->bInterval);99     uk_urb->transfer_dma = usb_buf_phys;   //设置DMA地址
100     uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //设置使用DMA地址
101 
102     /* 使用URB */
103     usb_submit_urb(uk_urb, GFP_KERNEL);
104     
105     return 0;
106 }
108 static void myusb_mousedisconnect(struct usb_interface *intf)
109 {
110     struct usb_device *dev = interface_to_usbdev(intf);
112     //printk("disconnect usbmouse!\n");
113     usb_kill_urb(uk_urb);
114     usb_free_urb(uk_urb);
115 
116     usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
117     input_unregister_device(uk_dev);
118     input_free_device(uk_dev);
119 }
121 /* 1. 分配/设置usb_driver */
122 static struct usb_driver myusb_mousedriver = {
123     .name        = "myusb_mouse",
124     .probe        = myusb_mouseprobe,
125     .disconnect    = myusb_mousedisconnect,
126     .id_table    = myusb_mouseid_table,
127 };
130 static int myusb_mouseinit(void)
131 {
132     /* 2. 注册 */
133     usb_register(&myusb_mousedriver);
134     return 0;
135 }
137 static void myusb_mouseexit(void)
138 {
139     usb_deregister(&myusb_mousedriver);    
140 }
142 module_init(myusb_mouseinit);
143 module_exit(myusb_mouseexit);
145 MODULE_LICENSE("GPL");

复制代码

 三、源码分析

3.1 id_table

1 struct usb_device_id usbmouse_id_table []=USB_INTERFACE_INFO(cl,sc,pr);

USB_INTERFACE_INFO()设置usb_driver驱动的id_table成员

cl:接口类,我们USB鼠标为HID类,所以填入0X03,也就是USB_INTERFACE_CLASS_HID

sc:接口子类为启动设备,填入USB_INTERFACE_SUBCLASS_BOOT

pr:接口协议为鼠标协议,填入USB_INTERFACE_PROTOCOL_MOUSE 

3.2 usb_rcvintpipe()函数

1 pipe=usb_rcvintpipe(dev,endpoint->bEndpointAddress);

创建一个接收(rcv)中断(int)类型的端点管道(pipe),用来端点和数据缓冲区之间的连接, 鼠标为接收中断型

dev: usb_device设备结构体

endpoint->bEndpointAddress:为端点描述符的成员,即端点地址

1)对于控制类型的端点管道使用: usb_sndctrlpipe()/usb_rcvctrlpipe()

2)对于实时类型的端点管道使用: usb_sndisocpipe()/usb_sndisocpipe()

3)对于批量类型的端点管道使用: usb_sndbulkpipe()/usb_rcvbulkpipe()

3.3 usb_driver注册函数

1 usb_deregister(struct usb_driver *driver);

注意:注册一个usb_driver驱动,然后内核会通过usb_driver的成员.id_table函数匹配一次USB设备,匹配成功就会调用usb_driver的成员.probe函数

3.4 usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);

1 char *usb_buffer_alloc(struct usb_device *dev,size_t size,gfp_t mem_flags,dma_addr_t *dma);

作用:分配一个usb缓冲区,该缓存区的物理地址会与虚拟地址的数据一致,分配成功返回一个char型缓冲区虚拟地址

*dev: usb_device设备结构体

size: 分配的缓冲区大小,这里填端点描述符的成员endpoint->wMaxPacketSize   len  //端点最大包长

mem_flags: 分配内存的参数,这里填GFP_ATOMIC,表示从不睡眠

dma: DMA缓冲区物理地址

注销/释放函数:

void usb_buffer_free(struct usb_device *dev,size_t size,void *addr,dma_addr_t dma);

注销分配的usb缓冲区,在usb_driver的disconnect成员函数中使用

addr: 要注销的缓冲区虚拟地址

dma: 要注销的DMA缓冲区虚拟地址

3.5 uk_urb = usb_alloc_urb(0, GFP_KERNEL);//usb传输数据的urb结构体

1 struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);

分配一个urb数据结构体, 分配成功返回一个urb结构体

urb全称为usb request block,USB传输数据时,就是打包成urb结构体来传输

iso_packets:表示iso类型的包个数,这里我们不是iso类型包,直接填0

mem_flags:分配内存的参数,这里填入GFP_KERNEL,正常分配

3.6 usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, myusb_mouseirq, NULL, endpoint->bInterval);

1 static inline void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,
2                       void *transfer_buffer,int buffer_length, 
3                       usb_complete_t complete_fn,void *context,int interval);

初始化中断型端点的urb数据结构体

1)针对批量型端点的urb使用usb_fill_bulk_urb()

2)针对控制型端点的urb使用usb_fill_control_urb()

3)针对等时型端点的urb  需要手动初始化。

urb:指向要初始化的urb

dev:指向要传输的usb设备

pipe:要传输的端点管道, 本节的pipe通过usb_rcvintpipe()宏获取

transfer_buffer:指向要传输数据的虚拟地址缓冲区

buffer_length:数据大小, 这里填端点描述符的成员endpoint->wMaxPacketS //端点最大包长

complete_fn:数据传输完成后产生的中断函数

context:会放在urb->context结构成员中,用来给中断函数用,本节不需要,填NULL即可

interval:间隔时间,表示间隔多少时间读一次数据,填入endpoint-> bInterval即可

3.7 usb_submit_urb(uk_urb, GFP_KERNEL);

1 int usb_submit_urb(struct urb *urb,gfp_t mem_flags);

提交urb到内核,初始化urb和中断函数退出时,都要重新提交一次,告诉内核初始化内存缓存等.

三、测试

1. 在源码下去除原先的usb鼠标驱动,执行 make menuconfig

  --> Device Drivers
      --> HID Devices
        <> USB Human Interface Device (full HID) support

make uImage编译内核,并烧写,启动。

2. ubuntu环境下编译生成驱动模块:myusbmouse.ko

3. 开发板挂载ubuntu的/work/nfs_root/first_fs目录: # mount -t nfs -o nolock,vers=2 10.70.12.103:/work/nfs_root/ /mnt

4. 加载驱动:# insmod myusbmouse.ko

5. 插入USB鼠标,查看设备节点: ls /dev/event*

6.  cat /dev/tty1    点击鼠标按键

(注意:出现乱码,需要关掉QT,#vi /etc/init.d/rcs   屏蔽一行 #/bin/qpe.sh &)

执行命令 # exec 0</dev/tty0 可以当做键盘使用ls命令,(这里exec 0</dev/tty1 表示将/dev/tty1的输入作为标准输,0代表的是STDIN,标准输入)

退出时输入:# exec 0 即可。 

7.  hexdump /dev/event1 

 


参考:https://www.cnblogs.com/lifexy/p/7641602.html


http://www.ppmy.cn/news/211206.html

相关文章

USB鼠标驱动开发流程

USB驱动开发&#xff0c;针对某一个USB设备的某个功能&#xff08;接口&#xff09;构建的驱动程序。USB驱动并不直接和USB设备进行数据交互&#xff0c;而是通过USB总线驱动程序&#xff08;USB Core和USB HCD&#xff09;来操作USB设备的。一般构建USB设备驱动的流程为&#…

USB驱动之Android usb鼠标驱动

1. 前言 HID是Human Interface Devices的缩写&#xff0c;翻译成中文即为人机交互设备。这里的人机交互设备是一个宏观上面的概念&#xff0c;任何设备只要符合HID spec都可以称之为HID设备&#xff0c;常见的HID设备有鼠标键盘&#xff0c;游戏操纵杆等等。 usb鼠标在android代…

编写USB鼠标驱动程序

编写USB鼠标驱动程序 文章目录 编写USB鼠标驱动程序参考资料&#xff1a;1. 目标2. 编程2.1 驱动框架2.2 实现usb_driver2.2.1 id_table2.2.2 probe函数 2.3 实现输入设备2.4 实现数据传输 3. 上机实验 致谢 参考资料&#xff1a; Linux内核源码&#xff1a;include\linux\usb.…

usb鼠标驱动(一)

Linux USB 鼠标驱动程序详解 注册一个usb driver&#xff0c;这个drvier不是usb设备driver&#xff0c;而是接口driver。 /* use a define to avoid include chaining to get THIS_MODULE & friends */ #define usb_register(driver) \usb_register_driver(driver, THIS_M…

设计模式入门:策略模式

现有一套模拟鸭子游戏&#xff0c;可以一边游泳&#xff0c;一边呱呱叫。 每种鸭子都会呱呱叫和游泳&#xff0c;只是外观不同。因此&#xff0c;quack和swim放在父类中&#xff0c;display放在子类中实现。 增加新的功能&#xff1a;鸭子飞翔。 1 我们可能想到直接在父类中增…

条码软件如何制作GS1-128条形码

GS1是国际物品编码组织的英文名称&#xff0c;全称为GS1全球统一标识系统。GS1是整个一个系统&#xff0c;或者说架构&#xff0c;其架构内部所包含的条码&#xff0c;可以有很多种&#xff0c;比如内部的条码是Code 128的&#xff0c;叫做GS1-128&#xff0c;而内部是DataBar的…

亚马逊条码标签(SSCC/FBA)的制作打印

近日&#xff0c;亚马逊对所有商家提出统一要求&#xff0c;产品须按照亚马逊制定的新规范来打印条码标签。做亚马逊外贸的商户都清楚&#xff0c;货品进入亚马逊仓库都必须通过扫描入库&#xff0c;否则无法进行上市销售。 有很多客户最近联系我们请求提供技术指导&#xff0c…

条码标签软件快速生成序列号标签

在做条码标签的时候&#xff0c;各行各业应该都会制作一种可变数据标签——序列号标签。其实输入序列号很简单&#xff0c;如果数量不多&#xff0c;手工输入都是可以的&#xff0c;可是如果需要100个、1000个、甚至10000个序列号的时候&#xff0c;难道还手工输入吗&#xff1…