zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Delphi XE4 FireMonkey 开发 IOS APP 发布到 AppStore 最后一步.
    Native iOS Control Delphi XE4
    Delphi XE4 iAD Framework 支持.
    using IOS API with Delphi XE4
    GoF23种设计模式之行为型模式之命令模式
    Android青翼蝠王之ContentProvider
    Android白眉鹰王之BroadcastReceiver
    Android倚天剑之Notification之亮剑IOS
    Android紫衫龙王之Activity
    GoF23种设计模式之行为型模式之访问者模式
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10155314.html
Copyright © 2011-2022 走看看