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

时间:2024-02-23 10:57:25

原文链接地址:http://www.linuxidc.com/Linux/2012-12/76197p9.htm

 

跟USB鼠标类型一样,USB键盘也属于HID类型,代码在/dirver/hid/usbhid/usbkbd.c下。USB键盘除了提交中断URB外,还需要提交控制URB。不多话,我们看代码

  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. }  
  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,       //驱动设备ID表,用来指定设备或接口  
  6. };  

下面跟踪usb_driver中的probe

 

  1. static int usb_kbd_probe(struct usb_interface *iface,  
  2.              const struct usb_device_id *id)  
  3. {  
  4.     struct usb_device *dev = interface_to_usbdev(iface);    //通过接口获取USB设备指针  
  5.     struct usb_host_interface *interface;                   //设置  
  6.     struct usb_endpoint_descriptor *endpoint;               //端点描述符  
  7.     struct usb_kbd *kbd;                                    //usb_kbd私有数据  
  8.     struct input_dev *input_dev;                            //input设备  
  9.     int i, pipe, maxp;  
  10.     int error = -ENOMEM;  
  11.   
  12.     interface = iface->cur_altsetting;                       //获取设置  
  13.   
  14.     if (interface->desc.bNumEndpoints != 1)                  //与mouse一样只有一个端点    
  15.         return -ENODEV;  
  16.   
  17.     endpoint = &interface->endpoint[0].desc;             //获取端点描述符  
  18.     if (!usb_endpoint_is_int_in(endpoint))                  //检查端点是否为中断输入端点  
  19.         return -ENODEV;  
  20.   
  21.     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  //将endpoint设置为中断IN端点  
  22.     maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));     //端点传输的最大数据包  
  23.   
  24.     kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);      //分配urb  
  25.     input_dev = input_allocate_device();                    //分配input设备空间  
  26.     if (!kbd || !input_dev)  
  27.         goto fail1;  
  28.   
  29.     if (usb_kbd_alloc_mem(dev, kbd))                        //分配urb空间和其他缓冲区  
  30.         goto fail2;  
  31.   
  32.     kbd->usbdev = dev;                                       //给内嵌结构体赋值  
  33.     kbd->dev = input_dev;  
  34.   
  35.     if (dev->manufacturer)   //拷贝厂商ID  
  36.         strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));  
  37.   
  38.     if (dev->product) {      //拷贝产品ID  
  39.         if (dev->manufacturer)  
  40.             strlcat(kbd->name, " ", sizeof(kbd->name));  
  41.         strlcat(kbd->name, dev->product, sizeof(kbd->name));  
  42.     }  
  43.   
  44.     if (!strlen(kbd->name))  //检测不到厂商名字  
  45.         snprintf(kbd->name, sizeof(kbd->name),  
  46.              "USB HIDBP Keyboard %04x:%04x",  
  47.              le16_to_cpu(dev->descriptor.idVendor),  
  48.              le16_to_cpu(dev->descriptor.idProduct));  
  49.     //设备链接地址  
  50.     usb_make_path(dev, kbd->phys, sizeof(kbd->phys));  
  51.     strlcat(kbd->phys, "/input0", sizeof(kbd->phys));  
  52.   
  53.     input_dev->name = kbd->name;          //给input_dev结构体赋值  
  54.     input_dev->phys = kbd->phys;  
  55.     usb_to_input_id(dev, &input_dev->id);    //拷贝usb_driver的支持给input,设置bustype,vendo,product等  
  56.     input_dev->dev.parent = &iface->dev;  
  57.   
  58.     input_set_drvdata(input_dev, kbd);      //将kbd设置为input的私有数据  
  59.   
  60.     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |  
  61.         BIT_MASK(EV_REP);                   //支持的按键事件类型  
  62.     input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |  
  63.         BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |  
  64.         BIT_MASK(LED_KANA);                 //EV_LED事件支持的事件码  
  65.   
  66.     for (i = 0; i < 255; i++)  
  67.         set_bit(usb_kbd_keycode[i], input_dev->keybit);  //EV_KEY事件支持的事件码(即设置支持的键盘码)  
  68.     clear_bit(0, input_dev->keybit);  
  69.   
  70.     input_dev->event = usb_kbd_event;        //定义event函数  
  71.     input_dev->open = usb_kbd_open;  
  72.     input_dev->close = usb_kbd_close;  
  73.   
  74.     usb_fill_int_urb(kbd->irq, dev, pipe,  
  75.              kbd->new, (maxp > 8 ? 8 : maxp),  
  76.              usb_kbd_irq, kbd, endpoint->bInterval);//填充中断urb  
  77.     kbd->irq->transfer_dma = kbd->new_dma;  
  78.     kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  
  79.   
  80.     kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;  
  81.     kbd->cr->bRequest = 0x09;//设置控制请求的格式  
  82.     kbd->cr->wValue = cpu_to_le16(0x200);  
  83.     kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);  
  84.     kbd->cr->wLength = cpu_to_le16(1);  
  85.   
  86.     usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),  
  87.                  (void *) kbd->cr, kbd->leds, 1,  
  88.                  usb_kbd_led, kbd);//填充控制urb  
  89.     kbd->led->transfer_dma = kbd->leds_dma;  
  90.     kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  
  91.   
  92.     error = input_register_device(kbd->dev);  
  93.     if (error)  
  94.         goto fail2;  
  95.   
  96.     usb_set_intfdata(iface, kbd);  
  97.     device_set_wakeup_enable(&dev->dev, 1);  
  98.     return 0;  
  99.   
  100. fail2:    
  101.     usb_kbd_free_mem(dev, kbd);  
  102. fail1:    
  103.     input_free_device(input_dev);  
  104.     kfree(kbd);  
  105.     return error;  
  106. }  

 

