日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

大话USB驱动之USB键盘驱动

發(fā)布時間:2023/12/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大话USB驱动之USB键盘驱动 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載請注明出處:http://blog.csdn.net/ruoyunliufeng/article/details/25040049

一.總體框圖


二.驅(qū)動代碼

/****************************************************************版權(quán)所有 (C)2014, *文件名稱:linux鍵盤驅(qū)動 *內(nèi)容摘要:用另一種方式改寫linux鍵盤驅(qū)動 *其它說明: *當前版本:V1.2 *作 者: 若云流風 *完成日期:2014.5.6 *修改記錄1: * 修改日期:2014.5.7 * 版本號: V1.1 * 修改人: 若云流風 * 修改內(nèi)容:消除程序硬釋放不能實現(xiàn)重復事件問題 ***************************************************************/ /** 參考:drivers\hid\usbhid\Usbkbd.c*/ #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 *uk_dev; static unsigned char *usb_buf; static dma_addr_t usb_buf_phys; static int len, pre_val,pre_val_change; static struct urb *uk_urb;/*鍵碼表*/ 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 };/* *接口描述符:類(HID),子類(boot),協(xié)議(KEYBOARD) */ static struct usb_device_id usb_keyboard_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD)},//{USB_DEVICE(0x1234,0x5678)},{ } /* Terminating entry */ };static void usb_keyboard_irq(struct urb *urb) { /* //讀者可自行調(diào)試,能夠看到usb_buf[i]的具體數(shù)值int i; static int cnt = 0;printk("data cnt %d: ", ++cnt);for (i = 0; i < len; i++){printk("%02x ", usb_buf[i]);}printk("\n"); */ int i;/*主要是上報是否按下了29, 42, 56,125, 97, 54,100,126這幾個鍵碼對應的鍵*也就是 Left Control、 Left Shift 、Left Alt 、 Left GUI 、 Right Control、 Right Shift 、 Right Alt 、 Right GUI *這幾個建。因為要處理大小寫等問題。要先判斷是否按下他們,然后再判斷是否按下其余按鍵*/ for (i = 0; i < 8; i++)input_report_key(uk_dev, usb_kbd_keycode[i + 224], (usb_buf[0] >> i) & 1);/*因為bit0上面已經(jīng)寫出,bit1保留,其余的按鍵在bit2到7*/for (i = 2; i < len; i++){if (pre_val!= usb_buf[i]) //判斷兩次值是否相等(松開和按下肯等都不會相等){/* 按下時候數(shù)組中不等于0的那個才能元素進來,進來后直接進入if*在if中上報鍵值,并把鍵值給pre_val_change用來松手判斷(如果直接賦值給pre_val會有BUG)*在沒有松按鍵時會一直打印(實現(xiàn)重復事件)*/if(usb_buf[i]!=0) {input_report_key(uk_dev, usb_kbd_keycode[usb_buf[i]], 1);input_sync(uk_dev);pre_val_change=usb_buf[i]; }/*當松手時,usb_buf[i]值全為0,所以必然進入大if中,*由于每個值都等于0,所以每次都會進入這個else(進入8次)*進的最后要清零pre_val_change,要不然下次你再按此鍵值的時候*pre_val!= usb_buf[i] 每次都會上報一次松開此鍵的BUG*/else{input_report_key(uk_dev, usb_kbd_keycode[pre_val], 0);input_sync(uk_dev);pre_val_change=0; //此處效果相同(pre_val_change=usb_buf[i];)要清零,否則會出現(xiàn)下次再按此鍵按不了的情況。}} }pre_val=pre_val_change; //將change值賦給per_val用來判斷是否按下/* 重新提交urb */usb_submit_urb(uk_urb, GFP_ATOMIC); }static int usb_keyboard_probe(struct usb_interface *intf, const struct usb_device_id *id) {struct usb_device *dev = interface_to_usbdev(intf);struct usb_host_interface *interface;struct usb_endpoint_descriptor *endpoint;int pipe,i;pre_val=0; //清零pre_val,這個是按鍵的old值pre_val_change=0; //清零pre_val_change,引入第三變量interface = intf->cur_altsetting;endpoint = &interface->endpoint[0].desc;/* a. 分配一個input_dev */uk_dev = input_allocate_device();/* b. 設(shè)置 *//* b.1 能產(chǎn)生哪類事件 */set_bit(EV_KEY, uk_dev->evbit);set_bit(EV_REP, uk_dev->evbit);/* b.2 能產(chǎn)生哪些事件 */for (i = 0; i < 255; i++)set_bit(usb_kbd_keycode[i], uk_dev->keybit);/* c. 注冊 */input_register_device(uk_dev);/* d. 硬件相關(guān)操作 *//* 數(shù)據(jù)傳輸3要素: 源,目的,長度 *//* 源: USB設(shè)備的某個端點 */pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //urb索要發(fā)送的特定目標struct dev端點信息。/* 長度: */len = endpoint->wMaxPacketSize; //最大包的長度/* 目的: */usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); //分配一個usb_buffer/* 使用"3要素" *//* 分配usb request block */uk_urb = usb_alloc_urb(0, GFP_KERNEL); //給urb分配內(nèi)存空間/* 使用"3要素設(shè)置urb" */usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usb_keyboard_irq, NULL, endpoint->bInterval);//用來正確地初始化即將被發(fā)送到USB設(shè)備的中斷端點urbuk_urb->transfer_dma = usb_buf_phys; //用dma方式傳輸數(shù)據(jù)到USB緩沖區(qū)uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //用于控制帶有已設(shè)置好的DMA緩沖區(qū)的URB/* 使用URB */usb_submit_urb(uk_urb, GFP_KERNEL); //提交到USB核心以發(fā)送到USB設(shè)備return 0; }static void usb_keyboard_disconnect(struct usb_interface *intf) {struct usb_device *dev = interface_to_usbdev(intf);//printk("disconnect usbmouse!\n");usb_kill_urb(uk_urb);usb_free_urb(uk_urb);usb_buffer_free(dev, len, usb_buf, usb_buf_phys);input_unregister_device(uk_dev);input_free_device(uk_dev); }/* 1. 分配/設(shè)置usb_driver */ static struct usb_driver usb_keyboadr_driver = {.name = "usb_keyboard",.probe = usb_keyboard_probe,.disconnect = usb_keyboard_disconnect,.id_table = usb_keyboard_table, };static int usb_keyboard_init(void) {/* 2. 注冊 */usb_register(&usb_keyboadr_driver);return 0; }static void usb_keyboard_exit(void) {usb_deregister(&usb_keyboadr_driver); }module_init(usb_keyboard_init); module_exit(usb_keyboard_exit);MODULE_LICENSE("GPL");


