title: usb输入子系统键盘

tags: linux

date: 2018/12/20/ 17:05:08

toc: true

usb输入子系统键盘

参考文档 https://www.cnblogs.com/lifexy/p/7645527.html

设计思路

  1. 修改匹配的id_table为键盘

    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协议:键盘协议
    };
  2. 按键键值的转换,上传的按键数据是8字节的

    buf[0]

        |--bit0:   Left Control是否按下,按下为1
    |--bit1: Left Shift 是否按下,按下为1
    |--bit2: Left Alt 是否按下,按下为1
    |--bit3: Left GUI 是否按下,按下为1
    |--bit4: Right Control是否按下,按下为1
    |--bit5: Right Shift 是否按下,按下为1
    |--bit6: Right Alt 是否按下,按下为1
    |--bit7: Right GUI 是否按下,按下为1

    buf[1]

    保留

    buf[2~6]

    按键值,也就是表示支持最多6个按键一起按,这里的数据需要转换,以按键A为例

    //input中上报
    #define KEY_A 30 //buf中上传的数据
    =04
  3. 按键上报

    这里就会有一个映射表/drivers/hid/usbhid/usbkbd.c,可以发现usb_kbd_keycode[4]=30也就是实际值,其中0表示保留的意思。

    static unsigned char usb_kbd_keycode[256] = {
    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
    };

    如何转换特殊的按键值也就是buf[0],搜索这个索引表有以下函数

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

    也就是对于kbd->new[0]的每一位上报的是usb_kbd_keycode[i + 224],查看下226的索引是56

    #define KEY_LEFTALT		56
  4. 具体的参考上报

    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 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]]) //再次判断键盘描述码表的值是否非0
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); //上传松开事件
else
info("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]]) //再次判断键盘描述码表的值是否非0
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); //上传按下事件
else
info("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);
}
  • 判断按键中if (kbd->old[i] > 3 因为按键值的索引是从A也就是4开始的

  • memscan 是在某个内存中寻找一个自身,这里memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8)也就是表示找不到的意思,也就是放置类似这种情况

    第一次扫描 A 按下,在 buf[3]

    第二次扫描 A按下,但是在buf[4],但是依然不去上报

    void *memscan(void *addr, int c, size_t size)
    {
    unsigned char *p = addr; while (size) {
    if (*p == c)
    return (void *)p;
    p++;
    size--;
    }
    return (void *)p;
    }

完整代码

#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; //urb static 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_dev
input_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");

usb输入子系统键盘(四)的更多相关文章

  1. usb输入子系统写程序(三)

    目录 usb输入子系统写程序 小结 内核修改 怎么写代码 类型匹配 probe disconnect 程序设计 1th匹配probe 2th 获取usb数据 3th 输入子系统上报按键 title: ...

  2. linux input输入子系统分析《四》:input子系统整体流程全面分析

    1      input输入子系统整体流程 本节分析input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.由于上节代码讲解了设备 ...

  3. 嵌入式Linux驱动学习之路(十六)输入子系统

    以前写的一些输入设备的驱动都是采用字符设备处理的.问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可以实现一种机制,可以对分散的.不同类别的输入设备进行统一的驱动,所以 ...

  4. linux input输入子系统应用分析

    输入设备(如按键.键盘.触摸屏.鼠标等)是典型的字符设备,其一般的工作机理是底层在按键.触摸等动作发送时产生一个中断(或驱动通过timer定时查询),然后CPU通过SPI.I2 C或外部存储器总线读取 ...

  5. Linux 输入子系统

    Technorati 标签: Kernel 输入子系统 Input      在Linux中,输入设备(如按键.键盘.触摸屏.鼠标等)是典型的字符设备,其一般的工作机理,是底层在按键.触摸时,触发一个 ...

  6. 12.Linux之输入子系统分析(详解)

    版权声明:本文为博主原创文章,转载请标注出处:   在此节之前,我们学的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥/非阻塞.定时 ...

  7. ARM Linux内核Input输入子系统浅解

    --以触摸屏驱动为例 第一章.了解linux input子系统         Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些 ...

  8. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  9. Linux 输入子系统 input

    一.输入子系统 针对输入设备设计:触摸屏.键盘.按键.传感器.鼠标...... 二.每种设备都属于字符设备驱动,程序的写法步骤也相同 1.实现入口函数 xxx_init() 和卸载函数 xxx_exi ...

随机推荐

  1. spring boot 扫描不到自定义Controller

    使用springboot启动类配置扫描的两种注解配置方式: 1.@Controller   @EnableAutoConfiguration   @ComponentScan 2.@SpringBoo ...

  2. LeetCode算法题-Construct the Rectangle(Java实现)

    这是悦乐书的第243次更新,第256篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第110题(顺位题号是492).对于Web开发人员,了解如何设计网页的大小非常重要.因此 ...

  3. 获取高精度时间注意事项 (QueryPerformanceCounter , QueryPerformanceFrequency)

    花了很长时间才得到的经验,与大家分享. 1. RDTSC - 粒度: 纳秒级 不推荐优势: 几乎是能够获得最细粒度的计数器抛弃理由: A) 定义模糊- 曾经据说是处理器的cycle counter,但 ...

  4. sqlalchemy外键的一些东西

    sqlalchemy中让MySQL支持中文字符 engine = create_engine("mysql+pymysql://root:mysql8@localhost/mysqltest ...

  5. Spring的单例模式底层实现

    http://blog.csdn.net/cs408/article/details/48982085

  6. yum源 Python3 Django mysql安装

    yum 源安装 yum源位置: yum源仓库的地址 在/etc/yum.repos.d/,并且只能读出第一层的repo文件 yum仓库的文件都是以.repo结尾的 linux软件包管理 yum工具如同 ...

  7. linux下 启动node 和关闭node

    1.用forever  进行管理 npm install -g forever forever start app.js //启动 forever stop app.js //关闭 2.用自带的服务n ...

  8. C# PDF转Image图片

    概述 PDF是常用的文件格式之一,通常情况下,我们可以使用itextsharp生产PDF文件:可是如何将PDF文件转换成图片那?目前常用的: 思路1.根据PDF绘画轨迹重新绘制图片: 思路2.是将PD ...

  9. cmd执行超大sql文件

    osql -S 127.0.0.1 -U sa -P 123456 -i d:\test.sql osql为SQL Server的命令,要在cmd中执行该命令,一般安装完SQL Server后该命令对 ...

  10. iOS 钥匙串存储用户数据

    参考: http://www.jianshu.com/p/f6d40065bb6c #import <Foundation/Foundation.h> #import <Securi ...