USB驱动框架

一开始分析USB转串口驱动一头扎到源码,也就是追了个流程,但是还是什么都不懂,再后来看USB驱动框架也还是晕乎乎,之后再看了USB基础才慢慢理解一点。

一、USB基础知识

USB是Universal Serial Bus的缩写,中文译为通用串行总线,USB出现之前,计算机领域中的接口太多太繁杂,USB出现之后减少了接口的种类,总的来说就是设计出了一个万能的接口,各种外设都能用同一种接口,所以才冠以“通用(是Universal)”为名。

USB设备,从物理上的逻辑结构来说,包含了主机Host端和设备Device端。

其中,主机Host端,有对应的硬件的USB的主机控制器Host Controller,而设备端,连接的是对应的USB设备。

USB Host(USB主机)

主机就是USB总线中作主设备角色的设备, 负责管理USB总线中的数据传输及端口管理.

比如一个U盘(USB大容量储存设备)和PC通讯, PC在这里就是USB Host.

USB Device(USB设备)

USB Device就是在USB总线中作从设备角色的设备

USB Hub(USB集线器)

USB Hub可以将一个USB口转换为多个,USB Host带有Root Hub,第一个USB设备是一个根集线器(Root_hub)它控制连接到其上的整个USB总线,该控制器是连接PCI总线和USB总线的桥,也是该总线上的第一个USB设备,USB Hub对于上游而言是一个USB Device, 对于下游而言扮演USB Host, 所以USB设备本身不知道自己连接在Hub还是Root Hub上

先了解有这些东西,USB分主机端和设备端,知道有主机控制器(Host Controller)、设备(Device)、还有USB集线器(USB Hub)这些东西,下面再看到会眼熟,不会说是凭空出现一头雾水,这里也主要只讲主机端

二、Linux内核USB驱动框架

Linux内核支持两种主要类型的USB驱动程序:宿主(host)系统上的驱动程序和设备(device)上的驱动程序。从宿主的USB驱动程序控制插入其中的USB设备,而USB设备的驱动程序控制该设备如何作为一个USB设备和主机通信,由于“USB设备驱动程序”(USB devices drivers)容易混淆,USB开发者创建了术语“USB器件驱动程序”(USB gadget drivers)来描述控制连接到计算机的USB的设备驱动程序。

USB驱动框架_第1张图片

USB的主机控制器(HUD),出现了多种不同的类型,即OHCI和UHCI,EHCI,和xHCI,不同USB控制器类型OHCI,UHCI,EHCI,xHCI的区别和联系

USB采用树形拓扑结构,主机侧和设备侧的USB控制器分别称为主机控制器(Host Controller)和USB设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备间的通信,设备不能主动向主机发送任何消息。

主机侧:

从主机侧去看,在Linux驱动中,处于USB驱动最底层的是USB主机控制器硬件,在其上的是USB主机控制器驱动,在主机控制器驱动上的为USB核心层,再上层为USB设备驱动层。因此,在主机侧的层级结构中,要实现的USB驱动包括:USB主机控制器驱动和USB设备驱动。

USB核心层(USB core)向上为USB设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口,维护整个系统的USB设备信息,完成设备热插拔控制、总线数据传输控制等。

USB设备驱动(USB Devices driver)负责驱动具体的设备,例如U盘,鼠标等设备。USB设备驱动既可以注册成某个类型的设备驱动,例如输入子系统,此时的主设备号依据具体的子系统而定;又可以作为一个独立的USB设备注册进系统,这时的主设备号是180(USB_MAJOR)。

USB主机控制器驱动的代码位于:driver/usb/host,根据具体的硬件实现一个该硬件对应的主机控制器驱动的文件。

USB核心层代码位于:driver/usb/core。

USB设备驱动代码依据具体的设备放在对应的目录下。

当然还不止这些,设备、USB控制器,还少了个USB集线器,主机通过根集线器连接到各种外围设备(集线器和功能部件),USB物理总线拓扑:

USB驱动框架_第2张图片

当USB设备连接到集线器,集线器状态将发生相应的变化,并将状态变化信息传递给USB主机。USB主机通过根集线器向USB设备发送命令,获取USB设备的各种信息,包含USB设备传输类型、ID号、Product、USB速度等信息

