找回密码
 立即注册
Qt开源社区 门户 查看内容

linux驱动篇-usbmouse

2019-3-17 11:36| 发布者: admin| 查看: 1381| 评论: 0

摘要: 前言在嵌入式行业,有很多从业者。我们工作的主旋律是拿开源代码,拿厂家代码,完成产品的功能,提升产品的性能,进而解决各种各样的问题。或者是主攻一个模块或方向,一搞就是好几年。时间长了,笔者发现我们对从零 ...

前言


在嵌入式行业,有很多从业者。我们工作的主旋律是拿开源代码,拿厂家代码,完成产品的功能,提升产品的性能,进而解决各种各样的问题。或者是主攻一个模块或方向,一搞就是好几年。

时间长了,笔者发现我们对从零开始编写驱动、应用、算法、系统、协议、文件系统等缺乏经验。没有该有的广度和深度。笔者也是这样,工作了很多年,都是针对某个问题点修修补补或者某个模块的局部删删改改。很少有机会去独自从零开始编写一整套完整的代码。

当然,这种现状对于企业来说是比较正常的,可以降低风险。但是对于员工本身,如果缺乏必要的规划,很容易工作多年却还是停留在单点的层面,而丧失了提升到较高层面的机会。随着时间的增长很容易丧失竞争力。

另外,根据笔者的经验,绝大多数公司对于0-5年经验从业者的定位主要是积极的问题解决者。而对于5-10经验从业者的定位主要是积极的系统规划者和引领者。在这种行业规则下,笔者认为,每个从业者都应该问自己一句,“5年后,我是否具备系统化把控软件的能力呢?”。

当前的这种行业现状,如果我们不做出一点改变,是没有办法突破的。有些东西,仅仅知道是不够的,还需要深思熟虑的思考和必要的训练,简单来说就是要知行合一。

也许有读者会有疑惑?这不就是重复造轮子么?我们确实是在重复造轮子,因为别人会造轮子那是别人的能力,我们自己会造轮子是我们自己的能力。在行业中,有太多的定制化需求是因为轮子本身有原生性缺陷,我们无法直接使用,或者需要对其进行改进,或者需要抽取开源代码的主体思想和框架,根据公司的需要定制自己的各项功能。设想,如果我们具备这种能力,必然会促使我们在行业中脱颖而出,而不是工作很多年一直在底层搬砖。底层搬砖没什么不好,问题是当有更廉价更激情的劳动力涌进来的时候,我们这些老的搬砖民工也就失去了价值。我们不会天天重复造轮子,我们需要通过造几个轮子使得自己具备造轮子的能力,从而更好的适应这个环境,适应这个世界。

针对当前行业现状,笔者经过深思熟虑,想为大家做点实实在在的事情,希望能够帮助大家在巩固基础的同时提升系统化把控软件的能力。当然,笔者的水平也有限,有些观点也只是一家之谈,希望大家独立思考,谨慎采用,如果写的有错误或者不对的地方还请读者们批评斧正,我们一起共同进步。

在这里简单介绍下笔者,笔者现在就职于一家大型国际化公司,工作经验6年,硕士毕业。曾经担任过组内的项目主管,项目经理,也曾经组建过新团队,带领大家冲锋陷阵。在工作中,有做的不错的地方,也有失误的地方,有激情的时刻,也有失落的时刻。现在偏安一隅,专心搞技术,目前个人规划的技术方向是嵌入式和AI基础设施建设,以及嵌入式和AI的融合发展。

最后,说了这么多,笔者希望,在未来的日子里和未知的领域里,你我同行,为我们的美好生活而努力奋斗。

总体目标


本篇文章的目标是介绍如何从自顶向下从零编写linux下的usbmouse_key驱动。着力从总体思路,需求端,分析端,实现端,详尽描述一个完整需求的开发流程,是笔者多年经验的提炼,希望读者能够有所收获。最后的实战目标,请读者尽量完成,这样读者才能形成自己的思路。

本示例采用arm920架构,天祥电子生产的tx2440a开发板,核心为三星的s3c2440。Linux版本为2.6.31,是已经移植好的版本。编译器为arm920t-eabi-4.1.2.tar。

总体思路


总体思路是严格遵循需求的开发流程来,不遗漏任何思考环节。读者在阅读时请先跟笔者的思路走一遍,然后再抛弃笔者的思路,按照自己的思路走一遍,如果遇到困难请先自己思考,实在不会再来参考笔者的思路和实现。

笔者在写代码的的总体思路如下:

