现在的位置: 首页 > 综合 > 正文

usb 硬盘驱动框架

2013年06月29日 ⁄ 综合 ⁄ 共 15018字 ⁄ 字号 评论关闭

 

去年年底调了下在公司内核usb storage部分驱动代码,把笔记部分传上来,有不对的地方可以和本人讨论大家互相学习

动在LINUX中占有重要地位,因此了解USB框架是十分必要的,下面以USB硬盘为例了解整个USB的框架。

USB体系简介

USB 是一种支持热插拔的高速串行传输总线,它使用差分信号来传输数据,最高速度

可达480Mb/S。USB 支持“总线供电”和“自供电”两种供电模式。在总线供电模式下,

设备最多可以获得500mA 的电流。USB2.0 被设计成为向下兼容的模式,当有全速(USB 1.1)

或者低速(USB 1.0)设备连接到高速(USB 2.0)主机时,主机可以通过分离传输来支持它

们。一条USB 总线上,可达到的最高传输速度等级由该总线上最慢的“设备”决定,该设

备包括主机、HUB 以及USB 功能设备。

USB 体系包括“主机”、“设备”以及“物理连接”三个部分。其中主机是一个提供USB

接口及接口管理能力的硬件、软件及固件的复合体,可以是PC,也可以是OTG 设备。一个

USB 系统中仅有一个USB 主机;设备包括USB 功能设备和USB HUB,最多支持127 个设

备;物理连接即指的是USB 的传输线。在USB 2.0 系统中,要求使用屏蔽的双绞线

一个USB HOST 最多可以同时支持128 个地址,地址0 作为默认地址,只在设备枚举期间临时使

用,而不能被分配给任何一个设备,因此一个USB HOST 最多可以同时支持127 个地址,如果一个设

备只占用一个地址,那么可最多支持127 个USB 设备。在实际的USB 体系中,如果要连接127 个USB

设备,必须要使用USB HUB,而USB HUB 也是需要占用地址的,所以实际可支持的USB 功能设备

的数量将小于127。

USB 体系采用分层的星型拓扑来连接所有USB 设备,如下图所示:

以HOST-ROOT HUB为起点,最多支持7 层(Tier),也就是说任何一个USB 系统中最多可以允许5个USB HUB 级联。一个复合设备(Compound Device)将同时占据两层或更多的

层。ROOT HUB 是一个特殊的USB HUB,它集成在主机控制器里,不占用地址。ROOT HUB 不但

实现了普通USB HUB 的功能,还包括其他一些功能,具体在增强型主机控制器的规范中有详细的介

绍。

“复合设备(Compound Device)”可以占用多个地址。所谓复合设备其实就是把多个功能设备

通过内置的USB HUB 组合而成的设备,比如带录音话筒的USB 摄像头等。

USB 采用轮询的广播机制传输数据,所有的传输都由主机发起,任何时刻整个USB 体

系内仅允许一个数据包的传输,即不同物理传输线上看到的数据包都是同一被广播的数据包。

USB 采用“令牌包”-“数据包”-“握手包”的传输机制,在令牌包中指定数据包去向

或者来源的设备地址和端点(Endpoint),从而保证了只有一个设备对被广播的数据包/令牌包作出响应。握手包表示了传输的成功与否。

数据包:USB 总线上数据传输的最小单位,包括SYNC、数据及EOP 三个部分。其中数据的格式针对不同的包有不同的格式。但都以8 位的PID 开始。PID 指定了数据包的类型(共16 种)。令牌包即指PID 为IN/OUT/SETUP 的包。

端点(Endpoint):是USB 设备中的可以进行数据收发的最小单元,支持单向或者双向的数据传输。设备支持端点的数量是有限制的,除默认端点外低速设备最多支持2 组端点(2 个输入,2 个输出),高速和全速设备最多支持15 组端点。管道(Pipe)是主机和设备端点之间数据传输的模型,共有两种类型的管道:无格式的流管道(Stream Pipe)和有格式的信息管道(Message Pipe)。任何USB 设备一旦上电就存在一个信息管道,即默认的控制管道,USB 主机通过该管道来获取设备的描述、配置、状态,并对设备进行配置。

USB 设备连接到HOST 时,HOST 必须通过默认的控制管道对其进行枚举,完成获得其设备描述、进行地址分配、获得其配置描述、进行配置等操作方可正常使用。USB 设备

的即插即用特性即依赖于此。