Linux内核通过USB代码通过一个称为(USB请求块)的东西和所有的USB设备通信。USB数据传输都以URB(USB Request Block)请求URB生成URB递交URB释放为主线。从上图可知,当加载控制器驱动之后,注册根据集线器,hub(集线器)和hcd(host controller driver)驱动成为一个整体。接着,主机通过控制传输获取设备的控制描述符等信息。
    USB从设备通过集线器或根集线器连接到USB主机上。比如:主机通过根集线器与外界进行数据交互,根集线器通过探测数据线状态的变化来通知USB主机是否有USB外围设备接入

在主机端控制器驱动加载的过程中,注册了根集线器,然后匹配了相应的hub驱动程序,同时完成了对Hub轮询函数和状态处理函数的设置。这样一旦hub集线器的状态发生变化,就会产生相应的中断,主机端控制器就会执行相应的中断处理函数Hub集线器主要是用于USB的匹配识别。

USB匹配识别的框架:

USB驱动框架_第3张图片

 

下面贴出主机驱动框架也就好理解了:

USB驱动框架_第4张图片

一旦hub集线器的状态发生变化,就会产生相应的中断,主机端控制器就会执行相应的中断处理函数完成USB设备的识别匹配,USB控制器通过根集线器获取USB设备的各种信息,包含USB设备传输类型、ID号、Product、USB速度等信息。从上图也可以看到USB通过一个称为Urb(USB请求块)的东西和所有的USB设备通信。USB数据传输都以URB(USB Request Block)请求URB生成URB递交URB释放为主线。从上图可知,当加载控制器驱动之后,注册根据集线器,hub(集线器)和hcd(host controller driver)驱动成为一个整体。接着,主机通过控制传输获取设备的控制描述符等信息。USB主机和USB设备之间的数据传输共有四种类型:控制传输、批量传输、中断传输和同频传输。与之对应,USB主机和USB设备之间有四种事务:控制、批量、中断和等时。

三、Linux USB 驱动简单介绍

USB驱动程序存在于不同的内核子系统(块设备、网络设备、字符设备等等)和USB硬件控制器之中。USB驱动框架_第5张图片

 

USB核心为USB驱动程序提供了一个用于访问和控制USB硬件的接口,而不必考虑系统当前存在的各种不同类型的USB硬件控制器

USB协议中共定义了以下四种描述符:
   1) 设备描述符
   2) 配置描述符
   3) 接口描述符
   4) 端点描述符

一个USB设备文件描述符可以有多个配置,一个配置又可以有多个接口,一个接口可以有多个端点。

USB驱动框架_第6张图片

端点:

从最基础的端点说起。USB通信最基本的形式是通过端点(endpoint)的东西。USB端点只能往一个方向传送数据,从主机到设备(称为输出端点out)或者从设备到主机(称为输入端点in),端点可以看作是单向的管道。需要注意的是每个USB设备都有一个名为“端口0”的控制端点

USB端点有四种不同的类型,分别具有不同的传送数据的方式:

控制、中断、批量、等时

内核中使用struct usb_host_endpoint结构体来描述USB端口,该结构体在另一个名为struct usb_endpoint_descriptor的结构体中包含真正的端点信息。

struct usb_endpoint_descriptor {

    __u8  bLength;                            /* 描述符长度 */

    __u8  bDescriptorType;                    /* 描述符类型 端点描述符类型值是5 */



    __u8  bEndpointAddress;                   /* 端点地址:0~3位是端点号,第7位是方向(0为输出,1为输入) */

    __u8  bmAttributes;                       /* 端点属性:bit[0:1]的值为00表示控制,为01表示同步,为02表示批量,为03表示中断 */

    __le16 wMaxPacketSize;                    /* 本端点接受或发送的最大信息包的大小 */

    __u8  bInterval;                          /* 轮训数据传送端点的时间间隔,不同类型的端点代表了不同的含义 */



    /* NOTE:  these two are _only_ in audio endpoints. */

    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */

    __u8  bRefresh;

    __u8  bSynchAddress;

} __attribute__ ((packed));

 

接口:

USB端口被捆绑为接口,USB接口只处理一种USB逻辑,例如鼠标,键盘或者音频流,一些USB设备具有多个接口,例如例如一个USB声卡有两个接口,一个用来播放声音,一个用来录音。因为一个USB接口代表一个基本功能,而每个USB驱动程序控制一个接口,因此,以USB声卡为例,需要两个不同的驱动程序来处理一个硬件设备。

内核使用struct usb_interface结构体来描述USB接口。USB核心把该结构体传递给USB驱动程序,之后由USB驱动程序来负责控制该结构体,同样的struct usb_endpoint_descriptor的结构体中包含真正的接口信息

