前言在嵌入式行业,有很多从业者。我们工作的主旋律是拿开源代码,拿厂家代码,完成产品的功能,提升产品的性能,进而解决各种各样的问题。或者是主攻一个模块或方向,一搞就是好几年。 时间长了,笔者发现我们对从零开始编写驱动、应用、算法、系统、协议、文件系统等缺乏经验。没有该有的广度和深度。笔者也是这样,工作了很多年,都是针对某个问题点修修补补或者某个模块的局部删删改改。很少有机会去独自从零开始编写一整套完整的代码。 当然,这种现状对于企业来说是比较正常的,可以降低风险。但是对于员工本身,如果缺乏必要的规划,很容易工作多年却还是停留在单点的层面,而丧失了提升到较高层面的机会。随着时间的增长很容易丧失竞争力。 另外,根据笔者的经验,绝大多数公司对于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 0
staticint 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。 ---------------------------------------------------------------------------------------------------------------------- |