目标:根据USB驱动分析和上节的USB鼠标驱动,编写键盘驱动,并测试。


一、原理分析

1. 首先通过打印usb_buf[i]中的8字节数据,看一下按键按下之后会接收到什么。

1)通过按完所有键盘按键打印的结果可知,有8个按键会打印在usb_buf[0]里,即:

ctrl左键----0x01----00000001

shift左键----0x02----00000010

alt左键----0x04------00000100

win左键----0x08-----00001000

ctrl右键----0x10------00010000

shift右键----0x20------00100000

alt右键----0x40--------01000000

win右键----0x80-------10000000

2)usb_buf[2]--usb_buf[7]为A B C D 1 2 3 4 F2 F4等普通的按键;

根据上节内容可知,按键的KEY_L等值得宏定义在input.h中,即:

从打印的usb_buf[i]数组可知,字符A为0x04,但alt左键也是0x04,该怎样在程序里区分呢?

。。。。。。。。。。

在键盘的源码程序/drivers/hid/usbhid/usbkbd.c中,使用过数组一一对应的:

static unsigned char usb_kbd_keycode[] = {
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , ,,,,,,,,,
,,, , , , , , , , , , , , , ,
, , , , ,,,,,,,,,,,,
,,,,,,,,,,,,,,,,
,, , , ,, , , ,, , , , , , ,
,, , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , ,, , ,,,,,,,,,,,
,,,,,,,,,,,
};

1)对于字符A的0x04就对应数组中的0x30;这里usb_buf[2-7]的0x04等数值,就对应usb_kbd_keycode[256]中的位置,确定每个按键的input码。

2)对于8个特殊按键,其位置为usb_buf[0]-1+ 224, input码为:usb_kbd_keycode[usb_buf[0]-1+ 224](数组从0开始)。例如:ctrl左键在usb_buf[0]为0x01,对应的input码为:usb_kbd_keycode[0+ 224]=29。正好和input.h中的宏定义的值相同。

同时,也可以有源码程序推理出:

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

程序中用于判断这8个按键的数据kbd->new[0](usb_buf[0])是否为1,然后通过input_report_key函数上传按键事件。

例如:alt左键----0x04------00000100按下后,判断(0x04 >> 2) & 1=1,则上报。

二、源码分析

1.中断函数

 static void usb_kbd_irq(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
int i; switch (urb->status) {
case : /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
//特殊的8个按键检测是否按下
for (i = ; i < ; i++)
input_report_key(kbd->dev, usb_kbd_keycode[i + ], (kbd->new[] >> i) & );
  //检测普通按键 数组下表为2-7
for (i = ; i < ; i++) {
     //usb_kbd_keycode数组中跳过前面3个0,从3开始;
       //上一次的按键kbd->old[i]非0数值 与 kbd->new + 2开始的6个字节的kbd->new[i]数组数值是否有相同的,若无,则按键已经松开
if (kbd->old[i] > && memscan(kbd->new + , kbd->old[i], ) == kbd->new + ) {
if (usb_kbd_keycode[kbd->old[i]]) //判断非零值
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], ); //上传松开事件
else
info("Unknown key (scancode %#x) released.", kbd->old[i]);
}
    //当前状态的按键kbd->new[i]非0数值 与 上一次的按键kbd->old[i]+ 2开始的6个字节的是否有相同的,若无,则按键已经按下
if (kbd->new[i] > && memscan(kbd->old + , kbd->new[i], ) == kbd->old + ) {
if (usb_kbd_keycode[kbd->new[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], );
else
info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
}
}
  
input_sync(kbd->dev); memcpy(kbd->old, kbd->new, ); //拷贝当前按键的8个字节到kbd->old,进行下一次的判断 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);
}

2. 修改id_table(用于驱动和设备匹配的)

 static struct usb_device_id usb_keyboardid_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },//USB协议:键盘协议 };

三、自己编写的程序

 #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;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *myusb_kbd_urb; //键盘码表共有252个数据
