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

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

[cpp] view plaincopy
  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 plaincopy
  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

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

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

先来看看usb_kbd_alloc_mem的实现

[cpp] view plaincopy
  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. return 0;
  14. }

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

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

[cpp] view plaincopy
  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

[cpp] view plaincopy
  1. static int usb_kbd_open(struct input_dev *dev)
  2. {
  3. struct usb_kbd *kbd = input_get_drvdata(dev);
  4. kbd->irq->dev = kbd->usbdev;
  5. if (usb_submit_urb(kbd->irq, GFP_KERNEL))
  6. return -EIO;
  7. return 0;
  8. }

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

[cpp] view plaincopy
  1. static void usb_kbd_irq(struct urb *urb)
  2. {
  3. struct usb_kbd *kbd = urb->context;
  4. int i;
  5. switch (urb->status) {
  6. case 0:         /* success */
  7. break;
  8. case -ECONNRESET:   /* unlink */
  9. case -ENOENT:
  10. case -ESHUTDOWN:
  11. return;
  12. /* -EPIPE:  should clear the halt */
  13. default:        /* error */
  14. goto resubmit;
  15. }
  16. //报告usb_kbd_keycode[224..231]8按键状态
  17. //KEY_LEFTCTRL,KEY_LEFTSHIFT,KEY_LEFTALT,KEY_LEFTMETA,
  18. //KEY_RIGHTCTRL,KEY_RIGHTSHIFT,KEY_RIGHTALT,KEY_RIGHTMETA
  19. for (i = 0; i < 8; i++)
  20. input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
  21. //若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下
  22. for (i = 2; i < 8; i++) {
  23. //获取键盘离开的中断
  24. //同时没有该KEY的按下状态
  25. if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
  26. if (usb_kbd_keycode[kbd->old[i]])
  27. input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
  28. else
  29. hid_info(urb->dev,
  30. "Unknown key (scancode %#x) released.\n",
  31. kbd->old[i]);
  32. }
  33. //获取键盘按下的中断
  34. //同时没有该KEY的离开状态
  35. if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
  36. if (usb_kbd_keycode[kbd->new[i]])
  37. input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
  38. else
  39. hid_info(urb->dev,
  40. "Unknown key (scancode %#x) released.\n",
  41. kbd->new[i]);
  42. }
  43. }
  44. input_sync(kbd->dev);            //同步设备,告知事件的接收者驱动已经发出了一个完整的报告
  45. memcpy(kbd->old, kbd->new, 8);    //防止未松开时被当成新的按键处理
  46. resubmit:
  47. i = usb_submit_urb (urb, GFP_ATOMIC);
  48. if (i)
  49. hid_err(urb->dev, "can't resubmit intr, %s-%s/input0, status %d",
  50. kbd->usbdev->bus->bus_name,
  51. kbd->usbdev->devpath, i);
  52. }

这个就是中断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
  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. if (type != EV_LED)//不支持LED事件
  6. return -1;
  7. //获取指示灯的目标状态
  8. kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
  9. (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |
  10. (!!test_bit(LED_NUML,    dev->led));
  11. if (kbd->led->status == -EINPROGRESS)
  12. return 0;
  13. //指示灯状态已经是目标状态则不需要再做任何操作
  14. if (*(kbd->leds) == kbd->newleds)
  15. return 0;
  16. *(kbd->leds) = kbd->newleds;
  17. kbd->led->dev = kbd->usbdev;
  18. if (usb_submit_urb(kbd->led, GFP_ATOMIC))
  19. pr_err("usb_submit_urb(leds) failed\n");
  20. //提交控制urb
  21. return 0;
  22. }

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

[cpp] view plaincopy
  1. static void usb_kbd_led(struct urb *urb)
  2. {
  3. struct usb_kbd *kbd = urb->context;
  4. if (urb->status)
  5. hid_warn(urb->dev, "led urb status %d received\n",
  6. urb->status);
  7. if (*(kbd->leds) == kbd->newleds)
  8. return;
  9. *(kbd->leds) = kbd->newleds;
  10. kbd->led->dev = kbd->usbdev;
  11. if (usb_submit_urb(kbd->led, GFP_ATOMIC))
  12. hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
  13. }

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