在上面的probe中,我们主要是初始化一些结构体,然后提交中断urb和控制urb,并注册input设备。其中有几个地方需要细看下,其一,usb_kbd_alloc_mem的实现。其二,设置控制请求的格式。

先来看看usb_kbd_alloc_mem的实现

 

  1. static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)  
  2. {  
  3.     if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))      //分配中断urb  
  4.         return -1;  
  5.     if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))      //分配控制urb  
  6.         return -1;  
  7.     if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma)))  
  8.         return -1;      //分配中断urb使用的缓冲区  
  9.     if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))  
  10.         return -1;      //分配控制urb使用的控制请求描述符  
  11.     if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))  
  12.         return -1;      //分配控制urb使用的缓冲区  
  13.   
  14.     return 0;  
  15. }  

 

这里我们需要明白中断urb和控制urb需要分配不同的urb结构体,同时在提交urb之前,需要填充的内容也不同,中断urb填充的是缓冲区和中断处理函数控制urb填充的是控制请求描述符与回调函数

设置控制请求的格式。cr是struct usb_ctrlrequest结构的指针,USB协议中规定一个控制请求的格式为一个8个字节的数据包,其定义如下

 

  1. /** 
  2.  * struct usb_ctrlrequest - SETUP data for a USB device control request 
  3.  * @bRequestType: matches the USB bmRequestType field 
  4.  * @bRequest: matches the USB bRequest field 
  5.  * @wValue: matches the USB wValue field (le16 byte order) 
  6.  * @wIndex: matches the USB wIndex field (le16 byte order) 
  7.  * @wLength: matches the USB wLength field (le16 byte order) 
  8.  * 
  9.  * This structure is used to send control requests to a USB device.  It matches 
  10.  * the different fields of the USB 2.0 Spec section 9.3, table 9-2.  See the 
  11.  * USB spec for a fuller description of the different fields, and what they are 
  12.  * used for. 
  13.  * 
  14.  * Note that the driver for any interface can issue control requests. 
  15.  * For most devices, interfaces don\'t coordinate with each other, so 
  16.  * such requests may be made at any time. 
  17.  */  
  18. struct usb_ctrlrequest {  
  19.     __u8 bRequestType;  //设定传输方向、请求类型等  
  20.     __u8 bRequest;      //指定哪个请求,可以是规定的标准值也可以是厂家定义的值  
  21.     __le16 wValue;      //即将写到寄存器的数据  
  22.     __le16 wIndex;      //接口数量,也就是寄存器的偏移地址  
  23.     __le16 wLength;     //数据传输阶段传输多少个字节  
  24. } __attribute__ ((packed));  

 

USB协议中规定,所有的USB设备都会响应主机的一些请求,这些请求来自USB主机控制器,主机控制器通过设备的默认控制管道发出这些请求。默认的管道为0号端口对应的那个管道。

同样这个input设备首先由用户层调用open函数,所以先看看input中定义的open

 

  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. }  

因为这个驱动里面有一个中断urb一个控制urb,我们先看中断urb的处理流程。中断urb在input的open中被提交后,当USB core处理完毕,会通知这个USB设备驱动,然后执行回调函数,也就是中断处理函数usb_kbd_irq

 

 

  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. }  

 

这个就是中断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干了些什么。

  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)//不支持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.         pr_err("usb_submit_urb(leds) failed\n");  
  23.     //提交控制urb  
  24.     return 0;  
  25. }  

当在input的event里提交了控制urb后,经过URB处理流程,最后返回给USB设备驱动的回调函数,也就是在probe中定义的usb_kbd_led

 

  1. static void usb_kbd_led(struct urb *urb)  
  2. {  
  3.     struct usb_kbd *kbd = urb->context;  
  4.   
  5.     if (urb->status)  
  6.         hid_warn(urb->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.         hid_err(urb->dev, "usb_submit_urb(leds) failed\n");  
  16. }  

 

总结下,我们的控制urb走的是先由input的event提交,触发后由控制urb的回调函数再次提交。好了,通过USB鼠标,我们已经知道了控制urb和中断urb的设计和处理流程。