static const unsigned char usb_kbd_keycode[] = {
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , ,,,,,,,,,
,,, , , , , , , , , , , , , ,
, , , , ,,,,,,,,,,,,
,,,,,,,,,,,,,,,,
,, , , ,, , , ,, , , , , , ,
,, , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , ,, , ,,,,,,,,,,,
,,,,,,,,,,,
}; static struct usb_device_id usb_keyboardid_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },//USB协议:键盘协议 }; void my_memcpy(unsigned char *dest,unsigned char *src,int len) //复制缓存
{
while(len--)
{
*dest++= *src++;
}
} static void usb_keyboardirq(struct urb *urb)
{
static unsigned char pre_val[]={,,,,,,,};
int i; //8个特殊按键,对比上一次的按键值pre_val[0]和当前usb_buf[0],如果不相等有按键按下,则上报
for (i = ; i < ; i++)
if(((pre_val[]>>i)&)!=((usb_buf[]>>i)&))
{
input_report_key(myusb_kbd_dev, usb_kbd_keycode[i + ], (usb_buf[]>> i) & );
input_sync(myusb_kbd_dev); //上传同步事件
} /*上传普通按键*/
for(i=;i<;i++)
if(pre_val[i]!=usb_buf[i]) //上一次和本次按键值不同,说明有按键按下
{
if(usb_buf[i]) //非0,按下事件上报
input_report_key(myusb_kbd_dev,usb_kbd_keycode[usb_buf[i]], );
else if(pre_val[i]) //上一次非零,本次为0,松开事件
input_report_key(myusb_kbd_dev,usb_kbd_keycode[pre_val[i]], );
input_sync(myusb_kbd_dev); //上传同步事件
} my_memcpy(pre_val, usb_buf, ); //更新数据 /* 重新提交urb */
usb_submit_urb(myusb_kbd_urb, GFP_KERNEL);
} static int usb_keyboardprobe(struct usb_interface *intf, const struct usb_device_id *id)
{
volatile unsigned char i;
struct usb_device *dev = interface_to_usbdev(intf); //设备,通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用
struct usb_host_interface *interface; // 当前接口
struct usb_endpoint_descriptor *endpoint;
int pipe; //端点管道 interface = intf->cur_altsetting;
endpoint = &interface->endpoint[].desc; //当前接口下的端点描述符 printk("VID=%x,PID=%x\n",dev->descriptor.idVendor,dev->descriptor.idProduct); /* a. 分配一个input_dev */
myusb_kbd_dev = input_allocate_device(); /* b. 设置 */
/* b.1 能产生哪类事件 */
set_bit(EV_KEY, myusb_kbd_dev->evbit);
set_bit(EV_REP, myusb_kbd_dev->evbit); /* b.2 添加所有按键 */
for (i = ; i < ; i++)
set_bit(usb_kbd_keycode[i], myusb_kbd_dev->keybit);
clear_bit(, myusb_kbd_dev->keybit);
/* c. 注册 */
input_register_device(myusb_kbd_dev); /* d. 硬件相关操作 */
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 长度: */
len = endpoint->wMaxPacketSize; /* 目的: */
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); /* 使用"3要素" */
/* 分配usb request block */
myusb_kbd_urb = usb_alloc_urb(, GFP_KERNEL);//usb传输素具的urb结构体
/* 使用"3要素设置urb" */
usb_fill_int_urb(myusb_kbd_urb, dev, pipe, usb_buf, len, usb_keyboardirq, NULL, endpoint->bInterval);
myusb_kbd_urb->transfer_dma = usb_buf_phys;
myusb_kbd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* 使用URB */
usb_submit_urb(myusb_kbd_urb, GFP_KERNEL); return ;
} static void usb_keyboarddisconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf); //printk("disconnect usbmouse!\n");
usb_kill_urb(myusb_kbd_urb);
usb_free_urb(myusb_kbd_urb); usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(myusb_kbd_dev);
input_free_device(myusb_kbd_dev);
} /* 1. 分配/设置usb_driver */
static struct usb_driver usb_keyboarddriver = {
.name = "usb_keyboard",
.probe = usb_keyboardprobe,
.disconnect = usb_keyboarddisconnect,
.id_table = usb_keyboardid_table,
}; static int usb_keyboardinit(void)
{
/* 2. 注册 */
usb_register(&usb_keyboarddriver);
return ;
} static void usb_keyboardexit(void)
{
usb_deregister(&usb_keyboarddriver);
} module_init(usb_keyboardinit);
module_exit(usb_keyboardexit); MODULE_LICENSE("GPL");

四、测试

1. 烧写内核,编译/加载驱动模块。

2. # cat /dev/tty1

3. # exec 0</dev/tty1 (直接可以用键盘操作开发板的控制台)

