Linux-写USB键盘驱动(详解)

1.首先我们通过上节的代码中修改,来打印下键盘驱动的数据到底是怎样的

先来回忆下,我们之前写的鼠标驱动的id_table是这样:

 

所以我们要修改id_table,使这个驱动为键盘的驱动,如下图所示:

 

然后修改中断函数,通过printk()打印数据:

我们先按下按键A为例,打印出0x04,如下图:

 

 

我们再同时按下按键A和S,打印出0x04,0X16, 如下图:

 

显然这些普通按键都是从buf[2]开始的,那第一个数组到底又存什么值?

我们按完所有键盘按键,发现只有8个按键会打印在buf[0]里,如下图所示:

 

所以buf[0]是用来保存键盘的特定功能的键,而buf[1]可能是个保留键,没有用到的,buf[2]~buf[7]是普通按键,比如ABCD,1234,F1,F2等等,能支持最多6个按键同时按下。

2.那么每个按键的数据又是怎么定义的?

2.1比如我们按下按键A,为什么打印0X04?

我们找到输入子系统(input.h)中按键A定义的值,它对应的却是30,看来不是直接调用的,如下图:

 

 

我们再来参考内核自带的USB键盘驱动 (/drivers/hid/usbhid/usbkbd.c)

发现它的中断函数中有个键盘描述码表(其中0表示保留的意思):

 

 

 

发现该数组的0X04就是0X30,看来要写个键盘驱动,还需要上面的数组才行.

那么问题又来了,如果我们按下左alt键,buf[0]中会出现0x04,如果也代入到键盘描述码表中,显然就会当作键盘按键A来使用。

2.2我们来分析内核的键盘中断函数是如何处理的:

发现有这么一句:

for (i = 0; i < 8; i++)input_report_key(kbd->dev, usb_kbd_keycode[i+ 224], (kbd->new[0] >> i) & 1);

 

其中kbd->new表示的就是键盘数据数组,它将buf[0]的每一位通通以usb_kbd_keycode[i+ 224]的形式上传到按键事件中

显然我们的buf[0]的0X04就是上传的usb_kbd_keycode[4+ 224]

2.3我们来看看usb_kbd_keycode[226]里的数据对应的到底是不是左ALT键

找到usb_kbd_keycode[226]=56:

 

然后再进入input.h,找到56的定义,刚好就是KEY_LEFTALT(左边的alt键)

 

 

3.接下来再来仔细分析下内核自带的USB键盘驱动usbkbd.c里的中断函数:

代码如下:

 

