ihhv2004
驱动牛犊
驱动牛犊
  • 注册日期2004-11-30
  • 最后登录2010-10-28
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望26点
  • 贡献值0点
  • 好评度6点
  • 原创分0分
  • 专家分0分
60楼#
发布于:2005-03-04 18:43
tiamo兄真是给小弟不少帮助啊,有没有什么联系方式?小弟有很多问题想向你请教
tiamo
VIP专家组
VIP专家组
  • 注册日期2002-02-26
  • 最后登录2018-01-09
  • 粉丝17
  • 关注4
  • 积分50分
  • 威望142点
  • 贡献值1点
  • 好评度40点
  • 原创分2分
  • 专家分15分
  • 原创先锋奖
  • 社区居民
61楼#
发布于:2005-03-03 20:16
啊~~~~~~~高级会员了啊我是....

谢谢斑主哟......
coolqiu
驱动牛犊
驱动牛犊
  • 注册日期2003-05-10
  • 最后登录2010-06-05
  • 粉丝0
  • 关注0
  • 积分6分
  • 威望17点
  • 贡献值0点
  • 好评度14点
  • 原创分0分
  • 专家分0分
62楼#
发布于:2005-03-03 11:44
好文章
znsoft
管理员
管理员
  • 注册日期2001-03-23
  • 最后登录2023-10-25
  • 粉丝300
  • 关注6
  • 积分910分
  • 威望14796点
  • 贡献值7点
  • 好评度2410点
  • 原创分5分
  • 专家分100分
  • 社区居民
  • 最爱沙发
  • 社区明星
63楼#
发布于:2005-03-02 20:53
感谢你的贡献,赠予高级会员.谢谢!!
http://www.zndev.com 免费源码交换网 ----------------------------- 软件创造价值,驱动提供力量! 淡泊以明志,宁静以致远。 ---------------------------------- 勤用搜索,多查资料,先搜再问。
bmyyyud
驱动老牛
驱动老牛
  • 注册日期2002-02-22
  • 最后登录2010-01-21
  • 粉丝0
  • 关注0
  • 积分1000分
  • 威望130点
  • 贡献值0点
  • 好评度106点
  • 原创分0分
  • 专家分0分
64楼#
发布于:2005-03-02 09:16
滚滚长江东逝水 浪花淘尽英雄 是非成败转头空 青山依旧在 几度夕阳红 白发渔樵江渚上 惯看秋月春风 一壶浊酒喜相逢 古今多少事 尽付笑谈中
tiamo
VIP专家组
VIP专家组
  • 注册日期2002-02-26
  • 最后登录2018-01-09
  • 粉丝17
  • 关注4
  • 积分50分
  • 威望142点
  • 贡献值1点
  • 好评度40点
  • 原创分2分
  • 专家分15分
  • 原创先锋奖
  • 社区居民
65楼#
发布于:2005-03-01 22:46
windows的usb整体架构

这里主要讲讲windows为usb设备维护的几个基本结构

首先第一个是device handle

对于每一个usb总线上的设备..windows为其创建一个device handle.关联到设备的pdo上面..这个handle就是填写在urb header的DeviceHandle成员的值...通常情况并不需要我们自己去填写这个值..因为你的irp会传递给你的设备对应的pdo..这个pdo会帮你填写这个成员...

在这个device handle结构里面还维护有几个其他的成员..比如device的usb地址...device连接到的hub所在的port number..device descriptor.还有就是一个内建的pipe handle(用于default endpoint构成的control pipe)..以及其他endpoints 构成的pipes的list header...同时还有保存一个config handle

接着第二个结构就是pipe handle...他表示了一个pipe...这个pipe也许是由有着两个相同endpoint地址的endpoints构成的contol pipe.或者是其他的interrupt pipe等等...

对于endpoint 0..windows特殊对待...他的结构不是动态分配的..而是直接作为一个inner class放置到device handle里面的...
而其他的pipe handle则是动态创建..通过LIST_ENTRY连接到device handle的listheader成员上的...

