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

歡迎訪問 生活随笔!

生活随笔

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

linux

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

發(fā)布時間:2024/4/17 linux 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下的USB总线驱动(04)——USB键盘驱动 usbkbd.c 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文鏈接地址: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,???????//驅(qū)動設(shè)備ID表,用來指定設(shè)備或接口??
  • };??
  • 下面跟蹤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設(shè)備指針??
  • ????struct?usb_host_interface?*interface;???????????????????//設(shè)置??
  • ????struct?usb_endpoint_descriptor?*endpoint;???????????????//端點描述符??
  • ????struct?usb_kbd?*kbd;????????????????????????????????????//usb_kbd私有數(shù)據(jù)??
  • ????struct?input_dev?*input_dev;????????????????????????????//input設(shè)備??
  • ????int?i,?pipe,?maxp;??
  • ????int?error?=?-ENOMEM;??
  • ??
  • ????interface?=?iface->cur_altsetting;???????????????????????//獲取設(shè)置??
  • ??
  • ????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設(shè)置為中斷IN端點??
  • ????maxp?=?usb_maxpacket(dev,?pipe,?usb_pipeout(pipe));?????//端點傳輸?shù)淖畲髷?shù)據(jù)包??
  • ??
  • ????kbd?=?kzalloc(sizeof(struct?usb_kbd),?GFP_KERNEL);??????//分配urb??
  • ????input_dev?=?input_allocate_device();????????????????????//分配input設(shè)備空間??
  • ????if?(!kbd?||?!input_dev)??
  • ????????goto?fail1;??
  • ??
  • ????if?(usb_kbd_alloc_mem(dev,?kbd))????????????????????????//分配urb空間和其他緩沖區(qū)??
  • ????????goto?fail2;??
  • ??
  • ????kbd->usbdev?=?dev;???????????????????????????????????????//給內(nèi)嵌結(jié)構(gòu)體賦值??
  • ????kbd->dev?=?input_dev;??
  • ??
  • ????if?(dev->manufacturer)???//拷貝廠商ID??
  • ????????strlcpy(kbd->name,?dev->manufacturer,?sizeof(kbd->name));??
  • ??
  • ????if?(dev->product)?{??????//拷貝產(chǎn)品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));??
  • ????//設(shè)備鏈接地址??
  • ????usb_make_path(dev,?kbd->phys,?sizeof(kbd->phys));??
  • ????strlcat(kbd->phys,?"/input0",?sizeof(kbd->phys));??
  • ??
  • ????input_dev->name?=?kbd->name;??????????//給input_dev結(jié)構(gòu)體賦值??
  • ????input_dev->phys?=?kbd->phys;??
  • ????usb_to_input_id(dev,?&input_dev->id);????//拷貝usb_driver的支持給input,設(shè)置bustype,vendo,product等??
  • ????input_dev->dev.parent?=?&iface->dev;??
  • ??
  • ????input_set_drvdata(input_dev,?kbd);??????//將kbd設(shè)置為input的私有數(shù)據(jù)??
  • ??
  • ????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事件支持的事件碼(即設(shè)置支持的鍵盤碼)??
  • ????clear_bit(0,?input_dev->keybit);??
  • ??
  • ????input_dev->event?=?usb_kbd_event;????????//定義event函數(shù)??
  • ????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;//設(shè)置控制請求的格式??
  • ????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中,我們主要是初始化一些結(jié)構(gòu)體,然后提交中斷urb和控制urb,并注冊input設(shè)備。其中有幾個地方需要細看下,其一,usb_kbd_alloc_mem的實現(xiàn)。其二,設(shè)置控制請求的格式。

    先來看看usb_kbd_alloc_mem的實現(xiàn)

    ?

    [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使用的緩沖區(qū)??
  • ????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使用的緩沖區(qū)??
  • ??
  • ????return?0;??
  • }??
  • ?

    這里我們需要明白中斷urb和控制urb需要分配不同的urb結(jié)構(gòu)體,同時在提交urb之前,需要填充的內(nèi)容也不同,中斷urb填充的是緩沖區(qū)和中斷處理函數(shù)控制urb填充的是控制請求描述符與回調(diào)函數(shù)

    設(shè)置控制請求的格式。cr是struct usb_ctrlrequest結(jié)構(gòu)的指針,USB協(xié)議中規(guī)定一個控制請求的格式為一個8個字節(jié)的數(shù)據(jù)包,其定義如下

    ?

    [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;??//設(shè)定傳輸方向、請求類型等??
  • ????__u8?bRequest;??????//指定哪個請求,可以是規(guī)定的標準值也可以是廠家定義的值??
  • ????__le16?wValue;??????//即將寫到寄存器的數(shù)據(jù)??
  • ????__le16?wIndex;??????//接口數(shù)量,也就是寄存器的偏移地址??
  • ????__le16?wLength;?????//數(shù)據(jù)傳輸階段傳輸多少個字節(jié)??
  • }?__attribute__?((packed));??
  • ?

    USB協(xié)議中規(guī)定,所有的USB設(shè)備都會響應(yīng)主機的一些請求,這些請求來自USB主機控制器,主機控制器通過設(shè)備的默認控制管道發(fā)出這些請求。默認的管道為0號端口對應(yīng)的那個管道。

    同樣這個input設(shè)備首先由用戶層調(diào)用open函數(shù),所以先看看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;??
  • }??
  • 因為這個驅(qū)動里面有一個中斷urb一個控制urb,我們先看中斷urb的處理流程。中斷urb在input的open中被提交后,當USB core處理完畢,會通知這個USB設(shè)備驅(qū)動,然后執(zhí)行回調(diào)函數(shù),也就是中斷處理函數(shù)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按鍵狀態(tài)??
  • ????//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]個字節(jié),若同時有兩個按鍵則第二個在第[3]字節(jié),類推最多可有6個按鍵同時按下??
  • ????for?(i?=?2;?i?<?8;?i++)?{??
  • ????????//獲取鍵盤離開的中斷??
  • ????????????//同時沒有該KEY的按下狀態(tà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]);??
  • ????????}??
  • ????????//獲取鍵盤按下的中斷??
  • ????????????//同時沒有該KEY的離開狀態(tà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);????????????//同步設(shè)備,告知事件的接收者驅(qū)動已經(jīng)發(fā)出了一個完整的報告??
  • ??
  • ????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子系統(tǒng),如果有事件被響應(yīng),我們會調(diào)用事件處理層的event函數(shù),而該函數(shù)最終調(diào)用的是input下的event。所以,對于input設(shè)備,我們在USB鍵盤驅(qū)動中只設(shè)置了支持LED選項,也就是ledbit項,這是怎么回事呢?剛才我們分析的那個中斷urb其實跟這個 input基本沒啥關(guān)系,中斷urb并不是像講鍵盤input實現(xiàn)的那樣屬于input下的中斷。我們在USB鍵盤驅(qū)動中的input子系統(tǒng)中只設(shè)計了 LED選項,那么當input子系統(tǒng)有按鍵選項的時候必然會使得內(nèi)核調(diào)用調(diào)用事件處理層的event函數(shù),最終調(diào)用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;??
  • ????//獲取指示燈的目標狀態(tài)??
  • ????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;??
  • ????//指示燈狀態(tài)已經(jīng)是目標狀態(tài)則不需要再做任何操作??
  • ????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后,經(jīng)過URB處理流程,最后返回給USB設(shè)備驅(qū)動的回調(diào)函數(shù),也就是在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");??
  • }??
  • ?

    總結(jié)下,我們的控制urb走的是先由input的event提交,觸發(fā)后由控制urb的回調(diào)函數(shù)再次提交。好了,通過USB鼠標,我們已經(jīng)知道了控制urb和中斷urb的設(shè)計和處理流程。

    轉(zhuǎn)載于:https://www.cnblogs.com/MMLoveMeMM/articles/4105665.html

    總結(jié)

    以上是生活随笔為你收集整理的Linux下的USB总线驱动(04)——USB键盘驱动 usbkbd.c的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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