三.程序分析

?????????? 上面的代碼是根據(jù)原USB鍵盤驅(qū)動代碼改寫而成,寫的比較簡單,但是有一個缺陷,不能實現(xiàn)重復事件(按下一個鍵不動,屏幕不會連續(xù)打印出這個鍵)。歡迎大家提供解決方法,后續(xù)有時間我還會改進,然后給大家分享。

??????1.框架分析

???????????????? a. 分配結(jié)構(gòu)體

static struct usb_driver usb_keyboadr_driver = {.name = "usb_keyboard", //只是個名字而已,隨便起.probe = usb_keyboard_probe, //探測函數(shù)(后面細講).disconnect = usb_keyboard_disconnect, //設(shè)備卸載(斷開)時調(diào)用(做清理工作).id_table = usb_keyboard_table, //用來比較接口描述符,有則調(diào)用probe };

????????????????? b.注冊

usb_register(&usb_keyboadr_driver);??????? 2.具體分析

??????????????????a.usb_keyboard_table

/*類(HID),子類(boot),協(xié)議(MOUSE)*/ static struct usb_device_id usb_keyboard_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_KEYBOARD)},//{USB_DEVICE(0x1234,0x5678)},{ } /* Terminating entry */ };

????????????????????????? 創(chuàng)建一個struct usb_device_id 的結(jié)構(gòu)體,僅和USB接口的制定類型匹配,例如你插入鍵盤能識別并調(diào)用probe,但是你插入個鼠標就不能識別了(你按左鍵、右鍵什么的基本就什么反應也沒有)。

? ? ??????????????b.usb_keboard_probe(重要)

?????????????????????????? 看代碼你會有似曾相識的感覺,沒錯相信你的感覺,USB驅(qū)動也在輸入子系統(tǒng)里面(不清楚輸入子系統(tǒng)可以看我之前寫的觸摸屏驅(qū)動三部曲,那對輸入子系統(tǒng)講的比較詳細),但USB驅(qū)動也有自己與眾不同之處。

?????????????????????????? ???? 1. 分配一個input_dev

uk_dev = input_allocate_device();

??????????????????????????? ??? 2. 設(shè)置
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 2.1 能產(chǎn)生哪類事件