8.2 USB键盘驱动编写和测试的更多相关文章

  1. USB键盘驱动分析

    简介 本文介绍USB驱动程序编写的流程,分析一个键盘的USB程序,基于linux-2.6.39 USB驱动概要 分层 主机层面的USB驱动的整体架构可以分成4层,自顶到下依次是 1.USB设备驱动:本 ...

  2. Linux驱动之USB鼠标驱动编写

    本篇博客分以下几部分讲解 1.介绍USB四大描述 2.介绍USB鼠标驱动程序功能及框架 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍USB四大描述符 USB设 ...

  3. 【驱动】USB驱动实例·串口驱动·键盘驱动

    Preface   USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_driver结构描述.    对于不同类型的 USB设备,内核使用传统的设备驱动模型建立设备驱动 ...

  4. 【驱动】USB驱动实例·串口驱动·键盘驱动【转】

    转自:http://www.cnblogs.com/lcw/p/3159370.html Preface USB体系支持多种类型的设备. 在 Linux内核,所有的USB设备都使用 usb_drive ...

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

    本节目的: 根据上节写的USB鼠标驱动,来依葫芦画瓢写出键盘驱动 1.首先我们通过上节的代码中修改,来打印下键盘驱动的数据到底是怎样的 先来回忆下,我们之前写的鼠标驱动的id_table是这样: 所以 ...

  6. usb摄像头驱动的移植

    相关软件下载地址:http://pan.baidu.com/s/16yo8Y 1.使用摄像头型号ov9650 ①修改.配置内核 1.修改vi drivers/i2c/busses/Kconfig (参 ...

  7. Linux下的USB总线驱动(一)

    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1.      USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...

  8. 8.1 编写USB鼠标驱动程序,并测试

    学习目标:编写USB鼠标驱动程序,并测试(将USB鼠标的左键当作L按键,将USB鼠标的右键当作S按键,中键当作回车按键). 一.怎么写USB设备驱动程序?步骤如下: 1. 首先先定义全局变量usb_d ...

  9. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

随机推荐

  1. sql server中将自增长列归零

    一个项目完成后数据库中会有很多无用的测试数据,可以使用delete * 将数据全部删除,但自增长列(一般是主键)基数不会归零,使用TRUNCATE函数可以将表中数据全部删除,并且将自增长列基数归零.一 ...

  2. Appium 如何模拟返回按键

    from appium.webdriver import Remote driver.keyevent(4) python中点击返回键是这样写的 附录 keycode 电话键 KEYCODE_CALL ...

  3. GET来传递数据的实例

    实例 下面实例是一点典型的使用GET来传递数据的实例: 客户端请求: GET /hello.txt HTTP/1.1 User-Agent: curl/7.16.3 libcurl/7.16.3 Op ...

  4. 【NLP_Stanford课堂】最小编辑距离

    一.什么是最小编辑距离 最小编辑距离:是用以衡量两个字符串之间的相似度,是两个字符串之间的最小操作数,即从一个字符转换成另一个字符所需要的操作数,包括插入.删除和置换. 每个操作数的cost: 每个操 ...

  5. GitLab 数据备份和恢复

    GitLab 备份 /opt/gitlab/bin/gitlab-rake gitlab:backup:create //只是备份各项目的数据 完成后会在默认路径下多出来备份的tar包! /var/o ...

  6. Android4.4 ContentResolver查询图片无效 及 图库删除 添加图片后,ContentResolver不更新的问题解决

    问题背景: 參考链接 做了一个图片浏览,用ContentResolver扫描图库照片.且严格依照时间拍摄顺序排好序显示在listview里.例如以下图所看到的: watermark/2/text/aH ...

  7. expected expression __bridge

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u013020103/article/details/30491117 expected expres ...

  8. Android应用经典主界面框架之中的一个:仿QQ (使用Fragment, 附源代码)

    备注:代码已传至https://github.com/yanzi1225627/FragmentProject_QQ 欢迎fork,如今来审视这份代码,非常多地方写的不太好,欢迎大家指正.有时间我会继 ...

  9. 【洛谷5279】[ZJOI2019] 麻将(“胡牌自动机”上DP)

    点此看题面 大致题意: 给你13张麻将牌,问你期望再摸多少张牌可以满足存在一个胡的子集. 似乎ZJOI2019Day1的最大收获是知道了什么是胡牌? 一个显然的性质 首先我们要知道一个显然的性质,即对 ...

  10. 在CentOs6.5下安装Python2.7.6和Scrapy

    总在网上看大家的安装教程,这回自己也贡献一份!!! 和民航大学合作项目,去给人家装环境,连简单的Scrapy都没装上,虽然有对方硬件设施坑爹的因素,但主要还是自己准备不充分. 一份好的安装文档应该是可 ...