zoukankan      html  css  js  c++  java
  • 《驱动学习

    1.首先我们通过上节的代码中修改,来打印下键盘驱动的数据到底是怎样的

      先来回忆下,我们之前写的鼠标驱动的id_table是这样:

       所以我们要修改id_table,使这个驱动为键盘的驱动,如下图所示:

      然后修改中断函数,通过printk()打印数据:

      我们先按下按键A为例,打印出0x04,如下图:  

      

      我们再同时按下按键A和S,打印出0x04,0X16, 如下图:

      

      显然这些普通按键都是从buf[2]开始的,那第一个数组到底又存什么值?

      我们按完所有键盘按键,发现只有8个按键会打印在buf[0]里,如下图所示:

      

       所以buf[0]是用来保存键盘的特定功能的键,而buf[1]可能是个保留键,没有用到的,buf[2]~buf[7]是普通按键,比如ABCD,1234,F1,F2等等,能支持最多6个按键同时按下。

      

    2.那么每个按键的数据又是怎么定义的?

    2.1比如我们按下按键A,为什么打印0X04?

      我们找到输入子系统(input.h)中按键A定义的值,它对应的却是30,看来不是直接调用的,如下图:

      

      我们再来参考内核自带的USB键盘驱动 (/drivers/hid/usbhid/usbkbd.c)

      发现它的中断函数中有个键盘描述码表(其中0表示保留的意思):

      

      发现该数组的0X04就是0X30,看来要写个键盘驱动,还需要上面的数组才行.

      那么问题又来了,如果我们按下左alt键,buf[0]中会出现0x04,如果也代入到键盘描述码表中,显然就会当作键盘按键A来使用。

    2.2我们来分析内核的键盘中断函数是如何处理的:

       发现有这么一句:

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

      其中kbd->new表示的就是键盘数据数组,它将buf[0]的每一位通通以usb_kbd_keycode[i+ 224]的形式上传到按键事件中

      显然我们的buf[0]的0X04就是上传的usb_kbd_keycode[4+ 224] 

    2.3我们来看看usb_kbd_keycode[226]里的数据对应的到底是不是左ALT键   

      找到usb_kbd_keycode[226]=56:

      

       然后再进入input.h,找到56的定义,刚好就是KEY_LEFTALT(左边的alt键)

      

    3.接下来再来仔细分析下内核自带的USB键盘驱动usbkbd.c里的中断函数:

    代码如下:

    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);
    }

    3.1上面获取普通按键时,为什么不直接判断非0,要判断按键数据> 3?

    之前我们就分析了,当按键数据=0X01、0X02时,代表的是特定功能的键(crtl、shift),是属于buf[0]的数据

    其中memscan()是用来匹配上次按键和当前按键的数据,它这么做的原因是怕上个buf[]和当前buf[]的数据错位,这里就不做详细分析了

    一切迎刃而解,我们只需要将自己的代码也通过这个码表添加所有按键按键事件,然后再在键盘中断函数中根据数据来上传事件即可

    4.本节键盘代码如下:

    #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");

    5.测试运行

    5.1 重新设置编译内核(去掉默认的hid_USB驱动)

    make menuconfig ,进入menu菜单重新设置内核参数:

    进入-> Device Drivers -> HID Devices 

    <> USB Human Interface Device (full HID) support     //hid:人机交互的USB驱动,比如鼠标,键盘等

    然后make uImage 编译内核

    将新的键盘驱动模块放入nfs文件系统目录中

    5.2然后烧写内核,装载触摸屏驱动模块

    如下图,当我们插上USB键盘时,可以看到该VID和PID,和电脑上的键盘的参数一样

    5.3使用cat  tty1进程测试

    5.4 使用exec 0</dev/tty1测试

    如下图,就能通过板子上的键盘来操作了

  • 相关阅读:
    hdu 5646 DZY Loves Partition
    bzoj 1001 狼抓兔子 平面图最小割
    poj 1815 Friendship 最小割 拆点 输出字典序
    spoj 1693 Coconuts 最小割 二者取其一式
    hdu 5643 King's Game 约瑟夫环变形
    约瑟夫环问题
    hdu 5642 King's Order
    CodeForces 631C Report
    1039: C语言程序设计教程(第三版)课后习题9.4
    1043: C语言程序设计教程(第三版)课后习题10.1
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11685602.html
Copyright © 2011-2022 走看看