Chinaunix首页 | 论坛 | 博客
  • 博客访问: 587685
  • 博文数量: 75
  • 博客积分: 988
  • 博客等级: 准尉
  • 技术积分: 1269
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-10 15:44
文章分类

全部博文(75)

文章存档

2022年(1)

2019年(1)

2018年(1)

2016年(9)

2015年(7)

2013年(6)

2012年(40)

2011年(10)

分类: LINUX

2012-03-25 21:05:58

USB驱动程序框架:

app:   
-------------------------------------------
          USB设备驱动程序      // 知道数据含义
内核 --------------------------------------
          USB总线驱动程序      // 1. 识别, 2. 找到匹配的设备驱动, 3. 提供USB读写函数 (它不知道数                                   据含义)
-------------------------------------------
           USB主机控制器
           UHCI OHCI EHCI
硬件        -----------
              USB设备

USB总线驱动程序的作用
1.识别USB设备
1.1 分配地址,并告诉USB设备(set address)
1.2 发出命令获取描述符 在\include\linux\usb\Ch9.h定义。


2.查找并安装对应的设备驱动程序

3.提供USB读定函数

可以通过将USB插入接入LINUX系统,根据终端打印出来的信息,在内核代码中查找相关字符就可以根据查到的信息来确定会调用哪些函数以用其实现过程。

在内核目录下面搜索:
grep "USB device using" * -nR 在搜到的信息里面有如下一行
drivers/usb/core/hub.c:2186:              "%s %s speed %sUSB device using %s and address %d\n"
每一个USB主机控制器里面都自带有一个HUB(可以认为是一个特殊的USB设备)。
查到打印类似字符串的地方为drivers/usb/core/hub.c这个函数中的hub_port_init中。
再查找哪里调用了hub_port_init,可以找到是在hub_port_connect_change中调用的。
再查找哪里调用了hub_port_connect_change,可以找到是hub_events中调用。
再查找哪里调用了hub_events,可以找到是hub_thread中调用。
hub_thread一般是处于休眠状态。查找是wake_up(&khubd_wait);来唤醒这个线程。即用的函数kick_khubd。
kick_khudb又是在hub_irq中被调用。该中断为主机控制器驱动程序中注册的中断。
这是一个逆向推导过程,其调用关系如下:
hub_irq
kick_khubd
hub_thread
hub_events
hub_port_connect_change
udev = usb_alloc_dev(hdev, hdev->bus, port1);
dev->dev.bus = &usb_bus_type;
choose_address(udev); // 给新设备分配编号(地址),从这个函数中可以得到一个USB主机最多可以接127个USB设备
hub_port_init   // usb 1-1: new full speed USB device                                                             using s3c2410-ohci and address 3
hub_set_address  // 把编号(地址)告诉USB设备
usb_get_device_descriptor(udev, 8); // 获取设                                                                                       备描述符
retval = usb_get_device_descriptor(udev,                                                                                 USB_DT_DEVICE_SIZE);
usb_new_device(udev)   
err = usb_get_configuration(udev); //                                         把所有的描述符都读出来,并解析usb_parse_configuration
device_add  // 把device放入                                                                               usb_bus_type的dev链表, 
           // 从usb_bus_type的driver                                                                        链表里取出usb_driver,
                                       // 把usb_interface和                                                                         usb_driver的id_table比较
        // 如果能匹配,调用usb_driver的probe


怎么写USB设备驱动程序?
1. 分配/设置usb_driver结构体
        .id_table
        .probe
        .disconnect
2. 注册


