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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下的USB总线驱动(04)——USB键盘驱动 usbkbd.c

發布時間:2024/4/17 linux 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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,???????//驅動設備ID表,用來指定設備或接口??
  • };??
  • 下面跟蹤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);????//通過接口獲取USB設備指針??
  • ????struct?usb_host_interface?*interface;???????????????????//設置??
  • ????struct?usb_endpoint_descriptor?*endpoint;???????????????//端點描述符??
  • ????struct?usb_kbd?*kbd;????????????????????????????????????//usb_kbd私有數據??
  • ????struct?input_dev?*input_dev;????????????????????????????//input設備??
  • ????int?i,?pipe,?maxp;??
  • ????int?error?=?-ENOMEM;??
  • ??
  • ????interface?=?iface->cur_altsetting;???????????????????????//獲取設置??
  • ??
  • ????if?(interface->desc.bNumEndpoints?!=?1)??????????????????//與mouse一樣只有一個端點????
  • ????????return?-ENODEV;??
  • ??
  • ????endpoint?=?&interface->endpoint[0].desc;?????????????//獲取端點描述符??
  • ????if?(!usb_endpoint_is_int_in(endpoint))??????????????????//檢查端點是否為中斷輸入端點??
  • ????????return?-ENODEV;??
  • ??
  • ????pipe?=?usb_rcvintpipe(dev,?endpoint->bEndpointAddress);??//將endpoint設置為中斷IN端點??
  • ????maxp?=?usb_maxpacket(dev,?pipe,?usb_pipeout(pipe));?????//端點傳輸的最大數據包??
  • ??
  • ????kbd?=?kzalloc(sizeof(struct?usb_kbd),?GFP_KERNEL);??????//分配urb??
  • ????input_dev?=?input_allocate_device();????????????????????//分配input設備空間??
  • ????if?(!kbd?||?!input_dev)??
  • ????????goto?fail1;??
  • ??
  • ????if?(usb_kbd_alloc_mem(dev,?kbd))????????????????????????//分配urb空間和其他緩沖區??
  • ????????goto?fail2;??
  • ??
  • ????kbd->usbdev?=?dev;???????????????????????????????????????//給內嵌結構體賦值??
  • ????kbd->dev?=?input_dev;??
  • ??
  • ????if?(dev->manufacturer)???//拷貝廠商ID??
  • ????????strlcpy(kbd->name,?dev->manufacturer,?sizeof(kbd->name));??
  • ??
  • ????if?(dev->product)?{??????//拷貝產品ID??
  • ????????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結構體賦值??
  • ????input_dev->phys?=?kbd->phys;??
  • ????usb_to_input_id(dev,?&input_dev->id);????//拷貝usb_driver的支持給input,設置bustype,vendo,product等??
  • ????input_dev->dev.parent?=?&iface->dev;??
  • ??
  • ????input_set_drvdata(input_dev,?kbd);??????//將kbd設置為input的私有數據??
  • ??
  • ????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);?????????????????//EV_LED事件支持的事件碼??
  • ??
  • ????for?(i?=?0;?i?<?255;?i++)??
  • ????????set_bit(usb_kbd_keycode[i],?input_dev->keybit);??//EV_KEY事件支持的事件碼(即設置支持的鍵盤碼)??
  • ????clear_bit(0,?input_dev->keybit);??
  • ??
  • ????input_dev->event?=?usb_kbd_event;????????//定義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);//填充中斷urb??
  • ????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);//填充控制urb??
  • ????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)))??????//分配中斷urb??
  • ????????return?-1;??
  • ????if?(!(kbd->led?=?usb_alloc_urb(0,?GFP_KERNEL)))??????//分配控制urb??
  • ????????return?-1;??
  • ????if?(!(kbd->new?=?usb_alloc_coherent(dev,?8,?GFP_ATOMIC,?&kbd->new_dma)))??
  • ????????return?-1;??????//分配中斷urb使用的緩沖區??
  • ????if?(!(kbd->cr?=?kmalloc(sizeof(struct?usb_ctrlrequest),?GFP_KERNEL)))??
  • ????????return?-1;??????//分配控制urb使用的控制請求描述符??
  • ????if?(!(kbd->leds?=?usb_alloc_coherent(dev,?1,?GFP_ATOMIC,?&kbd->leds_dma)))??
  • ????????return?-1;??????//分配控制urb使用的緩沖區??
  • ??
  • ????return?0;??
  • }??
  • ?

    這里我們需要明白中斷urb和控制urb需要分配不同的urb結構體,同時在提交urb之前,需要填充的內容也不同,中斷urb填充的是緩沖區和中斷處理函數控制urb填充的是控制請求描述符與回調函數

    設置控制請求的格式。cr是struct usb_ctrlrequest結構的指針,USB協議中規定一個控制請求的格式為一個8個字節的數據包,其定義如下

    ?

    [cpp]?view plaincopy
  • /**?
  • ?*?struct?usb_ctrlrequest?-?SETUP?data?for?a?USB?device?control?request?
  • ?*?@bRequestType:?matches?the?USB?bmRequestType?field?
  • ?*?@bRequest:?matches?the?USB?bRequest?field?
  • ?*?@wValue:?matches?the?USB?wValue?field?(le16?byte?order)?
  • ?*?@wIndex:?matches?the?USB?wIndex?field?(le16?byte?order)?
  • ?*?@wLength:?matches?the?USB?wLength?field?(le16?byte?order)?
  • ?*?
  • ?*?This?structure?is?used?to?send?control?requests?to?a?USB?device.??It?matches?
  • ?*?the?different?fields?of?the?USB?2.0?Spec?section?9.3,?table?9-2.??See?the?
  • ?*?USB?spec?for?a?fuller?description?of?the?different?fields,?and?what?they?are?
  • ?*?used?for.?
  • ?*?
  • ?*?Note?that?the?driver?for?any?interface?can?issue?control?requests.?
  • ?*?For?most?devices,?interfaces?don't?coordinate?with?each?other,?so?
  • ?*?such?requests?may?be?made?at?any?time.?
  • ?*/??
  • 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:?????????/*?success?*/??
  • ????????break;??
  • ????case?-ECONNRESET:???/*?unlink?*/??
  • ????case?-ENOENT:??
  • ????case?-ESHUTDOWN:??
  • ????????return;??
  • ????/*?-EPIPE:??should?clear?the?halt?*/??
  • ????default:????????/*?error?*/??
  • ????????goto?resubmit;??
  • ????}??
  • ????//報告usb_kbd_keycode[224..231]8按鍵狀態??
  • ????//KEY_LEFTCTRL,KEY_LEFTSHIFT,KEY_LEFTALT,KEY_LEFTMETA,??
  • ????//KEY_RIGHTCTRL,KEY_RIGHTSHIFT,KEY_RIGHTALT,KEY_RIGHTMETA??
  • ????for?(i?=?0;?i?<?8;?i++)??
  • ????????input_report_key(kbd->dev,?usb_kbd_keycode[i?+?224],?(kbd->new[0]?>>?i)?&?1);??
  • ????//若同時只按下1個按鍵則在第[2]個字節,若同時有兩個按鍵則第二個在第[3]字節,類推最多可有6個按鍵同時按下??
  • ????for?(i?=?2;?i?<?8;?i++)?{??
  • ????????//獲取鍵盤離開的中斷??
  • ????????????//同時沒有該KEY的按下狀態??
  • ????????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]);??
  • ????????}??
  • ????????//獲取鍵盤按下的中斷??
  • ????????????//同時沒有該KEY的離開狀態??
  • ????????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)//不支持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");??
  • ????//提交控制urb??
  • ????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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。