pipe handle也是一个很小的结构..除了endpoint descriptor以外还有一个指向endpoint 结构的指针

接下来的结构就是endpoint本身了..
这是一个很大的结构...
保存着诸多的信息...除了一些于endpoint相关的参数(比如device speed,max packet size等等)以外..最最主要的是若干个LIST_ENTRY..他们起着连接endpoint到某些不同的ListHeader的作用
比如closed 链表..state change链表等等...
还有些LIST_ENTRY起着urb排队的list header的作用...

然后值config handlez...在device handle结构里面有一个指向他的指针...他表示了当前所使用的配置信息....
很小....除了保存有configuration descriptor还有一个list header...用于连接interface handle

interface handle结构描述了某个interface
也是一个不大的结构...不过却是一个变长的结构
他保存有一份interface descriptor
然后还保存有若干个pipe handle
struct interface_handle
{
  //.....
  ULONG ulPipesCount;
  pipe_handle PipeHandles[1];
};

device handle   config handle   interface handle
+------------+  +------------+
|config   |--->|config desc |   +-----------------+
|pipe zero   |  |interface   |----->|list entry for   |
|pipes list  |<-+ |list header |<-----|config handle    |
|header      |+ | +------------+   |pipes count      |
+------------+| |                     +-----------------+
       | +---------------------|pipe1 list entry |
       +---------------------->|for device handle|
  +---------------------------------|endpoint pointer |
  +->endpoint +--------------+   +-----------------+
        |list entries  |   |pipe2 ...to pipeN|
        |list headers  |   |link with prev   |
        |..............|   |pipe struct......|
        +--------------+   +-----------------+


基本的连接情况就是如此
至于endpoint结构里面的诸多list entry与list header..他们大都是用在传输数据的时候...常常变化..而不像其他的结构里面的list entry只是起一个连接架构的作用..

上面这个图里面...
device handle是在创建pdo的时候创建的..
config handle与连接起来的interface handle.以及interface handle里面的pipe handle还有pipe handle所指向的endpoint这些结构是在你发出select configuration 的urb的时候才创建的

说完这些静态的结构以后..还必须要提到的就是每当你发出一个urb的时候所要进行的工作...

都知道urb是一个联合...他是由好多个struct组成的一个union

但是对于windows内部来说...urb被分成了4种
第一种..属于直接完成的urb..也就是说不会在usb总线上出现的urb..比如select configuration...select interface.abort pipe等等就属于这一类...他们几乎都不涉及数据的传送排队等等..属于同步完成范畴...

第二种用于interrupt根bulk 传输....使用URB_INTERRUTP_OR_BULK_TRANSFER这个结构

第三种用于ISO 传输...

最后一种就是control 传输了...他使用的结构只是在interrupt or bulk的后面多一个8个字节的setup packet
struct _setup_packet
{
  UCHAR bmRequest;
  UCHAR bRequest;
  USHORT wValue;
  USHORT wIndex;
  USHORT wLength;
};

很多的结构都属于这一类..对比一些就会看到windows把某些成员标记成了reserved..就是说windows会自己填写这些成员..而不需要程序员去填写...

对于第二种第三种第四种urb...会有某个对应的usb传输发生在usb总线上面....windows会对这些urb按照所属的endpoint进行排队..一个一个的提交给硬件

这个属于usbport完成的功能...在这里大致的提一下

每个需要进行传输的urb...windows创建一个transfer的结构...
这个结构里面包含了这次传输的基本的参数信息.比如device 地址..endpoint number.传输方式(IN,OUT,SETUP,ISO).等等..同时还有很多list entry配合endpoint结构里面的某些list header完成排队调度的功能..

