编写USB鼠标驱动程序,并测试
转载自: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
编写USB鼠标驱动程序,并测试相关推荐
- 编写USB鼠标驱动程序
编写USB鼠标驱动程序 文章目录 编写USB鼠标驱动程序 参考资料: 1. 目标 2. 编程 2.1 驱动框架 2.2 实现usb_driver 2.2.1 id_table 2.2.2 probe函 ...
- 自己写Linux Usb鼠标驱动程序
USB子系统相关内容参考<精通Linux设备驱动程序>第11章. USB鼠标驱动程序可以参考内核中的鼠标驱动,路径为linux-3.0.86\drivers\hid\usbhid\usbm ...
- hdb interface驱动是什么_Linux USB 鼠标驱动程序详解
USB 总线引出两个重要的链表! 一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 ...
- Linux USB鼠标驱动程序详解
USB 总线引出两个重要的链表! 一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 ...
- Linux USB 鼠标驱动程序解析
USB 总线引出两个重要的链表! 一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 ...
- Linux USB鼠标驱动程序笔记 A
关于这个驱动的源码,我看了几遍,每一次都有不同的感悟,只要自己本来就是菜鸟,慢慢领悟linux中包含的智慧... 本来一开始看的一本书,叫<Linux那些事>,但是太长了,实在看不下去,索 ...
- USB驱动——鼠标驱动程序(中断传输)
本文以 usbmouse.c 为例,简单分析usb鼠标驱动程序. static int __init usb_mouse_init(void) {int retval = usb_register(& ...
- 十五、Linux驱动之USB鼠标驱动
1. 如何编写USB鼠标驱动 结合十四.Linux驱动之USB驱动分析中的分析,我们开始写一个USB鼠标驱动. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动, ...
- linux3.4.2 之usb鼠标驱动,键盘驱动
目录 1 USB相关基本知识 2 USB鼠标编程指导 3 USB鼠标驱动程序完整源码 4 USB鼠标驱动测试 5 USB键盘基本知识 6 USB键盘驱动程序 4 USB键盘驱动测试 1 U ...
- 【移植驱动到Linux3.4.2内核之二】LCD,触摸屏,按键,USB等驱动程序的移植心得总结
学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 文章目录 一移植驱动程序心得体会 二移植LCD驱动程序记录 三移植按 ...
最新文章
- 干货!用大白话告诉你什么是Mock测试
- python解压zip文件_python-29 python解压压缩包的几种方法
- markdown简明语法
- 谷歌最强 NLP 模型 BERT 解读
- Visual Studio Code 配置指南
- 02- 流行歌曲 最新热门
- 一个Json在线格式化的网站
- 理解tcp关闭连接中的time_wait状态
- javascript核心_只需几分钟即可学习这些核心JavaScript概念
- 我来重新学习js的面向对象(part 4)
- C语言:替换字符串中某一段子字符串
- 机器学习算法_机器学习算法之PCA算法
- Android Browser学习七 书签历史模块: 书签UI的实现
- java压缩文件解压失败_java安装 解压缩核心文件失败
- 技巧:苹果电脑怎么清理缓存文件
- python怎么读取txt数据_对python .txt文件读取及数据处理方法总结
- App Links的使用以及坑
- python怎么爬虎牙_Python爬虫:爬取虎牙星秀主播图片
- week06_task_二分, 排序
- 易基因|Science:单细胞甲基化测序鉴定哺乳动物的新神经元亚型和调节元件
热门文章
- Android 仿微信二维码名片制作,生成二维码,扫码生成名片表单信息
- python爬虫模拟登录之图片验证码
- python xlwt_Python模块xlwt对excel进行写入操作
- 图灵 计算机 ppt,人工智能导论(ppt 155页)
- 周志华《机器学习》个人笔记
- wso2 mysql_windows下 WSO2 Application Server配置 及 MySQL数据服务部署
- java ftps_如何基于FTP4J实现FTPS连接过程解析
- ARQ协议与滑动窗口协议
- macOS linux 并发测试工具 wrk
- 记录自己装AMD黑苹果安装