struct usb_interface_descriptor {

    __u8  bLength;                            /* 描述符长度 */

    __u8  bDescriptorType;                    /* 描述符类型 接口描述符类型值是4 */



    __u8  bInterfaceNumber;                   /* 接口的编号,相同的编号代表相同的功能接口 */

    __u8  bAlternateSetting;                  /* 接口的设置的编号,同一个接口可以有一个或者多个设置代表该接口下不同的参数 */

    __u8  bNumEndpoints;                      /* 该接口下的端点数,不包括端点0 */

    __u8  bInterfaceClass;                    /* 接口类型 */

    __u8  bInterfaceSubClass;                 /* 接口子类型 */

    __u8  bInterfaceProtocol;                 /* 接口遵循的协议 */

    __u8  iInterface;                         /* 描述该接口的字符串索引值 */

} __attribute__ ((packed));

配置:

USB接口被捆绑为配置。一个USB设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。例如,一些允许下载固件到其上的设备包含多个配置以完成这个工作,而一个时刻只能激活一个配置

struct usb_config_descriptor {

    __u8  bLength;                            /* 描述符长度 */

    __u8  bDescriptorType;                    /* 描述符类型 配置描述符类型值是2 */



    __le16 wTotalLength;                      /* 配置下面所有描述符的长度,包括这个配置描述符 */

    __u8  bNumInterfaces;                     /* 配置所支持的接口数 */

    __u8  bConfigurationValue;                /* 配置值 */

    __u8  iConfiguration;                     /* 描述该配置的字符串的索引值 */

    __u8  bmAttributes;                       /* 供电模式的选择 */

    __u8  bMaxPower;                          /* 设备从总线提取的最大电流 */

} __attribute__ ((packed));

设备:

一个设备里包含了不同级别的配置,可以有一个或者多个配置。设备描述符描述了这个设备。

在linux中,结构体usb_device_descriptor对应于协议中的设备描述符。

struct usb_device_descriptor {

    __u8  bLength;                           /* 描述符长度 */

    __u8  bDescriptorType;                   /* 描述符类型 设备描述符类型值是1 */



    __le16 bcdUSB;                           /* USB版本号 */

    __u8  bDeviceClass;                      /* USB分配的设备类code */

    __u8  bDeviceSubClass;                   /* USB分配的子类code */

    __u8  bDeviceProtocol;                   /* USB分配的协议code */

    __u8  bMaxPacketSize0;                   /* 端点0最大包大小 */

    __le16 idVendor;                         /* 厂商编号 */

    __le16 idProduct;                        /* 产品编号 */

    __le16 bcdDevice;                        /* 设备出厂编号 */

    __u8  iManufacturer;                     /* 描述厂商字符串的索引 */

    __u8  iProduct;                          /* 描述产品字符串的索引 */

    __u8  iSerialNumber;                     /* 描述设备序列号字符串的索引 */

    __u8  bNumConfigurations;                /* 可能的配置数量 */

} __attribute__ ((packed));

URB

Linux内核通过一个称为urb(USB请求块)的东西和所有的USB设备通信。这个请求块使用struct urb结构体来形容,可从include/linux/usb.h文件中找到。Urb被用来以一种异步的方式往特定的USB设备上的特定USB端点发送/接收数据。USB设备驱动可能会为单个端点分配许多urb,也可能对许多不同的端点重用单个的urb,这取决于驱动程序的需要。Urb典型的生命周期如下:

  1. 由USB设备驱动程序创建。
  2. 分配给一个特定的USB设备的特定端点。
  3. 由USB设备驱动程序递交到USB核心。
  4. 由USB核心地递交到特定的设备的特定的USB主控制器驱动程序。
  5. 由USB主控制器驱动程序处理,从设备进行USB传送。
  6. 当urb结束之后,USB主控制器驱动通知USB设备驱动程序

Urb创建的时候就可以创建成中断urb【usb_fill_int_urb()】,批量urb【usb_fill_bulk_urb()】,控制urb【usb_fill_contorl_urb()】还有等时urb,对应USB端点有四种不同的类型,分别具有不同的传送数据的方式:控制、中断、批量、等时

其他的就可以自己追源码再去理解了

 

参考:USB设备开发基础知识整理(学习笔记)

http://noodlefighter.com/post/note_usb_application/

USB协议架构及驱动架构:

https://blog.csdn.net/liangdapo/article/details/43699785

 

你可能感兴趣的:(fl2440开发板)