USB设备创建过程与驱动学习笔记

讲述usb设备插入到设备创建过程的源码分析,以及以中断传输方式的鼠标驱动为例,讲述USB驱动实现流程。



USB设备创建过程

当外部插入USB设备后,系统都做了哪些工作?总的来说就是:插入USB设备后,USB控制识别到USB设备,会向系统触发一中断,在中断函数中唤醒休眠线程,在线程中获取USB设备信息,并创建USB设备。

该过程可参考hub.c 文件。USB控制器触发中断入口函数:hub_irq。整个调用流程如下:

->hub_irq //产生中断(由USB控制器程序完成中断触发)
    ->kick_khubd(hub); 
       ->wake_up(&khubd_wait); //唤醒khubd_wait,khubd线程继续执行

hub_thread 循环线程里执行如下:

->hub_thread
   ->wait_event_freezable(khubd_wait,  !list_empty(&hub_event_list) //khubd_wait列队被唤醒,开始往下执行↓
->hub_events   //遍历获取hub集线器上的ubs device
   ->  hub_port_connect_change(hub, i, portstatus, portchange);  锁定这个usbhub
     ->udev = usb_alloc_dev(hdev, hdev->bus, port1); 申请分配一个USB设备udev,
       ->dev->dev.type = &usb_device_type;指定总线为usb_device_type(方便后续match用)
     ->choose_devnum(udev);获取一个空余的USB编号(地址)
     ->status = hub_port_init(hub, udev, port1, i);
       -> hub_set_address(udev, devnum);把编号地址写进usb (udev->devnum)
       ->usb_get_device_descriptor(udev, 8); 获取该硬件的设备描述符 
     ->usb_new_device(udev);
        ->usb_enumerate_device(udev);   获取并解析设备/接口描述符等信息
      -> device_add(&udev->dev);  add设备。把该设备添加到USB总线

从上述的流程中可以看到,内核获取了USB几个关键参数:设备描述符;配置描述符;接口描述符等。这几个信息的结构体从属关系位于内核里ch9.h:

一个USB设备描述符:(包含厂家ID 信息,device ID,配置描述个数…) usb_device_descriptor
  -》配置描述(属性,接口个数,配置信息等…) usb_config_descriptor
     -》 接口描述(类/子类信息,协议,端口数量,接口含义 :如一个Usb同时有录音,播放…) usb_interface_descriptor
(如USB鼠标接口描述的类为:人体工学设备类HID,子类:1,协议:鼠标)
       -》 端点信息(输入/输出,实时,传输类型,包大小,消息长度…) usb_endpoint_descriptor


最后执行的device_add,会将dev会放入usb总线的dev链表,然后调用总线usb_device_match函数,遍历比较各USB驱动的idtable,匹配成功后则会调用对应的usb driver驱动的probe函数(这个过程是linux系统中常用的总线 -设备 - 驱动原理)

驱动的idtable匹配方法有2种类型来匹配:

  • 方法一:通过接口描述:USB_INTERFACE_INFO(类,子类,协议) 完成device匹配
  • 方法二:通过厂家ID 和device ID:USB_DEVICE(厂家ID ,device ID) 完成device匹配

USB驱动编写(中断传输方式)

USB设备的整个创建过程以及总线匹配都是系统自动完成的,因此需要用户做的仅是对应USB设备驱动的编写。

比如:一个鼠标类型的USB设备。

  • 插入设备后会匹配到对应驱动,而后调用probe函数,probe函数需要初始化:注册需要产生的input事件;Urb(USB数据传输的载体)的分配以及初始化;绑定urb通信的中断函数。

  • 当外部有USB数据输入后,USB控制器收到数据会自动触发urb通信的中断函数。在中断函数里接收urb数据并解析,完成需要的操作(如点击,移动等事件上报),因此该中断函数是USB功能实现的重要主体。

  • 当USB设备拔出后会调用disconnect,主要做一些移除,注销,释放操作。

因此驱动涉及:probe;中断函数;disconnect函数,三部分实现架构大致如下:
(具体可参考usbmouse.c范例)


probe(struct usb_interface *intf, const struct usb_device_id *id)函数:
->    
    //根据接口获取USB设备
    struct usb_device *dev = interface_to_usbdev(intf);  
    
        //根据接口获取端点
    interface = intf->cur_altsetting;
    endpoint = &interface->endpoint[0].desc;

     /*input 事件3要素:申请,配置,注册*/
    input_dev = input_allocate_device(); //申请设备
    __set_bit(EV_KEY,input_dev->evbit); //配置事件类型:按键
    __set_bit(EV_REL,input_dev->evbit); //配置事件类型:位移
    __set_bit(REL_X,input_dev->absbit); //配置具体事件:x方向相对位移
    ...
    input_register_device(input_dev); //注册事件

    /*定义数据传输3要素:端点长度,源地址,目的地址*/
    len=endpoint->wMaxPacketSize;    //自定义数据长度,这里定义为端点最大包大小
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //端点源地址
    vir_addr=usb_alloc_coherent(dev, len, GFP_ATOMIC, &dma_phy_addr); //目的地址,申请一段连续物理内存作为目的地址,长度为len

    //申请urb - USB REQUEST BLOCK(定义数据传输产生中断函数)可以理解urb为usb数据通信载体
    urb_t =usb_alloc_urb(0, GFP_KERNEL);//申请URB   
    usb_fill_int_urb(urb_t, dev, pipe, NULL,  //填充urb,中断传输方式
             (maxp > 8 ? 8 : maxp),
            test_irq_function, NULL, endpoint->bInterval);  //中断函数为test_irq_function
            即:USB数据传输到USB控制器,USB控制器会实时扫描数据,有数据会触发一中断,NULL为中断传入参数

    usb_submit_urb(urb_t, GFP_KERNEL);//提交urb

===========================================================================================

test_irq_function urb(struct urb *urb)  中断函数(即:USB数据传输到USB控制器,USB控制器会实时扫描数据,有数据会触发中断)
->
    data=urb->context->data
    input_report_key(dev, BTN_LEFT, data[0] & 0x01); //解析数据,上报事件(BTN_LEFT:左键)
    //input_report_rel(dev, REL_X,  data[1]);//位移上报
    ...
    input_sync(dev); //事件同步
    
    usb_submit_urb(urb, GFP_ATOMIC); //提交urb

===========================================================================================

disconnect(struct usb_interface *intf)函数:(USB设备拔出后调用)
->      
      usb_kill_urb(urb_t);
      input_unregister_device(input_dev);
      usb_free_urb(urb_t);
      usb_free_coherent(interface_to_usbdev(intf), len, vir_addr, &dma_phy_addr);
       

上述驱动讲述的是USB鼠标类型驱动实现,采用的中断传输方式,结合input事件实现。USB还涉及储存,音视频等类型,其传输和实现方式各有区别。因此需要了解如下几种传输方式的概念:

  • 中断: 传输固定少量数据,单向输入的:鼠标,键盘等,如上就是中断传输方式。
  • 控制:控制传输是双向传输,数据量通常较小。
  • 批量:通常用于USB存储器,大块数据传输。
  • 等时: 也是批量数据传输,但确保时效性,不保证数据是否到达,如:音视频设备。

本文主要介绍中断传输方式的实现 ,其他传输方式在USB驱动里实现框架类似。

猜你喜欢

转载自blog.csdn.net/ludaoyi88/article/details/113664790
今日推荐