生活随笔
收集整理的這篇文章主要介紹了
Linux下的USB总线驱动(04)——USB键盘驱动 usbkbd.c
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原文鏈接地址:http://www.linuxidc.com/Linux/2012-12/76197p9.htm
?
跟USB鼠標類型一樣,USB鍵盤也屬于HID類型,代碼在/dirver/hid/usbhid/usbkbd.c下。USB鍵盤除了提交中斷URB外,還需要提交控制URB。不多話,我們看代碼
[cpp]?view plaincopy
static?int?__init?usb_kbd_init(void)??{??????int?result?=?usb_register(&usb_kbd_driver);??????if?(result?==?0)??????????printk(KERN_INFO?KBUILD_MODNAME?":?"?DRIVER_VERSION?":"??????????????????DRIVER_DESC?"\n");??????return?result;??}?? [cpp]?view plaincopy
static?struct?usb_driver?usb_kbd_driver?=?{??????.name?=?????"usbkbd",??????.probe?=????usb_kbd_probe,??????.disconnect?=???usb_kbd_disconnect,??????.id_table?=?usb_kbd_id_table,???????};?? 下面跟蹤usb_driver中的probe
?
[cpp]?view plaincopy
static?int?usb_kbd_probe(struct?usb_interface?*iface,???????????????const?struct?usb_device_id?*id)??{??????struct?usb_device?*dev?=?interface_to_usbdev(iface);????????struct?usb_host_interface?*interface;???????????????????????struct?usb_endpoint_descriptor?*endpoint;???????????????????struct?usb_kbd?*kbd;????????????????????????????????????????struct?input_dev?*input_dev;????????????????????????????????int?i,?pipe,?maxp;??????int?error?=?-ENOMEM;????????interface?=?iface->cur_altsetting;?????????????????????????????if?(interface->desc.bNumEndpoints?!=?1)??????????????????????????return?-ENODEV;????????endpoint?=?&interface->endpoint[0].desc;?????????????????if?(!usb_endpoint_is_int_in(endpoint))??????????????????????????return?-ENODEV;????????pipe?=?usb_rcvintpipe(dev,?endpoint->bEndpointAddress);??????maxp?=?usb_maxpacket(dev,?pipe,?usb_pipeout(pipe));???????????kbd?=?kzalloc(sizeof(struct?usb_kbd),?GFP_KERNEL);??????????input_dev?=?input_allocate_device();????????????????????????if?(!kbd?||?!input_dev)??????????goto?fail1;????????if?(usb_kbd_alloc_mem(dev,?kbd))????????????????????????????????goto?fail2;????????kbd->usbdev?=?dev;???????????????????????????????????????????kbd->dev?=?input_dev;????????if?(dev->manufacturer)???????????strlcpy(kbd->name,?dev->manufacturer,?sizeof(kbd->name));????????if?(dev->product)?{??????????????if?(dev->manufacturer)??????????????strlcat(kbd->name,?"?",?sizeof(kbd->name));??????????strlcat(kbd->name,?dev->product,?sizeof(kbd->name));??????}????????if?(!strlen(kbd->name))??????????snprintf(kbd->name,?sizeof(kbd->name),???????????????"USB?HIDBP?Keyboard?%04x:%04x",???????????????le16_to_cpu(dev->descriptor.idVendor),???????????????le16_to_cpu(dev->descriptor.idProduct));??????????usb_make_path(dev,?kbd->phys,?sizeof(kbd->phys));??????strlcat(kbd->phys,?"/input0",?sizeof(kbd->phys));????????input_dev->name?=?kbd->name;??????????????input_dev->phys?=?kbd->phys;??????usb_to_input_id(dev,?&input_dev->id);????????input_dev->dev.parent?=?&iface->dev;????????input_set_drvdata(input_dev,?kbd);????????????input_dev->evbit[0]?=?BIT_MASK(EV_KEY)?|?BIT_MASK(EV_LED)?|??????????BIT_MASK(EV_REP);???????????????????????input_dev->ledbit[0]?=?BIT_MASK(LED_NUML)?|?BIT_MASK(LED_CAPSL)?|??????????BIT_MASK(LED_SCROLLL)?|?BIT_MASK(LED_COMPOSE)?|??????????BIT_MASK(LED_KANA);???????????????????????for?(i?=?0;?i?<?255;?i++)??????????set_bit(usb_kbd_keycode[i],?input_dev->keybit);??????clear_bit(0,?input_dev->keybit);????????input_dev->event?=?usb_kbd_event;????????????input_dev->open?=?usb_kbd_open;??????input_dev->close?=?usb_kbd_close;????????usb_fill_int_urb(kbd->irq,?dev,?pipe,???????????????kbd->new,?(maxp?>?8???8?:?maxp),???????????????usb_kbd_irq,?kbd,?endpoint->bInterval);????kbd->irq->transfer_dma?=?kbd->new_dma;??????kbd->irq->transfer_flags?|=?URB_NO_TRANSFER_DMA_MAP;????????kbd->cr->bRequestType?=?USB_TYPE_CLASS?|?USB_RECIP_INTERFACE;??????kbd->cr->bRequest?=?0x09;????kbd->cr->wValue?=?cpu_to_le16(0x200);??????kbd->cr->wIndex?=?cpu_to_le16(interface->desc.bInterfaceNumber);??????kbd->cr->wLength?=?cpu_to_le16(1);????????usb_fill_control_urb(kbd->led,?dev,?usb_sndctrlpipe(dev,?0),???????????????????(void?*)?kbd->cr,?kbd->leds,?1,???????????????????usb_kbd_led,?kbd);????kbd->led->transfer_dma?=?kbd->leds_dma;??????kbd->led->transfer_flags?|=?URB_NO_TRANSFER_DMA_MAP;????????error?=?input_register_device(kbd->dev);??????if?(error)??????????goto?fail2;????????usb_set_intfdata(iface,?kbd);??????device_set_wakeup_enable(&dev->dev,?1);??????return?0;????fail2:????????usb_kbd_free_mem(dev,?kbd);??fail1:????????input_free_device(input_dev);??????kfree(kbd);??????return?error;??}?? ?
在上面的probe中,我們主要是初始化一些結構體,然后提交中斷urb和控制urb,并注冊input設備。其中有幾個地方需要細看下,其一,usb_kbd_alloc_mem的實現。其二,設置控制請求的格式。
先來看看usb_kbd_alloc_mem的實現
?
[cpp]?view plaincopy
static?int?usb_kbd_alloc_mem(struct?usb_device?*dev,?struct?usb_kbd?*kbd)??{??????if?(!(kbd->irq?=?usb_alloc_urb(0,?GFP_KERNEL)))??????????????return?-1;??????if?(!(kbd->led?=?usb_alloc_urb(0,?GFP_KERNEL)))??????????????return?-1;??????if?(!(kbd->new?=?usb_alloc_coherent(dev,?8,?GFP_ATOMIC,?&kbd->new_dma)))??????????return?-1;??????????if?(!(kbd->cr?=?kmalloc(sizeof(struct?usb_ctrlrequest),?GFP_KERNEL)))??????????return?-1;??????????if?(!(kbd->leds?=?usb_alloc_coherent(dev,?1,?GFP_ATOMIC,?&kbd->leds_dma)))??????????return?-1;????????????return?0;??}?? ?
這里我們需要明白中斷urb和控制urb需要分配不同的urb結構體,同時在提交urb之前,需要填充的內容也不同,中斷urb填充的是緩沖區和中斷處理函數,控制urb填充的是控制請求描述符與回調函數。
設置控制請求的格式。cr是struct usb_ctrlrequest結構的指針,USB協議中規定一個控制請求的格式為一個8個字節的數據包,其定義如下
?
[cpp]?view plaincopy
struct?usb_ctrlrequest?{??????__u8?bRequestType;??????__u8?bRequest;??????????__le16?wValue;??????????__le16?wIndex;??????????__le16?wLength;?????}?__attribute__?((packed));?? ?
USB協議中規定,所有的USB設備都會響應主機的一些請求,這些請求來自USB主機控制器,主機控制器通過設備的默認控制管道發出這些請求。默認的管道為0號端口對應的那個管道。
同樣這個input設備首先由用戶層調用open函數,所以先看看input中定義的open
?
[cpp]?view plaincopy
static?int?usb_kbd_open(struct?input_dev?*dev)??{??????struct?usb_kbd?*kbd?=?input_get_drvdata(dev);????????kbd->irq->dev?=?kbd->usbdev;??????if?(usb_submit_urb(kbd->irq,?GFP_KERNEL))??????????return?-EIO;????????return?0;??}?? 因為這個驅動里面有一個中斷urb一個控制urb,我們先看中斷urb的處理流程。中斷urb在input的open中被提交后,當USB core處理完畢,會通知這個USB設備驅動,然后執行回調函數,也就是中斷處理函數usb_kbd_irq
?
?
[cpp]?view plaincopy
static?void?usb_kbd_irq(struct?urb?*urb)??{??????struct?usb_kbd?*kbd?=?urb->context;??????int?i;????????switch?(urb->status)?{??????case?0:?????????????????break;??????case?-ECONNRESET:???????case?-ENOENT:??????case?-ESHUTDOWN:??????????return;??????????default:????????????????goto?resubmit;??????}??????????????????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);??????????????else??????????????????hid_info(urb->dev,???????????????????????"Unknown?key?(scancode?%#x)?released.\n",???????????????????????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);??????????????else??????????????????hid_info(urb->dev,???????????????????????"Unknown?key?(scancode?%#x)?released.\n",???????????????????????kbd->new[i]);??????????}??????}????????input_sync(kbd->dev);??????????????????memcpy(kbd->old,?kbd->new,?8);??????resubmit:??????i?=?usb_submit_urb?(urb,?GFP_ATOMIC);??????if?(i)??????????hid_err(urb->dev,?"can't?resubmit?intr,?%s-%s/input0,?status?%d",??????????????kbd->usbdev->bus->bus_name,??????????????kbd->usbdev->devpath,?i);??}?? ?
這個就是中斷urb的處理流程,跟前面講的的USB鼠標中斷處理流程類似。好了,我們再來看看剩下的控制urb處理流程吧。
我們有個疑問,我們知道在probe中,我們填充了中斷urb和控制urb,但是在input的open中,我們只提交了中斷urb,那么控制urb什么時候提交呢?
我們知道對于input子系統,如果有事件被響應,我們會調用事件處理層的event函數,而該函數最終調用的是input下的event。所以,對于input設備,我們在USB鍵盤驅動中只設置了支持LED選項,也就是ledbit項,這是怎么回事呢?剛才我們分析的那個中斷urb其實跟這個 input基本沒啥關系,中斷urb并不是像講鍵盤input實現的那樣屬于input下的中斷。我們在USB鍵盤驅動中的input子系統中只設計了 LED選項,那么當input子系統有按鍵選項的時候必然會使得內核調用調用事件處理層的event函數,最終調用input下的event。好了,那我們來看看input下的event干了些什么。
[cpp]?view plaincopy
static?int?usb_kbd_event(struct?input_dev?*dev,?unsigned?int?type,???????????????unsigned?int?code,?int?value)??{??????struct?usb_kbd?*kbd?=?input_get_drvdata(dev);????????if?(type?!=?EV_LED)????????return?-1;??????????kbd->newleds?=?(!!test_bit(LED_KANA,????dev->led)?<<?3)?|?(!!test_bit(LED_COMPOSE,?dev->led)?<<?3)?|?????????????????(!!test_bit(LED_SCROLLL,?dev->led)?<<?2)?|?(!!test_bit(LED_CAPSL,???dev->led)?<<?1)?|?????????????????(!!test_bit(LED_NUML,????dev->led));????????if?(kbd->led->status?==?-EINPROGRESS)??????????return?0;??????????if?(*(kbd->leds)?==?kbd->newleds)??????????return?0;????????*(kbd->leds)?=?kbd->newleds;??????kbd->led->dev?=?kbd->usbdev;??????if?(usb_submit_urb(kbd->led,?GFP_ATOMIC))??????????pr_err("usb_submit_urb(leds)?failed\n");??????????return?0;??}?? 當在input的event里提交了控制urb后,經過URB處理流程,最后返回給USB設備驅動的回調函數,也就是在probe中定義的usb_kbd_led
?
[cpp]?view plaincopy
static?void?usb_kbd_led(struct?urb?*urb)??{??????struct?usb_kbd?*kbd?=?urb->context;????????if?(urb->status)??????????hid_warn(urb->dev,?"led?urb?status?%d?received\n",???????????????urb->status);????????if?(*(kbd->leds)?==?kbd->newleds)??????????return;????????*(kbd->leds)?=?kbd->newleds;??????kbd->led->dev?=?kbd->usbdev;??????if?(usb_submit_urb(kbd->led,?GFP_ATOMIC))??????????hid_err(urb->dev,?"usb_submit_urb(leds)?failed\n");??}?? ?
總結下,我們的控制urb走的是先由input的event提交,觸發后由控制urb的回調函數再次提交。好了,通過USB鼠標,我們已經知道了控制urb和中斷urb的設計和處理流程。
轉載于:https://www.cnblogs.com/MMLoveMeMM/articles/4105665.html
總結
以上是生活随笔為你收集整理的Linux下的USB总线驱动(04)——USB键盘驱动 usbkbd.c的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。