点击(此处)折叠或打开

  1. /*
  2.  * linux-2.6.22.6\drivers\hid\usbhid\Usbmouse.c
  3.  */

  4. #include <linux/kernel.h>
  5. #include <linux/slab.h>
  6. #include <linux/module.h>
  7. #include <linux/init.h>
  8. #include <linux/usb/input.h>
  9. #include <linux/hid.h>

  10. static struct input_dev *uk_dev;
  11. static char *usb_buf;
  12. static dma_addr_t *usb_buf_phys;
  13. static int len;
  14. static struct urb *uk_urb;

  15. static struct usb_device_id usbmouse_as_key_id_table [] = {
  16.     { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
  17.         USB_INTERFACE_PROTOCOL_MOUSE) },
  18.     //{ USB_DEVICE(vend,prod) }, /* 可以让USB驱动只支持指定的厂家生产的 */
  19.     { }    /* Terminating entry */
  20. };

  21. static void usbmouse_as_key_irq(struct urb *urb)
  22. {
  23.     static unsigned char pre_val;
  24. #if 0
  25.     int i;
  26.     static int cnt = 0;
  27.     printk("data cnt %d: ", ++cnt);
  28.     for (i=0;i<len;i++)
  29.     {
  30.         printk("%02x ", usb_buf[i]);
  31.     }
  32.     printk("/n");
  33. #endif

  34.     /* 上报事件 */
  35.     /* USB鼠标数据含义:
  36.      * data[0] : bit0-左键,1-按下, 0-松开
  37.      * bit1-右键,1-按下, 0-松开
  38.      * bit2-中键,1-按下, 0-松开
  39.      */
  40.     if ((pre_val & (1<<0)) != (usb_buf &[0] (1<<0)))
  41.     {
  42.         /* 左键发生了变化 */
  43.         input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0))?1:0);
  44.         input_sync(uk_dev);
  45.     }

  46.     if ((pre_val & (1<<1)) != (usb_buf &[0] & (1<<1)))
  47.     {
  48.         /* 右键发生了变化 */
  49.         input_event(uk_dev, EV_KEY, KEY_S, (usb_buf &[0] & (1<<1))?1:0);
  50.         input_sync(uk_dev);
  51.     }

  52.     if ((pre_val & (1<<2)) != (usb_buf &[0]& (1<<2)))
  53.     {
  54.         /* 中键发生了变化 */
  55.         input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf &[0] & (1<<2))?1:0);
  56.         input_sync(uk_dev);
  57.     }

  58.     pre_val = usb_buf[0];

  59.     /* 重新提交urb */
  60.     usb_submit_urb(uk_urb, GFP_KERNEL);
  61. }

  62. static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
  63. {
  64.     struct usb_device *dev = interface_to_usbdev(intf);
  65.     struct usb_host_interface *interface;
  66.     struct usb_endpoint_descriptor *endpoint;

  67.     int pipe;

  68.     interface = intf->cur_altsetting;
  69.     endpoint = &interface->endpoint[0].desc;
  70.     
  71.     #if 0
  72.     printk("Found usbmouse!\n");
  73.     
  74.     printk("bcdUSB = %x\n", dev->descriptor.bcdUSB); /* USB版本号 */
  75.     
  76.     printk("VID = 0x%x\n", dev->descriptor.idVendor);

  77.     printk("PID = 0x%x\n", dev->descriptor.idProduct);
  78.     #endif
  79.     
  80.     /* a.分配一个input_dev */
  81.     
  82.     uk_dev = input_allocate_device();
  83.     
  84.     /* b.设置 */
  85.     /* b.1 能产生哪类事件 */
  86.     set_bit(EV_KEY, uk_dev->evbit);
  87.     
  88.     set_bit(EV_REP, uk_dev->evbit);
  89.     
  90.     /* b.2 能产这类事件中的哪些事件 */
  91.     set_bit(KEY_L, uk_dev->keybit);
  92.     
  93.     set_bit(KEY_S, uk_dev->keybit);

  94.     set_bit(KEY_ENTER, uk_dev->keybit);
  95.     
  96.     /* c.注册 */
  97.     input_register_device(&uk_dev);
  98.     /* d.硬件相关操作 */
  99.     /* 数据传输3要素: 源、目的、长度 */
  100.     /*:USB设备的某个端点 */
  101.     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
  102.     /* 长度 */
  103.     len = endpoint->wMaxPacketSize;
  104.     /* 目的 */
  105.     usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);

  106.     /* 使用"3"要素 */
  107.     /* 分配usb request block */
  108.     uk_urb = usb_alloc_urb(0,GFP_KERNEL); /* urb USB请求块 */
  109.     /* 使用"3要素"设置urb bInterval(间隔)即查询频率 */
  110.     usb_fill_int_urb(urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
  111.     uk_urb->transfer_dma = usb_buf_phys;
  112.     uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
  113.     /* 使用 urb */
  114.     usb_submit_urb(uk_urb, GFP_KERNEL); /* 提交urb */
  115.     return 0;
  116. }


  117. static void usbmouse_as_key_disconnect(struct usb_interface *intf)
  118. {
  119.     struct usb_device *dev = interface_to_usbdev(intf);

  120.     //printk("Remove usbmouse!\n");
  121.     usb_kill_urb(uk_urb);
  122.     usb_free_urb(uk_urb);
  123.     usb_buffer_free(dev,len,usb_buf,usb_buf_phys);
  124.     input_unregister_device(uk_dev);
  125.     input_free_device(uk_dev);
  126.     return 0;
  127. }

  128. /* 1.分配/设置usb_driver */
  129. static struct usb_driver usbmouse_as_key_driver = {
  130.     .name        = "usbmouse_as_key",
  131.     .probe        = usbmouse_as_key_probe,
  132.     .disconnect    = usbmouse_as_key_disconnect,
  133.     .id_table    = usbmouse_as_key_id_table,
  134. };

  135. static int usbmouse_as_key_init(void)
  136. {
  137.     /* 2. 注册 */
  138.     usb_register(&usbmouse_as_key_driver);
  139.     return 0;
  140. }

  141. static void usbmouse_as_key_exit(void)
  142. {
  143.     usb_deregister(&usbmouse_as_key_driver);
  144. }

  145. module_init(usbmouse_as_key_init);
  146. module_exit(usbmouse_as_key_exit);

  147. MODULE_DESCRIPTION("usb driver test for the s3c2440");
  148. MODULE_LICENSE("GPL");

驱动程序是让左键、右键、中键分别为L、S、Enter键。测试方法和之前的测试方法类似。USB里面的调用关系比较复杂,总体而言,当一个USB设备接入的时候,会产生一个中断,这个中断会唤醒休眠的线程,产生一个hub事件,hub端口连接改变。就会分配一个新的usbdev即usb_bus_type,再给新的设备分配地址。并填入相关的描述符,这个可以认为是输入子系统里面的"硬件”层,接下来就是“软件”层的device_add调用内部的id_table进行对比,如果支持,则调用prob函数......
阅读(2195) | 评论(1) | 转发(4) |
给主人留下些什么吧!~~

happylishang2013-03-18 14:38:43

O(∩_∩)O 文章很有帮助,帮我解了不少疑惑,就是不知道 usb总线何时注册的,与实际总线如何联系与通讯