首先一个transfer进入对应的endpoint的pending 链表...
在特定的情况下..usbport会刷新这个pending链表..
transfer就进入了一个map 链表(这个链表不在endpoint管理范围内)
因为硬件操纵的是物理地址而不是虚拟地址..所以在提交给硬件之前..必须要进行map...所以transfer从pending链表出来以后进入了map 链表..等待完成虚拟地址到物理地址的映射..
这个map链表是一个全局的链表..并不是每个endpoint维护一个map 链表....
在一定的条件下..usbport刷新这个map 链表...通过dma的方式对transfer要使用的buffer进行map...完成以后.transfer进入到其对应的endpoint的active 链表...这个时候transfer就能提交给硬件了....
在一个workthread的帮助下..从active链表里面取出一个transfer提交给miniport driver...miniport driver再提交给硬件...

当硬件完成这次传输以后..通知miniport driver..再通知到usbport..于是这个transfer再次进入done 链表...
在usbport刷新done 链表的时候...最终完成这个transfer对应的urb以及对应的irp(如果有的话)...同时释放transfer结构对应的内存..

几个特别的地方要说一下...
第一就是...如果某个urb只是单方向的..不涉及数据交换的..比如设置某个port状态这种..用setup结构已经完全够了的情况..transfer从pending链表出来..并不进入map链表..而是直接进入endpoint的active 链表..因为并不需要buffer..所以不用map

第二...usbport操纵的结构是transfer..与transfer关联的是urb..而不是irp...完全可以自己构造一个urb进行传输...而不需要某个irp..

在无cancel跟无abort发生的情况下的transfer就是这样一种流程

很容易理解下面这个状态变迁图

pending----->map----->active---->done

加入了cancel跟abort的情况稍微复杂一点
因为任何一个状态都能转移到cancel 链表里面去

这个主要是通过cancel routine来完成了...先到这里..在说到usbport的时候会有详细的讨论这个问题...
这里只是需要对正常状态的流程有个了解..

明白了windows为每个usb设备维护的信息..明白了urb的传输完成
现在来看看usb设备的device stack搭建情况

stack的搭建工作就属于usb hub的任务了..分工很明确..usbport负责维护usb设备的信息.负责处理urb...而usb hub则是致力于bus relationship....

usbport实现了两个接口..一个是USB_BUS_INTERFACE_USBDI.另外一个是USB_BUS_INTERFACE_HUB
这两个接口都有公开的定义...能在ddk里面找到

usbhub利用这两个接口里面的函数通知usbport要创建一个新的device handle了...等等一类的操作...具体看看这两个接口的定义就能体会了...

想想也很显然...device handle是由usbport创建管理销毁的..usbhub没有必要去实现一份相同的工作...交给usbport完成就ok

所以..usbhub在enum出一个新的pdo的时候..通知usbport为这个pdo创建一个device handle结构并返回这个结构的地址...然后usbhub保存这个地址到pdo的device extension里面...
以后提交到pdo的urb..usbhub都填写urb的header里面的device handle成员....

这就是usbport在usbhub帮助下完成的device管理工作..
usbport同时还对urb进行过滤..以提供更多的usbport没有实现的功能..比如xp跟2003以后版本支持的URB_OS_FEATURE_DESCRIPTOR_REQUEST 就是由usbhub来完成的..

usbhub还要完成的几个功能就是usb设备的电源管理..以及与selective suspend关联的idle request...这些都会在讲到usbhub的时候具体分析

还有一个没有提到的驱动就是用于compose设备的usbccgp.sys..
我并没有对这个sys作ida...不过他的功能同usb hub差不多..

至于usbuhci....那是一个与硬件接口息息相关的驱动..谈不上什么特别的架构.....

今天就到这里...
这个作为以后的代码分析的一个总体大概
RED_spring
驱动中牛
驱动中牛
  • 注册日期2002-07-28
  • 最后登录2016-11-06
  • 粉丝0
  • 关注0
  • 积分3分
  • 威望19点
  • 贡献值0点
  • 好评度17点
  • 原创分0分
  • 专家分0分
  • 社区居民