枚举:是USB 体系中一个很重要的活动,由一系列标准请求组成(若设备属于某个子类,还包含该子类定义的特殊请求)。通过枚举HOST 可以获得设备的基本描述信息,如支持的USB 版本、PID、VID、设备分类(Class)、供电方式、最大消耗电流、配置数量、各种类型端点的数量及传输能力(最大包长度)。HOST 根据PID 和VID 加载设备驱动程序,并对设备进行合适的配置。只有经过枚举的设备才能正常使用。对于总线供电设备,在枚举完成前最多可从总线获取100mA 的流。

USB 体系定义了四种类型的传输,它们是:

控制传输:主要用于在设备连接时对设备进行枚举以及其他因设备而已的特定操作。

中断传输:用于对延迟要求严格、小量数据的可靠传输,如键盘、游戏手柄等。

批量传输:用于对延迟要求宽松,大量数据的可靠传输,如U 盘等。

同步传输:用于对可靠性要求不高的实时数据传输,如摄像头、USB 音响等。

注意:中断传输并不意味这传输过程中,设备会先中断HOST,继而通知HOST 启动传输。中断传输也是HOST 发起的传输,采用轮询的方式询问设备是否有数据发送,若有则传输数据,否则NAK 主机。

不同的传输类型在物理上并没有太大的区别,只是在传输机制、主机安排传输任务、可

占用USB 带宽的限制以及最大包长度有一定的差异。

USB 设备通过管道和HOST 通信,在默认控制管道上接受并处理以下三种类型的请求:

1. 标准请求:一共有11 个标准请求,如得到设备描述、设置地址、得到配置描述等。

所有USB 设备均应支持这些请求。HOST 通过标准请求来识别和配置设备。

2. 类(class)请求:USB 还定义了若干个子类,如HUB 类、大容量存储器类等。不

同的类又定义了若干类请求,该类设备应该支持这些类请求。设备所属类在设备描

述符中可以得到。

3. 厂商请求:这部分请求并不是USB 规范定义的,而是设备生产商为了实现一定的

功能而自己定义的请求。

USB HUB 提供了一种低成本、低复杂度的USB 接口扩展方法。HUB 的上行PORT 面

向HOST,下行PORT 面向设备(HUB 或功能设备)。在下行PORT 上,HUB 提供了设备

连接检测和设备移除检测的能力,并给各下行PORT 供电。HUB 可以单独使能各下行PORT,不同PORT 可以工作在不同的速度等级(高速/全速/低速)。

HUB 由HUB 重发器(HUB Repeater)、转发器(Transaction Translator)以及HUB 控制器(HUB Controller)三部分组成。HUB Repeater 是上行PORT 和下行PORT 之间的一个协议控制的开关,它负责高速数据包的重生与分发。HUB 控制器负责和HOST的通信,HOST通过HUB 类请求和HUB 控制器通讯,获得关于HUB 本身和下行PORT 的HUB 描述符,

进行HUB 和下行PORT 的监控和管理。转发器提供了从高速和全速/低速通讯的转换能力,通过HUB 可以在高速HOST 和全速/低速设备之间进行匹配。HUB 在硬件上支持Reset、Resume、Suspend。重生与分发:指的是HUB Repeater 需要识别从上行(下行)PORT 上接收到的数据,并分发到下行(上行)PORT。所谓分发主要是指从上行PORT 接收到的数据包需要向所有使能的高速下行PORT发送,即广播。USB HOST 在USB 体系中负责设备连接/移除的检测、HOST 和设备之间控制流和数据流的管理、传输状态的收集、总线电源的供给。

USB数据流模型

USB 体系在实现时采用分层的结构,如下图所示:

在HSOT 端,应用软件(ClientSW)不能直接访问USB 总线,而必须通过USB系统软件和USB主机控制器来访问USB 总线,在USB总线上和USB 设备进行通讯。从逻辑上可以分为功能层、设备层和总线接口层三个层次。其中功能层完成功能级的描述、定义和行为;设备级则完成从功能级到传输级的转换,把一次功能级的行为转换为一次一次的基本传输;USB 总线接口层则处理总线上的Bit 流,完成数据传输的物理层实现和总线管理。途中黑色箭头代表真实的数据流,灰色箭头代表逻辑上的通讯。

物理上,USB 设备通过分层的星型总线连接到HOST,但在逻辑上HUB 是透明的,各