set_bit(EV_KEY, uk_dev->evbit);

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 2.2 能產(chǎn)生哪些事件

for (i = 0; i < 255; i++)set_bit(usb_kbd_keycode[i], uk_dev->keybit);

在這里插將下簡碼表:

? ? ?? 君可見usb_kbd_keycode[256]? 這個大數(shù)組,沒錯他就是簡碼表,他是干啥用的?好了,讓我們想一下我們按鍵為什么系統(tǒng)能夠知道,我們按下一個A,系統(tǒng)可不知道你按下的是啥,于是我們給鍵盤編碼,叫每一個按鍵擁有一個確定的的值,例如A就對應30這個數(shù)字。好好理解下簡碼表對后面我們分析中斷函數(shù)十分重要。具體鍵值參考(linux-2.6.22.6\linux-2.6.22.6\include\linux\input.h)? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ??

??????????????????????????????? 3. 注冊

input_register_device(uk_dev);

??????? ? 程序到這里和我們以前寫的輸入子系統(tǒng)的框架沒有本質(zhì)差別,以前硬件相關(guān)操作我們都是去操作寄存器,現(xiàn)在我們只要操作urb就行了(想操作寄存器也沒有啊,一個USB,一共四根線,還一個電源一個地) ?????????????????????

??????????????????????????????? 4. 硬件相關(guān)操作

/* a. 分配一個input_dev */uk_dev = input_allocate_device();/* b. 設(shè)置 *//* b.1 能產(chǎn)生哪類事件 */set_bit(EV_KEY, uk_dev->evbit);set_bit(EV_REP, uk_dev->evbit);/* b.2 能產(chǎn)生哪些事件 */for (i = 0; i < 255; i++)set_bit(usb_kbd_keycode[i], uk_dev->keybit);/* c. 注冊 */input_register_device(uk_dev);/* d. 硬件相關(guān)操作 *//* 數(shù)據(jù)傳輸3要素: 源,目的,長度 *//* 源: USB設(shè)備的某個端點 */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 */uk_urb = usb_alloc_urb(0, GFP_KERNEL);/* 使用"3要素設(shè)置urb" */usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usb_keyboard_irq, NULL, endpoint->bInterval);uk_urb->transfer_dma = usb_buf_phys;uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* 使用URB */usb_submit_urb(uk_urb, GFP_KERNEL);


?????????????????????????????? c.usb_keyboard_irq(重要)

???????????????????????? 這個函數(shù)可以說是整個驅(qū)動的精華所在,由于編程能力有限,我改了很長時間才弄出來。

??????????????????????????????????? ? 首先我們來看下內(nèi)核自帶的源代碼是如何實現(xiàn)的.(我只是拷一個片段)

/*主要是上報是否按下了29, 42, 56,125, 97, 54,100,126這幾個鍵碼對應的鍵*也就是 Left Control、 Left Shift 、Left Alt 、 Left GUI 、 Right Control、 Right Shift 、 Right Alt 、 Right GUI *這幾個建。因為要處理大小寫等問題。要先判斷是否按下他們,然后再判斷是否按下其余按鍵*/ for (i = 0; i < 8; i++)input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);for (i = 2; i < 8; i++) {if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {if (usb_kbd_keycode[kbd->old[i]])input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);elseinfo("Unknown key (scancode %#x) released.", kbd->old[i]);}if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {if (usb_kbd_keycode[kbd->new[i]])input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);elseinfo("Unknown key (scancode %#x) pressed.", kbd->new[i]);}}input_sync(kbd->dev);memcpy(kbd->old, kbd->new, 8); //將new的數(shù)據(jù)拷到old里面去


下面我將用幾個問題揭開驅(qū)動這層神秘的面紗。三問驅(qū)動:

問1:第一個循環(huán)什么意思?沒有行不行?

?????? 答:主要是上報是否按下了29, 42, 56,125, 97, 54,100,126這幾個鍵碼對應的鍵

也就是 Left Control、 Left Shift 、Left Alt 、 Left GUI 、 Right Control、 Right Shift 、 Right Alt 、 Right GUI
這幾個鍵。因為要處理大小寫等問題。要先判斷是否按下他們,然后再判斷是否按下其余按鍵。右移i位,再與 1就能判斷是否按下。這個和他們數(shù)據(jù)格式有關(guān),看下個問題。沒有這個循環(huán)肯定不行,例如你想中斷某個程序要Control+c,親怎么辦?所以這個循環(huán)是必須的。

問2:為什么從第二個循環(huán)要從i=2開始?

