Linux驱动开发(十三)---USB驱动HID开发学习(鼠标)
前文回顾
《Linux驱动开发(一)—环境搭建与hello world》
《Linux驱动开发(二)—驱动与设备的分离设计》
《Linux驱动开发(三)—设备树》
《Linux驱动开发(四)—树莓派内核编译》
《Linux驱动开发(五)—树莓派设备树配合驱动开发》
《Linux驱动开发(六)—树莓派配合硬件进行字符驱动开发》
《Linux驱动开发(七)—树莓派按键驱动开发》
《Linux驱动开发(八)—树莓派SR04驱动开发》
《Linux驱动开发(九)—树莓派I2C设备驱动开发(BME280)》
《Linux驱动开发(十)—树莓派输入子系统学习(红外接收)》
《Linux驱动开发(十一)—树莓派SPI驱动学习(OLED)》
《Linux驱动开发(十二)—树莓派framebuffer学习(改造OLED)》
继续宣传一下韦老师的视频
70天30节Linux驱动开发快速入门系列课程【实战教学、技术讨论、直播答疑】
韦老师的视频基本学习完毕了,后续的知识,自己开始学习补充。先从常见的USB设备驱动开始,那手里最常见的,还是USB的鼠标键盘。
基础知识
关于USB的知识与数据相比之前的总线复杂的多了,这里推荐一篇博客,后续的代码也很大程度参考了里面的内容。
推荐DS小龙哥的《Linux驱动开发: USB驱动开发》
设备信息
在正常的树莓派操作系统下,我们插入两个usb设备,一个键盘一个鼠标
通过dmesg命令可以看到设备的相关信息。
[ 1027.622400] usb 1-1.3: new low-speed USB device number 6 using dwc_otg
[ 1027.757393] usb 1-1.3: New USB device found, idVendor=093a, idProduct=2510, bcdDevice= 1.00
[ 1027.757430] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 1027.757450] usb 1-1.3: Product: USB Optical Mouse
[ 1027.757467] usb 1-1.3: Manufacturer: PixArt
[ 1027.762689] input: PixArt USB Optical Mouse as /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/0003:093A:2510.0002/input/input5
[ 1027.763582] hid-generic 0003:093A:2510.0002: input,hidraw0: USB HID v1.11 Mouse [PixArt USB Optical Mouse] on usb-3f980000.usb-1.3/input0
[ 4087.784819] usb 1-1.1.2: new low-speed USB device number 7 using dwc_otg
[ 4087.931407] usb 1-1.1.2: New USB device found, idVendor=0c45, idProduct=760b, bcdDevice= 1.05
[ 4087.931448] usb 1-1.1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 4087.931469] usb 1-1.1.2: Product: USB Keyboard
[ 4087.931486] usb 1-1.1.2: Manufacturer: SONiX
[ 4087.945438] input: SONiX USB Keyboard as /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.1/1-1.1.2/1-1.1.2:1.0/0003:0C45:760B.0003/input/input6
[ 4088.016241] hid-generic 0003:0C45:760B.0003: input,hidraw1: USB HID v1.11 Keyboard [SONiX USB Keyboard] on usb-3f980000.usb-1.1.2/input0
[ 4088.026334] input: SONiX USB Keyboard Consumer Control as /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.1/1-1.1.2/1-1.1.2:1.1/0003:0C45:760B.0004/input/input7
[ 4088.095217] input: SONiX USB Keyboard System Control as /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.1/1-1.1.2/1-1.1.2:1.1/0003:0C45:760B.0004/input/input8
[ 4088.095656] hid-generic 0003:0C45:760B.0004: input,hidraw2: USB HID v1.11 Device [SONiX USB Keyboard] on usb-3f980000.usb-1.1.2/input1
可以看到
鼠标:idVendor=093a, idProduct=2510, bcdDevice= 1.00
键盘:idVendor=0c45, idProduct=760b, bcdDevice= 1.05
这两组ID后续测试要使用。
设备树
这里就不需要设备树参与了,就像IIC一样,是挂载到现有的总线上,所以这里只需要注册usb设备就可以了。
代码框架(HID)
先来看一个通用的框架
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>//定义USB的IDTAB
static const struct usb_device_id my_usb_ids[] =
{{USB_DEVICE(0x093a,0x2510)},{USB_DEVICE(0x0c45,0x760b)},{}
};/*
MODULE_DEVICE_TABLE 有两个功能。
一是:将设备加入到外设队列中,
二是告诉程序阅读者该设备是热插拔设备或是说该设备支持热插拔功能。
该宏定义在<linux/module.h>下
这个宏有两个参数,第一个参数设备名,第二个参数该设备加入到模块中时对应产生的设备搜索符号,这个宏生成了一个名为__mod_pci_device_table
局部变量,这个变量指向第二个参数
*/
MODULE_DEVICE_TABLE (usb,my_usb_ids);//USB设备信息与驱动端匹配成功的时候调用。
static int myusb_probe(struct usb_interface *intf,const struct usb_device_id *id) //资源探索函数
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}//USB断开的时候调用
static void myusb_disconnect(struct usb_interface *intf)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
}//定义USB驱动结构体
static struct usb_driver myusb_driver =
{.name = "myusb_drv",.id_table = my_usb_ids,.probe = myusb_probe,.disconnect = myusb_disconnect
};static int __init myusb_init(void)
{//注册USB设备驱动printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);usb_register(&myusb_driver);return 0;
}static void __exit myusb_exit(void)
{//注销USB设备驱动printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);usb_deregister(&myusb_driver);
}module_init(myusb_init);
module_exit(myusb_exit);
MODULE_AUTHOR("PGG");
MODULE_LICENSE("GPL");
我们来试一下这个框架能否起作用,在不卸载内核USB HID驱动下,通过ID是否能提前加载相关驱动。
不过重新插拔之后,设备加载的还是内核原有的驱动程序。所以这里先卸载内核相关驱动。
卸载内核USB HID驱动
通过重新编译内核
Device Drivers —>
HID support —>
USB HID support —>
原有配置
修改为
然后更新内核。
驱动框架测试
重新安装刚才的模块,然后插入USB鼠标,查看打印
[ 67.588846] drivers/char/myusbmouse.c myusb_init 49
[ 67.589037] usbcore: registered new interface driver myusb_drv
[ 82.983774] usb 1-1.3: new low-speed USB device number 5 using dwc_otg
[ 83.118950] usb 1-1.3: New USB device found, idVendor=093a, idProduct=2510, bcdDevice= 1.00
[ 83.118987] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 83.119007] usb 1-1.3: Product: USB Optical Mouse
[ 83.119024] usb 1-1.3: Manufacturer: PixArt
[ 83.125584] drivers/char/myusbmouse.c myusb_probe 27
顺利执行到了probe函数。
注册中断
修改probe函数
static int size;
static unsigned char *buf =NULL;
static struct urb *myurb=NULL;
static dma_addr_t buf_phy;static int myusb_probe(struct usb_interface *intf,const struct usb_device_id *id) //资源探索函数
{struct usb_device *dev = NULL;struct usb_host_interface *interface = NULL;struct usb_endpoint_descriptor *endpoint = NULL;int pipe;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);printk("USB驱动匹配成功! ID: 0x%X,0x%X\n",id->idVendor,id->idProduct);/*通过接口获取设备信息*/dev = interface_to_usbdev(intf);/*获取当前接口设置*/interface=intf->cur_altsetting;/*获取端点描述符*/endpoint = &interface->endpoint[0].desc;/*中断传输:创建输入管道*/pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);/*从端点描述符中获取传输的数据大小 */size = endpoint->wMaxPacketSize;printk("设备传输数据包大小:%d\n",size);/*分配数据传输缓冲区*/buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy);/*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/myurb = usb_alloc_urb(0,GFP_KERNEL);/*中断方式初始化urb*/usb_fill_int_urb(myurb,dev,pipe,buf,size,usb_complete,NULL,endpoint->bInterval);myurb->transfer_dma = buf_phy;myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*为端点提交异步传输请求*/usb_submit_urb(myurb, GFP_KERNEL); return 0;
}
大概过程就是
当中断设备有数据发送上来的时候,会触发中断,数据就在缓冲区中。
可以在回调中查看数据,按照缓存大小,输出一下数据看一下。
static void usb_irq_work(struct urb *urb)
{int i;for(i=0;i<size;i++){printk("0x%x ",buf[i]);}printk("\n");/* 重新提交异步请求*/usb_submit_urb(myurb, GFP_KERNEL);
}
然后断开的时候会调用disconnect函数,需要释放资源
static void myusb_disconnect(struct usb_interface *intf)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);struct usb_device *dev = interface_to_usbdev(intf);usb_kill_urb(myurb);usb_free_urb(myurb);usb_free_coherent(dev,size,buf, buf_phy);printk("USB 设备释放成功!\n"); }
然后测试一下模块驱动。
先装载驱动,再插上usb鼠标
root@raspberrypi:/home/pgg/work/driver# insmod myusbmouse.ko
root@raspberrypi:/home/pgg/work/driver# dmesg
[ 5684.233197] drivers/char/myusbmouse.c myusb_init 116
[ 5684.233391] usbcore: registered new interface driver myusb_drv
root@raspberrypi:/home/pgg/work/driver# dmesg
[ 5684.233197] drivers/char/myusbmouse.c myusb_init 116
[ 5684.233391] usbcore: registered new interface driver myusb_drv
[ 5702.442546] usb 1-1.3: new low-speed USB device number 8 using dwc_otg
[ 5702.577376] usb 1-1.3: New USB device found, idVendor=093a, idProduct=2510, bcdDevice= 1.00
[ 5702.577413] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 5702.577433] usb 1-1.3: Product: USB Optical Mouse
[ 5702.577450] usb 1-1.3: Manufacturer: PixArt
[ 5702.578716] drivers/char/myusbmouse.c myusb_probe 56
[ 5702.578738] USB驱动匹配成功! ID: 0x93A,0x2510
[ 5702.578752] 设备传输数据包大小:4
可以看到驱动匹配成功,数据包大小为4。
移动一下鼠标看看。打印了很多数据。
[ 5771.634961] 0x0
[ 5771.635024] 0xff
[ 5771.635044] 0xff
[ 5771.635061] 0x0 [ 5771.683970] 0x0
[ 5771.684017] 0xff
[ 5771.684036] 0xff
[ 5771.684053] 0x0 [ 5772.644962] 0x0
[ 5772.645013] 0xfe
[ 5772.645033] 0x0
[ 5772.645050] 0x0 [ 5773.925960] 0x0
[ 5773.926013] 0xff
[ 5773.926033] 0x0
[ 5773.926050] 0x0 [ 5796.702962] 0x0
[ 5796.703014] 0xff
[ 5796.703035] 0xff
[ 5796.703052] 0x0
是不是看不懂,别急
数据解析
那么这4个字节都是啥呢,我们在这篇文章中找到了答案
《Linux USB驱动学习总结(三)---- USB鼠标的加载、初始化和通信过程》
input_report_key(dev, BTN_LEFT, data[0] & 0x01);提交按键信息,data[0] 的第 0 位为 1,表示左键按下
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);提交按键信息,data[0] 的第 1 位为 1,表示右键按下
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);提交按键信息,data[0] 的第 2 位为 1,表示中键按下
input_report_key(dev, BTN_SIDE, data[0] & 0x08);提交按键信息,data[0] 的第 3 位为 1,表示边键按下
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);提交按键信息,data[0] 的第 4 位为 1,表示额外键按下input_report_rel(dev, REL_X, data[1]);///提交鼠标相对坐标值,data[1] 为 X 坐标
input_report_rel(dev, REL_Y, data[2]);///提交鼠标相对坐标值,data[2] 为 Y 坐标
input_report_rel(dev, REL_WHEEL, data[3]);///提交鼠标滚轮相对值,data[3] 为 滚轮相对值
- data[0]:按键信息,
第 0 位为 1,表示左键按下
第 1 位为 1,表示右键按下
第 2 位为 1,表示中键按下
第 3 位为 1,表示边键按下
第 4 位为 1,表示外键按下 - data[1~3]:鼠标相对坐标值,
data[1] 为 X 坐标
data[2] 为 Y 坐标
data[3] 为 滚轮相对值
所以我们修改一下
static void usb_irq_work(struct urb *urb)
{/*int i;for(i=0;i<size;i++){printk("0x%x ",buf[i]);}printk("\n");*/if(data[0]&0x01)printk("左键按下");if(data[0] & 0x02)printk("右键按下");if(data[0] & 0x04)printk("中键按下");if(data[0] & 0x08)printk("边键按下");if(data[0] & 0x10)printk("额外键按下");printk("鼠标相对坐标值x:[%x],y:[%x]",data[1],data[2]);printk("滚轮相对值:[%x]", data[3]);/* 重新提交异步请求*/usb_submit_urb(myurb, GFP_KERNEL);
}
再次测试。
测试结果
横移
竖移,有点歪
左右中键
没毛病。
结合输入子系统
我们在前面的《Linux驱动开发(十)—树莓派输入子系统学习(红外接收)》中已经讲过了输入子系统。
我们就可以将鼠标的消息,通过子系统,注册到系统当中去,然后再中断的时候,将消息发送给系统。这样就能通过event来实现输入了。
这块后续再实践一下。
源码中的usbmouse,已经完整的实现了这个,可以参考一下源码。
源码是我们学习linux最好的老师。
知识点
通配ID
通用的usb设备id可以写成如下写法。就可以识别所有的USB鼠标
//通用ID
static const struct usb_device_id usb_mouse_id_table[] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },{ } /* Terminating entry */
};
查看usb设备
通过命令lsusb
root@raspberrypi:/home/pgg/work/driver# lsusb
Bus 001 Device 010: ID 093a:2510 Pixart Imaging, Inc. Optical Mouse
Bus 001 Device 004: ID 0424:7800 Microchip Technology, Inc. (formerly SMSC)
Bus 001 Device 003: ID 0424:2514 Microchip Technology, Inc. (formerly SMSC) USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Microchip Technology, Inc. (formerly SMSC) USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
加上-v参数可以获得更多细节
结束语
夫人和孩子今天回来了,还是家人都在一起的感觉好,给孩子买的书,他很喜欢,在屋里自己看还不停的笑。
昨天看了《汉江怪物》,感觉不太值那么高的分,强行和ZZ挂钩的电影,有点不太令人理解。
今天就不看电影了,晚上打算学一下《瘟疫危机》,学习一下当今小朋友玩的新桌游,尽量跟一下年轻人的步伐。
Linux驱动开发(十三)---USB驱动HID开发学习(鼠标)相关推荐
- STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- Linux驱动开发之USB驱动深入学习(三)——USB2.0ECHI驱动注册
一.前言 本篇博客仅对ECHI主机控制器驱动的注册部分进行简要叙述,后面再对一些重要的接口进行分析讲解. 二.USB 1.概述 USB(Universal Serial Bus)即"通用外部 ...
- linux下r232转usb驱动,R232转usb驱动
R232 usb驱动可以有效解决没有COM口的问题,路由器交换机等设备初始时没有任何配置,必须通过CONSOLE口进行,如果您的电脑出现"找不到COM口"的提示问题,那就是您电脑中 ...
- linux下peci转usb驱动,USB口转并口驱动-USB口转并口打印线驱动程序下载 --pc6下载站...
USB口转并口打印线缆的驱动程序ProlificUSB-ParallelBridge,所支持的硬件ID为:USB\VID_067BPID_2305. 相关软件软件大小版本说明下载地址 USB口转并口打 ...
- Linux系统移植实验---USB驱动的移植
实验八 USB驱动的移植 [实验目的] USB接口是现在计算机系统中最通用的一种接口, 说明:在本系统移植课程实验中命令行提示符 "$"表示是在主机上执行,"#" ...
- 服务器系统usb驱动,云服务器usb驱动
云服务器usb驱动 内容精选 换一换 Windows弹性云服务器虚拟化驱动异常(Tools没有正常运行).为保证弹性云服务器的正常使用,请参见本节内容进行修复.弹性云服务器虚拟化驱动异常会影响弹性云服 ...
- 计算机硬件驱动的安装,usb驱动安装,手把手教你电脑usb驱动怎么安装
其实USB移动能够在电脑上实现一些操作,原因是因为有usb驱动去带动,说安装和更新usb驱动的工作很重要,它能够帮助用户解决USB设备接上电脑之后无法识别等一些常见的USB设备驱动问题.下面,小编就来 ...
- arm+linux+usb驱动开发,Linux+ARM下的USB驱动开发
驱动开发 嵌入式软件应用 文章编号:l帅8-0570(2∞8)08吨-∞86一m2 中文核心期刊'微计算机信息)(嵌入式与soc)2008年第24卷第8-2期 Linux+ARM下的USB驱动开发 U ...
- bulk interface驱动_(简易USB驱动)开发指导
1 实验七( 2 ) 设备驱动开发指导 块设备种类多, 使用广泛, 其驱动程序的开发也比字符设备复杂.通过本实验, 大家要 开发一个实际块设备 ( U 盘) 的驱动程序, 将能够更深入地掌握块设备驱动 ...
- windows7 64位 mini2440开发板 USB驱动安装
转自http://blog.csdn.net/sanshuei/article/details/8288569 在windows7 64位安装mini2440USB驱动问题汇总: 1.更新失败 开发板 ...
最新文章
- Python访问街区所有节点最短路径问题,并结合matplotlib可视化
- 强化学习入门教程(附学习大纲)
- java 2d划线 刷子_Java图形设计中,利用Bresenham算法实现直线线型,线宽的控制(NO2DGRAPHICS)...
- oracle 数据库备份恢复
- codeforces1493 D. GCD of an Array(数论)
- 逆向行驶!数据结构双向链表DoubleLinkedList,Java实现增删改查
- 2017年07月03号课堂笔记
- 构建你自己的论坛,基于windows服务器的xampp+discuz论坛
- [Android]Cygwin使用及NDK编译方法
- 一步步编写avalon组件03:切换卡组件
- 牛腩新闻发布系统(一)
- 华为与Emulex、Oracle合作发布数据完整性解决方案
- 华硕路由器远程连接配置指南
- 交互媒体专题设计大作业
- 自由技艺 (Liberal arts)
- ubuntu 安装chrome并禁止提示更新
- 【学术】英文写作中值得参考的语法、句式(二)
- java实现登陆验证码
- 图像拼接matlab
- 应用程序和操作系统的关系是什么