USB 设备和HOST 直接连接,和HOST 上的应用软件形成一对一的关系。如下图所示:

各应用软件-功能设备对之间的通讯相互独立,应用软件通过USB 设备驱动程序(USBD)发起IRQ 请求,请求数据传输。主机控制器驱动程序(HCD)接收IRQ 请求,并解析成为USB传输和传输事务(Transaction),并对USB 系统中的所有传输事务进行任务排定(因为可能同时有多个应用软件发起IRQ 请求)。主机控制器(Host Controller)执行排定的传输任务,在同一条共享的USB 总线上进行数据包的传输。如

下图所示。

USB 系统中数据的传输,宏观的看来是在HOST 和USB 功能设备之间进行;微观的看是

在应用软件的Buffer 和USB 功能设备的端点之间进行。一般来说端点都有Buffer,可以认为USB通讯就是应用软件Buffer 和设备端点Buffer之间的数据交换,交换的通道称为管道。应用软件通过和设备之间的数据交换来完成设备的控制和数据传输。通常需要多个管道来完成数据交换,因为同一管道只支持一种类型的数据传输。用在一起来对设备进行控制的若干管道称为设备的接口,这就是端点、管道和接口的关系。一个USB 设备可以包括若干个端点,不同的端点以端点编号和方向区分。不同端点可以支持不同的传输类型、访问间隔以及最大数据包大小。除端点0 外,所有的端点只支持一个方向的数据传输。端点0 是一个特殊的端点,它支持双向的控制传输。管道和端点关联,和关联的端点有相同的属性,如支持的传输类型、最大包长度、传输方向等。

四种传输类型

1. 控制传输:

控制传输是一种可靠的双向传输,一次控制传输可分为三个阶段。第一阶段为

从HOST 到Device 的SETUP 事务传输,这个阶段指定了此次控制传输的请求类型;

第二阶段为数据阶段,也有些请求没有数据阶段;第三阶段为状态阶段,通过一次

IN/OUT 传输表明请求是否成功完成。

控制传输通过控制管道在应用软件和Device 的控制端点之间进行,控制传输

过程中传输的数据是有格式定义的,USB 设备或主机可根据格式定义解析获得的

数据含义。其他三种传输类型都没有格式定义。

控制传输对于最大包长度有固定的要求。对于高速设备该值为64Byte;对于

低速设备该值为8;全速设备可以是8 或16 或32 或64。

最大包长度表征了一个端点单次接收/发送数据的能力,实际上反应的是该端点对应的

Buffer 的大小。Buffer 越大,单次可接收/发送的数据包越大,反之亦反。当通过一个端点

进行数据传输时,若数据的大小超过该端点的最大包长度时,需要将数据分成若干个数据

包传输,并且要求除最后一个包外,所有的包长度均等于该最大包长度。这也就是说如果

一个端点收到/发送了一个长度小于最大包长度的包,即意味着数据传输结束。

控制传输在访问总线时也受到一些限制,如:

_ 高速端点的控制传输不能占用超过20%的微帧,全速和低速的则不能超过10%。

_ 在一帧内如果有多余的未用时间,并且没有同步和中断传输,可以用来进行控

制传输。

2. 中断传输:

中断传输是一种轮询的传输方式,是一种单向的传输,HOST通过固定的间隔

对中断端点进行查询,若有数据传输或可以接收数据则返回数据或发送数据,否则

返回NAK,表示尚未准备好。

中断传输的延迟有保证,但并非实时传输,它是一种延迟有限的可靠传输,支

持错误重传。

对于高速/全速/低速端点,最大包长度分别可以达到1024/64/8 Bytes。

高速中断传输不得占用超过80%的微帧时间,全速和低速不得超过90%。

中断端点的轮询间隔由在端点描述符中定义,全速端点的轮询间隔可以是

1~255mS,低速端点为10~255mS,高速端点为(2interval-1)*125uS,其中interval 取1

到16 之间的值。

除高速高带宽中断端点外,一个微帧内仅允许一次中断事务传输,高速高带宽

端点最多可以在一个微帧内进行三次中断事务传输,传输高达3072 字节的数据。

所谓单向传输,并不是说该传输只支持一个方向的传输,而是指在某个端点上该传输

仅支持一个方向,或输出,或输入。如果需要在两个方向上进行某种单向传输,需要占用

两个端点,分别配置成不同的方向,可以拥有相同的端点编号。

3. 批量传输:

