转载自: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鼠标驱动程序,并测试相关推荐

  1. 编写USB鼠标驱动程序

    编写USB鼠标驱动程序 文章目录 编写USB鼠标驱动程序 参考资料: 1. 目标 2. 编程 2.1 驱动框架 2.2 实现usb_driver 2.2.1 id_table 2.2.2 probe函 ...

  2. 自己写Linux Usb鼠标驱动程序

    USB子系统相关内容参考<精通Linux设备驱动程序>第11章. USB鼠标驱动程序可以参考内核中的鼠标驱动,路径为linux-3.0.86\drivers\hid\usbhid\usbm ...

  3. hdb interface驱动是什么_Linux USB 鼠标驱动程序详解

    USB 总线引出两个重要的链表! 一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 ...

  4. Linux USB鼠标驱动程序详解

    USB 总线引出两个重要的链表! 一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 ...

  5. Linux USB 鼠标驱动程序解析

    USB 总线引出两个重要的链表! 一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表.设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 ...

  6. Linux USB鼠标驱动程序笔记 A

    关于这个驱动的源码,我看了几遍,每一次都有不同的感悟,只要自己本来就是菜鸟,慢慢领悟linux中包含的智慧... 本来一开始看的一本书,叫<Linux那些事>,但是太长了,实在看不下去,索 ...

  7. USB驱动——鼠标驱动程序(中断传输)

    本文以 usbmouse.c 为例,简单分析usb鼠标驱动程序. static int __init usb_mouse_init(void) {int retval = usb_register(& ...

  8. 十五、Linux驱动之USB鼠标驱动

    1. 如何编写USB鼠标驱动 结合十四.Linux驱动之USB驱动分析中的分析,我们开始写一个USB鼠标驱动.      USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动, ...

  9. linux3.4.2 之usb鼠标驱动,键盘驱动

    目录 1  USB相关基本知识 2  USB鼠标编程指导 3 USB鼠标驱动程序完整源码 4  USB鼠标驱动测试 5  USB键盘基本知识 6 USB键盘驱动程序 4  USB键盘驱动测试 1  U ...

  10. 【移植驱动到Linux3.4.2内核之二】LCD,触摸屏,按键,USB等驱动程序的移植心得总结

    学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 文章目录 一移植驱动程序心得体会 二移植LCD驱动程序记录 三移植按 ...

最新文章

  1. 干货!用大白话告诉你什么是Mock测试
  2. python解压zip文件_python-29 python解压压缩包的几种方法
  3. markdown简明语法
  4. 谷歌最强 NLP 模型 BERT 解读
  5. Visual Studio Code 配置指南
  6. 02- 流行歌曲 最新热门
  7. 一个Json在线格式化的网站
  8. 理解tcp关闭连接中的time_wait状态
  9. javascript核心_只需几分钟即可学习这些核心JavaScript概念
  10. 我来重新学习js的面向对象(part 4)
  11. C语言:替换字符串中某一段子字符串
  12. 机器学习算法_机器学习算法之PCA算法
  13. Android Browser学习七 书签历史模块: 书签UI的实现
  14. java压缩文件解压失败_java安装 解压缩核心文件失败
  15. 技巧:苹果电脑怎么清理缓存文件
  16. python怎么读取txt数据_对python .txt文件读取及数据处理方法总结
  17. App Links的使用以及坑
  18. python怎么爬虎牙_Python爬虫:爬取虎牙星秀主播图片
  19. week06_task_二分, 排序
  20. 易基因|Science:单细胞甲基化测序鉴定哺乳动物的新神经元亚型和调节元件

热门文章

  1. Android 仿微信二维码名片制作,生成二维码,扫码生成名片表单信息
  2. python爬虫模拟登录之图片验证码
  3. python xlwt_Python模块xlwt对excel进行写入操作
  4. 图灵 计算机 ppt,人工智能导论(ppt 155页)
  5. 周志华《机器学习》个人笔记
  6. wso2 mysql_windows下 WSO2 Application Server配置 及 MySQL数据服务部署
  7. java ftps_如何基于FTP4J实现FTPS连接过程解析
  8. ARQ协议与滑动窗口协议
  9. macOS linux 并发测试工具 wrk
  10. 记录自己装AMD黑苹果安装