导图社区 USB Core
USB Core相关知识总结笔记,内容包括USB控制器、USB连接、USB设备(包括Hub和功能设备)、对外接口、源码、模块等等,本专业和有兴趣的朋友们不要错过。
编辑于2023-03-01 09:10:30 四川省USB Core
子系统结构
包括
USB控制器
USB连接
USB设备(包括Hub和功能设备)
USB Hub
root hub: 一个USB控制器和一个USB hub绑定在一起形成根hub
线缆
compound设备
通信
通信是由主机发起的,通过主机与设备之间的管道,主机与设备的端点实现通信
端点: 有确定的方向,要么in,要么out。端点0很特殊,有两个方向,主机通过默认端点0控制设备
除开0的端点数: 低速设备最多2个,高速设备最多in, out各15个
四种传输类型
1、控制传输 - 用于配置设备,获取设备信息,或获取设备报告
2、中断传输 - 以固定速率传输少量数据,键盘,鼠标,屏幕等使用这种方式
3、批量传输 - 传输大量数据,保证数据没有丢失,不保证特定时间内完成,U盘使用这种方式
4、等时传输 - 传输大量数据,不保证数据一定到达,以稳定速率传输实时数据,对时延敏感,音视频使用的方式
拓扑结构
对外接口
/sys/对应的usb节点下有设备描述符,接口描述符等
/proc/bus/usb/device 可以查看usb的可配置选项
源码
目录: ls drivers/usb
目录
core
放的usb核心代码,初始化整个usb系统,root hub,主机控制器公共部分代码
host
初始化主机控制器
gadget
udc模块
针对具体cpu平台
gadget驱动
主要有file_storage、ether、serial等,还包括USB gadget API,即USB设备控制器硬件和gadget驱动通信的接口
storage
u盘驱动
input
鼠标和触摸屏驱动
class协议
U盘功能Mass Storage Class
数据交换协议CDC Class
Audio Class
Input Class
Kconfig
Makefile
模块
usbcore
usb子系统的核心,必不可少
usb主控制器模块驱动ehci_hcd,uhci_hcd,必不可少
为别的设备驱动提供服务 提供一个用于访问和控制USB硬件的接口
usb core,usb主控制器HCD,usb设备之间的关系
源码
Kconfig
USB_DEBUG
usb调试
USB_DEVICDFS
usb文件系统,显示当前连接的USB设备及总线的各种信息, 每个连接的USB设备在其中都有一个文件描述,同一设备两次连接后可能地址不同 /proc/bus/usb/xxx/yyy,xxx表示总线序号,yyy表示设备在总线的地址
挂接在/proc/bus/usb/,(mount -t usbfs none /proc/bus/usb)
USB_SUSPEND
当设备在指定时间内(3ms), 没有发生总线传输,就要进入挂起状态。 当它收到一个non-idle的信号,就被唤醒。
Makefile
USB子系统初始化
drivers/usb/core/usb.c
初始化: subsys_initcall(usb_init) 清理: module-exit(usb_exit)
__init 扩展(include/linux/init.h)
#define __init __attribute__ ((__section__(".init.text")))
__attribute__是指示编译器特定的优化和更仔细的代码检查
section属性,可以将函数或变量放到指定的节中, 连接器把相同节的代码或数据安排到一起
__init 修饰的代码放在 .init.text 节,初始化后释放这些内存
编译之后的对象文件通过连接脚本链接并装入,形成单个可执行文件,可执行文件的各个节装入到指定地址处 vmlinux.lds 是内核链接器脚本,位于arch/target/,负责链接内核的各个节并将他们装入内存特定偏移处
__initcall_start 到 __initcall_end 之间的各个节
pure_initcall 将函数放在了 .initcall0.init core_initcall 将函数放在了 .initcall1.init postcore_initcall 将函数放在了 .initcall2.init arch_initcall 将函数放在了 .initcall3.init subsys_initcall 将函数放在了 .initcall4.init fs_initcall 将函数放在了 .initcall5.init device_initcall 将函数放在了 .initcall6.init late_initcall 将函数放在了 .initcall7.init
在 init/main.c 中的函数 do_initcalls 依次按顺序调用初始化函数
usb_init 函数
1、判断nousb,在内核启动时通过内核传参数可以去掉usb, 去掉后会打印"USB support disable",然后退出 usb_init
2、按格式打印可变参数的方法(##当传递空可变参数时,去掉fmt后面那个逗号) #define pr_info(fmt, ...) \ printf("MODULE "fmt, ##__VA_ARGS__)
3、ksuspend_usb_init,电源管理 没有定义 CONFIG_PM 就什么也不做
4.0的内核中电源管理相关的定义在 usb_acpi_register 函数里面
4、bus_register(&usb_bus_type),注册USB总线 注册了总线之后才能向总线添加设备
struct bus_type usb_bus_type = { .nane = "usb", .match = usb_device_match, .uevent = usb_uevent, };
判断是USB设备
还不知道dev->type是什么时候被赋值的
判断是不是usb device driver,USB设备驱动,为设备做不同配置
扩展,usb接口驱动
USB驱动指的是USB接口的驱动
子主题
name,驱动程序的名字,在/sys/bus/usb/drivers/下面的子目录名称
probe,看这个USB驱动是否愿意接受某个接口的函数,一个驱动可以支持多个接口
disconnect,使用rmmod时,这个函数会调用
ioctl,通过usbfs与用户空间交流使用
suspend,resume,挂起和唤醒
pre_reset,post_reset,设备将要复位,设备已经复位后使用
id_table,驱动支持的所有设备的列表
dynids,支持动态id,当驱动已经加载,也可以添加id,添加新的id的方法如下: echo 0557 2008 > /sys/bus/usb/drivers/net1080/new_id,将厂商和产品id写进设备id表
drvwrap,判断是设备驱动还是接口驱动
no_dynamic_id,用来禁止动态id
supports_autosuspend,
usb设备驱动与接口驱动的区别
设备驱动几乎和接口驱动一样
probe函数,设备驱动比接口驱动少了设备列表
判断是USB接口
5、usb_host_init,执行主机控制器相关初始化
4.0的内核没有这个函数 但是多了一个 bus_register_notifier 函数
6、usb_major_init,将总线注册成一个字符设备 总线也是一个设备
7、usb_register usb_devio_init usbfs_init 都是usbfs相关的初始化
8、usb_hub_init,Hub的初始化
9、usb_register_device_driver,注册usb设备驱动 一个设备对应多个接口,每个接口对应不同驱动程序 这里的device driver是对应整个设备
总线,设备,驱动的结构设备模型及关系 定义于include/linux/device.h
总线
每出现一个设备要向总线汇报(注册),device插入devices链表 每出现一个驱动我要向总线汇报(注册),driver插入drivers链表
每当一个struct device诞生,就会去bus的drivers链表中寻找对应的驱动 每当一个struct device_driver诞生,就会去devices链表中寻找对应的设备 如果它们能够找到,就会调用device_bind_driver绑定好
驱动
子主题
设备
USB子系统的总线 usb_bus_type在usb_init函数中被注册
drivers/usb/core/driver.c
定义
name是总线的名字 match是在总线的设备和驱动之间充当"牵线搭桥"
match函数 return 0;
分为USB设备和USB接口
设备的接口
设备可以有多个接口,每个接口代表一个功能,每个接口对应一个驱动
设备模型中的device落实在usb子系统,成了两个结构
1、struct usb_device
2、struct usb_interface
结构
altsetting 表示可选设置 cur_altsetting 表示当前正在使用的设置 num_altsetting 表示这个接口可选设置的数量 minor 表示分配给接口的次设备号
ls /dev
逗号分开的两个数字代表主设备号,次设备号 主设备号表明设备的种类,即设备对应哪个驱动程序 次设备号表面驱动要支持多个设备而让驱动程序区分它们 (主设备号为了找到对应驱动程序,次设备号决定驱动对哪个设备进行操作)
USB_DEVICE_MAJOR 是为usbfs而生,usbfs是用户空间直接访问USB硬件设备的接口 内核是在drivers/usb/devio.c中的usb_devio_init函数去实现usbfs_driver的 USB_MAJOR 才是Linux为USB设备预留的主设备号
查看当前系统已经分配出去的主设备号命令 cat /proc/devices
设备 > 配置 > 接口 > 设置
接口的设置
include/linux/usb.h
desc 接口描述符,保存USB的属性,包括姓名,生产地等, 我们向设备获取他们的内容。 四种描述符: 设备描述符,配置描述符,接口描述符,端点描述符 这些描述符都存放在usb设备的eeprom中,等待主机去拿
endpoint,是个数组,表示接口所有使用到的端点
string表示从设备取出来的字符串描述符
extra,extralen,表示其他描述符,
接口描述符
__attribute__((packed)),表示不进行对齐操作,即1字节对齐
bLength,描述符的字长度,长度应该是9
bDescriptorType,描述符的类型,接口描述符为USB_DT_INTERFACE=0x4
bInterfaceNumber,每个配置包含多个接口,这个值就是索引
bAlternateSetting,接口使用的哪个可选配置,默认0
bNumEndpoints,接口拥有的端点数量,除开0端点
bInterfaceClass,bInterfaceProtocol, usb定义的class类型,协议类型, Mass storage class=0x8,Hub class=0x9
iInterface,接口对应的字符串描述符索引值, 使用lsusb可以显示,字符串描述符可以有多个,索引就是为了区分它们
端点描述符
0端点没有端点描述符
bLength,端点描述符的字节长度,7字节,后面为音频多了2字节扩展
bDescriptorType,描述符类型,端点是USB_DT_ENDPOINT=0x5
bEndpointAddress,描述端点是输入还是输出bit[8],端点地址,端点号bit[3:0]
bmAttributes,属性,bit[1:0]是transfer type
00 - 控制传输
01 - 等时传输
10 - 批量传输
11 - 中断传输
wMaxPacketSize,端点一次性处理的最大字节数
bInterval,希望主机轮循自己的世间间隔
urb_list,端点要处理的urb队列, urb包含了执行USB传输所需的全部信息,主机与usb通信: 创建一个urb并赋值 -> 交给USB Core,找到控制器 -> 数据传输
hcpriv,提供给HCD使用
ep_dev,提供给sysfs使用
extra,extralen,扩展描述符的
设备描述符
结构
devnum,设备的地址,即USB设备在USB总线上的编号, USB插上Hub后,会调用choose_address函数为设备选择一个地址
为记录usb树上的叶子节点,用一个结构体struct usb_bus结构里的struct usb_devmap devmap记录地址映射表
devpath[16],记录字符串,
使用 ls /sys/bus/usb/device/
usb1,usb2,usb3,usb4,表示计算机上连了4条USB总线
4-0:1.0,4表示4号总线(root hub);0表示devpath;1表示配置为1号;0表示接口号为0
4-0.1:1.0,表示4-0:1.0是一个hub,其后1号端口有级联一个设备 4-0.2:1.0,表示4-0:1.0是一个hub,其后2号端口有级联一个设备
state,设备的状态,
Attrached,表示设备已经连接到USB接口上了
Powered,表示加电状态,上电后并连上usb
Default,在powered之后,设备必须收到一个reset信号并复位才能使用默认地址回应主机发过来的设备和配置描述符请求
Address,表示主机分配了一个唯一的地址给设备,设备可以使用默认管道响应主机
Configured,表示设备已经被主机配置过了,主机可以使用设备提供的所有功能
Suspend,挂起状态,为了省电,设备在大约3ms没有发生总线传输进入挂起状态
speed,设备的速度
tt,ttport,特殊电路,为高速设备可以兼容到低速
toggle,表示在批量传输,控制传输,中断传输中,先传DATA0,再传DATA1, 然后交替传输,toggle中的每一位元素表示每个端点当前发送或接收的数据包是DATA0还是DATA1
parent,从root hub之后,这个指针指向齐父usb_device
bus,设备在那条总线上
ep0,端点0
dev,嵌入到struct usb_device结构中的struct device结构
desc,设备描述符,
bLength,描述符长度
bDscriptorType,设备描述符类型,USB_DT_DEVICE=0x1
bcdUSB,USB spec版本号,高速对应的是0200
bDeviceClass,bDeviceSubClass,bDeviceProtocol
bMaxPacketSize0,端点0一次性可以处理最大字节数
idVendor,idProduct,厂商号与产品ID号
bcdDevice,设备的版本号
iManufacturer,iProduct,iSerailNumber,厂商,产品,序列号对应的字符串描述符的索引值
bNumConfigurations,设备当前模式下支持的配置数量
ep_in[16],ep_out[16],对应输入端点和输出端点
rawdescriptors,字符指针数组,数组里每一项都指向使用GET_DESCRIPTOR请求获取配置描述符时得到的结果 这个请求会把所有的设备描述符,接口描述符,接口里的端点描述符,等
bus_mA,USB提供的端口提供的电流是500mA
portnum,指的是设备插在哪个端口上
level,表示USB设备树的级联关系,即哪一层
discon_suspended, have_langid,string_langid,指定哪种语言,判断是否string_langid有效
product,manufacturer,serial,保存产品,厂商和序列号对应的字符串描述符信息
usbfs相关信息
maxchild,hub的端口数
children[USB_MAXCHILDREN],hun一般接31个
pm_usage_cnt
quirks,毛病
电源管理的
配置
配置描述符
bLength,描述符的长度
bDescriptorType,描述符的类型,USB_DT_CONFIG=0x2或USB_DT_OTHER_SPEED_CONFIG=0x7(高速设备运行在低速或全速模式)
wTotalLength,获取配置描述符时返回的所有数据长度
bNumInterface,配置包含的接口数目
bConfigurationValue,对于有多个配置设备,以这个为参数,使用SET_CONFIGURATION请求改变正在使用的USB配置
iConfiguration,配置信息的字符串描述符的索引值
bmAttribute,配置的特点
bMaxPower,设备从总线那里获取的最大电流,2mA为单位
string,保存配置描述符iConfiguration字段对应的字符串描述符信息
interface[USB_MAXINTERFACES],配置包含的接口, 获取某个接口对应的struct usb_interface结构体,使用usb_ifnum_to_if函数
intf_cache[USB_MAXINTERFACES],是USB接口的缓存
额外的扩展
设备的生命线
设备插上hub后的过程
1.设备连接到hub的某个端口 -> 2.hub监测到有设备连接进来 -> 3.为设备分配一个struct usb_device结构并初始化 -> 4.将设备添加到USB总线的设备列表里 -> 5.USB总线遍历驱动列表里每个驱动,调用match函数看它们和设备或接口是否匹配
3.调用usb_alloc_dev函数为struct usb_device结构对象申请内存
参数: parent表示设备连接的hub,bus表示设备连接的总线,port1表示设备连接到hub上的端口
kzalloc函数,申请内存并初始化为0,申请内存不要忘了判空
USB中的一个主机控制器对应一条USB总线, 主机控制器的结构用struct usb_hcd表示,一条总线用struct usb_bus表示
bus_to_hcd函数是为了获得总线对应的主机控制器驱动 usb_get_hcd函数是将usb_hcd结构对象引用计数加1
device_initialize是设备模型中的初始化函数,第一个dev是struct usb_device结构体指针, 第二个dev是struct device结构体,是设备模型中的基本结构体
usb_bus_type,在usb子系统的初始化函数usb_init中注册
usb_device_type,将设备类型初始化为usb_device_type
dma_mask,将dma_mask设置成主机控制器的dma_mask
status,将USB设备的状态设置成ATTACHED,表示设备已经连上USB接口了
INIT_LIST_HEAD,将端点0初始化
USB_DT_ENDPOINT_SIZE,USB_DT_ENDPONIT,初始化端点0描述符长度和类型
将struct usb_device结构中的ep_in和ep_out指针数组的第一个成员指向ep0
unlikely(x),帮助处理分支预测,表示x发生的可能性不大,条件块的语句就放到比较远的位置 likely则相反。这里值设备放在root hub上的概率不大
判断设备是不是直接连到root hub上的,是,就将dev->devpath[0]赋值0,父设备是controller,同时把dev->bus_id[]设置为如usb1/usb2/...字符串; 不是连到root hub分两种情况,如果hub是连到root hub上的,dev->devpath等于端口号,否则dev->dev_path就等于父hub的基础上加一个'.',再加一个端口号,最后把bus_id[]设置成1-/2-/3-...后面加devpath
usb_control_msg函数 - drivers/usb/core/message.c
状态 -> Powered -reset> Default 获取端点0一次处理最大包长度, 高速设备,64 低速设备,8 全速设备,8,16,32,64 -> 进入Address状态,使用函数 usb_control_msg
这个函数的目的是创建一个urb,通过USB Core发送给USB设备,并等待它完成
这个包的request在SETUP包中发送
控制传输,SETUP + STATUS 或 STATUS + DATA + STATUS
transaction,可以理解为主机和设备之间形成的一次完整交流
transaction类型,Token,Data,Handshake
SETUP transaction,1、主机向设备发送Setup Token 2、发送Data0包, 3、设备回应ACK Handshake
DATA transaction,data0和data1交叉发送数据包
STATUS transaction,向主机汇报前面SETUP和DATA阶段的结果
bRequestType,[7]表示控制传输的data方向,[6:5]表示request的类型,[4:0]表示这个请求是针对设备、接口还是端点
bRequest,具体是哪个request
wValue,request的参数
wIndex,request参数,指明接口或端点
wLength,控制传输中Data transaction阶段的长度
改函数调用了usb_control_msg函数发送消息
usb_alloc_urb
全称是usb request block,为一个urb申请内存并初始化
usb_fill_control_urb
初始化urb
做基本的赋值使
usb_fill_bulk_urb,批量传输
usb_fill_int_urb,中断传输
usb_start_wait_urb
将urb提交给USB Core,以便分配给特定的主机控制器进行处理,并等待结果
usb_start_wait_urb
对urb做些前期处理后扔给HCD
urb结构
kref,urb的引用计数,多个端点可能只用同一个urb,最后那个使用者负责释放urb
引用计数使用原子变量来操作
kref_init,初始化
kref_get,将引用计数加1
kref-put,将引用计数减1并判断是不是0,是0则调用参数release指向的函数把对象销毁
lock,内核urb都有一把自旋锁
hcpriv,留给主机控制器驱动的
use_count,表明有没有HCD正在使用它
一次完整的USB通信
1、创建一个urb,并指定它的目的地址是设备上哪个端点
2、提交给USB Core,core将它修修补补,交给主机控制器的驱动程序HCD
3、HCD会解析这个urb,了解目的是什么,并与USB设备进行交流
4、urb的目的到达之后,HCD再把这个urb的所有权交给驱动程序
它与kref的区别,kref代表的是urb是否被销毁, use_count代表的是当前的urb是不是被哪个HCD处理,不代表销毁,还可以交给另一个HCD处理
终止urb
异步,usb_unlink_urb,驱动通过USB Core告诉urb不用处理,去忙别的事
同步,usb_kill_urb,驱动等着HCD处理结果,等着urb被终止,将urb结果交回驱动,通过use_count判断通信终止
reject,
urb_list,每个端点都会有个urb队列,HCD每收到一个urb,就会将它添加到urb指定的那个端点队列里去,头结点就是端点结构体里面那个struct list_head
dev,表示urb要去的那个USB设备
pipe整形,urb到达端点要经过的管道,管道的主机端是缓冲区,另一端是设备的端点
[7]表示方向,IN和OUT
[14:8]表示设备地址
[18:15]表示端点号
[31:30]管道类型,4种
status,urb当前状态,
transfer_flag,标记
URB_SHORT_NOT_OK,如果从IN端点处收到一个比wMaxPacketSize要短的包,同时也设置了改标志,就认为传输出错了
URB_ISO_ASAP,告诉HCD什么时候不忙开始等时传输
URB_NO_TRANSFER_DMA_MAP,URB_NO_SETUP_DMA_MAP 与DMA相关的
URB_NO_FSBR,给UHCI用的函数
URB_ZERO_PACKET,表示OUT传输必须使用一个wshort packet来结束,即最后一个包的长度等于wMaxPacketSize时,需要再发送0长度的数据包
URB_NO_INTERRUPT,告诉HCD,在urb完成后,不要请求一个硬件中断
transfer_buffer,transfer_dma,transfer_length,描述主机上的缓冲区 transfer_buffer使用kmalloc函数分配缓冲区,transfer_dma使用usb_buffer_alloc分配dma缓冲区,另一个表示其长度
actual_length,urb结束后,表示实际传输了多少数据
setup_packet,setup_dma,控制传输专用的缓冲区,保存的是struct usb_ctrlrequest结构体
start_frame,指定等时或中断传输在哪个帧或微帧开始
interval,间隔时间,等时传输或中断传输,希望主机轮循自己的时间
全速,1-255ms
低速,10-255ms
高速,1-16ms
context,给下面结束处理函数用的
complete,指向结束处理函数的指针
浮动主题
浮动主题
USB Core
子系统结构
包括
USB控制器
USB连接
USB设备(包括Hub和功能设备)
USB Hub
root hub: 一个USB控制器和一个USB hub绑定在一起形成根hub
线缆
compound设备
通信
通信是由主机发起的,通过主机与设备之间的管道,主机与设备的端点实现通信
端点: 有确定的方向,要么in,要么out。端点0很特殊,有两个方向,主机通过默认端点0控制设备
除开0的端点数: 低速设备最多2个,高速设备最多in, out各15个
四种传输类型
1、控制传输 - 用于配置设备,获取设备信息,或获取设备报告
2、中断传输 - 以固定速率传输少量数据,键盘,鼠标,屏幕等使用这种方式
3、批量传输 - 传输大量数据,保证数据没有丢失,不保证特定时间内完成,U盘使用这种方式
4、等时传输 - 传输大量数据,不保证数据一定到达,以稳定速率传输实时数据,对时延敏感,音视频使用的方式
拓扑结构
对外接口
/sys/对应的usb节点下有设备描述符,接口描述符等
/proc/bus/usb/device 可以查看usb的可配置选项
源码
目录: ls drivers/usb
目录
core
放的usb核心代码,初始化整个usb系统,root hub,主机控制器公共部分代码
host
初始化主机控制器
gadget
udc模块
针对具体cpu平台
gadget驱动
主要有file_storage、ether、serial等,还包括USB gadget API,即USB设备控制器硬件和gadget驱动通信的接口
storage
u盘驱动
input
鼠标和触摸屏驱动
class协议
U盘功能Mass Storage Class
数据交换协议CDC Class
Audio Class
Input Class
Kconfig
Makefile
模块
usbcore
usb子系统的核心,必不可少
usb主控制器模块驱动ehci_hcd,uhci_hcd,必不可少
为别的设备驱动提供服务 提供一个用于访问和控制USB硬件的接口
usb core,usb主控制器HCD,usb设备之间的关系
源码
Kconfig
USB_DEBUG
usb调试
USB_DEVICDFS
usb文件系统,显示当前连接的USB设备及总线的各种信息, 每个连接的USB设备在其中都有一个文件描述,同一设备两次连接后可能地址不同 /proc/bus/usb/xxx/yyy,xxx表示总线序号,yyy表示设备在总线的地址
挂接在/proc/bus/usb/,(mount -t usbfs none /proc/bus/usb)
USB_SUSPEND
当设备在指定时间内(3ms), 没有发生总线传输,就要进入挂起状态。 当它收到一个non-idle的信号,就被唤醒。
Makefile
USB子系统初始化
drivers/usb/core/usb.c
初始化: subsys_initcall(usb_init) 清理: module-exit(usb_exit)
__init 扩展(include/linux/init.h)
#define __init __attribute__ ((__section__(".init.text")))
__attribute__是指示编译器特定的优化和更仔细的代码检查
section属性,可以将函数或变量放到指定的节中, 连接器把相同节的代码或数据安排到一起
__init 修饰的代码放在 .init.text 节,初始化后释放这些内存
编译之后的对象文件通过连接脚本链接并装入,形成单个可执行文件,可执行文件的各个节装入到指定地址处 vmlinux.lds 是内核链接器脚本,位于arch/target/,负责链接内核的各个节并将他们装入内存特定偏移处
__initcall_start 到 __initcall_end 之间的各个节
pure_initcall 将函数放在了 .initcall0.init core_initcall 将函数放在了 .initcall1.init postcore_initcall 将函数放在了 .initcall2.init arch_initcall 将函数放在了 .initcall3.init subsys_initcall 将函数放在了 .initcall4.init fs_initcall 将函数放在了 .initcall5.init device_initcall 将函数放在了 .initcall6.init late_initcall 将函数放在了 .initcall7.init
在 init/main.c 中的函数 do_initcalls 依次按顺序调用初始化函数
usb_init 函数
1、判断nousb,在内核启动时通过内核传参数可以去掉usb, 去掉后会打印"USB support disable",然后退出 usb_init
2、按格式打印可变参数的方法(##当传递空可变参数时,去掉fmt后面那个逗号) #define pr_info(fmt, ...) \ printf("MODULE "fmt, ##__VA_ARGS__)
3、ksuspend_usb_init,电源管理 没有定义 CONFIG_PM 就什么也不做
4.0的内核中电源管理相关的定义在 usb_acpi_register 函数里面
4、bus_register(&usb_bus_type),注册USB总线 注册了总线之后才能向总线添加设备
struct bus_type usb_bus_type = { .nane = "usb", .match = usb_device_match, .uevent = usb_uevent, };
判断是USB设备
还不知道dev->type是什么时候被赋值的
判断是不是usb device driver,USB设备驱动,为设备做不同配置
扩展,usb接口驱动
USB驱动指的是USB接口的驱动
子主题
name,驱动程序的名字,在/sys/bus/usb/drivers/下面的子目录名称
probe,看这个USB驱动是否愿意接受某个接口的函数,一个驱动可以支持多个接口
disconnect,使用rmmod时,这个函数会调用
ioctl,通过usbfs与用户空间交流使用
suspend,resume,挂起和唤醒
pre_reset,post_reset,设备将要复位,设备已经复位后使用
id_table,驱动支持的所有设备的列表
dynids,支持动态id,当驱动已经加载,也可以添加id,添加新的id的方法如下: echo 0557 2008 > /sys/bus/usb/drivers/net1080/new_id,将厂商和产品id写进设备id表
drvwrap,判断是设备驱动还是接口驱动
no_dynamic_id,用来禁止动态id
supports_autosuspend,
usb设备驱动与接口驱动的区别
设备驱动几乎和接口驱动一样
probe函数,设备驱动比接口驱动少了设备列表
判断是USB接口
5、usb_host_init,执行主机控制器相关初始化
4.0的内核没有这个函数 但是多了一个 bus_register_notifier 函数
6、usb_major_init,将总线注册成一个字符设备 总线也是一个设备
7、usb_register usb_devio_init usbfs_init 都是usbfs相关的初始化
8、usb_hub_init,Hub的初始化
9、usb_register_device_driver,注册usb设备驱动 一个设备对应多个接口,每个接口对应不同驱动程序 这里的device driver是对应整个设备
总线,设备,驱动的结构设备模型及关系 定义于include/linux/device.h
总线
每出现一个设备要向总线汇报(注册),device插入devices链表 每出现一个驱动我要向总线汇报(注册),driver插入drivers链表
每当一个struct device诞生,就会去bus的drivers链表中寻找对应的驱动 每当一个struct device_driver诞生,就会去devices链表中寻找对应的设备 如果它们能够找到,就会调用device_bind_driver绑定好
驱动
子主题
设备
USB子系统的总线 usb_bus_type在usb_init函数中被注册
drivers/usb/core/driver.c
定义
name是总线的名字 match是在总线的设备和驱动之间充当"牵线搭桥"
match函数 return 0;
分为USB设备和USB接口
设备的接口
设备可以有多个接口,每个接口代表一个功能,每个接口对应一个驱动
设备模型中的device落实在usb子系统,成了两个结构
1、struct usb_device
2、struct usb_interface
结构
altsetting 表示可选设置 cur_altsetting 表示当前正在使用的设置 num_altsetting 表示这个接口可选设置的数量 minor 表示分配给接口的次设备号
ls /dev
逗号分开的两个数字代表主设备号,次设备号 主设备号表明设备的种类,即设备对应哪个驱动程序 次设备号表面驱动要支持多个设备而让驱动程序区分它们 (主设备号为了找到对应驱动程序,次设备号决定驱动对哪个设备进行操作)
USB_DEVICE_MAJOR 是为usbfs而生,usbfs是用户空间直接访问USB硬件设备的接口 内核是在drivers/usb/devio.c中的usb_devio_init函数去实现usbfs_driver的 USB_MAJOR 才是Linux为USB设备预留的主设备号
查看当前系统已经分配出去的主设备号命令 cat /proc/devices
设备 > 配置 > 接口 > 设置
接口的设置
include/linux/usb.h
desc 接口描述符,保存USB的属性,包括姓名,生产地等, 我们向设备获取他们的内容。 四种描述符: 设备描述符,配置描述符,接口描述符,端点描述符 这些描述符都存放在usb设备的eeprom中,等待主机去拿
endpoint,是个数组,表示接口所有使用到的端点
string表示从设备取出来的字符串描述符
extra,extralen,表示其他描述符,
接口描述符
__attribute__((packed)),表示不进行对齐操作,即1字节对齐
bLength,描述符的字长度,长度应该是9
bDescriptorType,描述符的类型,接口描述符为USB_DT_INTERFACE=0x4
bInterfaceNumber,每个配置包含多个接口,这个值就是索引
bAlternateSetting,接口使用的哪个可选配置,默认0
bNumEndpoints,接口拥有的端点数量,除开0端点
bInterfaceClass,bInterfaceProtocol, usb定义的class类型,协议类型, Mass storage class=0x8,Hub class=0x9
iInterface,接口对应的字符串描述符索引值, 使用lsusb可以显示,字符串描述符可以有多个,索引就是为了区分它们
端点描述符
0端点没有端点描述符
bLength,端点描述符的字节长度,7字节,后面为音频多了2字节扩展
bDescriptorType,描述符类型,端点是USB_DT_ENDPOINT=0x5
bEndpointAddress,描述端点是输入还是输出bit[8],端点地址,端点号bit[3:0]
bmAttributes,属性,bit[1:0]是transfer type
00 - 控制传输
01 - 等时传输
10 - 批量传输
11 - 中断传输
wMaxPacketSize,端点一次性处理的最大字节数
bInterval,希望主机轮循自己的世间间隔
urb_list,端点要处理的urb队列, urb包含了执行USB传输所需的全部信息,主机与usb通信: 创建一个urb并赋值 -> 交给USB Core,找到控制器 -> 数据传输
hcpriv,提供给HCD使用
ep_dev,提供给sysfs使用
extra,extralen,扩展描述符的
设备描述符
结构
devnum,设备的地址,即USB设备在USB总线上的编号, USB插上Hub后,会调用choose_address函数为设备选择一个地址
为记录usb树上的叶子节点,用一个结构体struct usb_bus结构里的struct usb_devmap devmap记录地址映射表
devpath[16],记录字符串,
使用 ls /sys/bus/usb/device/
usb1,usb2,usb3,usb4,表示计算机上连了4条USB总线
4-0:1.0,4表示4号总线(root hub);0表示devpath;1表示配置为1号;0表示接口号为0
4-0.1:1.0,表示4-0:1.0是一个hub,其后1号端口有级联一个设备 4-0.2:1.0,表示4-0:1.0是一个hub,其后2号端口有级联一个设备
state,设备的状态,
Attrached,表示设备已经连接到USB接口上了
Powered,表示加电状态,上电后并连上usb
Default,在powered之后,设备必须收到一个reset信号并复位才能使用默认地址回应主机发过来的设备和配置描述符请求
Address,表示主机分配了一个唯一的地址给设备,设备可以使用默认管道响应主机
Configured,表示设备已经被主机配置过了,主机可以使用设备提供的所有功能
Suspend,挂起状态,为了省电,设备在大约3ms没有发生总线传输进入挂起状态
speed,设备的速度
tt,ttport,特殊电路,为高速设备可以兼容到低速
toggle,表示在批量传输,控制传输,中断传输中,先传DATA0,再传DATA1, 然后交替传输,toggle中的每一位元素表示每个端点当前发送或接收的数据包是DATA0还是DATA1
parent,从root hub之后,这个指针指向齐父usb_device
bus,设备在那条总线上
ep0,端点0
dev,嵌入到struct usb_device结构中的struct device结构
desc,设备描述符,
bLength,描述符长度
bDscriptorType,设备描述符类型,USB_DT_DEVICE=0x1
bcdUSB,USB spec版本号,高速对应的是0200
bDeviceClass,bDeviceSubClass,bDeviceProtocol
bMaxPacketSize0,端点0一次性可以处理最大字节数
idVendor,idProduct,厂商号与产品ID号
bcdDevice,设备的版本号
iManufacturer,iProduct,iSerailNumber,厂商,产品,序列号对应的字符串描述符的索引值
bNumConfigurations,设备当前模式下支持的配置数量
ep_in[16],ep_out[16],对应输入端点和输出端点
rawdescriptors,字符指针数组,数组里每一项都指向使用GET_DESCRIPTOR请求获取配置描述符时得到的结果 这个请求会把所有的设备描述符,接口描述符,接口里的端点描述符,等
bus_mA,USB提供的端口提供的电流是500mA
portnum,指的是设备插在哪个端口上
level,表示USB设备树的级联关系,即哪一层
discon_suspended, have_langid,string_langid,指定哪种语言,判断是否string_langid有效
product,manufacturer,serial,保存产品,厂商和序列号对应的字符串描述符信息
usbfs相关信息
maxchild,hub的端口数
children[USB_MAXCHILDREN],hun一般接31个
pm_usage_cnt
quirks,毛病
电源管理的
配置
配置描述符
bLength,描述符的长度
bDescriptorType,描述符的类型,USB_DT_CONFIG=0x2或USB_DT_OTHER_SPEED_CONFIG=0x7(高速设备运行在低速或全速模式)
wTotalLength,获取配置描述符时返回的所有数据长度
bNumInterface,配置包含的接口数目
bConfigurationValue,对于有多个配置设备,以这个为参数,使用SET_CONFIGURATION请求改变正在使用的USB配置
iConfiguration,配置信息的字符串描述符的索引值
bmAttribute,配置的特点
bMaxPower,设备从总线那里获取的最大电流,2mA为单位
string,保存配置描述符iConfiguration字段对应的字符串描述符信息
interface[USB_MAXINTERFACES],配置包含的接口, 获取某个接口对应的struct usb_interface结构体,使用usb_ifnum_to_if函数
intf_cache[USB_MAXINTERFACES],是USB接口的缓存
额外的扩展
设备的生命线
设备插上hub后的过程
1.设备连接到hub的某个端口 -> 2.hub监测到有设备连接进来 -> 3.为设备分配一个struct usb_device结构并初始化 -> 4.将设备添加到USB总线的设备列表里 -> 5.USB总线遍历驱动列表里每个驱动,调用match函数看它们和设备或接口是否匹配
3.调用usb_alloc_dev函数为struct usb_device结构对象申请内存
参数: parent表示设备连接的hub,bus表示设备连接的总线,port1表示设备连接到hub上的端口
kzalloc函数,申请内存并初始化为0,申请内存不要忘了判空
USB中的一个主机控制器对应一条USB总线, 主机控制器的结构用struct usb_hcd表示,一条总线用struct usb_bus表示
bus_to_hcd函数是为了获得总线对应的主机控制器驱动 usb_get_hcd函数是将usb_hcd结构对象引用计数加1
device_initialize是设备模型中的初始化函数,第一个dev是struct usb_device结构体指针, 第二个dev是struct device结构体,是设备模型中的基本结构体
usb_bus_type,在usb子系统的初始化函数usb_init中注册
usb_device_type,将设备类型初始化为usb_device_type
dma_mask,将dma_mask设置成主机控制器的dma_mask
status,将USB设备的状态设置成ATTACHED,表示设备已经连上USB接口了
INIT_LIST_HEAD,将端点0初始化
USB_DT_ENDPOINT_SIZE,USB_DT_ENDPONIT,初始化端点0描述符长度和类型
将struct usb_device结构中的ep_in和ep_out指针数组的第一个成员指向ep0
unlikely(x),帮助处理分支预测,表示x发生的可能性不大,条件块的语句就放到比较远的位置 likely则相反。这里值设备放在root hub上的概率不大
判断设备是不是直接连到root hub上的,是,就将dev->devpath[0]赋值0,父设备是controller,同时把dev->bus_id[]设置为如usb1/usb2/...字符串; 不是连到root hub分两种情况,如果hub是连到root hub上的,dev->devpath等于端口号,否则dev->dev_path就等于父hub的基础上加一个'.',再加一个端口号,最后把bus_id[]设置成1-/2-/3-...后面加devpath
usb_control_msg函数 - drivers/usb/core/message.c
状态 -> Powered -reset> Default 获取端点0一次处理最大包长度, 高速设备,64 低速设备,8 全速设备,8,16,32,64 -> 进入Address状态,使用函数 usb_control_msg
这个函数的目的是创建一个urb,通过USB Core发送给USB设备,并等待它完成
这个包的request在SETUP包中发送
控制传输,SETUP + STATUS 或 STATUS + DATA + STATUS
transaction,可以理解为主机和设备之间形成的一次完整交流
transaction类型,Token,Data,Handshake
SETUP transaction,1、主机向设备发送Setup Token 2、发送Data0包, 3、设备回应ACK Handshake
DATA transaction,data0和data1交叉发送数据包
STATUS transaction,向主机汇报前面SETUP和DATA阶段的结果
bRequestType,[7]表示控制传输的data方向,[6:5]表示request的类型,[4:0]表示这个请求是针对设备、接口还是端点
bRequest,具体是哪个request
wValue,request的参数
wIndex,request参数,指明接口或端点
wLength,控制传输中Data transaction阶段的长度
改函数调用了usb_control_msg函数发送消息
usb_alloc_urb
全称是usb request block,为一个urb申请内存并初始化
usb_fill_control_urb
初始化urb
做基本的赋值使
usb_fill_bulk_urb,批量传输
usb_fill_int_urb,中断传输
usb_start_wait_urb
将urb提交给USB Core,以便分配给特定的主机控制器进行处理,并等待结果
usb_start_wait_urb
对urb做些前期处理后扔给HCD
urb结构
kref,urb的引用计数,多个端点可能只用同一个urb,最后那个使用者负责释放urb
引用计数使用原子变量来操作
kref_init,初始化
kref_get,将引用计数加1
kref-put,将引用计数减1并判断是不是0,是0则调用参数release指向的函数把对象销毁
lock,内核urb都有一把自旋锁
hcpriv,留给主机控制器驱动的
use_count,表明有没有HCD正在使用它
一次完整的USB通信
1、创建一个urb,并指定它的目的地址是设备上哪个端点
2、提交给USB Core,core将它修修补补,交给主机控制器的驱动程序HCD
3、HCD会解析这个urb,了解目的是什么,并与USB设备进行交流
4、urb的目的到达之后,HCD再把这个urb的所有权交给驱动程序
它与kref的区别,kref代表的是urb是否被销毁, use_count代表的是当前的urb是不是被哪个HCD处理,不代表销毁,还可以交给另一个HCD处理
终止urb
异步,usb_unlink_urb,驱动通过USB Core告诉urb不用处理,去忙别的事
同步,usb_kill_urb,驱动等着HCD处理结果,等着urb被终止,将urb结果交回驱动,通过use_count判断通信终止
reject,
urb_list,每个端点都会有个urb队列,HCD每收到一个urb,就会将它添加到urb指定的那个端点队列里去,头结点就是端点结构体里面那个struct list_head
dev,表示urb要去的那个USB设备
pipe整形,urb到达端点要经过的管道,管道的主机端是缓冲区,另一端是设备的端点
[7]表示方向,IN和OUT
[14:8]表示设备地址
[18:15]表示端点号
[31:30]管道类型,4种
status,urb当前状态,
transfer_flag,标记
URB_SHORT_NOT_OK,如果从IN端点处收到一个比wMaxPacketSize要短的包,同时也设置了改标志,就认为传输出错了
URB_ISO_ASAP,告诉HCD什么时候不忙开始等时传输
URB_NO_TRANSFER_DMA_MAP,URB_NO_SETUP_DMA_MAP 与DMA相关的
URB_NO_FSBR,给UHCI用的函数
URB_ZERO_PACKET,表示OUT传输必须使用一个wshort packet来结束,即最后一个包的长度等于wMaxPacketSize时,需要再发送0长度的数据包
URB_NO_INTERRUPT,告诉HCD,在urb完成后,不要请求一个硬件中断
transfer_buffer,transfer_dma,transfer_length,描述主机上的缓冲区 transfer_buffer使用kmalloc函数分配缓冲区,transfer_dma使用usb_buffer_alloc分配dma缓冲区,另一个表示其长度
actual_length,urb结束后,表示实际传输了多少数据
setup_packet,setup_dma,控制传输专用的缓冲区,保存的是struct usb_ctrlrequest结构体
start_frame,指定等时或中断传输在哪个帧或微帧开始
interval,间隔时间,等时传输或中断传输,希望主机轮循自己的时间
全速,1-255ms
低速,10-255ms
高速,1-16ms
context,给下面结束处理函数用的
complete,指向结束处理函数的指针
浮动主题
浮动主题