Linuxlinux驱动我用 Linux

linux驱动篇-usbmouse

2019-03-17  本文已影响4人  84f22431fa2a

前言

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

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

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

另外,根据笔者的经验,绝大多数公司对于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获取的信息 */
static char *usb_buf = NULL;
 
/* 2.4需要定义usb设备驱动支持哪些设备 */
struct usb_device_id usb_mouse_as_key_idtable []= {
       {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
              USB_INTERFACE_PROTOCOL_MOUSE)},
       {},
};
 
/* 3.1需要定义并注册中断处理函数 */
void usbmouse_askey_irq(struct urb *urb)
{
/* 3.2在中断处理函数中获取usb总线上的信息buf。
  * 3.3根据已经获取的buf信息,判断应该上报那类事件
  *(属于input子系统)
  */
}
 
 
/* 2.5需要定义probe函数来探测设备和分配相关资源,提交urb */
int usb_mouse_as_key_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
{
      
}
 
/* 2.6需要定义disconnect函数来和设备断开连接和清理资源 */
void usb_mouse_as_key_disconnect(struct usb_interface *intf)
{
 
}
 
/* 2.3需要定义usb设备驱动,结构体类型为struct usb_driver */
static struct usb_driver usb_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设备驱动 */
static int usb_mouse_as_key_init(void)
{
       usb_register(&usb_mouse_as_key);
       return 0;
}
 
/* 2.2需要能卸载usb设备驱动 */
static void usb_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 0
 
static int len = 0;
static char *usb_buf = NULL;
static dma_addr_t usbbuf_phyaddr;
static struct urb *usbmouse_askey_urb = NULL;
static struct input_dev *usbmouse_askey_inputdev = NULL;
 
/* 2.4定义usb设备驱动支持哪些设备 */
struct usb_device_id usb_mouse_as_key_idtable []= {
       {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
              USB_INTERFACE_PROTOCOL_MOUSE)},
       {},
};
 
/* 3.1需要定义和注册中断处理函数 */
void usbmouse_askey_irq(struct urb *urb)
{
#if USBMOUSE_ASKEY_DEBUG
       /* 3.3.0先观察usb_buf中会传来什么样的信息,
         * 可以分别测试滑动鼠标,滑动滚轮,
         * 按下鼠标左键或右键,按下滚轮
         */
        
       int i = 0;
       static int 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]);
       static int 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) */
int usb_mouse_as_key_probe(struct usb_interface *intf,
                const struct usb_device_id *id)
{
       struct usb_device *usbmouse_askey_usbdev = interface_to_usbdev(intf);
       struct usb_host_interface *interface;
       struct usb_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);
 
       return 0;
}
 
/* 2.6定义disconnect函数来和设备断开连接和清理资源 */
void usb_mouse_as_key_disconnect(struct usb_interface *intf)
{
       struct usb_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四个成员
  */
static struct usb_driver usb_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设备驱动 */
static int usb_mouse_as_key_init(void)
{
       usb_register(&usb_mouse_as_key);
       return 0;
}
 
/* 2.2在出口函数xxx_exit中卸载usb设备驱动 */
static void usb_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

KERN_DIR =  /home/linux/tools/linux-2.6.31_TX2440A
 
all:
       make -C $(KERN_DIR) M=`pwd` modules
clean:
       make -C $(KERN_DIR) M=`pwd` modules clean
       rm -rf modules.order
obj-m += usb_mouse_askey.o
obj-m += usb_mouse_askey_skeleton.o

目录结构

xxx@xxx:~/nfs/440/201903-train/12usb-mouse$ tree
.
├── Makefile
├── usb_mouse_askey.c
└── usb_mouse_askey_skeleton.c

测试步骤

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=2 192.168.0.105:/home/linux/nfs_root /mnt/nfs

6 insmod usbmouse_as_key.ko,会打印如下数据
[root@TX2440A 12usb-mouse]# insmod usb_mouse_askey.ko
found usbmouse
bcdUSB 0x110
VID 0x93aPID 0x2510
input: Unspecified device as /class/input/input1
usbcore: registered new interface driver usb_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 counts 860
usbbuf[0] 0x0 usbbuf[1] 0xfb usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 861
usbbuf[0] 0x0 usbbuf[1] 0xfb usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 862
usbbuf[0] 0x0 usbbuf[1] 0xfc usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 863
usbbuf[0] 0x0 usbbuf[1] 0xfe usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 864
usbbuf[0] 0x0 usbbuf[1] 0xfd usbbuf[2] 0xff usbbuf[3] 0x0 irq counts 865
usbbuf[0] 0x0 usbbuf[1] 0xfe usbbuf[2] 0xff usbbuf[3] 0x0 irq counts 866
usbbuf[0] 0x0 usbbuf[1] 0xfe usbbuf[2] 0xff usbbuf[3] 0x0 irq counts 867
usbbuf[0] 0x0 usbbuf[1] 0xfe usbbuf[2] 0xff usbbuf[3] 0x0 irq counts 868

当只滑动滚轮时,打印如下数据,请关注usbbuf[3]的变化
usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0xff irq counts 896
usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[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] 0x0 usbbuf[3] 0xff irq counts 899
usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0xff irq counts 900

当只按下鼠标左键时,打印如下数据,请关注usbbuf[0]的变化,按下时usbbuf[0]为1,松开时usbbuf[0]为0。
usbbuf[0] 0x1 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 1033
usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 1034

当只按下鼠标右键时,打印如下数据,请关注usbbuf[0]的变化,按下时usbbuf[0]为2,松开时usbbuf[0]为0
usbbuf[0] 0x2 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 1048
usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 1049

当只按下滚轮时,打印如下数据,请关注usbbuf[0]的变化,按下时usbbuf[0]为4,松开时usbbuf[0]为0
usbbuf[0] 0x4 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[3] 0x0 irq counts 1058
usbbuf[0] 0x0 usbbuf[1] 0x0 usbbuf[2] 0x0 usbbuf[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
更多原创文章请关注微信公众号

自顶向下学嵌入式公众号.jpg 赞赏码2.png
上一篇 下一篇

猜你喜欢

热点阅读