批量传输是一种可靠的单向传输,但延迟没有保证,它尽量利用可以利用的带

宽来完成传输,适合数据量比较大的传输。

低速USB 设备不支持批量传输,高速批量端点的最大包长度为512,全速批

量端点的最大包长度可以为8、16、32、64。批量传输在访问USB 总线时,相对其他传输类型具有最低的优先级,USBHOST 总是优先安排其他类型的传输,当总线带宽有富余时才安排批量传输。高速的批量端点必须支持PING 操作,向主机报告端点的状态,NYET 表示否定应答,没有准备好接收下一个数据包,ACK 表示肯定应答,已经准备好接收下

4. 同步传输:

同步传输是一种实时的、不可靠的传输,不支持错误重发机制。只有高速和全速端点支持同步传输,高速同步端点的最大包长度为1024,低速的为1023。除高速高带宽同步端点外,一个微帧内仅允许一次同步事务传输,高速高带宽端点最多可以在一个微帧内进行三次同步事务传输,传输高达3072 字节的数据。全速同步传输不得占用超过80%的帧时间,高速同步传输不得占用超过90%的微帧时间。同步端点的访问也和中断端点一样,有固定的时间间隔限制。在主机控制器和USB HUB 之间还有另外一种传输——分离传输(Split Transaction),它仅在主机控制器和HUB 之间执行,通过分离传输,可以允许全速/低速设备连接到高速主机。分离传输对于USB 设备来说是透明的、不可见的。

分离传输:顾名思义就是把一次完整的事务传输分成两个事务传输来完成。其出发点是高速传输和全速/低速传输的速度不相等,如果使用一次完整的事务来传输,势必会造成比较长的等待时间,从而降低了高速USB 总线的利用率。通过将一次传输分成两此,将令牌(和数据)的传输与响应数据(和握手)的传输分开,这样就可以在中间插入其他高速传输,从而提高总线的利用率。

USB 协议层规范

USB 采用little edian 字节顺序,在总线上先传输一个字节的最低有效位,最后传输最高

有效位,采用NRZI 编码,若遇到连续的6 个1 要求进行为填充,即插入一个0。

所有的USB 包都由SYNC 开始,高速包的SYNC 宽度为32bit,全速/低速包的SYNC

段度为8bit。实际接收到的SYNC 产度由于USB HUB 的关系,可能会小于该值。

USB 数据包的格式

PID 表征了数据包的类型,分为令牌(Token)、数据(Data)、握手(Handshacke)以及特殊包4 大类,共16 种类型的PID。具体定义见英文协议第196 页。对于令牌包来说,PID 之后是7 位的地址和4 位的端点号。令牌包没有数据域,以5 位的CRC 校验和结束。SOF 是一类特殊的令牌包,PID 后跟的是11 位的帧编号。对于数据包来说,PID 之后直接跟数据域,数据域的长度为N 字节,数据域后以16 位的CRC 校验和结束。握手包仅有PID 域,没有数据也没有校验和。分离传输会用到一类特殊的包,Start-Split 和Complete-Split 包,格式如下:

在Start-Split 和Complete-Split 包中主要指定了此次分离传输所在的HUB 的地址和下行端口编号以及端点类型(控制、中断、批量、同步)。以及此次传输中数据包在整个数据中的位置(第一个包、中间的包、末尾的包)。握手包包括ACK,NAK,STALL以及NYET 四种,其中ACK表示肯定的应答,成功的数据传输;NAK 表示否定的应答,失败的数据传输,要求重新传输;STALL 表示功能错误或端点被设置了STALL 属性;NYET 表示尚未准备好,要求等待。数据在USB 总线上的传输以包为单位,包只能在帧内传输。高速USB 总线的帧周期为125uS,全速以及低速USB 总线的帧周期为1mS。帧的起始由一个特定的包(SOF 包)表示,帧尾为EOF。EOF 不是一个包,而是一种电平状态,EOF 期间不允许有数据传输。注意:虽然高速USB 总线和全速/低速USB 总线的帧周期不一样,当时SOF 包中帧编号的增加速度是一样的,因为在高速USB 系统中,SOF 包中帧编号实际上取得是计数器的高11 位,最低三位作为微帧编号没有使用,因此其帧编号的增加周期也为1mS。

事务传输(Transaction)的流程

1. 批量事务传输

图中一个方框表示一个Packet,灰色的包表示主机发出的包,白色的包表示

