zoukankan      html  css  js  c++  java
  • usb输入子系统键盘(四)


    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
    ",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");
    
  • 相关阅读:
    关于python3在centOS7下源码安装的配置
    nginx服务器多虚拟主机配置
    完全数
    高精度计算组合数
    算法竞赛入门经典第六章总结
    线段树
    奶牛
    算法竞赛入门经典第五章总结
    优先队列的使用方法
    放魔法石的游戏
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10155330.html
Copyright © 2011-2022 走看看