需求描述—能够详细完整的描述一个需求。

需求分析—根据需求描述,提取可供实现的功能,需要有定量或者定性的指标。(从宏观上确定需要什么功能)。

需求分解—根据需求分析,考虑要实现需求所需要做的工作(根据宏观确定的功能,拆分成小的可单独实现的功能)。

编写思路—根据需求分解从总体上描述应该如何编写代码,(解决怎么在宏观上实现)。

详细步骤—根据编写思路,落实具体步骤,(解决怎么在微观上实现)。

编写框架—根据编写思路,实现总体框架(实现编写思路里主体框架,细节内容留在具体代码里编写)。

具体代码—根据编写框架,编写每一个函数里所需要实现的小功能,主要是实现驱动代码,测试代码

Makefile—用来编译驱动代码

目录结构—用来说明当完成编码后的结果

测试步骤—说明如何对驱动进行测试,主要是加载驱动模块,执行测试代码

执行结果—观察执行结果是否符合预期

结果总结—回顾本节的思路,知识点,api,结构体

实战目标—说明如何根据本文档训练

需求描述


编写usbmouse_key驱动达到以下效果:

能够使用usb鼠标来模拟ls命令,要求左键模拟l键,右键模拟s键,滚轮模拟enter键,达到的效果就和用键盘输入ls+enter一样。

需求分析


要分析出需要实现哪些功能,经过分析,可以得出需要实现以下功能。

1插上usb鼠标之后,需要usb鼠标的设备节点,用来被用户操作

2在用户按下按键时需要能够获取按键信息

3获取完信息后需要能够识别是哪种信息,进而对该信息作出反应

比如按下左键,驱动需要提取信息,识别信息为左键,然后输出到屏幕

比如按下enter,驱动需要提取信息,识别信息为enter按键,然后输出回车

需求分解


根据需求分析的结果,分解出需要实现的功能

1在usb鼠标插上hub的时候创建设备节点

2驱动能够提取usb总线上的信息并存起来

3驱动能够识别提取的信息作为输入,并根据输入作出相应的反应

编写思路


1在usb鼠标插上hub的时候创建设备节点,这个功能总线会自动完成。

2提取usb总线上的信息并存起来。步骤如下

2.1需要能注册usb设备驱动

2.2需要能卸载usb设备驱动

2.3需要定义usb设备驱动,结构体类型为struct usb_driver

2.4需要定义usb设备驱动支持哪些设备

2.5需要定义probe函数来探测设备和分配相关资源,提交urb

2.6需要定义disconnect函数来和设备断开连接和清理资源

2.7需要定义buf缓冲区存放usb获取的信息

3识别提取的信息并作出反应,步骤如下

3.1需要定义并注册中断处理函数

3.2在中断处理函数中获取usb总线上的信息buf。

3.3根据已经获取的buf信息,判断应该上报那类事件(属于input子系统)

详细步骤


根据需求分解的结果,需要逐步逐条实现所需要的结构体和函数。

0首先创建usb_mouse_askey.c和Makefile,Makefile的编写请参考《Makefile》章节。

1在usb鼠标插上hub的时候创建设备节点,这个功能总线会自动完成。

2提取usb总线上的信息并存起来。步骤如下

2.0搭建代码框架,拷贝头文件(usb-skeleton.c),编写和修饰出口函数和入口函数,声明LICENSE

头文件,xxx_init函数,xxx_exit函数,module_init(),module_exit(),MODULE_LICENSE(“GPL”);

2.1在入口函数xxx_init中注册usb设备驱动

usb_register(&usb_mouse_as_key);

2.2在出口函数xxx_exit中卸载usb设备驱动

usb_deregister(&usb_mouse_as_key);

2.3定义静态usb设备驱动结构体,主要是填充name,probe,disconnect,id_table四个成员

static struct usb_driver usb_mouse_as_key

2.4定义usb设备驱动支持哪些设备

实现usb_mouse_as_key中的id_table

2.5定义probe函数来探测设备和分配相关资源(关键目标是注册input_dev和提交urb)

实现usb_mouse_as_key中的probe函数

2.5.1分配一个input_dev(因为中断中要向input子系统上报事件,所以必须要有input device)

2.5.2设置可以产生哪类事件,设置可以产生哪些事件

2.5.3注册input_dev

2.5.4申请usb_buf内存,申请urb,填充urb,提交urb

2.6定义disconnect函数来和设备断开连接和清理资源

实现usb_mouse_as_key中的disconnect函数

