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

本文以 usbkbd.c 为例,分析 usb 键盘驱动程序。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static int __init usb_kbd_init(void)  
  2. {  
  3.     int result = usb_register(&usb_kbd_driver);  
  4.     if (result == 0)  
  5.         printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"  
  6.                 DRIVER_DESC "\n");  
  7.     return result;  
  8. }  
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static struct usb_driver usb_kbd_driver = {  
  2.     .name =     "usbkbd",  
  3.     .probe =    usb_kbd_probe,  
  4.     .disconnect =   usb_kbd_disconnect,  
  5.     .id_table = usb_kbd_id_table,  
  6. };  
    还是来看一下 id_table ,与鼠标相比,仅仅是协议不一样。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static struct usb_device_id usb_kbd_id_table [] = {  
  2.     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,  
  3.         USB_INTERFACE_PROTOCOL_KEYBOARD) },  
  4.     { }                     /* Terminating entry */  
  5. };  

    下面来看probe函数

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static int usb_kbd_probe(struct usb_interface *iface,  
  2.              const struct usb_device_id *id)  
  3. {  
  4.     /* 获得usb_device */  
  5.     struct usb_device *dev = interface_to_usbdev(iface);  
  6.       
  7.     /* 接口的设置 */  
  8.     struct usb_host_interface *interface;  
  9.       
  10.     /* 端点描述符 */  
  11.     struct usb_endpoint_descriptor *endpoint;  
  12.       
  13.     /* 键盘结构体 */  
  14.     struct usb_kbd *kbd;  
  15.       
  16.     /* 输入设备 */  
  17.     struct input_dev *input_dev;  
  18.       
  19.     int i, pipe, maxp;  
  20.     int error = -ENOMEM;  
  21.       
  22.     /* 获取该接口当前的设置 */  
  23.     interface = iface->cur_altsetting;  
  24.       
  25.     /* 如果当前设置的端点数量不是1,那么错误,返回 */  
  26.     if (interface->desc.bNumEndpoints != 1)  
  27.         return -ENODEV;  
  28.       
  29.     /* 如果当前设置第一个端点的类型不是中断端点,错误,返回 */  
  30.     endpoint = &interface->endpoint[0].desc;  
  31.     if (!usb_endpoint_is_int_in(endpoint))  
  32.         return -ENODEV;  
  33.       
  34.     /* 第一个端点的管道 */  
  35.     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  
  36.       
  37.     /* 最大传输包大小 */  
  38.     maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));  
  39.       
  40.     /* 为键盘结构体分配空间 */  
  41.     kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);  
  42.       
  43.     /* 分配一个输入设备 */  
  44.     input_dev = input_allocate_device();  
  45.       
  46.     /*  
  47.         kbd->irq = usb_alloc_urb(0, GFP_KERNEL) 
  48.         kbd->led = usb_alloc_urb(0, GFP_KERNEL) 
  49.         kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma) 
  50.         kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma) 
  51.         kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma) 
  52.     */  
  53.     if (usb_kbd_alloc_mem(dev, kbd))  
  54.         goto fail2;  
  55.   
  56.     /* 填充键盘结构体。以及一些字符串 */  
  57.     kbd->usbdev = dev;  
  58.     kbd->dev = input_dev;  
  59.   
  60.     if (dev->manufacturer)  
  61.         strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));  
  62.   
  63.     if (dev->product) {  
  64.         if (dev->manufacturer)  
  65.             strlcat(kbd->name, " "sizeof(kbd->name));  
  66.         strlcat(kbd->name, dev->product, sizeof(kbd->name));  
  67.     }  
  68.   
  69.     if (!strlen(kbd->name))  
  70.         snprintf(kbd->name, sizeof(kbd->name),  
  71.              "USB HIDBP Keyboard %04x:%04x",  
  72.              le16_to_cpu(dev->descriptor.idVendor),  
  73.              le16_to_cpu(dev->descriptor.idProduct));  
  74.   
  75.     usb_make_path(dev, kbd->phys, sizeof(kbd->phys));  
  76.     strlcpy(kbd->phys, "/input0"sizeof(kbd->phys));  
  77.   
  78.     /* 填充输入设备 */  
  79.     input_dev->name = kbd->name;  
  80.     input_dev->phys = kbd->phys;  
  81.     usb_to_input_id(dev, &input_dev->id);  
  82.     input_dev->dev.parent = &iface->dev;  
  83.   
  84.     input_set_drvdata(input_dev, kbd);  
  85.       
  86.     /* 设置它支持的事件类型和具体事件 1、按键类事件 2、LED灯(大小写灯等)3、重复上报*/  
  87.     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |  
  88.         BIT_MASK(EV_REP);  
  89.     input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |  
  90.         BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |  
  91.         BIT_MASK(LED_KANA);  
  92.   
  93.     for (i = 0; i < 255; i++)  
  94.         set_bit(usb_kbd_keycode[i], input_dev->keybit);  
  95.     clear_bit(0, input_dev->keybit);  
  96.   
  97.     /* 对于LED类型的事件,首先会调用到dev->event 然后再调用事件处理层的event */  
  98.     input_dev->event = usb_kbd_event;  
  99.     input_dev->open = usb_kbd_open;  
  100.     input_dev->close = usb_kbd_close;  
  101.       
  102.     /* 填充中断类型Urb */  
  103.     usb_fill_int_urb(kbd->irq, dev, pipe,  
  104.              kbd->new, (maxp > 8 ? 8 : maxp),  
  105.              usb_kbd_irq, kbd, endpoint->bInterval);  
  106.     kbd->irq->transfer_dma = kbd->new_dma;  
  107.     kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  
  108.       
  109.     /*  
  110.      *  bit7    控制传输 data 阶段的方向 0 主机到设备 1设备到主机, 这里 0 主机到设备 
  111.      *  bit5-6  表示 request 类型,是标准的还是厂家定义的,          这里为hid class定义 
  112.      *  bit0-4  表示这个请求针对的是设备、接口还是端点 ,           这里是接口 
  113.      */  
  114.     kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE; // 0x01 << 5 |   
  115.       
  116.     /* 
  117.         #define USB_REQ_GET_STATUS      0x00 
  118.         #define USB_REQ_CLEAR_FEATURE       0x01 
  119.         #define USB_REQ_SET_FEATURE     0x03 
  120.         #define USB_REQ_SET_ADDRESS     0x05 
  121.         #define USB_REQ_GET_DESCRIPTOR      0x06 
  122.         #define USB_REQ_SET_DESCRIPTOR      0x07 
  123.         #define USB_REQ_GET_CONFIGURATION   0x08 
  124.         #define USB_REQ_SET_CONFIGURATION   0x09 
  125.         #define USB_REQ_GET_INTERFACE       0x0A 
  126.         #define USB_REQ_SET_INTERFACE       0x0B 
  127.         #define USB_REQ_SYNCH_FRAME     0x0C 
  128.     */  
  129.     kbd->cr->bRequest = USB_REQ_SET_CONFIGURATION;  
  130.       
  131.     /* request 的参数 */  
  132.     kbd->cr->wValue = cpu_to_le16(0x200);  
  133.       
  134.     /* bRequestType 中,针对接口、端点时,它表示那个接口或端点 */  
  135.     kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);  
  136.       
  137.     /* data阶段数据长度 */  
  138.     kbd->cr->wLength = cpu_to_le16(1);  
  139.       
  140.     /*  
  141.     static inline void usb_fill_control_urb( 
  142.                     struct urb *urb, 
  143.                     struct usb_device *dev, 
  144.                     unsigned int pipe, 
  145.                     unsigned char *setup_packet, 
  146.                     void *transfer_buffer, 
  147.                     int buffer_length, 
  148.                     usb_complete_t complete_fn, 
  149.                     void *context)  
  150.     */  
  151.     /* 这里使用的是默认端点0 */  
  152.     usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),(void *) kbd->cr, kbd->leds, 1, usb_kbd_led, kbd);  
  153.     kbd->led->setup_dma = kbd->cr_dma;  
  154.     kbd->led->transfer_dma = kbd->leds_dma;  
  155.     kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);  
  156.   
  157.     error = input_register_device(kbd->dev);  
  158.   
  159.     usb_set_intfdata(iface, kbd);  
  160.     return 0;  
  161. }  
    这里与鼠标驱动程序相比,多了一个控制传输过程,而且,这个控制传输的 bRequestType 中说明了这个传输不是标准的请求,而是Class,我们的键盘是HID类型,因此还要看USB HID协议中,关于这个请求是如何定义的,才能知道wValue 、wIndex等是什么意思(参考:http://blog.csdn.net/leo_wonty/article/details/6721214),这就是就将knd->leds 内的一字节数据发送给从设备。在鼠标驱动程序中,中断 urb 是在open函数中提交的,这里也不例外。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static int usb_kbd_open(struct input_dev *dev)  
  2. {  
  3.     struct usb_kbd *kbd = input_get_drvdata(dev);  
  4.   
  5.     kbd->irq->dev = kbd->usbdev;  
  6.     if (usb_submit_urb(kbd->irq, GFP_KERNEL))  
  7.         return -EIO;  
  8.   
  9.     return 0;  
  10. }  
    中断传输完成之后会调用完成函数,usb_kbd_irq
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static void usb_kbd_irq(struct urb *urb)  
  2. {  
  3.     struct usb_kbd *kbd = urb->context;  
  4.     int i;  
  5.   
  6.     switch (urb->status) {  
  7.     case 0:         /* success */  
  8.         break;  
  9.     case -ECONNRESET:   /* unlink */  
  10.     case -ENOENT:  
  11.     case -ESHUTDOWN:  
  12.         return;  
  13.     /* -EPIPE:  should clear the halt */  
  14.     default:        /* error */  
  15.         goto resubmit;  
  16.     }  
  17.     //报告usb_kbd_keycode[224..231]8按键状态  
  18.     //KEY_LEFTCTRL,KEY_LEFTSHIFT,KEY_LEFTALT,KEY_LEFTMETA,  
  19.     //KEY_RIGHTCTRL,KEY_RIGHTSHIFT,KEY_RIGHTALT,KEY_RIGHTMETA  
  20.     for (i = 0; i < 8; i++)  
  21.         input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);  
  22.     //若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下  
  23.     for (i = 2; i < 8; i++) {  
  24.         //获取键盘离开的中断  
  25.             //同时没有该KEY的按下状态  
  26.         if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {  
  27.             if (usb_kbd_keycode[kbd->old[i]])  
  28.                 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);  
  29.             else  
  30.                 hid_info(urb->dev,  
  31.                      "Unknown key (scancode %#x) released.\n",  
  32.                      kbd->old[i]);  
  33.         }  
  34.         //获取键盘按下的中断  
  35.             //同时没有该KEY的离开状态  
  36.         if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {  
  37.             if (usb_kbd_keycode[kbd->new[i]])  
  38.                 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);  
  39.             else  
  40.                 hid_info(urb->dev,  
  41.                      "Unknown key (scancode %#x) released.\n",  
  42.                      kbd->new[i]);  
  43.         }  
  44.     }  
  45.   
  46.     input_sync(kbd->dev);            //同步设备,告知事件的接收者驱动已经发出了一个完整的报告  
  47.   
  48.     memcpy(kbd->old, kbd->new, 8);    //防止未松开时被当成新的按键处理  
  49.   
  50. resubmit:  
  51.     i = usb_submit_urb (urb, GFP_ATOMIC);  
  52.     if (i)  
  53.         hid_err(urb->dev, "can't resubmit intr, %s-%s/input0, status %d",  
  54.             kbd->usbdev->bus->bus_name,  
  55.             kbd->usbdev->devpath, i);  
  56. }  
    这里,都是上报的按键类事件,我们在前边的Probe函数中,设置了输入设备支持按键类事件,还有一个LED类事件,但是搜遍代码也没有找到,LED类事件是在哪里上报的。还有,probe函数中定义了一个dev->event函数,在浏览资料时发现,有些人说在input_event时,调用事件处理层的event函数的同时会调用dev->event,我认为这种说法是不正确的,看过input_event代码的同学应该不难发现,只有上报LED类等事件的时候才会触发dev->event,我们这里单纯上报的按键类事件并不会触发dev->event 。那么,probe 函数里定义的 dev->event 函数岂不是成了摆设么?确实,我暂时没发现它有什么用。手头没有usb键盘,这个后边实验证实。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static int usb_kbd_event(struct input_dev *dev, unsigned int type,  
  2.              unsigned int code, int value)  
  3. {  
  4.     struct usb_kbd *kbd = input_get_drvdata(dev);  
  5.   
  6.     if (type != EV_LED)  
  7.         return -1;  
  8.   
  9.     kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |  
  10.                (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |  
  11.                (!!test_bit(LED_NUML,    dev->led));  
  12.   
  13.     if (kbd->led->status == -EINPROGRESS)  
  14.         return 0;  
  15.   
  16.     if (*(kbd->leds) == kbd->newleds)  
  17.         return 0;  
  18.   
  19.     *(kbd->leds) = kbd->newleds;  
  20.     kbd->led->dev = kbd->usbdev;  
  21.     if (usb_submit_urb(kbd->led, GFP_ATOMIC))  
  22.         err_hid("usb_submit_urb(leds) failed");  
  23.   
  24.     return 0;  
  25. }  
    这里的 dev->led 记录的是led的状态,比如,我们上报一个LED事件时,input_event 中会将对应的事件记录在 dev->led 中。这里检测LED事件是否发生,然后通过控制传输将1字节数据传送给usb键盘,Usb键盘的灯相应做出改变。但是,说到底,代码里没有上报过LED类事件,一切都是白扯。
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static void usb_kbd_led(struct urb *urb)  
  2. {  
  3.     struct usb_kbd *kbd = urb->context;  
  4.   
  5.     if (urb->status)  
  6.         dev_warn(&urb->dev->dev, "led urb status %d received\n",  
  7.              urb->status);  
  8.   
  9.     if (*(kbd->leds) == kbd->newleds)  
  10.         return;  
  11.   
  12.     *(kbd->leds) = kbd->newleds;  
  13.     kbd->led->dev = kbd->usbdev;  
  14.     if (usb_submit_urb(kbd->led, GFP_ATOMIC))  
  15.         err_hid("usb_submit_urb(leds) failed");  
  16. }  
    这里的控制传输完成函数也是个累赘?每次有LED事件上报的话,那么控制传输urb就自动提交了。那么,*(kbd->leds)== kbd->newleds 必然是相等的,除非又有新的事件上报了,但是新事件上报时,在usb_kbd_event 函数里urb不就自动提交了么? 会出现不相等的情况?


    此篇文章存在诸多疑问,如果有大神看到,还请解答一下。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值