66楼#
发布于:2005-02-24 18:39
学习学习。。。 继续,继续!
Leopard
驱动老牛
驱动老牛
  • 注册日期2001-07-13
  • 最后登录2021-12-15
  • 粉丝0
  • 关注0
  • 积分8分
  • 威望53点
  • 贡献值0点
  • 好评度19点
  • 原创分0分
  • 专家分0分
  • 社区居民
  • 忠实会员
67楼#
发布于:2005-02-24 11:47
学习学习!
tiamo
VIP专家组
VIP专家组
  • 注册日期2002-02-26
  • 最后登录2018-01-09
  • 粉丝17
  • 关注4
  • 积分50分
  • 威望142点
  • 贡献值1点
  • 好评度40点
  • 原创分2分
  • 专家分15分
  • 原创先锋奖
  • 社区居民
68楼#
发布于:2005-02-22 10:54
第二部分开始了.......

进度稍微有点慢....大家忍受一下...

usb的详细规范就没有办法在这里一一说明了
这里只是关注我自己觉得可能要注意的地方...

还有呢...我只是一个作软件的勉强算是程序员吧
所以对硬件的东西很不熟悉..以至于作硬件的朋友会觉得我有些过分的强调某些或许是很基础很简单的事实..呵呵..见谅...

还是一样...
硬件部分开头..接着是软件部分

usb使用2根数据线传送数据D+跟D-
但是理论上只是需要一根数据线就够了...比如并行总线使用的方式

usb使用这2根数据线作为差分放大电路的输入端..经过差分放大以后
使用差分放大的输出端作为接收信号...发送的时候刚刚相反

差分放大电路主要的目的是为了解决所谓\"零点漂移\"的问题..
所谓零点漂移是指在直接耦合放大电路里面即使输入点电压为0
输出端也会有变化缓慢的输出电压..
所谓直接耦合放大电路......呃...打住...这样下去就没完没了了
具体的请参考模拟电路相关部分

所以usb不使用D+跟D-上的信号作为自己的信号定义
而是使用他们经过差分放大以后的信号作为信号定义
具体的定义见usb spec 1.1的表7.1

有了信号定义
现在来看几个特别的事件的信号
第一是连接跟断开信号
在hub的downstream port上连接有两个电阻..所谓下拉电阻..
他们把D+跟D-信号都掐位到小于VIL(输入最小电压)...也就是所谓的
SE0状态

当有设备连接到port上的时候..因为设备里面的上拉电阻使得D+或者D-信号提升.超过VIH..经过差分放大呈现出1或者0...这样就能让hub知道有设备连接上来了...
而对于full speed跟low speed的设备来说...上拉电阻所接入的信号线是不同的low speed的上拉电阻接在D-上面..而full speed的上拉电阻接在D+上面...
所以差分以后的信号..对full speed来说是1..对low speed来说就是0...这也是辨认接入设备的速度的方法...

设备接入的时候信号从SE0变迁到差分1或者差分0的时间超过了2.5微秒...hub就认为有新的设备连接进来了
设备断开的时候刚刚相反...信号变迁到SE0..持续时间超过2.5微秒就认为设备断开了....

动态连接的硬件辨认方式就是如此...那么如何让软件知道呢..
hub检测到这种情况了以后设置对应的port的connect change位..然后用interrupt transfer的方式告诉给hub的驱动程序...hub的驱动程序然后调用IoInvalidateDeviceRelations....然后内核发出pnp irp..然后再然后..呵呵...具体的过程后面会描述

接下来要说的就是数据信号...
数据信号是由差分0跟差分1组成的...
发送端通过驱动D+跟D-以差模信号(幅度相等、极性相反)..经过差分放大以后体现\"逻辑0跟逻辑1\"来

数据都是组装在packet里面发送的....
在每个packet的开头发送一个start of packet告诉总线上的设备
要开始一个新的packet了...在packet结束的时候发送end of packet信号...然后进行idle状态

start of packet的标志就是bus从idle状态转入data k状态
而end of packet标志是bus从data 状态转入SE0状态2 bits的时间然后转入data j状态1 bits的时间...然后bus就进入idle状态

