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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

USB驱动——键盘驱动(控制传输)

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

?

USB驅動——鍵盤驅動(控制傳輸)

http://blog.csdn.net/lizuobin2/ https://blog.csdn.net/lizuobin2/article/details/51971393

? ? 本文以 usbkbd.c 為例,分析 usb 鍵盤驅動程序。

?

  • 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;

  • }

  • static struct usb_driver usb_kbd_driver = {

  • .name = "usbkbd",

  • .probe = usb_kbd_probe,

  • .disconnect = usb_kbd_disconnect,

  • .id_table = usb_kbd_id_table,

  • };

  • ? ? 還是來看一下 id_table ,與鼠標相比,僅僅是協議不一樣。

    ?

    ?

  • static struct usb_device_id usb_kbd_id_table [] = {

  • { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,

  • USB_INTERFACE_PROTOCOL_KEYBOARD) },

  • { } /* Terminating entry */

  • };

  • ? ? 下面來看probe函數

    ?

  • static int usb_kbd_probe(struct usb_interface *iface,

  • const struct usb_device_id *id)

  • {

  • /* 獲得usb_device */

  • 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;

  • ?
  • /* 如果當前設置的端點數量不是1,那么錯誤,返回 */

  • 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();

  • ?
  • /*

  • kbd->irq = usb_alloc_urb(0, GFP_KERNEL)

  • kbd->led = usb_alloc_urb(0, GFP_KERNEL)

  • kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)

  • kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)

  • kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)

  • */

  • 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));

  • strlcpy(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);

  • ?
  • /* 設置它支持的事件類型和具體事件 1、按鍵類事件 2、LED燈(大小寫燈等)3、重復上報*/

  • 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);

  • ?
  • /* 對于LED類型的事件,首先會調用到dev->event 然后再調用事件處理層的event */

  • input_dev->event = usb_kbd_event;

  • input_dev->open = usb_kbd_open;

  • input_dev->close = usb_kbd_close;

  • ?
  • /* 填充中斷類型Urb */

  • 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;

  • ?
  • /*

  • * bit7 控制傳輸 data 階段的方向 0 主機到設備 1設備到主機, 這里 0 主機到設備

  • * bit5-6 表示 request 類型,是標準的還是廠家定義的, 這里為hid class定義

  • * bit0-4 表示這個請求針對的是設備、接口還是端點 , 這里是接口

  • */

  • kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; // 0x01 << 5 |

  • ?
  • /*

  • #define USB_REQ_GET_STATUS 0x00

  • #define USB_REQ_CLEAR_FEATURE 0x01

  • #define USB_REQ_SET_FEATURE 0x03

  • #define USB_REQ_SET_ADDRESS 0x05

  • #define USB_REQ_GET_DESCRIPTOR 0x06

  • #define USB_REQ_SET_DESCRIPTOR 0x07

  • #define USB_REQ_GET_CONFIGURATION 0x08

  • #define USB_REQ_SET_CONFIGURATION 0x09

  • #define USB_REQ_GET_INTERFACE 0x0A

  • #define USB_REQ_SET_INTERFACE 0x0B

  • #define USB_REQ_SYNCH_FRAME 0x0C

  • */

  • kbd->cr->bRequest = USB_REQ_SET_CONFIGURATION;

  • ?
  • /* request 的參數 */

  • kbd->cr->wValue = cpu_to_le16(0x200);

  • ?
  • /* bRequestType 中,針對接口、端點時,它表示那個接口或端點 */

  • kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);

  • ?
  • /* data階段數據長度 */

  • kbd->cr->wLength = cpu_to_le16(1);

  • ?
  • /*

  • static inline void usb_fill_control_urb(

  • struct urb *urb,

  • struct usb_device *dev,

  • unsigned int pipe,

  • unsigned char *setup_packet,

  • void *transfer_buffer,

  • int buffer_length,

  • usb_complete_t complete_fn,

  • void *context)

  • */

  • /* 這里使用的是默認端點0 */

  • usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1, usb_kbd_led, kbd);

  • kbd->led->setup_dma = kbd->cr_dma;

  • kbd->led->transfer_dma = kbd->leds_dma;

  • kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);

  • ?
  • error = input_register_device(kbd->dev);

  • ?
  • usb_set_intfdata(iface, kbd);

  • return 0;

  • }

  • ? ? 這里與鼠標驅動程序相比,多了一個控制傳輸過程,而且,這個控制傳輸的?bRequestType 中說明了這個傳輸不是標準的請求,而是Class,我們的鍵盤是HID類型,因此還要看USB HID協議中,關于這個請求是如何定義的,才能知道wValue 、wIndex等是什么意思(參考:http://blog.csdn.net/leo_wonty/article/details/6721214),這就是就將knd->leds 內的一字節數據發送給從設備。在鼠標驅動程序中,中斷 urb 是在open函數中提交的,這里也不例外。

    ?

    ?

  • 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;

  • }

  • ? ? 中斷傳輸完成之后會調用完成函數,usb_kbd_irq

  • 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);

  • }

  • ? ? 這里,都是上報的按鍵類事件,我們在前邊的Probe函數中,設置了輸入設備支持按鍵類事件,還有一個LED類事件,但是搜遍代碼也沒有找到,LED類事件是在哪里上報的。還有,probe函數中定義了一個dev->event函數,在瀏覽資料時發現,有些人說在input_event時,調用事件處理層的event函數的同時會調用dev->event,我認為這種說法是不正確的,看過input_event代碼的同學應該不難發現,只有上報LED類等事件的時候才會觸發dev->event,我們這里單純上報的按鍵類事件并不會觸發dev->event 。那么,probe 函數里定義的 dev->event 函數豈不是成了擺設么?確實,我暫時沒發現它有什么用。手頭沒有usb鍵盤,這個后邊實驗證實。

    ?

    ?

  • 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))

  • err_hid("usb_submit_urb(leds) failed");

  • ?
  • return 0;

  • }

  • ? ? 這里的 dev->led 記錄的是led的狀態,比如,我們上報一個LED事件時,input_event 中會將對應的事件記錄在 dev->led 中。這里檢測LED事件是否發生,然后通過控制傳輸將1字節數據傳送給usb鍵盤,Usb鍵盤的燈相應做出改變。但是,說到底,代碼里沒有上報過LED類事件,一切都是白扯。

  • static void usb_kbd_led(struct urb *urb)

  • {

  • struct usb_kbd *kbd = urb->context;

  • ?
  • if (urb->status)

  • dev_warn(&urb->dev->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))

  • err_hid("usb_submit_urb(leds) failed");

  • }

  • ? ? 這里的控制傳輸完成函數也是個累贅?每次有LED事件上報的話,那么控制傳輸urb就自動提交了。那么,*(kbd->leds)== kbd->newleds 必然是相等的,除非又有新的事件上報了,但是新事件上報時,在usb_kbd_event 函數里urb不就自動提交了么? 會出現不相等的情況?

    ?

    總結

    以上是生活随笔為你收集整理的USB驱动——键盘驱动(控制传输)的全部內容,希望文章能夠幫你解決所遇到的問題。

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