• linux usb总线驱动(一)



    title: linux usb总线驱动
    tags: linux
    date: 2018/12/11/ 17:14:30
    toc: true

    linux usb总线驱动框架

    USB 介绍

    • 当插入一个未知的usb设备,电脑也会有相应的提示?
      1. 插入有反应,是因为电脑的usb作为主机设备,有一个下拉电阻,从机上拉电压通知
      2. 识别到一些信息,是因为有着标准的 usb 总线协议,有一些简单的标准的交互方式获取信息。
    • usb 设备的基本构成
      1. 控制usb设备,其实也就是控制usb的寄存器来通过usb总线协议来与设备读写来实现具体的功能。
      2. usb 总线协议一般内核都会自带,我们只需要写总线协议下面的逻辑控制就好了
    • usb也是主从结构,读写只能由主机发起
    • 新接入的USB设备的默认地址(编号)是0,在未分配新编号前,PC主机使用0地址和它通信
    • 所谓热拔插,其实是因为在主机上的D+和D-都接了15k下拉电阻,平时为低电平。从机有上拉电阻
      • 全速设备(12M/s)和高速设备(480M/s):D+ 上拉1.5k
      • 低俗设备:D-上拉1.5k

    传输类型

    类型 可靠性 时间性 举例
    控制传输 可靠 有保证 usb设备识别
    批量传输 可靠 没有保证 U盘
    中断传输 可靠 实时 鼠标
    实时同步传输 不可靠 实时 摄像头

    USB中传输的对象为端点,端点0是每个设备都有的,作为控制传输,可读可写。其他端点只有一个方向。

    控制器接口

    USB规范都是从寄存器级别规定好的,不过各个厂商可能有自己的几个专用的寄存器 参考

    OHCI(Open Host Controller Interface):微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂 其实这个不止是usb上用,其他接口也有用的

    UHCI(Universal Host Controller Interface): Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps), 而UHCI接口的软件复杂,硬件简单

    EHCI(Enhanced Host Controller Interface):高速USB2.0(480Mbps),

    xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设计,同时也支持USB2.0、1.1等

    2440接口

    2440是兼容OHCI Rev 1.0的在数据手册上有标准,同时我们插入usb的时候也会有指示类似如下:

    # usb 1-1: new full speed USB device using s3c2410-ohci and address 2
    usb 1-1: configuration #1 chosen from 1 choice
    usb 1-1: USB disconnect, address 2
    

    基本流程

    1. 硬件插入,在D+或者D-上有电平变化,通知主机
    2. 主机通过端点地址0与usb设备交互,分配地址给usb从设备
    3. 通过端点0获取从设备的信息
    4. 安装对应的设备驱动,提供读写设备的函数

    当插入usb时候,提示如下,搜索相关字符grep "USB device using" * -nR,可以在driversusbcorehub.c中找到hub_port_init中调用

    # usb 1-1: new full speed USB device using s3c2410-ohci and address 2
    usb 1-1: configuration #1 chosen from 1 choice
    usb 1-1: USB disconnect, address 2
    

    每一个 USB 主机控制器,都自带了一个 USB HUB, HUB 上再接“ USB 设备”。可以认为
    "HUB"是一个特殊的"USB 设备"。

    mark

    继续搜索可以有如下关系

    >hub_thread						
    	>hub_events						
    		>hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
    			//分配一个 usb_device 空间
    			>usb_alloc_dev(hdev, hdev->bus, port1);
    				>kzalloc(sizeof(*dev), GFP_KERNEL)
    				>device_initialize(&dev->dev)
    				>dev->dev.bus = &usb_bus_type;
    				>dev->dev.type = &usb_device_type
    				>sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); //usb 的编号
    			//分配地址,最大127
    			>choose_address(udev)
    				>if (devnum >= 128) devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
    			//根据 usb高速低速全速设置一些数据
    			>hub_port_init(hub, udev, port1, i)
    				//设置地址
    				>hub_set_address
    					>usb_control_msg
    				//先读取必备的描述符 8
    				>usb_get_device_descriptor(udev, 8)
    					>usb_control_msg
    				//18个长度的描述符
    				>usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE)
    				//打印usb信息
    				>dev_info
    			>usb_new_device(udev)
    				//获取配置
    				>usb_get_configuration
    				//分配设备号
    				>dev->dev.devt = MKDEV(USB_DEVICE_MAJOR,(((udev->bus->busnum-1) * 128) + (udev->devnum-1)))
    				//这个是通用的函数哦,并不是单独给usb用的
    				>device_add
    					//创建设备文件
    					>device_create_file
    					>bus_add_device
    					>bus_attach_device
    						>bus_attach_device
    							>device_attach(dev)
    								>bus_for_each_drv(dev->bus, NULL, dev, __device_attach)
    									>__device_attach //(while ((drv = next_driver(&i)) && !error))
    										>driver_probe_device(drv, dev)
    											>if (drv->bus->match && !drv->bus->match(dev, drv))
    												goto done;
    											>really_probe(dev, drv);
    												>dev->bus->probe
    												>drv->probe //没有dev->bus->probe执行
    					
    					
    					>list_for_each_entry
    	//休眠				
    	>wait_event_interruptible
    	
    >hub_irq
    	//唤醒队列
    	>wake_up(&khubd_wait)
    	
    

    mark

    mark

    alloc_dev

    usb_alloc_dev中初始化了总线设备类型,这与之前初始化platform的总线类型是类似的

    dev->dev.bus = &usb_bus_type;
    dev->dev.type = &usb_device_type;
    

    都是属于bus_type,类比如下:

    struct bus_type platform_bus_type = {
    	.name		= "platform",
    	.dev_attrs	= platform_dev_attrs,
    	.match		= platform_match,
    	.uevent		= platform_uevent,
    	.suspend	= platform_suspend,
    	.suspend_late	= platform_suspend_late,
    	.resume_early	= platform_resume_early,
    	.resume		= platform_resume,
    };
    
    
    struct bus_type usb_bus_type = {
    	.name =		"usb",
    	.match =	usb_device_match,
    	.uevent =	usb_uevent,
    	.suspend =	usb_suspend,
    	.resume =	usb_resume,
    };
    

    这里的match也是同样的作用:设备插入时,总线驱动调用match来匹配

    choose_address

    分配usb的地址,支持最多127个设备端点

    	devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
    			bus->devnum_next);
    	if (devnum >= 128)
    		devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
    	udev->devnum = devnum;
    

    hub_port_init

    这里进行usb插入后的信息打印,设置地址,获取描述符等

    hub_set_address(udev);
    usb_get_device_descriptor(udev, 8);  
    	> memcpy(&dev->descriptor, desc, size);//存储的描述符结构
    ...
    usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
    

    usb_get_device_descriptor

    hub_port_init首先获取8字节的描述符,因为所有设备都支持这8字节的描述符,也支持8字节这个最小长度。具体描述符先结构如下:

    
    struct usb_device_descriptor {
     __u8  bLength;                          //本描述符的size
     __u8  bDescriptorType;                //描述符的类型,这里是设备描述符DEVICE
     __u16 bcdUSB;                           //指明usb的版本,比如usb2.0
     __u8  bDeviceClass;                    //类
     __u8  bDeviceSubClass;                 //子类
     __u8  bDeviceProtocol;                  //指定协议
     __u8  bMaxPacketSize0;                 //端点0对应的最大包大小
     __u16 idVendor;                         //厂家ID
     __u16 idProduct;                        //产品ID
     __u16 bcdDevice;                        //设备的发布号
     __u8  iManufacturer;                    //字符串描述符中厂家ID的索引
     __u8  iProduct;                         //字符串描述符中产品ID的索引
     __u8  iSerialNumber;                   //字符串描述符中设备序列号的索引
     __u8  bNumConfigurations;               //可能的配置的数目
    } __attribute__ ((packed));
    

    usb_control_msg

    hub_set_addressusb_get_device_descriptor中最终也是调用了这个usb_control_msg来与usb设备交互的。

    usb_new_device

    int usb_new_device(struct usb_device *udev)
    {
       ... ...
       err = usb_get_configuration(udev); //(1)获取配置描述块
      ... ...
      err = device_add(&udev->dev);      // (2)把device放入bus的dev链表中,并寻找对应的设备驱动
    }
    

    usb_get_configuration

    //支持最大8种配置
    if (ncfg > USB_MAXCONFIG) {
        dev_warn(ddev, "too many configurations: %d, "
                 "using maximum allowed: %d
    ", ncfg, USB_MAXCONFIG);
        dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
    }
    
    //分配9字节存储,读取描述符中的各种配置
    buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
    usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);
    
    //分配配置使用实际允许的大小
    length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);
    bigbuffer = kmalloc(length, GFP_KERNEL);
    usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);
    
    //填充
    dev->rawdescriptors[cfgno] = bigbuffer;
    //解析
    usb_parse_configuration
    

    device_add

    dev = get_device(dev);
    parent = get_device(dev->parent);
    error = setup_parent(dev, parent);
    //创建类下的文件
    error = device_create_file(dev, &dev->uevent_attr);
    。。
    //把这个设备添加到dev->bus的device表中
    bus_add_device
    ///来匹配对应的驱动程序
    bus_attach_device
    //链表遍历
    list_for_each_entry(class_intf, &dev->class->interfaces, node)
    

    bus_attach_device

    device_add中会调用bus_attach_device来运行匹配函数后运行probe

    bus_attach_device
    	>device_attach(dev)
    		>bus_for_each_drv(dev->bus, NULL, dev, __device_attach)//函数指针在这里
    			>__device_attach   //(while ((drv = next_driver(&i)) && !error))
    				>driver_probe_device(drv, dev)
    					>if (drv->bus->match && !drv->bus->match(dev, drv))
    						goto done;
    					>really_probe(dev, drv);
    						>dev->bus->probe
    						>drv->probe //如果没有dev->bus->probe执行
    

    match

    这个时候再来看匹配总线的函数

    struct bus_type usb_bus_type = {
    	.name =		"usb",
    	.match =	usb_device_match,
    	.uevent =	usb_uevent,
    	.suspend =	usb_suspend,
    	.resume =	usb_resume,
    };
    
    
    static int usb_device_match(struct device *dev, struct device_driver *drv)
    {
    
           if (is_usb_device(dev)) {                       //判断是不是USB设备
                  if (!is_usb_device_driver(drv))
                         return 0;
                  return 1;
             }
    else {                                                //否则就是USB驱动或者USB设备的接口
    
                  struct usb_interface *intf;
                  struct usb_driver *usb_drv;
                  const struct usb_device_id *id;           
    
                  if (is_usb_device_driver(drv))   //如果是USB驱动,就不需要匹配,直接return
                         return 0; 
    
                  intf = to_usb_interface(dev);               //获取USB设备的接口
                  usb_drv = to_usb_driver(drv);                    //获取USB驱动
    
                  id = usb_match_id(intf, usb_drv->id_table);  //匹配USB驱动的成员id_table
                  if (id)
                         return 1;
    
                  id = usb_match_dynamic_id(intf, usb_drv);
                  if (id)
                         return 1;
           }
           return 0;
    }
    

    查看下具体的id_table,具体的类型在includelinuxusb.h

    struct usb_device_id {
            
           __u16             match_flags;   //与usb设备匹配那种类型?比较类型的宏如下:
     //USB_DEVICE_ID_MATCH_INT_INFO : 用于匹配设备的接口描述符的3个成员
     //USB_DEVICE_ID_MATCH_DEV_INFO: 用于匹配设备描述符的3个成员
     //USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION: 用于匹配特定的USB设备的4个成员
     //USB_DEVICE_ID_MATCH_DEVICE:用于匹配特定的USB设备的2个成员(idVendor和idProduct)
    
     
           /* 以下4个用匹配描述特定的USB设备 */
           __u16             idVendor;              //厂家ID
           __u16             idProduct;             //产品ID
           __u16             bcdDevice_lo;        //设备的低版本号
           __u16             bcdDevice_hi;        //设备的高版本号
    
           /*以下3个就是用于比较设备描述符的*/
           __u8        bDeviceClass;                    //设备类
           __u8        bDeviceSubClass;                 //设备子类
           __u8        bDeviceProtocol;                 //设备协议
    
           /* 以下3个就是用于比较设备的接口描述符的 */
           __u8        bInterfaceClass;                   //接口类型
           __u8        bInterfaceSubClass;             //接口子类型
           __u8        bInterfaceProtocol;           //接口所遵循的协议
    
           /* not matched against */
           kernel_ulong_t       driver_info;
    };
    
    

    参考/drivers/hid/usbhid/usbmouse.c(内核自带的USB鼠标驱动)

    static struct usb_device_id usb_mouse_id_table [] = {
    	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE) },
    	{ }	/* Terminating entry */
    };
    
    #define USB_INTERFACE_INFO(cl,sc,pr) 
    //设置id_table的.match_flags成员
    	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), 
    //设置id_table的3个成员,用于与匹配USB设备的3个成员
    	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
    
            
    //最终结果如下        
    .bInterfaceClass =USB_INTERFACE_CLASS_HID;  
       //设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03
       //HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03
    
    .bInterfaceSubClass =USB_INTERFACE_SUBCLASS_BOOT;  
       //设置匹配USB的接口子类型为启动设备
    
    .bInterfaceProtocol=USB_INTERFACE_PROTOCOL_MOUSE;
      //设置匹配USB的接口协议为USB鼠标的协议,等于2
      //当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议        
    

    查看下win7系统鼠标的设备信息,VID:表示厂家(vendor)ID,PID:表示产品(Product) ID

    mark

  • 相关阅读:
    python之private variable
    python实例、类方法、静态方法
    python常用option
    access
    FD_CLOEXEC
    fork后父子进程文件描述问题
    split
    信号
    kill
    进程组&Session
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10155314.html
Copyright © 2020-2023  润新知