bus上的设备必须要检查start of packet..解码随后的packet的内容
决定自己是否要对这个packet作出响应.对于不响应的packet必须要忽略...这里必须要实现一个状态机..因为并不能只是判断当前收到的packet就能决定自己是否响应..后面的软件协议部分会有详细的解释

接着说reset信号...hub驱动总线进入SE0状态超过2.5微秒..连接的设备就应该把这个当然是一个reset信号..设备进行reset.完成以后对缺省地址(也就是0)进行响应

suspend信号...她其实不是一个信号...设备检测到bus进入idle状态超过3毫秒就应该进入suspend状态..只是从bus上获得suspend状态所需要不超过10毫安的电流..任何bus上的其他信号都应该让设备从suspend状态回复过来..对于full speed的设备.每毫秒一次的start of frame信号能阻止设备进入suspend状态..而对于low speed的设备.hub会在start of frame的时候产生keep alive信号来阻止设备进入suspend状态...只有在明确的让hub把某个port设置成suspend状态的时候...hub才不转发start of frame信号跟keep alive信号..这样设备才会进入suspend状态..这也就是selective suspend..在设置了某个port进入suspend状态以后..hub停止转发所以信号到这个设备..设备就进入了suspend状态...在这个状态里面..设备必须要继续驱动连接在D+D-上的上拉电阻..一来保持bus的idle状态..二来让hub不会认为设备连接断开了...

global suspend 信号...host controller停止发送start of frame信号..这样bus上的所有设备都进入suspend状态(hub只是转发.不会产生信号)....

resume...分成两种..
第一是host发起的resume信号...host驱动总线进入resume状态..也就是data k状态至少20毫秒.然后驱动一个low speed的SE0信号接着一个data j信号..而且必须在3毫秒以内发出start of frame信号以阻止设备再次进入suspend状态..

第二种是remote wakeup的时候设备发出的resume信号..设备驱动总线进入data k状态1毫秒到15毫秒之间.然后放弃总线驱动权...设备连接的hub检测到这个resume信号.如果hub的upstream port也是resume状态的....她就把这个resume信号发送给她每个enabled port..包括先前产生resume信号的设备..hub同时向上发送这个resume信号..直到遇到非resume状态的hub或者达到host为止..
最后遇到的这个hub会在15毫秒以后驱动bus..所有接受到resume信号的bus必须要在15ms以内让自己的upstream port来驱动自己的enabled port...最终到达的hub维持resume信号至少20毫秒..然后跟一个low speed的SE0然后1bit的data j来结束这个resume信号进入正常的bus 操作(比如转发start of frame以及开始进行数据传输)

明白了总线上的信号表示方法
接下来要说的就是数据传输本身了
首先是数据编码..使用nrzi + bit stuffing的方式作为数据编码方式每遇到6个1就插入一个0..

接着就进入了software管理范围...
数据传输被分成了单个的packet.实际的数据被放置在每个packet里面进行传输...packet作为数据传输的一个单位.

bus从idle状态转入data k状态就表示了一个新的packet开始
在每个packet的开头传输一个同步数据..这个同步数据的目的就像她的名字一样..用来让设备同步自己的内部时钟...
他是有7个0紧跟一个1构成的..经过nrzi以后就变成kjkjkjkk信号
设备利用信号的变化边缘同步自己的内部时钟
紧跟这同步数据以后的是一个PID packet identifier..他指出了这个packet使用的格式信息错误处理方式等等..他的格式以及含义请参考spec..

根据pid的不同.随后跟的内容也不同..同样参考spec

packet结束以后发送end of packet信号(2 bits时间的SE0 data j)

bus上的设备必须识别start of packet信号..然后同步自己的时钟.然后解析pid..决定自己是否响应这个pid..不响应的忽略随后的信号..等待end of packet..重新检测start of packet..解码pid.重复这个过程..