?????? 答:這個和數(shù)據(jù)的格式有關(guān)系,鍵盤發(fā)送給PC的數(shù)據(jù)每次8個字節(jié)?data0?data1?data2?data3?data4?data5?data6?data7? ,其中data0就是我上面第一個問題將的那8個按鍵(??????? |--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

),data1保留,data2--data7 是 普通按鍵,既然我們上面已經(jīng)判斷的data0了,那我們現(xiàn)在理所當然要從i=2開始判斷 data2--data7了。這樣也能解決Control+c的問題,先判斷control在判斷c。

問3:怎么判斷是否按下還是松開?

??????? 答:判斷按下:第二個for循環(huán)里的第二個if里面

?????????????? 首先判斷new[i]是否大于3,因為由于簡碼表可知usb_kbd_keycode[0]到usb_kbd_keycode[3]都是0(KEY_RESERVED),沒有意義。并且在old[2]到old[8]中沒有出現(xiàn)過,也就是和上次的鍵值不一樣(memcpy函數(shù)把每次的鍵值進行拷貝,用來判斷是否按下),如果按下了,肯定值會不一樣的嘛(就是memescan沒找到)。然后再判斷一下鍵值是否為0,如何非0,則上報按鍵。如果是0,就是不知道的鍵按下(你會發(fā)現(xiàn)鍵碼表中除了前4個還是有許多0的)。

?????????????? 判斷松開:第二個for循環(huán)里的第一個if里面

?????????????? 和判斷按下類似,如果你松開,你的new[]數(shù)組里面全是0,肯定和上次的old[]不一樣,所以會進入if();然后繼續(xù)判斷是否是0,如果非0,上報按鍵已經(jīng)松開。注意第二次判斷用的都是old,因為你要上報的是上次是否松開,這次的new里面全是0.


四.程序改寫

?????????? 版本V1.2:

static void usb_keyboard_irq(struct urb *urb) {int i;/*主要是上報是否按下了29, 42, 56,125, 97, 54,100,126這幾個鍵碼對應的鍵*也就是 Left Control、 Left Shift 、Left Alt 、 Left GUI 、 Right Control、 Right Shift 、 Right Alt 、 Right GUI *這幾個建。因為要處理大小寫等問題。要先判斷是否按下他們,然后再判斷是否按下其余按鍵*/ for (i = 0; i < 8; i++)input_report_key(uk_dev, usb_kbd_keycode[i + 224], (usb_buf[0] >> i) & 1);/*因為bit0上面已經(jīng)寫出,bit1保留,其余的按鍵在bit2到7*/for (i = 2; i < len; i++){if (pre_val!= usb_buf[i]) //判斷兩次值是否相等(松開和按下肯等都不會相等){/* 按下時候數(shù)組中不等于0的那個才能元素進來,進來后直接進入if*在if中上報鍵值,并把鍵值給pre_val_change用來松手判斷(如果直接賦值給pre_val會有BUG)*在沒有松按鍵時會一直打印(實現(xiàn)重復事件)*/if(usb_buf[i]!=0) {input_report_key(uk_dev, usb_kbd_keycode[usb_buf[i]], 1);input_sync(uk_dev);pre_val_change=usb_buf[i]; }/*當松手時,usb_buf[i]值全為0,所以必然進入大if中,*由于每個值都等于0,所以每次都會進入這個else(進入8次)*進的最后要清零pre_val_change,要不然下次你再按此鍵值的時候*pre_val!= usb_buf[i] 每次都會上報一次松開此鍵的BUG*/else{input_report_key(uk_dev, usb_kbd_keycode[pre_val], 0);input_sync(uk_dev);pre_val_change=0; //此處效果相同(pre_val_change=usb_buf[i];)要清零,否則會出現(xiàn)下次再按此鍵按不了的情況。}} }pre_val=pre_val_change; //將change值賦給per_val用來判斷是否按下/* 重新提交urb */usb_submit_urb(uk_urb, GFP_ATOMIC); }

?????? 看了上圖你或許能夠清楚的明白程序了,但是這里你會發(fā)現(xiàn)我引入了一個pre_val_change變量,因為我怕繼續(xù)循環(huán)會造成BUG,因為當你上報按下還是松開的時候循環(huán)并沒有結(jié)束(調(diào)這個BUG調(diào)了好久,才發(fā)現(xiàn)的),這將帶來冗余的循環(huán)。(但對效率影響不大)




總結(jié)

以上是生活随笔為你收集整理的大话USB驱动之USB键盘驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。