Device 发出的包。批量传输是可靠的传输,需要握手包来表明传输的结果。若数据

量比较大,将采用多次批量事务传输来完成全部数据的传输,传输过程中数据包的

PID 按照DATA0-DATA1-DATA0-…的方式翻转,以保证发送端和接收端的同步。USB 允许连续3 次以下的传输错误,会重试该传输,若成功则将错误次数计数器清零,否则累加该计数器。超过三次后,HOST 认为该端点功能错误(STALL),放弃该端点的传输任务。

一次批量传输(Transfer)由1 次到多次批量事务传输(Transaction)组成。

翻转同步:发送端按照DATA0-DATA1-DATA0-…的顺序发送数据包,只有成功的事务传输

才会导致PID 翻转,也就是说发送段只有在接收到ACK 后才会翻转PID,发送下一个数据包,否则会重试本次事务传输。同样,若在接收端发现接收到到的数据包不是按照此顺序翻转的,比如连续收到两个DATA0,那么接收端认为第二个DATA0 是前一个DATA0 的

重传。

2. 控制传输(Transaction)

一次控制传输分为三(或两个)个阶段:建立(Setup)、数据(DATA)(可能

没有)以及状态(Status)。每个阶段都由一次或多次(数据阶段)事务传输组成

(Transaction)。

左图为建立阶段的事务传输流程图。可以看出:

与批量传输相比,在流程上并没有多大区别,区别只在于该事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。建立阶段过后,可能会有数据阶段,这个阶段将会通过一次或多次控制传输事务,完成数据的传输。同样也会采用PID 翻转的机制。建立阶段,Device 只能返回ACK 包,或者不返回任何包。最后是状态阶段,通过一次方向与前一次相反的控制事务传输来表明传输的成功与否。如果成功会返回一个长度为0 的数据包,否则返回NAK 或STALL。下图为整个控制传输的示意图:

3. 中断传输

中断传输在流程上除不支持PING 之外,其他的跟批量传输是一样的。他们之间的区别也仅在于事务传输发生的端点不一样、支持的最大包长度不一样、优先级不一样等这样一些对用户来说透明的东西。主机在排定中断传输任务时,会根据对应中断端点描述符中指定的查询间隔发起中断传输。中断传输有较高的优先级,仅次于同步传输。同样中断传输也采用PID 翻转的机制来保证收发端数据同步。下图为中断传输

的流程图。

4. 同步传输

同步传输是不可靠的传输,所以它没有握手包,也不支持PID 翻转。主机在排定事务传输时,同步传输有最高的优先级。

USB 总线上的情形是怎样的?

包是USB 总线是数据传输的最小单位,不能被打断或干扰,否则会引发错误。若干个数据包组成一次事务传输,一次事务传输也不能打断,属于一次事务传输的几个包必须连续,不能跨帧完成。一次传输由一次到多次事务传输构成,可以跨帧完成。

下图为USB HUB 的架构图

USB HUB 自身的工作速度由上行PORT 的连接速度决定。从结构上分,USB HUB 由HUB

Repeater 、HUB Controller 及Transaction Translator 三部分组成。其中HUB Repeater 主要负责连接的建立和撤销,即完成上行PORT 和下行PORT 工作在相同速度的连接管理。同时还支持错误的检测与恢复以及设备连接/移除的检测。HUB Controller负责与HOST 通讯,完成与HOST 的交互(请求的响应)、HUB 的控制及管理。TransactionTranslator 主要负责高速的分离传输,并把它们分发到连接了全/低速设备的下行PORT。Routing Logic 负责将下行


DAVINCI USB驱动的框架图:

USB-CORE
HCD(MUSB CONTROLLER)
LOW-LEVEL DRIVER

引言:首先make menuconfig选中usb support(CONFIG_USB)
在driver/usb/makefie里定义:
obj-$(CONFIG_USB)                += core/
找到driver/usb /core/makefie里定义:
usbcore-objs        := usb.o hub.o hcd.o urb.o message.o /
                        config.o file.o buffer.o sysfs.o
obj-$(CONFIG_USB)        += usbcore.o
usb.c这就是USB 总线驱动的核心文件,那现在打开driver/usb/core/usb.c看看usb总线是如何初始化的

