目标:根据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. matlab练习程序(单源最短路径Bellman-Ford)

    该算法可以用来解决一般(边的权值为负)的单源最短路径问题,而dijkstra只能解决权值非负的情况. 此算法使用松弛技术,对每一个顶点,逐步减少源到该顶点的路径的估计值,直到达到最短的路径. 算法运算 ...

  2. 【转】python安装库

    1.打开网址https://pypi.python.org/pypi/numpy,找到安装的python版本对应的numpy版本. 我的python版本是 下载的对应numpy版本是 2.将numpy ...

  3. 【Spring实战】—— 10 AOP针对参数的通知

    通过前面的学习,可以了解到 Spring的AOP可以很方便的监控到方法级别的执行 ,针对于某个方法实现通知响应. 那么对于方法的参数如何呢? 比如我们有一个方法,每次传入了一个字符串,我想要知道每次传 ...

  4. May 28th 2017 Week 22nd Sunday

    Behind every beautiful thing, there's some kind of pain. 美丽背后,必有某种努力. No pains, no gains. Maybe we n ...

  5. Scrum _GoodJob

    作为长大的大三老腊肉,我们已经在长大生活了两年多,对于什么是长大人最想完善的校园需求.最想拥有的校园服务媒介也有了更加深切的体会. 于是,GoodJob小团队blingbling闪现啦!! GoodJ ...

  6. python入门12 列表list

    列表使用率较高,方法也多. 列表的定义 #coding:utf-8 #/usr/bin/python """ 2018-11-10 dinghanhua 列表 " ...

  7. Android(java)学习笔记39:Android 修改字体

    首先如果android内部自带的字体不是我们需要的字体,那我们就需要字体文件导入到android开发工程中,下午我们详细讲述: 1.我们首先分析知道,我想要TextView控件中文字的字体是:华文楷体 ...

  8. 关于SessionFactory的不同实现类分别通过getCurrentSession()方法 和 openSession() 方法获取的Session对象在保存对象时的一些区别

    一.单向多对一关联关系 一).使用LocalSessionFactoryBean类,即在applicationContext中配置的 <!-- 配置SessionFactory 使用LocalS ...

  9. E. XOR and Favorite Number

    题意:很多询问,求每个询问下,有多少个区间,异或=k. 分析:异或也有前缀和.[L,R] = pre[R] ^ pre[L-1]: 莫队算法:是莫涛队长发明的,一种改良版的暴力离线算法. 首先将问题重 ...

  10. POJ 2365【YY水题】

    题目链接:POJ 2365 Rope Rope Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7488   Accepted ...