static void usb_kbd_irq(struct urb *urb)
{struct usb_kbd *kbd = urb->context;int i;switch (urb->status) {                        // 只有urb->status==0时,说明数据传输成功case 0:                  /* success */break;case -ECONNRESET:     /* unlink */case -ENOENT:case -ESHUTDOWN:return;/* -EPIPE:  should clear the halt */default:          /* error */goto resubmit;}for (i = 0; i < 8; i++)                           //上传crtl、shift、atl、windows 等按键input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);for (i = 2; i < 8; i++) {                  //上传普通按键/*通过上个状态的按键数据kbd->old[i]的非0值,来查找当前状态的按键数据,若没有找到,说明已经松开了该按键 */if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {if (usb_kbd_keycode[kbd->old[i]])              //再次判断键盘描述码表的值是否非0input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); //上传松开事件elseinfo("Unknown key (scancode %#x) released.", kbd->old[i]);}/*通过当前状态的按键数据kbd->new[i]的非0值,来查找上个状态的按键数据,若没有找到,说明已经按下了该按键 */if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {if (usb_kbd_keycode[kbd->new[i]]) //再次判断键盘描述码表的值是否非0input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);      //上传按下事件elseinfo("Unknown key (scancode %#x) pressed.", kbd->new[i]);}}input_sync(kbd->dev); memcpy(kbd->old, kbd->new, 8);                     //更新上个状态值 
resubmit:i = usb_submit_urb (urb, GFP_ATOMIC);if (i)err ("can't resubmit intr, %s-%s/input0, status %d",kbd->usbdev->bus->bus_name,kbd->usbdev->devpath, i);
}

 

3.1上面获取普通按键时,为什么不直接判断非0,要判断按键数据> 3?

之前我们就分析了,当按键数据=0X01、0X02时,代表的是特定功能的键(crtl、shift),是属于buf[0]的数据

其中memscan()是用来匹配上次按键和当前按键的数据,它这么做的原因是怕上个buf[]和当前buf[]的数据错位,这里就不做详细分析了

一切迎刃而解,我们只需要将自己的代码也通过这个码表添加所有按键按键事件,然后再在键盘中断函数中根据数据来上传事件即可

4.本节键盘代码如下:

 

#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>static struct input_dev *myusb_kbd_dev;           //input_dev
static unsigned char *myusb_kbd_buf;                //虚拟地址缓存区
static dma_addr_t myusb_kbd_phyc;                  //DMA缓存区;static __le16 myusb_kbd_size;                            //数据包长度
static struct urb  *myusb_kbd_urb;                     //urbstatic const unsigned char usb_kbd_keycode[252] = {0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,150,158,159,128,136,177,178,176,142,152,173,140
};       //键盘码表共有252个数据void my_memcpy(unsigned char *dest,unsigned char *src,int len)      //复制缓存
{while(len--){*dest++= *src++;}
}static void myusb_kbd_irq(struct urb *urb)               //键盘中断函数
{static unsigned char buf1[8]={0,0,0,0,0,0,0,0};int i;/*上传crtl、shift、atl、windows 等按键*/for (i = 0; i < 8; i++)if(((myusb_kbd_buf[0]>>i)&1)!=((buf1[0]>>i)&1)){    input_report_key(myusb_kbd_dev, usb_kbd_keycode[i + 224], (myusb_kbd_buf[0]>> i) & 1);input_sync(myusb_kbd_dev);             //上传同步事件}/*上传普通按键*/for(i=2;i<8;i++)if(myusb_kbd_buf[i]!=buf1[i]){if(myusb_kbd_buf[i] )      //按下事件input_report_key(myusb_kbd_dev,usb_kbd_keycode[myusb_kbd_buf[i]], 1);   else  if(buf1[i])                                             //松开事件input_report_key(myusb_kbd_dev,usb_kbd_keycode[buf1[i]], 0);input_sync(myusb_kbd_dev);             //上传同步事件}my_memcpy(buf1, myusb_kbd_buf, 8);       //更新数据    usb_submit_urb(myusb_kbd_urb, GFP_KERNEL);
}static int myusb_kbd_probe(struct usb_interface *intf, const struct usb_device_id *id)
{volatile unsigned char  i;struct usb_device *dev = interface_to_usbdev(intf);                 //设备struct usb_endpoint_descriptor *endpoint;                            struct usb_host_interface *interface;                                              //当前接口int pipe;                                                                               //端点管道interface=intf->cur_altsetting;                                                                   endpoint = &interface->endpoint[0].desc;                                    //当前接口下的端点描述符printk("VID=%x,PID=%x\n",dev->descriptor.idVendor,dev->descriptor.idProduct);   /*   1)分配一个input_dev结构体  */myusb_kbd_dev=input_allocate_device();/*   2)设置input_dev支持 按键事件*/set_bit(EV_KEY, myusb_kbd_dev->evbit);set_bit(EV_REP, myusb_kbd_dev->evbit);        //支持重复按功能for (i = 0; i < 252; i++)set_bit(usb_kbd_keycode[i], myusb_kbd_dev->keybit);     //添加所有键clear_bit(0, myusb_kbd_dev->keybit);/*   3)注册input_dev结构体*/input_register_device(myusb_kbd_dev);/*   4)设置USB键盘数据传输 *//*->4.1)通过usb_rcvintpipe()创建一个端点管道*/pipe=usb_rcvintpipe(dev,endpoint->bEndpointAddress); /*->4.2)通过usb_buffer_alloc()申请USB缓冲区*/myusb_kbd_size=endpoint->wMaxPacketSize;myusb_kbd_buf=usb_buffer_alloc(dev,myusb_kbd_size,GFP_ATOMIC,&myusb_kbd_phyc);/*->4.3)通过usb_alloc_urb()和usb_fill_int_urb()申请并初始化urb结构体 */myusb_kbd_urb=usb_alloc_urb(0,GFP_KERNEL);usb_fill_int_urb (myusb_kbd_urb,              //urb结构体dev,                                       //usb设备pipe,                                      //端点管道myusb_kbd_buf,               //缓存区地址myusb_kbd_size,              //数据长度myusb_kbd_irq,               //中断函数0,endpoint->bInterval);              //中断间隔时间/*->4.4) 因为我们2440支持DMA,所以要告诉urb结构体,使用DMA缓冲区地址*/myusb_kbd_urb->transfer_dma   =myusb_kbd_phyc;                  //设置DMA地址myusb_kbd_urb->transfer_flags   =URB_NO_TRANSFER_DMA_MAP;     //设置使用DMA地址/*->4.5)使用usb_submit_urb()提交urb*/usb_submit_urb(myusb_kbd_urb, GFP_KERNEL);   return 0;
}static void myusb_kbd_disconnect(struct usb_interface *intf)
{struct usb_device *dev = interface_to_usbdev(intf);        //设备usb_kill_urb(myusb_kbd_urb);usb_free_urb(myusb_kbd_urb);usb_buffer_free(dev, myusb_kbd_size, myusb_kbd_buf,myusb_kbd_phyc);input_unregister_device(myusb_kbd_dev);               //注销内核中的input_devinput_free_device(myusb_kbd_dev);                        //释放input_dev
}static struct usb_device_id myusb_kbd_id_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,                      //接口类:hid类USB_INTERFACE_SUBCLASS_BOOT,             //子类:启动设备类USB_INTERFACE_PROTOCOL_KEYBOARD) }, //USB协议:键盘协议
};static struct usb_driver myusb_kbd_drv = {.name            = "myusb_kbd",.probe           = myusb_kbd_probe,                        .disconnect     = myusb_kbd_disconnect,.id_table  = myusb_kbd_id_table,
};/*入口函数*/
static int myusb_kbd_init(void)
{ usb_register(&myusb_kbd_drv);return 0;
}/*出口函数*/
static void myusb_kbd_exit(void)
{usb_deregister(&myusb_kbd_drv);
}module_init(myusb_kbd_init);
module_exit(myusb_kbd_exit);
MODULE_LICENSE("GPL");

 

 

