USB驱动(持续更新中)

每个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,也就是指向归零。


在我差不多全部忘光的时候,又回来补充了。。。。。。

转载的

USB系统在物理连接上是分层的星型拓扑结构,而在逻辑上,所有的USB设备都直接连接到HOST Controller。不管中间接了几个HUB。USB最多支持7层,127个设备。也就是说在一个USB系统中,最多可以有5个HUB级联。
在USB规范中,对传输线和HUB的延迟都有比较严格的定义。

USB总线上数据内容的含义是由USB体系解析的,USB的硬件不负责内容的解析。
USB总线上共有两种数据,一种是流数据(Stream),一种是信息数据(Message)
USB体系可分为三层:
             HOST端------------------------Device端
客户软件(USB应用软件)     功能接口(Interface)
         USB系统软件                   端点(默认端点和其他端点集)
       USB主机控制器                 USB总线接口
端点(Endpoint)是USB通讯模型的终端
多个端点组成接口(interface)
0端点是一个特别的端点,任何USB设备均有0端点,也称为默认端点,主要用于USB设备的配置、命令等。
USB应用软件通过接口访问USB设备,接口又由端点组成,USB通讯在USB主机控制器的控制下按照USB规范定义的格式和方式进行。
USB总线上的数据逐帧传送,高速USB总线上的帧称为微帧(microFrame),周期为125uS,全速以及低速USB总线上的帧周期为1mS。不论高速、全速还是低速总线,帧序号以1mS为单位递增,即连续8个微帧的帧号是一样的。
每个帧(微帧)可以包含多个数据传输,例如可以由若干和同步传输、若干个中断传输以及批量传输构成。
控制传输在端点0进行,主要完成设备的配置、命令发送等。
控制传输由三个步骤组成:令牌+数据+握手,也可能没有数据,而只包括令牌和握手两个阶段。
不论有没有数据,最后均由一个方向和前一阶段相反的握手传输结束,如。
token out (set up) + Data0(out) + IN handshake;
token in(set up) + IN handshake;
token in(set up) + Data1(in) + out handshake(data length = 0);

同步传输是一种实时性极强的传输,没有重传机制,所以是不可靠的。在每一(微)帧的传输任务排定时,同步传输具有最高的优先级。

中断传输是一种延迟有限的传输,具备重传机制,是一种可靠的传输。传输任务排定时优先级低于同步传输,但高于其他两种传输。高速-高带宽端点可以在每个(微)帧中最多进行3次中断传输。

批量传输是一种延迟不确定的传输,这种传输尽量利用带宽进行传输,可以实现较高的速率,比较适合传送大的数据。也是一种可靠的传输。

USB分层结构中,客户软件通过设备驱动程序访问USB设备,设备驱动程序则通过HCD(主机控制器驱动)访问USB总线,完成数据的传输。

USB总线上,数据的传输按照little endian进行,即一个字节中最低有效位在前,最高有效位在后。

Packet是USB总线上数据传输的最小单位。
每个Packet均由sync同步域开始。(KJKJKK for L/F speed (15个KJ)KJKJKJ...KJKK for high-speed)

Packet的格式由USB规范定义,格式如下:

USB响应
USB的响应共包括ACK、NAK、NYET、STALL几种。
ACK表示肯定的应答,数据已经被接收,并准备好接收下一个数据包。
NAK表示否定的应答,当前数据包未被接收,希望重传。
NYET表示当前的数据已经被接收,但尚未准备好接收下一个数据包,期待PING测试。
STALL则表示错误或端点被设置STALL。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值