设备只是对指定了自己的地址以及能提供适当操作的endpoint进行响应...在某些pid里面指定了packet所要操作的设备以及endpoint(通过设备地址跟endpoint number指定)...其他的pid未指定这些信息的都不能作为一个传输的首个pid发送....

设备检测到带有地址信息的packet..然后与自己的地址比较来决定自己是否要响应这个已经以后若干个不带地址信息的packet...对于不响应的设备..必须要忽略后面紧跟的所有不带地址信息的packet..直到遇到了新的带有地址信息的packet再重复如上判断..而对于要响应的设备..同时暗示了随后的那些不带地址信息的packet都是在跟自己交流..自己必须要进行响应....
整体逻辑如上....
具体的传输使用什么的数据格式..使用什么样子的packet组合方式.请参考spec

接下来就是收尾的工作了...大致的说说windows对一个usb设备的操作过程

当hub启动的时候..hub的fdo发出一个interrupt transfer的urb到hub的endpoint1...这个endpoint是一个用来通知hub的外部事件的endpoint....

当有事件发生的时候(比如一个设备连接进来了)...host controller完成这个irp...hub的fdo从输出结果里面知道了是哪个port上发生了变化...hub fdo读取这个port的状态..判断发生了什么样子的变化..假设是有某个设备连接进来了..hub fdo就reset port..完成以后读取device descriptor利用里面的vendor id,product id和bcddevice组成device id,同时当然要生成hardware id跟compatible id..查找合适的驱动程序..安装或者加载...
驱动程序加载以后..就能对这个设备进行操作了...

大体的流程
首先读取config descriptor.选择一个config
然后对于config descriptor里面的每个interface发出一个select interface请求...windows为每个interface里面的每个endpoint返回一个pipe handle...
完成了这些步骤以后..驱动程序就能跟设备进行交流了

交流的方式是利用urb进行了...驱动程序建立好一个urb.然后用internal io control的方式传递给自己attached的设备...
urb结构里面最重要的一个成员便是pipe handle.他指定了要操作的endpoint是哪个..

驱动程序必须要设置这个成员(他是在select interface的时候返回的)..除非指定了使用默认的control endpoint(也就是endpoint0)

底层的pdo把这个urb交给root hub的pdo...然后转换成具体的操作数据传递给host controller..然后由host controller转换成usb的信号发送出去...

至于...urb的排队..urb的完成..urb的取消..等等操作就属于usbport.sys的功能....

而设备的enum...则是属于usbhub.sys的功能

至于usbuhci.sys这些则是完成把urb转换成usb bus信号的功能..

所以以后的几个部分分别有不同的侧重点...
到时候会详细描述...
linestyle
驱动小牛
驱动小牛
  • 注册日期2004-01-28
  • 最后登录2010-01-05
  • 粉丝0
  • 关注0
  • 积分1000分
  • 威望139点
  • 贡献值0点
  • 好评度135点
  • 原创分0分
  • 专家分0分
69楼#
发布于:2005-02-22 10:22
pfpf
loading is waiting ...
yymrhxf
驱动牛犊
驱动牛犊
  • 注册日期2002-08-07
  • 最后登录2011-10-25
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望4点
  • 贡献值0点
  • 好评度3点
  • 原创分0分
  • 专家分0分
70楼#
发布于:2005-02-21 13:01
先鼓鼓掌.
wowocock
VIP专家组
VIP专家组
  • 注册日期2002-04-08
  • 最后登录2016-01-09
  • 粉丝16
  • 关注2
  • 积分601分
  • 威望1651点
  • 贡献值1点
  • 好评度1227点
  • 原创分1分
  • 专家分0分
71楼#
发布于:2005-02-15 11:56
老大,继续啊.....
花开了,然后又会凋零,星星是璀璨的,可那光芒也会消失。在这样 一瞬间,人降生了,笑者,哭着,战斗,伤害,喜悦,悲伤憎恶,爱。一切都只是刹那间的邂逅,而最后都要归入死亡的永眠
上一页 下一页
游客

返回顶部