subsys_initcall(usb_init);
我们知道subsys_initcall是子系统的初始化,关于subsys__initcall在include/linux/init.h里定义如下:
#define subsys_initcall(fn)                __define_initcall("4",fn)
而__define_initcall在
#define __define_initcall(level,fn) /
        static initcall_t __initcall_##fn __attribute_used__ /
        __attribute__((__section__(".initcall" level ".init"))) = fn
相当于".initcall"4 ".init"
.initcall4.init在vmlinux.lds
__initcall_start = .;
                        *(.initcall1.init)
                        *(.initcall2.init)
                        *(.initcall3.init)
                        *(.initcall4.init)
                        *(.initcall5.init)
                        *(.initcall6.init)
                        *(.initcall7.init)
                __initcall_end = .;
把函数放到段地址里遍历段时就调用函数了 妙招!
__initcall_start在init里do_initcalls引用的-_-
for (call = &__initcall_start; call < &__initcall_end; call++)
{
}
废话不说了,回到usb_init里
最重要的是:
bus_register
usb_host_init
usb_hub_init
driver_register(&usb_generic_driver);

这。。。。。。。

subsys_initcall(musb_init); davinci usb-controller 的初始化,
主要是这步driver_register(&musb_driver); usb-controller的核心了

static struct device_driver musb_driver = {
        .name = (char *)musb_driver_name,
        .bus = &platform_bus_type,
        .owner = THIS_MODULE,
        .probe = musb_probe,
        .remove = __exit_p(musb_remove),
        .shutdown = musb_shutdown,
        .suspend = musb_suspend,
        .resume = musb_resume,
};
又回到linux驱动的框架了 哈 熟吧
最终还是会去做musb_probe,目标只有一个,在musb_probe里会musb_init_controller,当然只有初始化了才能用controller终于找到组织了。下面来看看musb_init_controller

。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。

提到HUB了,在usb_init core 早为我们准备了usb_hub_init();这样通过kernel_thread创建一个守护进程khubd轮询USB口的有没有设备接入,一个while(1)搞定简单吧老谭的C有用了,
调用 hub_port_connect_change处理

1        USB硬盘的识别:
接入USB硬盘hub_events就开始工作了,hub_port_connect_change发现有设备连上USB口于是就有了后来的事
usb_new_device
device_add
bus_add_device
device_attach

。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。。
storage_probe,USB Mass Storage device? 调用usb_stor_scan_thread  创建守护进程(usb-stor-scan)
storage_probe在usb_stor_init引用:
usb_register(&usb_storage_driver)

struct usb_driver usb_storage_driver = {
        .owner =        THIS_MODULE,
        .name =                "usb-storage",
        .probe =        storage_probe,
        .disconnect =        storage_disconnect,
        .id_table =        storage_usb_ids,
};
usb_register是usb 设备驱动向usb core层注册

struct usb_driver {
        struct module *owner;
        const char *name;
        int (*probe) (struct usb_interface *intf,
                      const struct usb_device_id *id);
        void (*disconnect) (struct usb_interface *intf);
        int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
        int (*suspend) (struct usb_interface *intf, u32 state);
        int (*resume) (struct usb_interface *intf);
        const struct usb_device_id *id_table;
        struct device_driver driver;
};
usb_driver是usb -core的驱动struct device_driver driver在这哈 linux驱动框架是不是很熟啊 ,对 这就是封装 。。。C++
回首usb core原来帮你做了这么多事

scsi_scan_host到SCSI层了!又看到scsi_scan_channel了
scsi设备都是挂到某个伦上的 比如:Attached scsi disk sda at scsi11, channel 0, id 0, lun 0  因为SCSI controller是数据结构LINUX 内核SCSI  core虚拟的

2  USB        硬盘数据传输:
在storage_probe时调用result = usb_stor_acquire_resources(us); 在此函数里创建的usb_stor_control_thread守护进程(usb-storage),在传输数据时用的是us->proto_handler(us->srb, us)决定用的是哪种协议的传输方式SCSI用的是usb_stor_transparent_scsi_command在(get_protocol里定义的),
说到get_protocol主要是得到一些设备信息比如:vender protocol manfatuer等
它用的ID是USB_PROBE是传入的。
get_transport决定用的是哪种的传输模式:硬盘是存储设备用的是BULK的方式,当然USB  core里也定义了(Bulk,Control传输模式因设备类型而异)。

