每个usb传输都分成多次的transaction,每个transaction又分成多个packet。
trasaction已知的有,setup transaction, data transaction, status transaction,还有isochronous transaction.比如说控制传输就可以分为2~3个transaction, setup - (DATA) - status
而packet可以分成四种,每种有不同的PID用来标识自己,分别是data, token, handshake.
而不同的包而分不同的字段,分别有sync, pid…
其中data有独有的数据字段,无地址字段(devnum, 总线地址),token和handshake是无data,有sync,PID, 地址域还有CRC。也就是说data和其他两种的区别在于data和地址。其中token包只有主机可以发,又可以分为SOF,setup, out, in.data包的长度根据usb版本不同而不同,8 – 1023 - 1024。handshake包括sync和pid。
setup transaction又分成三个阶段,setup toketn(host->device), data0(若失败则无), ack handshake.
data transaction则根据data toggle的有无来决定如何发送。
status transaction则是汇报setup 和 data段结果。(ok/error/busy)
等时传输:
微帧,高速时1帧是125ms。
其他时候分别是:1~255 10~255 1 ~16
等时传输的速度可以由device driver来协商,但是要根据端点能力来,2^(bitintval-1) * 125ms.
只有等时传输可以确定传输字节数。
isochronous transactoin 包括out/in token, data. data payload的上限受限于端点的maxpacketsize。
终端传输的IN和OUT。IN的时候,发送in token,是host->device, 然后等待device 返回data 或 ack/error 或 stall
OUT的时候,发送out token 和data, 等待device 返回 ack 或 nak 或 stall/
lock_tree()是在hub的树拓补发生变化的时候,锁住所有子节点,再开始变化,这个在2.6.23之后被取消了。
usb_get_descriptor分成两布,显示使用get设备描述符,去查询bNumConfigurations的,然后再调用usb_get_configuration去获得配置描述符。而后面这一步也分为两步,一步是先用USB_DT_CONFIG_SIZE获得一个配置描述符的内容,再通过其中的wTotalLength来获取所有内容(配置,接口,端点,class, vendor)
一个设备最多支持8钟配置。
get_descriptor后返回的raw_descriptor(是个Buffer)中的内容,将在之后分三步如下解析,
其中第三四字节是wTotalLength.
Quote:
一个配置,最大接口数为128,一个接口,最大端点数为30(去掉ep0和32),但是Linux好像是限制为不能大于16(因为用不到)…
1. parse_configuration
2.parse_interface
3.parse_endpoints
这中间最好的技巧是
struct usb_descriptor_header {
_u8 bLength;
_u8 bDescriptorType;
} __attribute__((packed))
在3中初始化每个endpoint的urb_list.
判断端点类型使用:
usb_endpoint_descriptor *epd->bmAttributes && USB_ENDPOINT_XFERTYPE_MASK
关于端点中的bInterval,是由三个值决定的,i(最小), j(最大),n(default)。而几种不同的传输,bInterval也是不同的。而且是只有中断和同步有bInterval这个值,而bulk和control是没有的。每种传输对于不同的usb速度,值也不一样。默认:i=0,j=255,n=default
中断传输:i=1
HIGH: n=9, j=16, 2^(9-1) uframes = 32ms
FULL/LOW:n=32 比spec中所说的最小的10ms要大
同步传输:i=1, j=16
HIGH:n=9, 32ms
LOW:n=6, 32ms
在usb_bus_type上所挂的driver_list上只有一个是usb_device的驱动,其余全部都是接口驱动。
如果epd->bMaxPower * 2 > udev->bus_mA, 那么就是说供电不足。
Linux更喜欢标准的东西,如果找到usb_class_***的东西,就不会去找usb_class_vendor_sepc的东西
plural, 打印一个词的复数形式。
驱动层:choose_configuration->set_configuration
这个实际上是在SPEC中所说的那般,发送REQ_SET_CONFIGURATION命令。一般来说configuration不会为0,但是有些设备就是设计成用0的configuration,所以就先令configuration为-1,然后再试探有没有为0的configuration,有则发0,无则发-1.
实际上在此之前还有发送改变设备状态为address的命令。
actconfig->活动配置
set_configuration只是改变了intf_cache,而真正填充interface是在激活配置后。
配置失败后的清理工作内容:除了ep0外都要禁用,1是禁用endpoint,2是对应hcd中此ep的urb_list。需要禁用ep0的情况就是1设备断开,2是从default状态进入到address状态时(也就是ep0_reinit)。
strcat前一定要将buf初始化为’/0’,因为strcat的原理就是在buf中寻找’/0’, while((*dest++ = *src++) != '/0’。在ascii码中,'/0’就是用0来表示。就是说0和'/0‘是可以互换的。
在spec 9.6.7中说明了,编码为0的字符描述符包括了设备所支持的所有语言的ID。而接口默认是设置为0号设置。dev->toggle[2]中间的两位分别是IN和OUT,其中那位若为0表示data0,为1就表示为data1。对于这个成员还有几个操作函数,gettoggle, dotoggle, settoggle分别是获取,取反,复位为0/1.对于bulk, control, int,都是初始化位0, 然后再一次是data0, data1…
从此之后,usb_dev变成usb_if_device_type,这时候就会去匹配usb_bus中的inf driver。
字符串描述符。
使用的是unicode, 其中语言id是两个字节,中文简体就是0x04:0x02,其中0-9bit是prime,10-15是sub。usb_cache_string----usb_string,index表示是第几个,不能为0.
首先检查设备以有没有毛病, USB_QUIRK_STRING_FETCH_255,这就是一个毛病, 表示如果取string descriptor时设备会crash, 一些usb设备常有的毛病都在quirks.h中,flakey:古灵精怪的。
rc = rc –(rc & 1) 变成2的整数倍
编码实际上一个整体,包括字符集,编码方式,字型。常用的编码方式有utf-8和utf-16,utf-16又分成LE和DE,get_string用的就是utf-16.
ascii码最多能表示256个字符,对于英文字符128就足够,iso-8859-1主要用来表示西欧语言,扩展了ascii的最高位。而unicode的0-255和iso-8859-1一样,而256-65535则是扩展的。字节不能识别的就打印?,isprint().
this指针,就是module。。
usb_register_driver 和 usb_register_device_driver,唯一不同就是for_device不一样。
usb_device_match分成usb_dev和interface两种,对于usb_dev是直接放行,对于interface则会在id_tables和dynids中间进行匹配。id_table中包括id->id,id->bDeviceClass,id->bInterfaceClass, id->driver_info, 而dynids则是一个链表,里面有struct list_head node, struct usb usb_device_id id.
usb_match_id/usb_match_one_id 都是返回0为失败,1为成功。
接口的parent为usb_dev, usb_match_dev(inf->parent), 必须parent也得符合。
需要比对哪些方面就看哪些方面的USB_DEVICE_ID_MATCH_VENDOR/PRODUCT/DEV_LO/DEV_HI(指的是上下限)/DEV_CLASS/DEV_SUBCLASS/DEV_PROTOCAL/INT_CLASS/INT_SUBCLASS/INT_PROTOCAL,以上是按优先级排的。
如果接口dev的class是厂商定义的,且没有指定USB_DEVICE_ID_MATCH_VENDOR就不用match。
总体来说,dev des 是唯一的,conf des是多样的,需要用index来帮忙确定。
获取des都是用usb_control_msg,他们的type都是0x80, 就是IN。
在设置地址的时候,实际上是用devnum来设置的,
devnum != 0, 0是用来做默认地址的,是用来群发的,如果原来的default address是非0, 那就赋给它新的address,如果set_address失败,则赋给其0 address.
usb_bulk_msg –> usb_start_wait_urb, 实际上就是配合competiton来使用的。
对于hub
usb_hub_descriptor hub_event_list
hub中的巧妙设置,设置hub_event_list, 每个hub都有event_list, 当有event时,插入到hub_event_list, 触发hub_events(),在hub_events中,根据event_list来确定是哪个hub.
tmp = hub_event_list.next;
hub = list_entry(tmp, struct usb_hub, event_list)
在hub_events()中调用kbubd_wait(),而停止等待的条件由kick_khubd()来触发。hub的子类但是0,内核里面允许为1.altsetting->devscbNumEndpoints是不含共有的控制端点的,hub只有一个端点(除了端点0外),就是端点1(也就是中断传输端点)。实际上hub的event是挂在内核的per cpu的工作队列上的。
在usb_hcd中有usb_bus, 是因为要描述hcd很困难,所以让usb_bus来完成设备模型, sysfs等其他东西。
pci_driver –> ohci_pci_driver –> probe –> create_hcd –> self : controller = device, 这样就将usb_hcd , usb_bus, pci_dev都联系起来了。
在内核中把usb这种设置方式叫做,bus_glue_layer, flatten out it 。
bus_num 是在多个usb总线时进行编号
lspci –> pci:单个256条总线,256个domain ,每个domain 32条线。
一般计算机只有一个domain 0x0
带宽规定,高速80%,其他90%
hcd中的ms表示有多少时间留给中断源
usb传输的dma 处理:
使用的是dma pool,没有dma_pool 就分配buffer, 如果没有setup dma, 就用dma_single_map来将buffer映射为dma,如果有dma但是pool大小不一样,就用dma_alloc_coherent来实际分配一个。
hcd_state 决定了urb是否上交,比如running/resuming时就可以,suspend但是uplink ok的时候也可以,但是此时是将urb提交到root hub。
对于hub 也要获取它的des,也有专门获取hub des的命令,USB_DT_HUB。hub的描述符前面7个字节固定,从第8个开始就不确定,但是至少有2个,前面7个叫非变量,也就是USB_DT_HUB_NONVA_SIZE,这是因为hub上端口的数量不同造成的。也就是说如果返回小于9就是出错。
bNrports 小于31
HUB_CHAR_LPSH:power switching 方式,ganged power switching:所有端口一次性上电。usb 1.0无power switching.
COMPOUND:除了hub外不能有其他功能
OCPM: 过流保护。
TTTT:transaction translator.高速hub中的电路,用来连低速设备的,无tt的话,ehci只能连告诉设备。ohci/uhci是spec 1.0, ehci 2.0.tt分成single tt和multi tt, hub_tt_kevent用来处理tt事件。在bDeviceProtocal里面, 0 表示L/F speed, 无tt, 1表示single tt, 2表示multi tt.usb_set_interface中对于multi tt默认使用接口0.hub->tt.multi_flag
对于tt,单位为FSbit time. 也就是连个L/F传输间隔,没有用到。
PORTIND
INDICATOR,支持不支持指示灯。
GET_STATUS,是用来分别获得设备状态, 接口状态,和断电状态。
hub电流设置的流程:
hdev->bus_mA:hcd设置的负载能力
hub->ma_per_port:每个端口的电流
hub->limit_power:主机是否限制电流
具体的执行流程如下:
待贴图。
hub传输方式就是控制和终端,而且hub的终端断电一定是in而不是out。 另外hub的中断实际上是轮询,就是等待轮询的中断。
usb的状态是usb_state_notattached可能有三种情况,usb_hc_died, hub_port_disable, usb_disconnect。还有个叫usb_state_default是reset后的状态。
quiescing和active是相对的,默认都为0, 然后reset 和suspend操作都会将前者变成1,后者变成0, 而hcd_active则会相反。reset操作在state_notattached和state_suspend的时候是不允许的。
interface的状态有unbound, binding, bound, unbinding
win和lin的区别
请求设备描述符的方法。现请求8位,如果支持仅兼容win的设备就请求64位。
8(low)/16/32(full)/64(high)/512(wusb)
device_init_wakeuz中会检测can_wakeup。它和should_wakeup这两个是pm_info –> pm_message_t中的成员。是由bmAttributes的D5位决定的。如果要转化的new_state是usb_configured,就先不打开usb的wakeup功能。
如果原来的状态不是notattached那么现在就必须设置成为notattached.
usb设备的速度种类:
unkown
low/full
high
variable spec2.5提出,就是无限usb,3m内是480, 10m的时候到110.
spin_lock替代mutex
TDRST最少10ms TDRSTR最少50ms
set_port_feature和clear_port_feature实际上是通过usb_control_msg发送req_set_feature。
reset不能改变设备的速度,不然是不合理的。
wusb的spec规定可以使用255的buff,虽然实际上512的packet。
hub_set_address, 实际上设下的是dev_map中的dev_num。此后设备的状态从default变成到address.
ep0_reinit()->usb_disable_endpoint,在此中会触发hcd来清空此端点上的urb,并且使得ep_out[0]和ep_in[0]都为NULL,也就是指向归零。
在我差不多全部忘光的时候,又回来补充了。。。。。。
转载的
