一、USB概念概述
USB1.0版本速度1.5Mbps(低速USB) USB1.1版本速度12Mbps(全速USB) USB2.0版本速度480Mbps(高速USB)。 USB 分为主从两大体系,一般而言, PC 中的 USB 系统就是作主,而一般的 USB 鼠标, U 盘则是典型的 USB 从系统。
下是简单的列出了 USB 设备类型,理想的情况 USB 系统要对这些设备作完整的支持,设备也必须符合 USB 规范中的要求。 随着 USB 技术的发展, USB 系统中的一些不足也逐渐被承认, OTG 就是这种情况下的主要产物,OTG(On-The-Go), 即可以作主也可以作从,传说中的雌雄同体。这主要是为嵌入式设备准备的,因为 USB 是一种主从系统,不能支持点对点平等的传输数据, OTG 正是在这种需求下产生的, OTG 不仅支持控制器的主从切换,在一定层度上,也支持相同设备之间的数据交换
1.USB传输线及供电
一条USB的传输线分别由地线、电源线、D+、D-四条线构成,D+和D-是差分输入线(抗干扰),它使用的是3.3V的电压,而电源线和地线可向设备提供5V电压,最大电流为500MA。OTG 的做法就是增来一个 ID pin 来判断设备是接入设备的是主还是从(ID高则必为主,低则根据协议判断主从)。vbus 主要是供电, D+/D- 则是用来传输数据,就是我们前面所讲的主设备和从设备间唯一的一条铁路 USB设备有两种供电方式 ? 自供电设备:设备从外部电源获取工作电压 ? 总线供电设备:设备从VBUS(5v) 取电
对总线供电设备,区分低功耗和高功耗USB设备 ? 低功耗总线供电设备:最大功耗不超过100mA ? 高功耗总线供电设备: 枚举时最大功耗不超过100mA,枚举完成配置结束后功耗不超过500mA
设备在枚举过程中,通过设备的配置描述符向主机报告它的供电配置(自供电/总线供电)以及它的功耗要求
2.USB可以热插拔的硬件原理
USB主机是如何检测到设备的插入的呢?首先,在USB集线器的每个下游端口的D+和D-上,分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻拉到了低电平。而在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和高速设备,上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。这样,当设备插入到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的一条拉高了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上一层的集线器报告给USB主控制器),这样就检测到设备的插入了。USB高速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到高速模式的。在高速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
3.USB设备的构成
USB设备的构成包括了配置,接口和端点。 需要注意的是,驱动是绑定到USB接口上,而不是整个设备。 1.设备通常具有一个或者更多个配置 2.配置经常具有一个或者更多个接口 3.接口通常具有一个或者更多个设置 4.接口没有或者具有一个以上的端点
配置由接口组成,每个设备都有不同级别的配置信息; 接口由多个端点组成,代表一个基本的功能; 端点是USB设备中的唯一可寻址部分,可以理解为USB设备或主机上的一个数据缓冲区。 配置和设置的理解:一个手机可以有多重配置,比如可以作为电话,可以接在PC上当成一个U盘,这两种情况就属于不同的配置。再来看设置,一个手机作为电话已经确定了,但是通话场景(室外模式,会议模式等等)可以改变,每种场景就可以算一个设置。
例如:一个USB播放器带有音频,视频功能,还有旋钮和按钮。 配置1:音频(接口) + 旋钮(接口) 配置2:音频(接口) + 视频(接口) + 旋钮(接口) 配置3:视频(接口) + 旋钮(接口)
每一个接口均需要一个驱动程序。每个USB设备有一个唯一的地址,这个地址是在设备连上主机时,由主机分配的,而设备中的每个端点在设备内部有唯一的端点号,这个端点号是在设计设备时给定的。每个端点都是一个简单的连接点,是单向的。
端点0是一个特殊的端点,用于设备枚举和对设备进行一些基本的控制功能。除了端点0,其余的端点在设备配置之前不能与主机通信,只有向主机报告这些端点的特性并被确认后才能被激活。
例如: USB总线,类似于高速公路; 收发的数据,类似于汽车; USB端点,类似于高速公路收费站的入口或出口。
a.USB描述符
当USB设备插入到主机系统中,主机系统会自动检测USB设备的相关信息,就是通过USB描述符来实现的。
标准的USB设备有五种USB描述符:
- 设备描述符
- 配置描述符
- 接口描述符
- 端点描述符
- 字符串描述符(可选项)
一个设备只有一个设备描述符,而一个设备描述符可以包含多个配置描述符,而一个配置描述符可以包含多个接口描述符,一个接口使用了几个端点,就有几个端点描述符。
关于字符串描述符:在USB中,字符串描述符是可选的,也就是属于可有可无的角色,USB并没有强制规定必须有,但是一般产品是有的,至少能说明生产厂家、产品信息等等,如果设备没有字符串描述符,那么在设备描述符、配置描述符、接口描述符等处的字符串索引值必须为0,要不然在枚举过程中,USB主机会尝试去获取字符串描述符,而刚好你又没有,那么枚举就会失败,所以必须指定为0
b.硬件构成
高速模块一般分为控制器Controller和PHY两部分,Controller大多为数字逻辑实现,PHY通常为模拟逻辑实现。
USB芯片也分为Controller部分和PHY部分。Controller部分主要实现USB的协议和控制。内部逻辑主要有MAC层、CSR层和FIFO控制层,还有其他低功耗管理之类层次。MAC实现按USB协议进行数据包打包和解包,并把数据按照UTMI总线格式发送给PHY(USB3.0为PIPE)。CSR层进行寄存器控制,软件对USB芯片的控制就是通过CSR寄存器,这部分和CPU进行交互访问,主要作为Slave通过AXI或者AHB进行交互。FIFO控制层主要是和DDR进行数据交互,控制USB从DDR搬运数据的通道,主要作为Master通过AXI/AHB进行交互。PHY部分功能主要实现并转串的功能,把UTMI或者PIPE口的并行数据转换成串行数据,再通过差分数据线输出到芯片外部。
USB芯片内部实现的功能就是接受软件的控制,进而从内存搬运数据并按照USB协议进行数据打包,并串转换后输出到芯片外部。或者从芯片外部接收差分数据信号,串并转换后进行数据解包并写到内存里。
4.USB传输事务
USB通信最基本的形式是通过一个名为端点(endpoint)的东西。它是真实存在的。
端点只能往一个方向传送数据(端点0除外,端点0使用message管道,它既可以IN又可以OUT),或者IN,或者OUT。除了端点0,低速设备只能有2个端点,高速设备也只能有15个IN端点和15个OUT端点。
主机和端点之间的数据传输是通过管道。端点只有在device上才有,协议说端点代表在主机和设备端点之间移动数据的能力。USB通信都是由host端发起的。
首先明确一点USB协议规定所有的数据传输都必须由主机发起。所以这个传输的一般格式:令牌包(表明传输的类型),数据包(实际传输的数据),握手包(数据的正确性)。首先是由主机控制器发出令牌包,然后主机/设备发送数据包,甚至可以没有,最后设备/主机发送握手包,这么一个过程就叫做一个USB传输事务。一个USB传输事务就实现了一次从主机和设备间的通讯。
a.四种传输类型
端点有4中不同的类型:控制,批量,等时,中断。对应USB的4种不同的传输类型:
- 控制传输:适用于小量的,对传输时间和速率没有要求的设备。如USB设备配置信息。
- 批量传输:适用于类似打印机,扫描仪等传输量大,但对传输时间和速度无要求的设备。
- 等时传输:适用于大量的,速率恒定,具有周期性的数据,对实时性有要求的,比如音视频。
- 中断传输:适用于非大量,但具有周期性的数据,比如鼠标键盘。当USB宿主要求设备传输数据时,中断端点会以一个固定的数率传输数据。鼠标,键盘以及游戏手柄等。此种中断和经常说的硬件中断是不同的,此种中断会以固定的时间间隔来查询USB设备。
b.四种事务类型
一次传输由一个或多个事务构成。
- IN:IN事务为host输入服务,当host需要从设备获得数据的时候,就需要IN事务。
- OUT:OUT事务为host输出服务,当host需要输出数据到设备的时候,就需要OUT事务。
- SETUP:SETUP事务为host控制服务,当host希望传输一些USB规范的默认操作的时候就需要使用setup事务。
- SOF:这个用于帧同步
c.四种包(package)类型
一个事务由一个或多个包构成,包可分为令牌包(setup),数据包(data),握手包(ACK)和特殊包
- 令牌包:可分为OUT包、IN包、SetUp包和帧起始包,OUT包就是说明接下来的数据包的方向时从主机到设备。
- SYNC + PID + ADDR + ENDP + CRC5 :(同步) + (IN/OUT/SetUp) + (设备地址)+(设备端点) + (校验)
- 数据包:里面包含的就是我们实际要传输的东西。分为DATA0包和DATA1包,当USB发送数据的时候,当一次发送的数据长度大于相应端点的容量时,就需要把数据包分为好几个包,分批发送,DATA0包和DATA1包交替发送,即如果第一个数据包是 DATA0,那第二个数据包就是DATA1。
- SYNC + PID + DATA0/1 + CRC5:(同步) + (DATA0/1) + (数据) + (校验)。
- 但也有例外情况,在同步传输中(四类传输类型中之一),所有的数据包都是为DATA0,格式如下: SYNC + PID + 0~1023字节 + CRC16:(同步) + (DATA0) + (数据) + (校验)。
- 握手包:发送方发送了数据,接受方收应答。
- SYNC+PID:(同步)+(HandShake)
d.域
一个包由多个域构成,域可分为同步域(SYNC),标识域(PID),地址域(ADDR),端点域(ENDP),帧号域(FRAM),数据域(DATA),校验域(CRC)。 下图是一个USB鼠标插入Linux系统时完整的枚举过程 这里有一个概念需要注意,这里的中断传输与硬件中断那个中断是不一样的,这个中断传输实际是靠USB host control轮询usb device来实现的,而USB host control对于CPU则是基于中断的机制。
拿USB鼠标为例,USB host control对USB鼠标不断请求,这个请求的间隔是很短的,在USB spec Table 9-13端点描述符中的bInterval域中指定的,当鼠标发生过了事件之后,鼠标会发送数据回host,这时USB host control中断通知CPU,于是usb_mouse_irq被调用,在usb_mouse_irq里,就可以读取鼠标发回来的数据,当读完之后,驱动再次调用usb_submit_urb发出请求,就这么一直重复下去,一个usb鼠标的驱动也就完成了。
下面是USB鼠标中断传输图,可以看到USB host control向usb device发送了IN包,没有数据的时候device回复的是NAK,有数据的时候才向host control发送DATA包。
5.USB设备被识别的过程
当USB设备插上主机时,主机就通过一系列的动作来对设备进行枚举配置。
1、接入态(Attached):设备接入主机后,主机通过检测信号线上的电平变化来发现设备的接入; 2、供电态(Powered):就是给设备供电,分为设备接入时的默认供电值,配置阶段后的供电值(按数据中要求的最大值,可通过编程设置) 3、缺省态(Default):USB在被配置之前,通过缺省地址0与主机进行通信; 4、地址态(Address):经过了配置,USB设备被复位后,就可以按主机分配给它的唯一地址来与主机通信,这种状态就是地址态; 5、配置态(Configured):通过各种标准的USB请求命令来获取设备的各种信息,并对设备的某此信息进行改变或设置。 6、挂起态(Suspended):总线供电设备在3ms内没有总线动作,即USB总线处于空闲状态的话,该设备就要自动进入挂起状态,在进入挂起状态后,总的电流功耗不超过280UA。
6.标准的USB设备请求命令
USB设备请求命令是在控制传输的第一个阶段:setup事务传输的数据传输阶段发送给设备的。
标准USB设备请求命令共有11个,大小都是8个字节,具有相同的结构,由5 个字段构成。通过标准USB准设备请求,我们可以获取存储在设备EEPROM里面的信息;知道设备有哪些的设置或功能;获得设备的运行状态;改变设备的配置等。
标准USB准设备请求 = bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)
a.bmRequestType
[7 bit]= 0主机到设备; 1设备到主机 [6-5 bit]= 00标准请求命令; 01类请求命令; 10用户定义命令; 11保留 [4-0 bit]= 00000 接收者为设备; 00001 接收者为接口; 00010 接收者为端点; 00011 接收者为其他接收者; 其他 其他值保留
b.bRequest
0) 0 GET_STATUS:用来返回特定接收者的状态 1) 1 CLEAR_FEATURE:用来清除或禁止接收者的某些特性 2) 3 SET_FEATURE:用来启用或激活命令接收者的某些特性 3) 5 SET_ADDRESS:用来给设备分配地址 4) 6 GET_DEscriptOR:用于主机获取设备的特定描述符 5) 7 SET_DEscriptOR:修改设备中有关的描述符,或者增加新的描述符 6) 8 GET_CONFIGURATION:用于主机获取设备当前设备的配置值、 7) 9 SET_CONFIGURATION:用于主机指示设备采用的要求的配置 8) 10 GET_INTERFACE:用于获取当前某个接口描述符编号 9) 11 SET_INTERFACE:用于主机要求设备用某个描述符来描述接口 10) 12 SYNCH_FRAME:用于设备设置和报告一个端点的同步
wvalue: 这个字段是 request 的参数,request 不同,wValue就不同。 wIndex:wIndex,也是request 的参数,bRequestType指明 request 针对的是设备上的某个接口或端点的时候,wIndex 就用来指明是哪个接口或端点。 wLength:控制传输中 DATA transaction 阶段的长度。
二、 USB关键数据结构分析
1. USB设备结构体
在内核中使用数据结构 struct usb_device来描述整个USB设备
struct usb_device {
int devnum;
char devpath[16];
enum usb_device_state state;
enum usb_device_speed speed;
struct usb_tt *tt;
int ttport;
unsigned int toggle[2];
struct usb_device *parent;
struct usb_bus *bus;
struct usb_host_endpoint ep0;
struct device dev;
struct usb_device_descriptor descriptor;
struct usb_host_config *config;
struct usb_host_config *actconfig;
struct usb_host_endpoint *ep_in[16];
struct usb_host_endpoint *ep_out[16];
unsigned short bus_mA;
u8 portnum;
u8 level;
unsigned can_submit:1;
unsigned persist_enabled:1;
unsigned have_langid:1;
unsigned authorized:1;
unsigned authenticated:1;
unsigned wusb:1;
int string_langid;
char *product;
char *manufacturer;
char *serial;
struct list_head filelist;
#if defined(CONFIG_USB_DEVICEFS)
struct dentry *usbfs_dentry;
#endif
int maxchild;
atomic_t urbnum;
unsigned long active_duration;
#ifdef CONFIG_PM
unsigned long connect_time;
unsigned do_remote_wakeup:1;
unsigned reset_resume:1;
unsigned port_is_suspended:1;
#endif
struct wusb_dev *wusb_dev;
};
2. USB四大描述符
在USB描述符中,从上到下分为四个层次:USB设备描述符、USB配置描述符、USB接口描述符、USB端点描述符。
? 一个USB设备只有一个设备描述符 ? 一个设置描述符可以有多个配置描述符(注:配置同一时刻只能有一个生效,但可以切换) ? 一个配置描述符可以有多个接口描述符(比如声卡驱动,就有两个接口:录音接口和播放接口) ? 一个接口描述符可以有多个端点描述符
详细关系如下图所示
a. USB设备描述符(usb_device_descriptor)
struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdUSB;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0;
__le16 idVendor;
__le16 idProduct;
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__ ((packed));
linux中类的定义如下
b. USB配置描述符(usb_config_descriptor)
struct usb_config_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 wTotalLength;
__u8 bNumInterfaces;
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed));
c. USB接口描述符(usb_interface_descriptor)
USB接口只处理一种USB逻辑连接。一个USB接口代表一个逻辑上的设备,比如声卡驱动,就有两个接口:录音接口和播放接口。这可以在windows系统中看出,有时插入一个USB设备后,系统会识别出多个设备,并安装相应多个的驱动
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
d. USB端点描述符(usb_endpoint_descriptor)
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval;
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
3. USB接口结构体
USB 端点被绑为接口,USB接口只处理一种USB逻辑连接。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要 一个以上的驱动程序。实际上在linux中写的USB驱动大多都是接口驱动,复杂的USB设备驱动已经由usbcore完成了
一个接口可能有多个设置(一个接口多种功能),也就是这些接口所包含的端点凑起来可能有多种功能
struct usb_interface {
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting;
unsigned num_altsetting;
struct usb_interface_assoc_descriptor *intf_assoc;
int minor;
enum usb_interface_condition condition;
unsigned sysfs_files_created:1;
unsigned ep_devs_created:1;
unsigned unregistering:1;
unsigned needs_remote_wakeup:1;
unsigned needs_altsetting0:1;
unsigned needs_binding:1;
unsigned resetting_device:1;
unsigned authorized:1;
struct device dev;
struct device *usb_dev;
atomic_t pm_usage_cnt;
struct work_struct reset_ws;
};
4. USB端点结构体
USB 通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道(端点0除外)
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev;
unsigned char *extra;
int extralen;
int enabled;
int streams;
};
5. URB(USB Request Block,USB请求块)
a. 结构
USB 请求块是USB 设备驱动中用来描述与USB 设备通信所用的基本载体和核心数据结构,是usb主机与设备通信的电波
struct urb {
struct kref kref;
void *hcpriv;
atomic_t use_count;
atomic_t reject;
int unlinked;
struct list_head urb_list;
struct list_head anchor_list;
struct usb_anchor *anchor;
struct usb_device *dev;
struct usb_host_endpoint *ep;
unsigned int pipe;
unsigned int stream_id;
int status;
unsigned int transfer_flags;
void *transfer_buffer;
dma_addr_t transfer_dma;
struct scatterlist *sg;
int num_mapped_sgs;
int num_sgs;
u32 transfer_buffer_length;
u32 actual_length;
unsigned char *setup_packet;
dma_addr_t setup_dma;
int start_frame;
int number_of_packets;
int interval;
int error_count;
void *context;
usb_complete_t complete;
struct usb_iso_packet_descriptor iso_frame_desc[0];
};
b. 流程
urb 以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 URB队列, 所以多个 urb 可在队列清空之前被发送到相同的端点 在队列清空之前. 一个 URB的典型生命循环如下:
- 被一个 USB驱动(接口)创建.
- 申请:usb_alloc_urb
- 释放:usb_free_urb
- 安排给一个特定 USB 设备的特定端点(目标USB设备的指定端点)
- 中断urb:调用usb_fill_int_urb,此时urb绑定了对应设备,pipe指向对应的端点
- 批量urb:使用usb_fill_bulk_urb()函数来初始化urb
- 控制urb:使用usb_fill_control_urb()函数来初始化urb
- 等时urb:手工初始化
- 被 USB 设备驱动提交给 USB 核心,
- 在完成创建和初始化后,urb 便可以提交给USB 核心,通过usb_submit_urb()函数来完成
- 如果usb_submit_urb()调用成功,即URB 的控制权被移交给USB core。
- USB core提交该URB到USB主控制器驱动程序.
- USB主控制器驱动程序根据URB描述的信息,来访问USB设备.
- 以上操作完成后,USB主机控制器驱动通知 USB 设备驱动.
注:第4和第5步,由USB 核心和主机控制器完成,不受USB 设备驱动的控制
urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放,如下3 种情况,urb 将结束,urb 完成函数将被调用。
1、 urb 被成功发送给设备,并且设备返回正确的确认。如果urb→status 为0,意味着对于一个输出urb,数据被成功发送;对于一个输入urb,请求的数据被成功收到。 2、 如果发送数据到设备或从设备接收数据时发生了错误,urb→status 将记录错误值. 3、 urb 被从USB 核心“去除连接”,这发生在驱动通过usb_unlink_urb()或usb_kill_urb()函数取消urb,或urb 虽已提交,而USB 设备被拔出的情况下
三、 USB驱动架构简述
USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)、USB核心驱动(USBD)和不同种类的USB设备类驱动,如下所示。其中HCD和USBD被称为协议软件或者协议栈,这两部分共同处理与协议相关的操作
在Linux USB子系统中,HCD是直接和硬件进行交互的软件模块,是USB协议栈的最底层部分,是USB主机控制器硬件和数据传输的一种抽象。
HCD向上仅对USB总线驱动程序服务,HCD提供了一个软件接口,即HCDI,使得各种USB主机控制器的硬件特性都被软件化,并受USB总线驱动程序的调用和管理。HCD向下则直接管理和检测主控制器硬件的各种行为。HCD提供的功能主要有:主机控制器硬件初始化;为USBD层提供相应的接口函数;提供根HUB(ROOT HUB)设备配置、控制功能;完成4种类型的数据传输等。
USBD部分是整个USB主机驱动的核心,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接口、提供应用程序访问USB系统的文件接口等。其中USB HUB作为一类特殊的USB设备,其驱动程序被包含在USBD层。
在嵌入式Linux系统中,已经包含HCD模块和USB核心驱动USBD,不需要用户重新编写,用户仅仅需要完成USB设备类驱动即可
全流程如下图
四、 USB CORE
1. USB子系统初始化
a. usb_debugfs
如上图,其输出的意义如下:
- T—topology,表示的是拓扑结构上的意思。
- Bus:是其所在的usb总线号,一个总线号会对应一个rootHub,并且一个总线号对应的设备总数<=127,这是倒不是因为电气特性限制,而是因为USB规范中规定用7bit寻址设备,第八个bit用于标识数据流向。00就是0号总线。
- Lev:该设备所在层,这个Lev信息看图最明显了。
- Prnt:parent Devicenumber父设备的ID号,rootHUb没有父设备,该值等于零,其它的设备的父设备一定指向一个hub。
- port:该设备连接的端口号,这里指的端口号是下行端口号,并且一个hub通常下行端口号有多个,上行端口号只有一个。
- Cnt:这个Lev上设备的总数,hub也会计数在内,hub也是usb设备,其是主机控制器和usb设备通信的桥梁。
- Dev:是设备号,按顺序排列的,一个总线上最多挂127个;可以有多个总线。
- spd:设备的速率,12M(1.1)、480M(2.0)等。
- MxCh:最多挂接的子设备个数,这个数值通常对应于HuB的下行端口号个数。
- B—Band width
- Alloc:该总线分配得到的带宽
- Int:中断请求数
- ISO:同步传输请求数,USB有四大传输,中断、控制、批量和同步。
- D–Device Descriptor 设备描述符。
- Ver:设备USB版本号。
- Cls:设备的类(hub的类是9),
- sub:设备的子类
- Prot:设备的协议
- MxPS:default 端点的最大packet size
- Cfgs: 配置的个数;USB里共有四大描述符,它们是设备描述符、端点描述符、接口描述符和配置描述符。
- P—设备信息
- Vendor: 厂商ID,Linuxfoundation的ID是1d6b,http://www.linux-usb.org/usb.ids
- Rev: 校订版本号
-
S—Manufacturer -
S—产品 -
S—序列号 -
C*—配置描述信息
- #Ifs:接口的数量,
- Atr:属性
- MxPwr:最大功耗,USB设备供电有两种方式,self-powered和bus-powered两种方式,驱动代码会判断设备标志寄存器是否过流的。最大500mA。
- I–描述接口的接口描述符
- If#:接口号
- Alt:接口属性
- #EPs:接口具有的端点数量,端点零必须存在,在USB设备addressed之前,会使用该端口配置设备。
- Cls:接口的类
- Sub:接口的子类
- Prot:接口的协议
- Driver:驱动的名称。
- E—端点描述符
- Ad(s):端点地址,括号的s为I或者O表示该端点是输入还是输出端点。
- Atr(sss):端点的属性,sss是端点的类型,对应上述的四大传输类型。
- MxPS:端点具有的最大传输包
- Ivl:传输间的间隔。
b. USB总线注册与通知
retval = bus_register(&usb_bus_type);
if (retval)
goto bus_register_failed;
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
if (retval)
goto bus_notifier_failed;
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
Usb总线注册后,通过usb_device_match将驱动与设备进行匹配,匹配如果通过,则调用驱动的probe进行注册,其具体注册流程为: driver_register-> bus_add_driver-> driver_attach-> __driver_attach-> driver_match_device(bus->match)-> driver_probe_device-> really_probe(dev->bus->probe)
注册通知则是通过以下函数实现,可以看出,如果一个驱动与设备匹配完成后并成功注册,则notify实现了将该设备注册进sysfs中,并且区分是具体的设备还是设备下的接口。当设备卸载时,从sysfs中对其删除
static struct notifier_block usb_bus_nb = {
.notifier_call = usb_bus_notify,
};
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
if (dev->type == &usb_device_type)
(void) usb_create_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
usb_create_sysfs_intf_files(to_usb_interface(dev));
break;
case BUS_NOTIFY_DEL_DEVICE:
if (dev->type == &usb_device_type)
usb_remove_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
usb_remove_sysfs_intf_files(to_usb_interface(dev));
break;
}
return 0;
}
c. USB驱动注册
在USB子系统初始化过程中,一共注册了3个驱动,后面随着启动又注册了几个usb驱动,可以看出,设备驱动只有一个,剩下的全是接口驱动 如上图,在USB子系统初始化阶段就注册的三个驱动分别为usbfs,hub和usb设备驱动 如下图为USB子系统注册驱动的关键函数,一般的接口驱动最终都会调用usb_probe_interface 匹配完成后在调用具体驱动的probe进行进一步的匹配和初始化
2. USB设备通用驱动(usb core)
a. 通用流程
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
该驱动由usb子系统初始化时注册,通常情况下,任何一个USB实体设备在与驱动匹配时都会与该驱动匹配,该驱动是接口驱动的接口,获取USB配置并进行配置,详情如下(部分删减) 其中获取配置的环节为:register_root_hub -> usb_get_device_descriptor + usb_new_device -> usb_enumerate_device(设备枚举) -> usb_get_configuration 获取配置
static int generic_probe(struct usb_device *udev)
{
int err, c;
if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
}
}
usb_notify_add_device(udev);
return 0;
}
在设备枚举过程中,会获取到usb设备配置(通过一系列复杂的usb通信流程),然后在通用驱动中选择一个合适的配置(根据总线所能支持的最大电流来选择一个),随后进入配置阶段
int usb_set_configuration(struct usb_device *dev, int configuration)
{
int i, ret;
struct usb_host_config *cp = NULL;
struct usb_interface **new_interfaces = NULL;
struct usb_hcd *hcd = bus_to_hcd(dev->bus);
int n, nintf;
if (dev->authorized == 0 || configuration == -1)
configuration = 0;
else {
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue ==
configuration) {
cp = &dev->config[i];
break;
}
}
}
if ((!cp && configuration != 0))
return -EINVAL;
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
n = nintf = 0;
if (cp) {
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
GFP_NOIO);
if (!new_interfaces)
return -ENOMEM;
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(
sizeof(struct usb_interface),
GFP_NOIO);
if (!new_interfaces[n]) {
ret = -ENOMEM;
free_interfaces:
while (--n >= 0)
kfree(new_interfaces[n]);
kfree(new_interfaces);
return ret;
}
}
i = dev->bus_mA - usb_get_max_power(dev, cp);
if (i < 0)
dev_warn(&dev->dev, "new config #%d exceeds power "
"limit by %dmA\n",
configuration, -i);
}
ret = usb_autoresume_device(dev);
if (ret)
goto free_interfaces;
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device(dev, 1);
中间省略部分对接口的配置过程
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
dev_info(&dev->dev,
"adding %s (config #%d, interface %d)\n",
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
device_enable_async_suspend(&intf->dev);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
dev_name(&intf->dev), ret);
continue;
}
create_intf_ep_devs(intf);
}
usb_autosuspend_device(dev);
return 0;
}
b. 驱动匹配(接口)
在设备注册过程中会经由总线__device_attach -> __device_attach_driver -> driver_match_device最终会执行drv->bus->match(dev, drv),实际上执行了usb_device_match 可以看出usb设备驱动分成两大类:设备驱动和接口驱动(一般来说,usb驱动大都是接口驱动,一个usb接口对应一个驱动,但是从大的整体上看,usb设备也是要有驱动的,使用一般就是usb的通用驱动,还有要注意接口驱动和设备驱动驱动结构体都是不同的)
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
if (is_usb_device(dev)) {
if (!is_usb_device_driver(drv))
return 0;
return 1;
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
USB设备驱动,使用usb_device_driver,USB接口驱动,使用usb_driver 对于SOC上的USB,当驱动加载时,会先有USB设备驱动匹配,在有USB驱动(接口)匹配 对于大多数USB设备,作为USB设备的驱动都是通用usb_generic_driver,接口驱动各不相同。 在匹配过程中会有一个结构体(由驱动去决定)起到关键作用
struct usb_device_id {
__u16 match_flags;
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 bInterfaceNumber;
kernel_ulong_t driver_info
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
比如hub进行匹配时有如下图,这样直接将hub驱动与设备就匹配到了
3. hub驱动(usb core)
a. 驱动注册
USB子系统注册的3个最初的驱动之一,usb主机会自动匹配并注册成roothub,流程继设备通用驱动的接口驱动匹配,匹配成功后即进入hub_probe,下面来具体分析一下整个驱动,驱动接口如下
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
根据id_table,只要match_flag符合如下3种就是hub类,就能顺利匹配上了
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ }
};
在USB子系统初始化过程中调用usb_hub_init即开始了hub的注册,实际上就做了两件事情:驱动注册和开启一个工作队列
int usb_hub_init(void)
{
usb_register(&hub_driver)
……
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
}
b. hub激活过程
i. hub_probe
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *hdev;
struct usb_hub *hub;
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
省略部分
if (hdev->level == MAX_TOPO_LEVEL) {
return -E2BIG;
}
endpoint = &desc->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
goto descriptor_error;
dev_info(&intf->dev, "USB hub found\n");
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
if (!hub)
return -ENOMEM;
kref_init(&hub->kref);
hub->intfdev = &intf->dev;
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event);
省略部分
if (hub_configure(hub, endpoint) >= 0)
return 0;
hub_disconnect(intf);
return -ENODEV;
}
ii. hub_configure
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
……
ret = get_hub_descriptor(hdev, hub->descriptor);
if (ret < 0) {
message = "can't read hub descriptor";
goto fail;
}
maxchild = hub->descriptor->bNbrPorts;
dev_info(hub_dev, "%d port%s detected\n", maxchild,
(maxchild == 1) ? "" : "s");
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
mutex_lock(&usb_port_peer_mutex);
for (i = 0; i < maxchild; i++) {
ret = usb_hub_create_port_device(hub, i + 1);
if (ret < 0) {
dev_err(hub->intfdev,
"couldn't create port%d device.\n", i + 1);
break;
}
}
hdev->maxchild = i;
mutex_unlock(&usb_port_peer_mutex);
if (hcd->driver->update_hub_device) {
ret = hcd->driver->update_hub_device(hcd, hdev,
&hub->tt, GFP_KERNEL);
if (ret < 0) {
message = "can't update HCD hub info";
goto fail;
}
}
usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
hub_activate(hub, HUB_INIT);
}
iii. kick_hub_wq
最终调用在一开始创建的工作队列,而工作队列里要做的事情就是 hub->events 也就是 hub_event这个函数
static void kick_hub_wq(struct usb_hub *hub)
{
……
kref_get(&hub->kref);
if (queue_work(hub_wq, &hub->events))
return;
……
kref_put(&hub->kref, hub_release);
}
c. hub枚举过程
i. hub_irq
USB枚举由HUB激活(首次或者重启)或者hub_irq两种方式激活,由hub激活在激活过程已经分析过,接下来分析hub_irq这条路线,从程序上分析出:该urb触发后调用hub_event工作队列,然后该urb又被该函数本身提交,即该urb循环利用
static void hub_irq(struct urb *urb)
{
switch (status) {
case -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
return;
default:
dev_dbg(hub->intfdev, "transfer --> %d\n", status);
if ((++hub->nerrors < 10) || hub->error)
goto resubmit;
hub->error = status;
case 0:
bits = 0;
for (i = 0; i < urb->actual_length; ++i)
bits |= ((unsigned long) ((*hub->buffer)[i]))
<< (i*8);
hub->event_bits[0] = bits;
break;
}
hub->nerrors = 0;
kick_hub_wq(hub);
resubmit:
if (hub->quiescing)
return;
status = usb_submit_urb(hub->urb, GFP_ATOMIC);
if (status != 0 && status != -ENODEV && status != -EPERM)
dev_err(hub->intfdev, "resubmit --> %d\n", status);
}
ii. hub_event
static void hub_event(struct work_struct *work)
{
……
for (i = 1; i <= hdev->maxchild; i++) {
struct usb_port *port_dev = hub->ports[i - 1];
if (test_bit(i, hub->event_bits)
|| test_bit(i, hub->change_bits)
|| test_bit(i, hub->wakeup_bits)) {
……
port_event(hub, i);
……
}
}
}
iii. port_event之后到新设备识别
函数 port_event对需要改变的port进行处理,并根据协议想该端口进行通讯,最后调用函数 hub_port_connect_change
static void port_event(struct usb_hub *hub, int port1)
__must_hold(&port_dev->status_lock)
{
……
if (portchange & USB_PORT_STAT_C_CONNECTION) {
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
connect_change = 1;
}
其他portchange略
……
if (connect_change)
hub_port_connect_change(hub, port1, portstatus, portchange);
}
然后 hub_port_connect_change 在调用hub_port_connect
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
usb_disconnect(&port_dev->child);
udev = usb_alloc_dev(hdev, hdev->bus, port1);
choose_devnum(udev);
status = hub_port_init(hub, udev, port1, i);
port_dev->child = udev;
status = usb_new_device(udev);
……
}
iv. usb_new_device新设备注册
USB子系统注册了bus对应的probe接口即usb_probe_interface
int usb_new_device(struct usb_device *udev)
{
……
err = usb_enumerate_device(udev);
……
err = device_add(&udev->dev);
流程如下
device_add –> bus_probe_device -> device_initial_probe -> __device_attach -> __device_attach_driver -> driver_probe_device -> really_probe -> dev->bus->probe(即usb_probe_interface) -> driver->probe(到这里根据id_table匹配的设备去注册对应设备驱动)
……
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
……
}
d. hub中的“irq”的产生
对于hub_irq这个“中断”,是注册在中断urb中的,当该urb完成时调用,也即是说和urb的流程一一相关,而在hub驱动中调用usb_submit_urb后,urb就归于主机和usbcore去控制了,所以需要深入了解一下
i. urb在hcd中实际是定时器轮询
usb_submit_urb – > usb_hcd_submit_urb
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
……
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
usbmon_urb_submit(&hcd->self, urb);
if (is_root_hub(urb->dev)) {
status = rh_urb_enqueue(hcd, urb);
} else {
……
}
return status;
}
rh_urb_enqueue -> rh_queue_status
static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
{
……
hcd->status_urb = urb;
urb->hcpriv = hcd;
if (!hcd->uses_new_polling)
mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
else if (HCD_POLL_PENDING(hcd))
mod_timer(&hcd->rh_timer, jiffies);
retval = 0;
……
return retval;
}
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
struct urb *urb;
int length;
unsigned long flags;
char buffer[6];
length = hcd->driver->hub_status_data(hcd, buffer);
if (length > 0) {
spin_lock_irqsave(&hcd_root_hub_lock, flags);
urb = hcd->status_urb;
if (urb) {
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
usb_hcd_giveback_urb(hcd, urb, 0);
} else {
length = 0;
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
}
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
}
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
也就是说,对于roothub的hub_irq实际上就是一个定时器,去不停的轮询prot的状态,而端口状态由函数hcd->driver->hub_status_data(hcd, buffer)获取
ii. 实际的状态值是中断产生
在dwc2(USB架构中的主机控制器驱动)中有如下函数
static struct hc_driver dwc2_hc_driver = {
……
.irq = _dwc2_hcd_irq,
……
.hub_status_data = _dwc2_hcd_hub_status_data,
……
};
static int _dwc2_hcd_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
buf[0] = dwc2_hcd_is_status_changed(hsotg, 1) << 1;
return buf[0] != 0;
}
static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port)
{
int retval;
if (port != 1)
return -EINVAL;
retval = (hsotg->flags.b.port_connect_status_change ||
hsotg->flags.b.port_reset_change ||
hsotg->flags.b.port_enable_change ||
hsotg->flags.b.port_suspend_change ||
hsotg->flags.b.port_over_current_change);
return retval;
}
以端口状态被改变为例
void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
{
if (hsotg->lx_state != DWC2_L0)
usb_hcd_resume_root_hub(hsotg->priv);
hsotg->flags.b.port_connect_status_change = 1;
hsotg->flags.b.port_connect_status = 1;
}
而dwc2_hcd_connect由dwc2_port_intr调用,而该函数的调用路径为: _dwc2_hcd_irq -> dwc2_handle_hcd_intr –> dwc2_port_intr(dwc2中详解) 即最终被注册成了一个实际上的中断
static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
{
u32 hprt0;
u32 hprt0_modify;
dev_vdbg(hsotg->dev, "--Port Interrupt--\n");
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
hprt0_modify = hprt0;
hprt0_modify &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG |
HPRT0_OVRCURRCHG);
if (hprt0 & HPRT0_CONNDET) {
dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0);
dev_vdbg(hsotg->dev,
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
hprt0);
dwc2_hcd_connect(hsotg);
}
……
}
也就是说,roothub识别的最终流程为:实际物理设备的中断调用改变了prot的状态标志,由主机控制器的定时器轮询到然后将该状态的位表返回给hub,然后hub调用工作队列执行hub_event在根据位表去判断具体那个prot发生了改变然后进行进一步的操作。
4. HCD
a. usb_add_hcd
Hcd负责urb的一些控制,具体不去详细深入,在上面hub产生irq已经有过部分分析,这里对usbcore中的hcd初始化分析一下
int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
struct phy *phy = phy_get(hcd->self.controller, "usb");
……
retval = phy_init(phy);
……
retval = phy_power_on(phy);
……
hcd->phy = phy;
hcd->remove_phy = 1;
}
……
retval = usb_register_bus(&hcd->self);
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
……
hcd->self.root_hub = rhdev;
……
if (hcd->driver->reset) {
retval = hcd->driver->reset(hcd);
……
}
……
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
……
retval = hcd->driver->start(hcd);
……
retval = register_root_hub(hcd);
……
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
return retval;
}
b. register_root_hub
static int register_root_hub(struct usb_hcd *hcd)
{
struct device *parent_dev = hcd->self.controller;
struct usb_device *usb_dev = hcd->self.root_hub;
const int devnum = 1;
int retval;
usb_dev->devnum = devnum;
usb_dev->bus->devnum_next = devnum + 1;
……
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
……
retval = usb_new_device (usb_dev);
……
}
五、 主机控制器驱动(以dwc2进行分析)
1. 驱动注册
驱动匹配设备树(涉及到真的硬件了)以及match_table,之后进行probe
static struct platform_driver dwc2_platform_driver = {
.driver = {
.name = dwc2_driver_name,
.of_match_table = dwc2_of_match_table,
.pm = &dwc2_dev_pm_ops,
},
.probe = dwc2_driver_probe,
.remove = dwc2_driver_remove,
.shutdown = dwc2_driver_shutdown,
};
probe函数中主要进行了硬件信息获取与初始化,中断设置以及模式设置,下面来具体分析一下
static int dwc2_driver_probe(struct platform_device *dev)
{
……
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
retval = dwc2_lowlevel_hw_init(hsotg);
hsotg->core_params = devm_kzalloc(&dev->dev,
sizeof(*hsotg->core_params), GFP_KERNEL);
dwc2_set_all_params(hsotg->core_params, -1);
hsotg->irq = platform_get_irq(dev, 0);
retval = devm_request_irq(hsotg->dev, hsotg->irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
retval = dwc2_lowlevel_hw_enable(hsotg);
retval = dwc2_get_dr_mode(hsotg);
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
retval = dwc2_get_hwparams(hsotg);
dwc2_set_parameters(hsotg, params);
dwc2_force_dr_mode(hsotg);
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg, hsotg->irq);
hsotg->gadget_enabled = 1;
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
retval = dwc2_hcd_init(hsotg, hsotg->irq);
hsotg->hcd_enabled = 1;
}
platform_set_drvdata(dev, hsotg);
dwc2_debugfs_init(hsotg);
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
dwc2_lowlevel_hw_disable(hsotg);
return 0;
error:
dwc2_lowlevel_hw_disable(hsotg);
return retval;
}
2. 注册成主机
可以分析出其根基于dwc2_hcd_init开始,重点分析该函数
int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
{
hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev,
dev_name(hsotg->dev));
dwc2_disable_global_interrupts(hsotg);
retval = dwc2_core_init(hsotg, true);
hsotg->wq_otg = alloc_ordered_workqueue("dwc2", 0);
INIT_WORK(&hsotg->wf_otg, dwc2_conn_id_status_change);
setup_timer(&hsotg->wkp_timer, dwc2_wakeup_detected,
(unsigned long)hsotg);
INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func);
if (!IS_ERR_OR_NULL(hsotg->uphy))
otg_set_host(hsotg->uphy->otg, &hcd->self);
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
device_wakeup_enable(hcd->self.controller);
dwc2_enable_global_interrupts(hsotg);
return 0;
}
int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
int retval;
struct usb_device *rhdev;
if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
struct phy *phy = phy_get(hcd->self.controller, "usb");
retval = phy_init(phy);
retval = phy_power_on(phy);
hcd->phy = phy;
}
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
retval = usb_register_bus(&hcd->self);
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
mutex_lock(&usb_port_peer_mutex);
hcd->self.root_hub = rhdev;
mutex_unlock(&usb_port_peer_mutex);
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
hcd->state = HC_STATE_RUNNING;
retval = hcd->driver->start(hcd);
retval = register_root_hub(hcd);
return retval;
}
3. 中断
USB通用中断,也就是主从都可能触发的中断
irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
{
struct dwc2_hsotg *hsotg = dev;
u32 gintsts;
irqreturn_t retval = IRQ_NONE;
spin_lock(&hsotg->lock);
if (!dwc2_is_controller_alive(hsotg)) {
dev_warn(hsotg->dev, "Controller is dead\n");
goto out;
}
gintsts = dwc2_read_common_intr(hsotg);
if (gintsts & ~GINTSTS_PRTINT)
retval = IRQ_HANDLED;
if (gintsts & GINTSTS_MODEMIS)
dwc2_handle_mode_mismatch_intr(hsotg);
if (gintsts & GINTSTS_OTGINT)
dwc2_handle_otg_intr(hsotg);
if (gintsts & GINTSTS_CONIDSTSCHNG)
dwc2_handle_conn_id_status_change_intr(hsotg);
if (gintsts & GINTSTS_DISCONNINT)
dwc2_handle_disconnect_intr(hsotg);
if (gintsts & GINTSTS_SESSREQINT)
dwc2_handle_session_req_intr(hsotg);
if (gintsts & GINTSTS_WKUPINT)
dwc2_handle_wakeup_detected_intr(hsotg);
if (gintsts & GINTSTS_USBSUSP)
dwc2_handle_usb_suspend_intr(hsotg);
if (gintsts & GINTSTS_PRTINT) {
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev,
" --Port interrupt received in Device mode--\n");
dwc2_handle_usb_port_intr(hsotg);
retval = IRQ_HANDLED;
}
}
out:
spin_unlock(&hsotg->lock);
return retval;
}
主机(HCD)中断,HCD驱动由dwc2_hcd_init进行注册。具体的中断注册路线为:dwc2_hcd_init(hsotg, hsotg->irq)(注:这是hsotg->irq已经有通用中断了) –> usb_add_hcd(hcd, irq, IRQF_SHARED) -> usb_hcd_request_irqs(hcd, irqnum, irqflags) -> request_irq(irqnum, &usb_hcd_irq, irqflags,hcd->irq_descr, hcd)-> hcd->driver->irq(hcd)
static struct hc_driver dwc2_hc_driver = {
其他全部省略
.irq = _dwc2_hcd_irq,(由最后一步hcd->driver->irq(hcd)调用)
};
static irqreturn_t _dwc2_hcd_irq(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
return dwc2_handle_hcd_intr(hsotg);
}
irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
{
u32 gintsts, dbg_gintsts;
irqreturn_t retval = IRQ_NONE;
if (!dwc2_is_controller_alive(hsotg)) {
dev_warn(hsotg->dev, "Controller is dead\n");
return retval;
}
spin_lock(&hsotg->lock);
if (dwc2_is_host_mode(hsotg)) {
gintsts = dwc2_read_core_intr(hsotg);
if (!gintsts) {
spin_unlock(&hsotg->lock);
return retval;
}
retval = IRQ_HANDLED;
if (gintsts & GINTSTS_SOF)
dwc2_sof_intr(hsotg);
if (gintsts & GINTSTS_RXFLVL)
dwc2_rx_fifo_level_intr(hsotg);
if (gintsts & GINTSTS_NPTXFEMP)
dwc2_np_tx_fifo_empty_intr(hsotg);
if (gintsts & GINTSTS_PRTINT)
dwc2_port_intr(hsotg);
if (gintsts & GINTSTS_HCHINT)
dwc2_hc_intr(hsotg);
if (gintsts & GINTSTS_PTXFEMP)
dwc2_perio_tx_fifo_empty_intr(hsotg);
}
spin_unlock(&hsotg->lock);
return retval;
}
六、 接口驱动
1. usb-skeleton
a. 设备与驱动匹配
usb-skeleton.c是USB Host端代码的一个骨架,如果想要编写自己的Host端 bulk传输的代码,可以参考这个部分的代码进行编写,至于其他 isoc的传输方式,可能还需要参考其他的驱动代码进行编写 使用module_usb_driver注册HOST端驱动,声明匹配的gadget驱动列表(主要依赖VID与PID),完善相关的探测、断开连接等函数
static struct usb_driver skel_driver = {
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
.suspend = skel_suspend,
.resume = skel_resume,
.pre_reset = skel_pre_reset,
.post_reset = skel_post_reset,
.id_table = skel_table,
.supports_autosuspend = 1,
};
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
static const struct usb_device_id skel_table[] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ }
};
b. probe
关键数据如下 遍历接口的当前配置的所有端点,找到bulk in端点地址并申请urb传输描述符
static int skel_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_skel *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);
init_waitqueue_head(&dev->bulk_in_wait);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
buffer_size = usb_endpoint_maxp(endpoint);
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!dev->bulk_in_buffer)
goto error;
dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->bulk_in_urb)
goto error;
}
if (!dev->bulk_out_endpointAddr &&
usb_endpoint_is_bulk_out(endpoint)) {
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
dev_err(&interface->dev,
"Could not find both bulk-in and bulk-out endpoints\n");
goto error;
}
usb_set_intfdata(interface, dev);
retval = usb_register_dev(interface, &skel_class);
if (retval) {
dev_err(&interface->dev,
"Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL);
goto error;
}
dev_info(&interface->dev,
"USB Skeleton device now attached to USBSkel-%d",
interface->minor);
return 0;
error:
if (dev)
kref_put(&dev->kref, skel_delete);
return retval;
}
c. write
可以看出该write每次写入大小有限制的,如果超过一定长度,需要应用层去尝试多次写入,在填充urb是,write_back在写完触发时,释放信号量
static ssize_t skel_write(struct file *file, const char *user_buffer,
size_t count, loff_t *ppos)
{
struct usb_skel *dev;
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER);
dev = file->private_data;
if (count == 0)
goto exit;
if (!(file->f_flags & O_NONBLOCK)) {
if (down_interruptible(&dev->limit_sem)) {
retval = -ERESTARTSYS;
goto exit;
}
} else {
if (down_trylock(&dev->limit_sem)) {
retval = -EAGAIN;
goto exit;
}
}
spin_lock_irq(&dev->err_lock);
retval = dev->errors;
if (retval < 0) {
dev->errors = 0;
retval = (retval == -EPIPE) ? retval : -EIO;
}
spin_unlock_irq(&dev->err_lock);
if (retval < 0)
goto error;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
&urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, writesize)) {
retval = -EFAULT;
goto error;
}
mutex_lock(&dev->io_mutex);
if (!dev->interface) {
mutex_unlock(&dev->io_mutex);
retval = -ENODEV;
goto error;
}
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, writesize, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
retval = usb_submit_urb(urb, GFP_KERNEL);
mutex_unlock(&dev->io_mutex);
if (retval) {
dev_err(&dev->interface->dev,
"%s - failed submitting write urb, error %d\n",
__func__, retval);
goto error_unanchor;
}
usb_free_urb(urb);
return writesize;
error_unanchor:
usb_unanchor_urb(urb);
error:
if (urb) {
usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
up(&dev->limit_sem);
exit:
return retval;
}
d. read
读取函数与写类似,read_back时将读取的数据长度赋值给in_filled,也是如果读取超过了端点的最大长度,一次读的只能是端点大小,需要应用层进行多次读取操作
static ssize_t skel_read(struct file *file, char *buffer, size_t count,
loff_t *ppos)
{
struct usb_skel *dev;
int rv;
bool ongoing_io;
dev = file->private_data;
if (!dev->bulk_in_urb || !count)
return 0;
rv = mutex_lock_interruptible(&dev->io_mutex);
if (rv < 0)
return rv;
if (!dev->interface) {
rv = -ENODEV;
goto exit;
}
retry:
spin_lock_irq(&dev->err_lock);
ongoing_io = dev->ongoing_read;
spin_unlock_irq(&dev->err_lock);
if (ongoing_io) {
if (file->f_flags & O_NONBLOCK) {
rv = -EAGAIN;
goto exit;
}
rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
if (rv < 0)
goto exit;
}
rv = dev->errors;
if (rv < 0) {
dev->errors = 0;
rv = (rv == -EPIPE) ? rv : -EIO;
goto exit;
}
if (dev->bulk_in_filled) {
size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
size_t chunk = min(available, count);
if (!available) {
rv = skel_do_read_io(dev, count);
if (rv < 0)
goto exit;
else
goto retry;
}
if (copy_to_user(buffer,
dev->bulk_in_buffer + dev->bulk_in_copied,
chunk))
rv = -EFAULT;
else
rv = chunk;
dev->bulk_in_copied += chunk;
if (available < count)
skel_do_read_io(dev, count - chunk);
} else {
rv = skel_do_read_io(dev, count);
if (rv < 0)
goto exit;
else
goto retry;
}
exit:
mutex_unlock(&dev->io_mutex);
return rv;
}
|