5.测试运行

5.1 重新设置编译内核(去掉默认的hid_USB驱动)

make menuconfig ,进入menu菜单重新设置内核参数:

进入-> Device Drivers -> HID Devices 

<> USB Human Interface Device (full HID) support     //hid:人机交互的USB驱动,比如鼠标,键盘等

然后make uImage 编译内核

将新的键盘驱动模块放入nfs文件系统目录中

5.2然后烧写内核,装载触摸屏驱动模块

如下图,当我们插上USB键盘时,可以看到该VID和PID,和电脑上的键盘的参数一样

 

 

5.3使用cat  tty1进程测试

 

 

5.4 使用exec 0</dev/tty1测试

 

如下图,就能通过板子上的键盘来操作了

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1549728.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v7.0版已发布

关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架&#xff0c;超轻量级、高度提炼&#xff0c;一套API优雅支持 UDP 、TCP 、WebSocket 三种协议&#xff0c;支持 iOS、Android、H5、标准Java、小程序、Uniapp&#xff0c;服务端基于Netty编写。 工程…

基于swing的教务管理系统java jsp学生教师信息mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的教务管理系统 系统有3权限&#xff1a;管…

推荐百度的八个不常用产品

大家最常用的百度产品就是搜索&#xff08;尤其是mp3搜索&#xff09;、贴吧&#xff08;我倒还没有用过&#xff09;、空间&#xff08;功能简单&#xff0c;但确实非常稳定&#xff09;。 其实百度还有一些不为人常用的产品&#xff0c;在这里。 如下图&#xff1a; 其中文档…

运行android项目时出现Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE错误!

[2011-12-14 11:10:00 - imusic] Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE [2011-12-14 11:10:00 - imusic] Please check logcat output for more details. [2011-12-14 11:10:02 - imusic] Launch canceled! 1: 我们再调试一个稍微比较大的应用&#xff0c;…

续一:《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

DDD领域驱动设计批评文集 “软件方法建模师”不再考查基础题 《软件方法》各章合集 我写了一篇文章&#xff0c;批评付施威的《DDD诊所——聚合过大综合症》&#xff08;以下简称《DDD诊所》&#xff09;&#xff0c;文章是《你的医书是假的&#xff01;批评付施威的《DDD诊…

qq windows版客户端0day复现——远程代码执行(七夕小礼物)

##ps&#xff1a;本文章仅用来分享&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff01;&#xff01;如有非法行为与本文章作者无任何关系。一切行为以遵守《中华人民共…

停车场收费软件兼容电脑操作系统问题如何搞定?

随着网络快速发展各式各样软件应运而生&#xff0c;同时操作系统对软件的要求也越来越高&#xff0c;兼容性问题越发重要。安装停车场收费软件&#xff0c;为了保障高效稳定的运行&#xff0c;前提也要兼容电脑的操作系统。 为此&#xff0c;易泊车牌识别停车场收费软件&#…

专访捷顺科技:“停车老司机”的智慧生态构想

本公众号已经改版&#xff0c;推出了线上线下课程&#xff0c;并且推出免费2个月广告服务业界优质产品。 实现智慧停车的过程中&#xff0c;互联网驱动下的模式创新是必要的&#xff0c;但有一个前提&#xff0c;即行业本身的技术创新。近日&#xff0c;捷顺科技总经理赵勇在接…