2.6.1杀掉urb,释放urb,释放usb_buf

2.6.2卸载input_dev,释放input_dev所占用的内存空间

2.7定义buf缓冲区存放usb获取的信息

存放usb获取的信息就是usb_buf,因此这一步需要在2.5.4中去做

3识别提取的信息并作出反应,步骤如下

3.1需要定义和注册中断处理函数

这一步是在2.5.4中填充urb中做的

3.2在中断处理函数中获取的usb总线上的信息usb_buf

可以把usb_buf定义成静态全局变量,直接在中断函数中使用

3.3根据已经获取的usb_buf信息,判断应该上报那类事件(属于input子系统)

3.3.0先观察usb_buf中会传来什么样的信息,可以分别测试滑动鼠标,滑动滚轮,按下鼠标左键或右键,按下滚轮

3.3.1根据已经获取的buf信息,判断哪个按键发生了变化

3.3.2根据发生的变化,确定应该上报哪类事件中的哪个事件

3.3.3重新提交urb,以便于获取下一次usb_buf中的信息

编写框架


首先根据编写思路编写总体框架代码,先将总体的结构搭建起来。然后再在框架代码里编写比较具体的代码。
    usb_mouse_askey_skeleton.c/* 本文件是依照usbmouse-key驱动编写思路章节编写,本文件 * 的目的是编写代码框架,不做具体细节的编写 *//* 本头文件是linux2.6.31内核所提供的,其他版本按需调整 *//** drivers\hid\usbhid\usbmouse.c 参考代码*/#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>/* 2.7需要定义buf缓冲区存放usb获取的信息 */staticchar *usb_buf = NULL;/* 2.4需要定义usb设备驱动支持哪些设备 */structusb_device_idusb_mouse_as_key_idtable []= { {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE)}, {},};/* 3.1需要定义并注册中断处理函数 */voidusbmouse_askey_irq(struct urb *urb){/* 3.2在中断处理函数中获取usb总线上的信息buf。 * 3.3根据已经获取的buf信息,判断应该上报那类事件 *(属于input子系统) */}/* 2.5需要定义probe函数来探测设备和分配相关资源,提交urb */intusb_mouse_as_key_probe(struct usb_interface *intf,const struct usb_device_id *id){}/* 2.6需要定义disconnect函数来和设备断开连接和清理资源 */voidusb_mouse_as_key_disconnect(struct usb_interface *intf){}/* 2.3需要定义usb设备驱动,结构体类型为struct usb_driver */staticstructusb_driverusb_mouse_as_key = { .name = "usb_mouse_as_key", .probe = usb_mouse_as_key_probe, .disconnect = usb_mouse_as_key_disconnect, .id_table = &usb_mouse_as_key_idtable,};/* 2.1需要能注册usb设备驱动 */staticintusb_mouse_as_key_init(void){ usb_register(&usb_mouse_as_key);return0;}/* 2.2需要能卸载usb设备驱动 */staticvoidusb_mouse_as_key_exit(void){ usb_deregister(&usb_mouse_as_key);}/* 修饰入口函数 */module_init(usb_mouse_as_key_init);/*修饰出口函数*/module_exit(usb_mouse_as_key_exit);MODULE_LICENSE("GPL");


    驱动代码

      usb_mouse_askey.c/* 本文件是依照usbmouse-key驱动详细步骤章节编写,本文件 * 的目的是编写和介绍具体代码,不介绍框架 *//* 本头文件是linux2.6.31内核所提供的,其他版本按需调整 */
      /* 2.0搭建代码框架,拷贝头文件(参考usb-skeleton.c), * 编写和修饰出口函数和入口函数,声明LICENSE *//** drivers\hid\usbhid\usbmouse.c 参考代码*/#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 USBMOUSE_ASKEY_DEBUG 0staticint len = 0;staticchar *usb_buf = NULL;staticdma_addr_t usbbuf_phyaddr;staticstructurb *usbmouse_askey_urb = NULL;staticstructinput_dev *usbmouse_askey_inputdev = NULL;/* 2.4定义usb设备驱动支持哪些设备 */structusb_device_idusb_mouse_as_key_idtable []= { {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE)}, {},};/* 3.1需要定义和注册中断处理函数 */voidusbmouse_askey_irq(struct urb *urb){#if USBMOUSE_ASKEY_DEBUG/* 3.3.0先观察usb_buf中会传来什么样的信息, * 可以分别测试滑动鼠标,滑动滚轮, * 按下鼠标左键或右键,按下滚轮 */int i = 0;staticint cnt = 0;for (;i < len; i++) { printk("usbbuf[%d] 0x%x\t",i,usb_buf[i]); } printk("irq counts %d\n",cnt++); usb_submit_urb(usbmouse_askey_urb,GFP_KERNEL);#else//printk("usbbuf[0] 0x%x\t",usb_buf[0]);staticint pre_val;/* 3.2在中断处理函数中获取的usb总线上的信息usb_buf *//* 3.3根据已经获取的usb_buf信息,判断应该上报那类事件(属于input子系统) *//* 3.3.1根据已经获取的buf信息,判断哪个按键发生了变化 */if ((pre_val & (1 << 0)) != (usb_buf[0] & (1 << 0))) {/* 3.3.2根据发生的变化,确定应该上报哪类事件中的哪个事件 */ input_event(usbmouse_askey_inputdev,EV_KEY, KEY_L, (usb_buf[0] & (1 << 0)) ? 1:0);/* 上报同步事件 */ input_sync(usbmouse_askey_inputdev); }if ((pre_val & (1 << 1)) != (usb_buf[0] & (1 << 1))) { input_event(usbmouse_askey_inputdev,EV_KEY, KEY_S, (usb_buf[0] & (1 << 1)) ? 1:0); input_sync(usbmouse_askey_inputdev); }if ((pre_val & (1 << 2)) != (usb_buf[0] & (1 << 2))) { input_event(usbmouse_askey_inputdev,EV_KEY, KEY_ENTER, (usb_buf[0] & (1 << 2)) ? 1:0); input_sync(usbmouse_askey_inputdev); }/* 保存当前的数据,便于下次比较*/ pre_val = usb_buf[0];/* 3.3.3重新提交urb,以便于获取下一次usb_buf中的信息 */ usb_submit_urb(usbmouse_askey_urb, GFP_KERNEL);#endif}/* 2.5定义probe函数来探测设备和分配相关资源(关键目标是注册input_dev和提交urb) */intusb_mouse_as_key_probe(struct usb_interface *intf,const struct usb_device_id *id){structusb_device *usbmouse_askey_usbdev = interface_to_usbdev(intf);structusb_host_interface *interface;structusb_endpoint_descriptor *endpoint;int pipe; printk("found usbmouse\n"); printk("bcdUSB 0x%x\n",usbmouse_askey_usbdev->descriptor.bcdUSB); printk("VID 0x%x",usbmouse_askey_usbdev->descriptor.idVendor); printk("PID 0x%x",usbmouse_askey_usbdev->descriptor.idProduct);/* 2.5.1分配一个input_dev */ usbmouse_askey_inputdev = input_allocate_device();/* 2.5.2设置可以产生哪类事件 */ set_bit(EV_KEY,usbmouse_askey_inputdev->evbit); set_bit(EV_REL,usbmouse_askey_inputdev->evbit);/* 2.5.2设置可以产生哪些事件 */ set_bit(KEY_L,usbmouse_askey_inputdev->keybit); set_bit(KEY_S,usbmouse_askey_inputdev->keybit); set_bit(KEY_ENTER,usbmouse_askey_inputdev->keybit);/* 2.5.3注册input_dev */ input_register_device(usbmouse_askey_inputdev);/* 2.5.4申请usb_buf内存,申请urb,填充urb,提交urb *//* 获取当前的逻辑接口 */ interface = intf->cur_altsetting;/* 获取当前接口的控制端点描述符 */ endpoint = &interface->endpoint[0].desc;/* 获取填充urb所需要的参数,原地址,目的地址,数据长度*//* pipe表示原地址 */ pipe = usb_rcvintpipe(usbmouse_askey_usbdev,endpoint->bEndpointAddress);/* len表示数据的最大长度 */ len = endpoint->wMaxPacketSize;/* usb_buf表示目的地址*/ usb_buf = usb_buffer_alloc(usbmouse_askey_usbdev, len, GFP_ATOMIC, &usbbuf_phyaddr);/* 申请urb usb request block */ usbmouse_askey_urb = usb_alloc_urb(0,GFP_KERNEL);/* 填充urb */ usb_fill_int_urb(usbmouse_askey_urb, usbmouse_askey_usbdev, pipe, usb_buf, len, usbmouse_askey_irq, NULL, endpoint->bInterval); usbmouse_askey_urb->transfer_dma = usbbuf_phyaddr; usbmouse_askey_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* 提交urb */ usb_submit_urb(usbmouse_askey_urb, GFP_KERNEL);return0;}/* 2.6定义disconnect函数来和设备断开连接和清理资源 */voidusb_mouse_as_key_disconnect(struct usb_interface *intf){structusb_device *usbmouse_askey_usbdev = interface_to_usbdev(intf);/* 2.6.1杀掉urb,释放urb,释放usb_buf */ usb_kill_urb(usbmouse_askey_urb); usb_free_urb(usbmouse_askey_urb); usb_buffer_free(usbmouse_askey_usbdev, len,usb_buf,usbbuf_phyaddr);/* 2.6.2卸载input_dev,释放input_dev所占用的内存空间 */ input_unregister_device(usbmouse_askey_inputdev); input_free_device(usbmouse_askey_inputdev);}/* 2.3定义静态usb设备驱动结构体,主要是填充name, * probe,disconnect,id_table四个成员 */staticstructusb_driverusb_mouse_as_key = { .name = "usb_mouse_as_key", .probe = usb_mouse_as_key_probe, .disconnect = usb_mouse_as_key_disconnect, .id_table = &usb_mouse_as_key_idtable,};/* 2.1在入口函数xxx_init中注册usb设备驱动 */staticintusb_mouse_as_key_init(void){ usb_register(&usb_mouse_as_key);return0;}/* 2.2在出口函数xxx_exit中卸载usb设备驱动 */staticvoidusb_mouse_as_key_exit(void){ usb_deregister(&usb_mouse_as_key);}/* 2.0搭建代码框架,拷贝头文件(参考usb-skeleton.c), * 编写和修饰出口函数和入口函数,声明LICENSE */module_init(usb_mouse_as_key_init);module_exit(usb_mouse_as_key_exit);MODULE_LICENSE("GPL");

      测试代码


      本示例无需测试代码,只需要滑动鼠标,滑动滚轮,按下鼠标左键,按下鼠标右键,按下滚轮即可以测试。

      Makefile


      目录结构


      测试步骤


      0 在linux下的makefile +180行处配置好arch为arm,cross_compile为arm-linux-(或者arm-angstrom-linux-gnueabi-)

      1在menuconfig中配置好内核源码的目标系统为s3c2440

      2 linux默认是有编译进hid模块的,如果我们的驱动也加载进去,usb鼠标的数据默认是会传给系统默认的usb鼠标驱动,因此需要在menuconfig中去掉原生的usb鼠标驱动

      make menuconfig去掉原来的USB鼠标驱动

      -> Device Drivers

       -> HID Devices

       <> USB Human Interface Device (full HID) support

      如果读者使用的是别的版本的内核,请搜索Kconfig里的USB_HID找到tristate变量“”,比如3.18.20内核的在drivers/hid/usbhid/Kconfig中,内容为

      config USB_HID

        tristate "USB HID transport layer"

      然后再找到menuconfig中对应的"USB HID transport layer"选项,取消选中即可。

      3 make uImage 生成uImage,并使用新的内核启动

      4 在pc上将驱动程序编译生成.ko,命令:make

      5 挂载nfs,这样就可以在开发板上看到pc端的.ko文件和测试文件

      mount -t nfs -o nolock,vers=2192.168.0.105:/home/linux/nfs_root /mnt/nfs

      6 insmod usbmouse_as_key.ko,会打印如下数据

      [root@TX2440A 12usb-mouse]# insmodusb_mouse_askey.ko

      found usbmouse

      bcdUSB 0x110

      VID 0x93aPID 0x2510

      input: Unspecified device as/class/input/input1

      usbcore: registered new interface driverusb_mouse_as_key

      7在开发板上接入、拔出USB鼠标,滑动鼠标或者滚轮,按下鼠标按键或滚轮

      执行结果


      当打开USBMOUSE_ASKEY_DEBUG宏时,执行结果如下所示

      当只滑动鼠标时,打印如下数据。请关注usbbuf[1],usbbuf[2]的变化

      usbbuf[0] 0x0 usbbuf[1] 0xfb       usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts860

      usbbuf[0] 0x0 usbbuf[1] 0xfb       usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts861

      usbbuf[0] 0x0 usbbuf[1] 0xfb       usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts862

      usbbuf[0] 0x0 usbbuf[1] 0xfc       usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts863

      usbbuf[0] 0x0 usbbuf[1] 0xfe       usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts864

      usbbuf[0] 0x0 usbbuf[1] 0xfd       usbbuf[2] 0xff usbbuf[3] 0x0 irq counts865

      usbbuf[0] 0x0 usbbuf[1] 0xfe       usbbuf[2] 0xff usbbuf[3] 0x0 irq counts866

      usbbuf[0] 0x0 usbbuf[1] 0xfe       usbbuf[2] 0xff usbbuf[3] 0x0 irq counts867

      usbbuf[0] 0x0 usbbuf[1] 0xfe       usbbuf[2] 0xff usbbuf[3] 0x0 irq counts868

      当只滑动滚轮时,打印如下数据,请关注usbbuf[3]的变化

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0xff irq counts 896

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x1 irq counts 897

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3]0x1 irq counts 898

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0xff irq counts 899

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0xff irq counts 900

      当只按下鼠标左键时,打印如下数据,请关注usbbuf[0]的变化,按下时usbbuf[0]为1,松开时usbbuf[0]为0。

      usbbuf[0] 0x1 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x0 irq counts 1033

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x0 irq counts 1034

      当只按下鼠标右键时,打印如下数据,请关注usbbuf[0]的变化,按下时usbbuf[0]为2,松开时usbbuf[0]为0

      usbbuf[0] 0x2 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x0 irq counts 1048

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x0 irq counts 1049

      当只按下滚轮时,打印如下数据,请关注usbbuf[0]的变化,按下时usbbuf[0]为4,松开时usbbuf[0]为0

      usbbuf[0] 0x4 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x0 irq counts 1058

      usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0usbbuf[3] 0x0 irq counts 1059

      通过上述测试,发现usbbuf[0]中存放着按键是否按下,是哪个按键按下的数据。

      当关闭USBMOUSE_ASKEY_DEBUG宏时,执行结果如下所示

      [root@TX2440A 12usb-mouse]# cat /dev/tty1

      llssl

      ls

      lll

      sss

      在按下左键或者右键时没有数据输出,需要按下滚轮才有数据输出。

      结果总结


      在本篇文章中,笔者跟读者分享了usbmouse驱动的编写思路和方法,其中贯穿始终的有几个函数和关键数据结构,它们分别是:

      struct usb_device

      struct usb_host_interface

      struct usb_endpoint_descriptor

      input_allocate_device

      input_register_device

      set_bit

      input_event

      input_sync

      usb_rcvintpipe

      usb_buffer_alloc

      usb_alloc_urb

      usb_fill_int_urb

      usb_submit_urb

      usb_kill_urb

      usb_free_urb

      usb_buffer_free

      input_unregister_device

      input_free_device

      usb_register

      usb_deregister

      请读者尽力去了解这些函数的作用,入参,返回值。

      实战目标


      1请读者根据《需求描述》章节,独立编写需求分析和需求分解。

      2请读者根据需求分析和需求分解,独立编写编写思路和详细步骤。

      3请读者根据编写思路,独立写出编写框架。

      4请读者根据详细步骤,独立编写驱动代码和测试代码。

      5请读者根据《Makefile》章节,独立编写Makefile。

      6请读者根据《测试步骤》章节,独立进行测试。

      7请读者抛开上述练习,自顶向下从零开始再编写一遍驱动代码,测试代码,makefile

      8如果无法独立写出7,请重复练习1-6,直到能独立写出7。

      参考资料


      《linux设备驱动开发祥解》

      《TX2440开发手册及代码》

      致谢


      感谢在嵌入式领域深耕多年的前辈,感谢笔者的家人,感谢读者。没有前辈们的开拓,我辈也不能站在巨人的肩膀上看世界;没有家人的鼎力支持,我们也不能集中精力完成自己的工作;没有读者的关注,我们也没有充足的动力来编写和完善文章。希望读者学到的不仅仅是如何编写代码,更进一步能够学到一种思路和一种方法。

      笔者后续有计划按照本模板编写linux下的常见驱动,敬请读者关注。

      联系方式


      微信订阅号:自顶向下学嵌入式

      公众号微信:EmbeddedAIOT

      CSDN博客:chichi123137

      CSDN博客网址:https://blog.csdn.net/chichi123137?utm_source=blog_pc_recommand

      QQ邮箱:834759803@qq.com

      QQ群:766756075




      ----------------------------------------------------------------------------------------------------------------------
      我们尊重原创,也注重分享,文章来源于微信公众号:自顶向下学嵌入式,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
      ----------------------------------------------------------------------------------------------------------------------

      鲜花

      握手

      雷人

      路过

      鸡蛋
      
      公告
      可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了