一次传输从usb_stor_transparent_scsi_command开始:。。。首先usb_stor_invoke_transport                 temp_result = us->transport(us->srb, us);还记得吗这就是前面get_transport里定义好的传输模式,哈USB就是这样的
主要提到usb_stor_bulk_transfer_buf和usb_stor_bulk_transfer_sg,前者调用usb_stor_msg_common里面status = usb_submit_urb(us->current_urb, GFP_NOIO);提交URB,
URB是USB-core的主要传输单位,像net的packet一样
注意了op->submit_urb (urb, mem_flags)用到的是struct usb_operations        *op是
struct usb_operations {
        int (*allocate)(struct usb_device *);
        int (*deallocate)(struct usb_device *);
        int (*get_frame_number) (struct usb_device *usb_dev);
        int (*submit_urb) (struct urb *urb, int mem_flags);
        int (*unlink_urb) (struct urb *urb, int status);

        /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
        void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
                        int mem_flags,
                        dma_addr_t *dma);
        void (*buffer_free)(struct usb_bus *bus, size_t size,
                        void *addr, dma_addr_t dma);

        void (*disable)(struct usb_device *udev, int bEndpointAddress);

        /* global suspend/resume of bus */
        int (*hub_suspend)(struct usb_bus *);
        int (*hub_resume)(struct usb_bus *);
};
调用的是musb_submit_urb而这是哪来的的呢?
哈 具体的USB controller的submit方法:我们用的DAVINCI usb-controller
在plat_uds.c里DAVINCI usb-controller device-driver初始化函数musb_init
driver_register(&musb_driver);
定义如下:
static struct device_driver musb_driver = {
        .name = (char *)musb_driver_name,
        .bus = &platform_bus_type,
        .owner = THIS_MODULE,
        .probe = musb_probe,
        .remove = __exit_p(musb_remove),
        .shutdown = musb_shutdown,
        .suspend = musb_suspend,
        .resume = musb_resume,
};
还记得LINUX 驱动框架吗? Driver 的probe必然会去找设备
musb_init_controller这时才登场
bus = usb_alloc_bus(&musb_host_bus_ops)里bus->op = op;连上了
musb_init_controller除了这还做了:比如 platform_get_irq(pdev, 1)为MUSB申请中断 为何是platform呢? USB-controller无家可归的娃啊
musb_platform_init又是另一个比较重要的函数
clkp = clk_get (NULL, "USBCLK") USB的clock在这里申请还记得在CONTROLLER会检查 没时钟怎行
musb->isr = davinci_interrupt 中断处理函数
davinci usb controller通过中断来检测硬盘寄存器复位USB controller
回到usb_stor_bulk_transfer_sg里Transfer an entire SCSI command's worth of data payload over the bulk 这就是scsi协议用到的

2        USB硬盘的移除:
简单的可以说是USB热插拔的实现了,当USB口的设备拔出时,USB是如何知道的呢?还记得前面提到的hub_port_connect_change 吗?对USB有守护进程这个宝
void usb_disconnect(struct usb_device **pdev)
{
}
原因是这样的慢慢说来,当USB口的设备拔出时,从硬件上看是一个叫MUSB中断(中断号是12),davinci 的驱动就跳到中断处理函数davinci_interrupt里查状态寄存器的位变化,代码如下:
        tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
        musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp);

        musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK)
            >> DAVINCI_USB_RXINT_SHIFT;
        musb->int_tx = (tmp & DAVINCI_USB_TXINT_MASK)
            >> DAVINCI_USB_TXINT_SHIFT;
        musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK)
            >> DAVINCI_USB_USBINT_SHIFT;

续。。。。。。
        if (musb->int_tx || musb->int_rx || musb->int_usb)
                retval |= musb_interrupt(musb);
这不进到usb-conroller中断处理函数里去le。后面的故事更精彩
然后起一个定时器去查询hub的状态(?。。。。。),同时 hub会向usbcontroller发送Change Bitmap的消息(硬件上做的东东),那么usb控制器就接受这个urb 处理完后调用那个hub_irq处理函数,在它那里再去kick_khubd里做了这一关键的一步,看下
static void kick_khubd(struct usb_hub *hub)
{
        unsigned long        flags;

        spin_lock_irqsave(&hub_event_lock, flags);
        if (list_empty(&hub->event_list)) {
                list_add_tail(&hub->event_list, &hub_event_list);
                wake_up(&khubd_wait);
        }
        spin_unlock_irqrestore(&hub_event_lock, flags);
}
把事件加到event链表里,这样hub_events就知道了,这就是热插拔。。。

抱歉!评论已关闭.