资本加速圈地,智慧停车战火越烧越旺

配图来自Canva 半年前&#xff0c;AIPARK爱泊车宣布完成B1和B2轮融资&#xff0c;投资方包括中美绿色一期基金、蔚来资本、中金资本、中关村启航基金等。 6月3日&#xff0c;城市级智慧云停车平台享停车宣布已完成数千万元融资&#xff0c;并获得了上亿元配套建设资金&#x…

SSM停车场管理系统-计算机毕设 附源码97557

SSM停车场管理系统 摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于停车场管理也是十分常见的。过去使用手工的管理方式对停车场进行管理&#xff0c;造成了管理繁琐、难以维护等问题&#xff0c;如今使用计算机对停车场的各…

django 停车场管理系统 计算机毕设源码19517

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

道闸系统服务器内存不足,停车场智能道闸系统常见问题及解决方法

[导读]停车场智能道闸系统一般是口控制机、出口控制机、智能道闸、地感线圈、余位显示屏、图象识别设备、系统软件、管理工作站等这些设备组成的&#xff0c;所以哪方面出了问题&#xff0c;就可以从哪方面着手解决。 停车场智能道闸系统一般是口控制机、出口控制机、智能道闸、…

北安服务器维修,北安停车场收费系统企业

捷顺智慧停车场采用的ETC停车是基于车牌识别管理系统&#xff0c;将ETC技术运用于智能停车场中&#xff0c;采用ETC车牌识别&#xff0c;实现不停车收费和无人值守&#xff0c;使车辆进出停车识别率无限靠近一百&#xff0c;ETC采用国家交通部颁布的标准&#xff0c;实名制管理…

python+停车管理系统 毕业设计-附源码271400

Python停车管理系统 摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于停车场管理也是十分常见的。过去使用手工的管理方式对停车进行管理&#xff0c;造成了管理繁琐、难以维护等问题&#xff0c;如今使用计算机对停车场停车的…

uuid怎么获取_基于车位引导系统的捷顺室内导航项目怎么调试?

对于停车难&#xff0c;找车难&#xff0c;捷顺在几年前就已经推出成熟的解决方案&#xff0c;经过这几年&#xff0c;技术越发成熟、先进&#xff01;那么对于这款可以在室内蓝牙导航的先进系统要怎么调试呢&#xff1f;下面就让我们一起来学习下吧&#xff01; 一、介绍 基于…

《剑指Offer》模块2 二叉树【15道二叉树帮助你掌握二叉树】

二叉树 二叉树1. 树中两个结点的最低公共祖先方法一&#xff1a;公共路径方法二&#xff1a;递归 2. 重建二叉树根据前序遍历和中序遍历 得到树 补充题&#xff1a;树的遍历 3. 二叉树的下一个节点4. 树的子结构&#xff08; 递归中调用递归 &#xff09;5. 二叉树的镜像&#…

win7桌面图标突然消失,鼠标右键不管用―解决

win7电脑桌面图标突然消失&#xff0c;鼠标右键不管用–解决 我的电脑是win7&#xff0c;当时正在下软件&#xff0c;快下完的时候突然桌面上的图标都没了&#xff0c;只剩一张桌面壁纸&#xff0c;鼠标右键没用&#xff0c;关机重启也不行&#xff0c;去网上查了拼拼凑凑终于…

win7桌面图标不可读文件样式的东西遮挡

win7桌面图标不可读文件样式的东西遮挡 解决方法 1&#xff1a; 右击桌面空白处&#xff0c;打开“个性化”设置&#xff0c;更改Windows主题&#xff0c;让系统重新加载快捷方式图标&#xff1b; 2&#xff1a; 打开路径C:\Users\你的用户名\AppData\Local&#xff0c;找到其中…

简述计算机网络系统图标添加在桌面上的步骤,如何将网络图标添加到win7桌面上...

win7网上邻居在哪&#xff1f;相信对于一些刚从WinXP升级到win7的盆友来说&#xff0c;对win7这款操作系统都不是特别的了&#xff0c;以致许多的盆友都都无法找到网上邻居&#xff0c;那么那么网上邻居在哪呢&#xff0c;下面小编就来为大家揭晓一下哈~ 方法一、 1、最简单的方…

win7变成xp风格了怎么改回_win7桌面怎么改成xp风格

随着win7系统不断发展和稳定&#xff0c;许多用户都从xp系统升级到win7系统&#xff0c;可是一些用户反馈说win7功能虽然多&#xff0c;但是界面不习惯&#xff0c;很多设置都不知道如何操作&#xff0c;和xp界面有很大不同。有什么办法可以让win7桌面改成xp风格&#xff1f;方…