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\n",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!\n");
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 (直接可以用鍵盤操作開發板的控制台)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM