• 8.2 USB键盘驱动编写和测试


    目标:根据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[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
    };

    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中的宏定义的值相同。

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

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

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

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

    二、源码分析

    1.中断函数

     1 static void usb_kbd_irq(struct urb *urb)
     2 {
     3     struct usb_kbd *kbd = urb->context;
     4     int i;
     5 
     6     switch (urb->status) {
     7     case 0:            /* success */
     8         break;
     9     case -ECONNRESET:    /* unlink */
    10     case -ENOENT:
    11     case -ESHUTDOWN:
    12         return;
    13     /* -EPIPE:  should clear the halt */
    14     default:        /* error */
    15         goto resubmit;
    16     }
    17     //特殊的8个按键检测是否按下
    18     for (i = 0; i < 8; i++)
    19         input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
    20   //检测普通按键 数组下表为2-7
    21     for (i = 2; i < 8; i++) {
    22      //usb_kbd_keycode数组中跳过前面3个0,从3开始;
           //上一次的按键kbd->old[i]非0数值 与 kbd->new + 2开始的6个字节的kbd->new[i]数组数值是否有相同的,若无,则按键已经松开 23 if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
    24 if (usb_kbd_keycode[kbd->old[i]]) //判断非零值 25 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); //上传松开事件 26 else 27 info("Unknown key (scancode %#x) released.", kbd->old[i]); 28 } 29     //当前状态的按键kbd->new[i]非0数值 与 上一次的按键kbd->old[i]+ 2开始的6个字节的是否有相同的,若无,则按键已经按下 30 if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { 31 if (usb_kbd_keycode[kbd->new[i]]) 32 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); 33 else 34 info("Unknown key (scancode %#x) pressed.", kbd->new[i]); 35 } 36 } 37    38 input_sync(kbd->dev); 39 40 memcpy(kbd->old, kbd->new, 8); //拷贝当前按键的8个字节到kbd->old,进行下一次的判断 41 42 resubmit: 43 i = usb_submit_urb (urb, GFP_ATOMIC); 44 if (i) 45 err ("can't resubmit intr, %s-%s/input0, status %d", 46 kbd->usbdev->bus->bus_name, 47 kbd->usbdev->devpath, i); 48 }

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

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

    三、自己编写的程序

      1 #include <linux/kernel.h>
      2 #include <linux/slab.h>
      3 #include <linux/module.h>
      4 #include <linux/init.h>
      5 #include <linux/usb/input.h>
      6 #include <linux/hid.h>
      7 
      8 static struct input_dev *myusb_kbd_dev;
      9 static char *usb_buf;
     10 static dma_addr_t usb_buf_phys;
     11 static int len;
     12 static struct urb *myusb_kbd_urb;
     13 
     14 //键盘码表共有252个数据
     15 static const unsigned char usb_kbd_keycode[252] = {
     16          0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
     17         50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
     18          4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,
     19         27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
     20         65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
     21        105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
     22         72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
     23        191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
     24        115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,
     25        122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     26          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     27          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     28          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     29          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     30         29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
     31        150,158,159,128,136,177,178,176,142,152,173,140
     32 };       
     33 
     34 static struct usb_device_id usb_keyboardid_table [] = {
     35     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
     36         USB_INTERFACE_PROTOCOL_KEYBOARD) },//USB协议:键盘协议
     37 
     38 };
     39 
     40 void my_memcpy(unsigned char *dest,unsigned char *src,int len)      //复制缓存
     41 {
     42        while(len--)
     43         {
     44             *dest++= *src++;
     45         }
     46 }
     47 
     48 static void usb_keyboardirq(struct urb *urb)
     49 {
     50     static unsigned char pre_val[8]={0,0,0,0,0,0,0,0};
     51     int i;
     52 
     53      //8个特殊按键,对比上一次的按键值pre_val[0]和当前usb_buf[0],如果不相等有按键按下,则上报
     54     for (i = 0; i < 8; i++)
     55     if(((pre_val[0]>>i)&1)!=((usb_buf[0]>>i)&1))
     56     {
     57         input_report_key(myusb_kbd_dev, usb_kbd_keycode[i + 224], (usb_buf[0]>> i) & 1);
     58         input_sync(myusb_kbd_dev);             //上传同步事件
     59     }
     60 
     61     
     62     /*上传普通按键*/
     63     for(i=2;i<8;i++)
     64     if(pre_val[i]!=usb_buf[i])  //上一次和本次按键值不同,说明有按键按下
     65     {
     66         if(usb_buf[i])      //非0,按下事件上报
     67             input_report_key(myusb_kbd_dev,usb_kbd_keycode[usb_buf[i]], 1);   
     68         else  if(pre_val[i])                                             //上一次非零,本次为0,松开事件
     69             input_report_key(myusb_kbd_dev,usb_kbd_keycode[pre_val[i]], 0);
     70         input_sync(myusb_kbd_dev);             //上传同步事件
     71     }
     72 
     73     my_memcpy(pre_val, usb_buf, 8);       //更新数据
     74 
     75     /* 重新提交urb */
     76     usb_submit_urb(myusb_kbd_urb, GFP_KERNEL);
     77 }
     78 
     79 static int usb_keyboardprobe(struct usb_interface *intf, const struct usb_device_id *id)
     80 {
     81     volatile unsigned char  i;
     82     struct usb_device *dev = interface_to_usbdev(intf); //设备,通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用
     83     struct usb_host_interface *interface;               // 当前接口
     84     struct usb_endpoint_descriptor *endpoint;
     85     int pipe;                                            //端点管道
     86     
     87     interface = intf->cur_altsetting;
     88     endpoint = &interface->endpoint[0].desc;            //当前接口下的端点描述符
     89 
     90     printk("VID=%x,PID=%x
    ",dev->descriptor.idVendor,dev->descriptor.idProduct); 
     91     
     92     /* a. 分配一个input_dev */
     93     myusb_kbd_dev = input_allocate_device();
     94     
     95     /* b. 设置 */
     96     /* b.1 能产生哪类事件 */
     97     set_bit(EV_KEY, myusb_kbd_dev->evbit);
     98     set_bit(EV_REP, myusb_kbd_dev->evbit);
     99     
    100     /* b.2 添加所有按键 */
    101     for (i = 0; i < 252; i++)
    102     set_bit(usb_kbd_keycode[i], myusb_kbd_dev->keybit);  
    103     clear_bit(0, myusb_kbd_dev->keybit);
    104 
    105     
    106     /* c. 注册 */
    107     input_register_device(myusb_kbd_dev);
    108     
    109     /* d. 硬件相关操作 */
    110     /* 数据传输3要素: 源,目的,长度 */
    111     /* 源: USB设备的某个端点 */
    112     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    113 
    114     /* 长度: */
    115     len = endpoint->wMaxPacketSize;
    116 
    117     /* 目的: */
    118     usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);
    119 
    120     /* 使用"3要素" */
    121     /* 分配usb request block */
    122     myusb_kbd_urb = usb_alloc_urb(0, GFP_KERNEL);//usb传输素具的urb结构体
    123     /* 使用"3要素设置urb" */
    124     usb_fill_int_urb(myusb_kbd_urb, dev, pipe, usb_buf, len, usb_keyboardirq, NULL, endpoint->bInterval);
    125     myusb_kbd_urb->transfer_dma = usb_buf_phys;
    126     myusb_kbd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    127 
    128     /* 使用URB */
    129     usb_submit_urb(myusb_kbd_urb, GFP_KERNEL);
    130     
    131     return 0;
    132 }
    133 
    134 static void usb_keyboarddisconnect(struct usb_interface *intf)
    135 {
    136     struct usb_device *dev = interface_to_usbdev(intf);
    137 
    138     //printk("disconnect usbmouse!
    ");
    139     usb_kill_urb(myusb_kbd_urb);
    140     usb_free_urb(myusb_kbd_urb);
    141 
    142     usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
    143     input_unregister_device(myusb_kbd_dev);
    144     input_free_device(myusb_kbd_dev);
    145 }
    146 
    147 /* 1. 分配/设置usb_driver */
    148 static struct usb_driver usb_keyboarddriver = {
    149     .name        = "usb_keyboard",
    150     .probe       = usb_keyboardprobe,
    151     .disconnect  = usb_keyboarddisconnect,
    152     .id_table    = usb_keyboardid_table,
    153 };
    154 
    155 
    156 static int usb_keyboardinit(void)
    157 {
    158     /* 2. 注册 */
    159     usb_register(&usb_keyboarddriver);
    160     return 0;
    161 }
    162 
    163 static void usb_keyboardexit(void)
    164 {
    165     usb_deregister(&usb_keyboarddriver);    
    166 }
    167 
    168 module_init(usb_keyboardinit);
    169 module_exit(usb_keyboardexit);
    170 
    171 MODULE_LICENSE("GPL");

    四、测试

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

    2. # cat /dev/tty1

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

  • 相关阅读:
    C# 字典类 Dictionary 基本用法 Mark
    SQL语句监测耗时
    jQuery Select Option 操作 删除新增
    C# DataTable 过滤重复数据
    IE8 overflow:hidden 无效问题解决方案
    动态拼接LINQ 查询条件
    解决.net中"未能创建 Mutex”异常
    创建Cookies 包含子健和无子健的创建及用法 做个笔记留着参考
    常用的一些加密算法,留着以备不时之需
    Centos7 nginx安装
  • 原文地址:https://www.cnblogs.com/lxl-lennie/p/10213404.html
Copyright © 2020-2023  润新知