转载于:https://www.cnblogs.com/MMLoveMeMM/articles/4105665.html

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

  1. linux网卡驱动rtl8188cu,linux下编译基于rtl8188cu控制芯片的USB无线网卡驱动 -电脑资料...

    本人因为实验的需要,需要在嵌入式linux环境下添加wifi功能,通过在网上调研,选择基于rtl8188cu控制芯片的水星NW150UM无线USB网卡,现简单记录下配置过程, 1.下载最新的rtl81 ...

  2. USB总线电平标准、USB总线状态、USB总线信号详解

    目录 1.USB总线电平标准 2.USB总线状态 3.USB总线信号 3.1 SOP信号 3.2 EOP信号 3.3 Reset信号 3.4 Suspend信号 3.5 Resume信号 3.6 SY ...

  3. 键盘驱动系列---JIURL键盘驱动 2

    2 应用层基础知识 在讨论使用键盘的应用程序这个问题之前,我们首先介绍一下 Windows 中,应用程序使用驱动,应用程序与驱动通信的一些问题. 2.1 应用程序如何使用驱动 应用程序中使用 Crea ...

  4. 键盘驱动系列---JIURL键盘驱动 3

    4 编译与调试环境简介 4.1 源码 ps/2键盘驱动的设备栈有3层,最底层设备对象的驱动是 acpi,中间层设备对象的驱动是 i8042prt,最高层设备对象的驱动是 kbdclass. DDK 所 ...

  5. Linux下烧写工具DNW和USB驱动安装

    Linux下编译记录: 编译PC端USB驱动和写入工具 dnw_linux.tgz压缩包文件结构如下 dnw_linux/ dnw_linux/secbulk/ dnw_linux/secbulk/M ...

  6. Ubuntu Linux下与MOTO E2手机进行USB NET连接

    买了台新手机MOTO E2,linux操作系统.几天来几乎都在为了能让E2能在UBUNTU下进行SyncML而忙乎.E2刷到了49P(屠龙刀),手机支持USB NET方式连接,在WINDOWS下装好驱 ...

  7. linux 驱动入门 魏清,Linux下的SPI总线驱动(三)

    版权所有,转载请说明转自 原创作者:南京邮电大学  通信与信息系统专业 研二 魏清 五.SPI测试代码 对于SPI总线驱动,我们可以分为SPI控制设备驱动和SPI接口设备驱动.而作为驱动开发人员主要是 ...

  8. linux内核spi总线驱动分析,Linux下的SPI总线驱动(三)

    版权所有,转载请说明转自 原创作者:南京邮电大学  通信与信息系统专业 研二 魏清 五.SPI测试代码 对于SPI总线驱动,我们可以分为SPI控制设备驱动和SPI接口设备驱动.而作为驱动开发人员主要是 ...

  9. c 调用 linux驱动程序,Linux下的C编程实战(五)――驱动程序设计

    Linux下的C编程实战(五) ――驱动程序设计 1.引言 设备驱动程序是操作系统内核和机器硬件之间的接口,它为应用程序屏蔽硬件的细节,一般来说,Linux的设备驱动程序需要完成如下功能: (1)初始 ...

最新文章

  1. 推荐 | 统计学权威盘点过去50年最重要的统计学思想,因果推理、bootstrap等上榜,Judea Pearl点赞...
  2. ubuntu 各版本的区别
  3. python Logging日志记录模块详解
  4. 图像分类任务中的tricks总结
  5. kettle查询mysql获取uuid_java中调用kettle转换文件
  6. D3D游戏关于窗口中如何精确确定鼠标位置的相关讨论
  7. “元宇宙”是个啥?都有哪些大招?
  8. 2022CTFSHOW菜狗杯部分MISC(一)
  9. Ubuntu18.04下 LOAM_Velodyne 的编译安装(PCL为1.8.1)
  10. python 多态app_Python——多态
  11. 负载均衡获得真实源IP的6种方法
  12. html怎么显示一个点赞的心形,jquery心形点赞关注效果的简单实现
  13. 微信小程序开发的基础学习
  14. char *与char []的区别
  15. 单片机中的浮点数转换成串口可打印格式
  16. 函数最值题目及答案_关于函数的习题及答案
  17. 北京大学计算机考博英语,2019年北京大学博士英语考题回忆
  18. 预警信息发布程序设计
  19. Android 卡片、证件识别
  20. Cent OS 安装 opencv 2.4.4 人脸识别 linux

热门文章

  1. 【Tools】Centos7.5安装MySQL5.7
  2. apache2.4.9 开启path_info访问_如何通过SSH访问NAS?
  3. 每天一道LeetCode-----链表插入排序
  4. mac m1下编译spring框架
  5. 记录docker开发hadoop,解决bug Datanode denied communication with namenode because hostname cannot be
  6. 水利水电工程管理与实务电子版习题_每日一练:一级建造师案例题(水利水电工程管理与实务)...
  7. 《iOS应用逆向工程(第2版)》高清电子书 PDF
  8. 第八章 PX4-SDlog解析
  9. HDU - 4461 The Power of Xiangqi
  10. 记录 之 tensorflow常见的数据预处理操作