QPhome# 青浦之家论坛

QQ登录

只需一步,快速开始

4244

积分

53

好友

508

主题
1
发表于 2007-3-22 20:30:37 | 查看: 1653| 回复: 0
信息来源 驱动程序开发网




提纲
1.背景知识介绍..一些关于电路理论,计算机组成原理的东西
2.usb相关背景知识
3.windows usb整体架构介绍
4.usbport.sys分析
5.usbuhci.sys分析
6.usbhub.sys分析

usb协议方面的涉及的不算太多..毕竟都是有文档的东西
主要着眼于windows的实现部分..

过年了..我去买了个logitech的无线光电鼠..正好是个usb接口的..于是就想了解下windows关于usb这个部分的东西..
于是就有了这些东西

先还是说说我的工作环境
自己的系统是2003 build 3790...
反汇编的sys文件(没有源代码,当然只能用ida了哟)是checked windows xp 2600(没有打sp的)...
硬件环境...i815e主板..ich2南桥...82801BA...uhci..支援usb 1.0.
VID = 8086(这个当然了)...PID = 2442 REV = 02

因为是反汇编...所以信息严重依赖我的硬件平台
而且以为我的主板usb host controller不支援usb 2.0...所以关于2.0的代码我全部ignore了...

好了...进入今天的主题...

今天要说的是关于bus的一些基本的知识
说道一个总线...
那就有几个基本的问题需要回答
1.总线是什么样的类型,串行还是并行的?这个决定了总线的基本组成逻辑以及通讯方式
2.总线上的设备是怎么进行交流的?这个涉及设备的地址解码,总线信号的响应的问题
3.总线的仲裁(arbiter)是怎么进行的?这个涉及总线的使用权所有权归谁管理,谁驱动总线信号的问题

任何一个总线..最关心的问题就是第二个..因为她实际上就是我们最直接能体会到的....
比如我的数据是怎么发送到我的设备上的..而不会发送到别的设备上....我设备的数据又是怎么传递给我的..这个就是最基本的问题..

首先..我们来看看总线的两种基本组成逻辑..他们会决定具体的总线操作形式...

第一类总线她的数据传输是并行的...也就是并行总线..她把要发送的数据分成位..每一个位(bit)用一根数据线发送出去..总线拥有的数据线的条数决定了一次能发送的bit的数目...如果有32根线..那么一次就能发送32 bits的数据也就是一个dword..如果有4个dword要发送..就得分成4次发送..每一次占据一个固定的时间长度...这个时间长度就叫作总线时钟周期..一般把一次读写操作花的时间叫一个总线周期....

并行总线是现在用得非常多的总线类型..她的特点决定了她只是用在近距离的局部范围内(因为得用很多线,而且后面会知道她对时钟要求很高..所以不太适合远距离传输)..比如像pci..就是并行的....

第二类的总线..数据传输是串行的..就是串行总线..她的数据发送方式跟并行的不同...同样她把数据分成bit..但是每次只是传输一个bit..过一定的时间以后再发送第二个bit...然后第三个bit...这样就只是需要很少的几根线就行了...就像usb才4根线而已..而且还有一个是电源线...串行总线又会根据是否同步发送接收的方式分成同步跟异步两种....同步就是说..发送端跟接收端是工作在同一个时钟信号下的(时钟信号通常在一组单独的信号线传输)..总线总是处于工作状态...而异步方式就是说发送端跟接收端工作不需要同步..总线是可以空闲的...发送端跟接收端约定一个协议表示又数据要传输了...传输完成了总线就进入空闲状态...

接下来就来看看如何作地址解码...马上就会看到两种总线是如何影响到地址解码过程的...

那么什么是地址解码?...一个总线上总是要连接很多的设备..那么怎么去表示每一个不同的设备呢?....设备总是跟总线连接到一起的..那么对于总线上的信号(也就是数据)..设备怎么知道是送给自己的还是送给旁边某个设备的呢?....这就需要一种方式对设备进行标记..
总线给每个连接到她上面的设备设定一个唯一的地址..这就是总线地址..然后总线每次在传输数据的时候用某种方式告诉总线上的设备..我要发送或者接收的数据是给哪个地址的....每个总线上的设备用这个地址跟自己拥有的地址作比较..如果相同.或者在一个范围内(很多设备都可能会占据有一段的总线地址)..那么这个设备就被选中..作为跟另外那一端(一般会是一个总线控制器)交流的对象..数据就在他们两个之间流动..而其他的设备就忽略这些总线上的信号..当这次总线操作完成了...以后又有新的操作要进行了..总线又通知每个设备这次她要交流的对象..被选中的对象就又去交流..其他的设备一样忽略...
以上就是基本的原理...听起来是不是很智能? 呵呵....

回过头来..你会不会想究竟是怎么个控制法的?
某个设备怎么知道要忽略这些数据?某个设备又怎么知道应该去读写这些数据?
这些问题就落到地址解码逻辑部分了..或许你觉得很复杂..其实基本的原理并不难....我在学校念书的时候..就自己用74LSxxx的电路作过8086 cpu的地址解码逻辑的..说穿了其实很简单..虽然那些技术已经完全不适应与现在的总线结构了...但是基本原理还是一样的..

下面我就一点一点的来讲这个部分是怎么完成的...不感兴趣的同学可以跳过了...呵呵
一般的讲芯片都会设计一个总开关..当你打开这个开关的时候.芯片就开始作正常的工作..而当你关上这个开关的时候..芯片的内部操作就完全停止..或许芯片会对外表现一种高阻态(这个部分参看下面的解释)....
这样我们如果能从地址信号里面提取出某种控制信息来控制这个开关的话..就能达到设备对某些地址工作..对其他地址不工作的功能了.

这个芯片的总开关就被称作芯片的"使能"端(enable)...一般用EN来表示....我们对总线提供的地址信号进行解释..产生适当的信号引入到芯片的EN脚..这样就能达到控制的目的了...这也就是地址解码逻辑的主要功能...

那..我们怎么去获取这个地址信号呢? 作为例子.看最简单的情况..总线把每个设备用一个8位的数字表示..然后用8根线(这也就是地址总线)连接到设备的某8根脚上面..然后每次总线操作的时候就在这8个脚上面送适当的信号...比如总线要访问00001001b地址的设备..他们总线就在8根线上(假设叫A0到A8)送的电信号为
A0 = A3 = 1
A1 = A2 = A4 = A5 = A6 = A7 = 0

如果我们的设备要对这个地址响应..就必须要让EN有效
写作表达试就是
EN = A0*A3*(~A1)*(~A2)*(~A4)*(~A5)*(~A6)*(~A7)
其中*表示逻辑与...~表示非

有了这个表达式就能选择与门非门或门等等搭建好一个组合电路..产生合适的EN信号...enable芯片了...
看看上面这个表达式..什么时候EN才是1..
必须要A0 = A3 = 1而且其他全部为0的时候EN才是1
所以只有在地址线上送00001001的时候芯片才工作..这样就达到了芯片对某个特定的地址响应的目的...

那如果芯片要对某些地址响应怎么办?
同样的方法..只是EN的表达式得变化了..
比如芯片响应00000001到00000011得地址
那么EN就是
EN = A0*(~A2)*(~A3)*(~A4)*(~A5)*(~A6)*(~A7)

这个例子还是太简单..具体怎么从这个里面提取合适得EN表达式..有很多的方法..都是属于数字电路的基本功..真值表.卡诺图..这里没有办法详讲了...

这样地址解码逻辑的部分的基本原理就算是清楚了..
回头再说几个现实的问题.
首先..设备的地址是谁负责分配的...plug and play 都知道了...
pci总线上的设备地址是靠系统软件分配的..通过写某个pci寄存器完成..isa总线上的设备地址是需要人去完成的..就是所谓跳线..你需要去调整在设备上的几个开关来设定设备的地址...usb总线上的设备地址也是系统软件来设置的..通过所谓的set address request完成.

然后...上面的例子可以看到..地址解码逻辑需要总线送地址信号..因为EN端必须要有效.芯片才会工作..如果你在芯片工作的时候..比如正在交换数据的时候让EN变的无效了..芯片工作就不正常了..这就需要总线在和设备交流的过程中一直送地址信号..来保证EN总是有效的....
可以看到这样的方式效率很低...必须要有额外的线来送地址信号...有没有办法减少这些线呢...如果只是在与设备交流的开始的一段时间里面送地址信号..然后让设备"记忆"到她自己是这次操作的对象..那就可以让地址线根数据线合而为一....送完地址后..设备记忆下来...然后用同样的线作为数据交流的通道...

办法当然是有的..从8088开始就是这种数据地址线共享的方式了..现在的pci总线也是这样地址数据线共享..所以他们的引脚都写AD0..AD1...A就表示Address...D表示Data...表示这个线某些时候是地址信号某些时候是数据信号...

嗯...注意...上面这种共享的方式只是限于并行总线...串行总线用完全不同的方式进行地址解码...后面将会看到

共享马上就带来好多的问题...
首先是设备记忆的问题...怎么能记忆?..这就是数字电路里面的时序电路发挥作用的时候了...

数字电路一般被分成了两类..一类就上像上面EN那样完成一个输入到输出的运算的功能的..被叫做组合逻辑电路....他们最大的特点就是输出总是根据输入发送变化..也就是没有办法保存自己的状态..任何一个时刻你去查看他们的输出..都是用他们的输入计算出来的...
另外一类就有些智能了...她能记忆自己的状态..输出的状态很可能跟自己的前一个输出状态有关系...他们就是时序电路..跟时间序列有关系的电路....
两类电路相互配合完成不同的功能...
组合逻辑完成一些运算的功能...比如作作加法..作作乘法..完成"逻辑"运行的功能
时序电路主要的功能是实现一种"有穷状态自动机"...用来保存一些状态信息...关于有穷状态自动机(Finite State Machine)的话题..就没有办法多说了...形式语言与自动机本身就是一门很深的学问

锁存器是一个很普通的时序电路...一般的..她在时钟上升沿或者下降沿来的时候锁存输入..然后产生输出...在其他的时候输出都不跟随输入变化...这就是所谓边缘触发的D触发器...当然也有电平触发的D触发器...具体使用哪钟...得看你使用得总线配置...

设想下面得情况...
总线控制器送地址信号..然后让锁存器Enable(她也有自己得EN端)
锁存器根据地址信号产生出要送给设备得EN信号...然后锁存..这也是他名字的由来....这样送给设备得EN信号就一直到下一次总线控制器送锁存器得EN信号之前都保持不变...这样就完成了记忆功能..

现在责任就全转移到总线控制器头上了..因为她要产生合适得锁存器使用EN信号....这个当然很简单了..如果是电平触发得...在送地址信号得同时送这个信号就行了...这信号一般被称为ALE Address Latch Enable....
地址锁存以后..总线就不用担心设备中途突然被Disable得情况了...

并行总线的地址解码部分得逻辑基本就是这样..是不是很简单?
串行总线的我放到后面讲...

接下来看看数据传输的部分....
总线即要发送数据又要接收数据...怎么办呢?怎么知道是要把数据从总线上引进来还应让自己的数据发送到总线上去?

引?发送?这是什么意思?学过电路的知道..如果给定一个点的电压是固定的啊...电压并不存在方向的啊....
说的没错....是这样的....在某种电路的帮助下..你可以实现这样的功能...让某个时候你内部的电压跟随外部变化(实现引的功能)..让某种时候...你的内部电压去改变外部电压(实现发送功能)...而在另外的时候...可以让你完全从电路里面断开...你也不影响到外部电压...外部也不影响到你....
是不是很诡异...哈哈...

完成这种功能的电路叫三态门...tri-state..这个词相信很多地方看到了吧....

我的计算机接口技术的老师说...三态门是总线实现的关键技术...确实有同感...

所谓3态门..是说电路内部有3种状态..有一种就是高阻态...高阻就是你对地阻抗(是说电阻电容电感一起产生的影响)...对电源阻抗都很大...这样就相当于断开一样...另外两种状态算是正常工作状态..输出由输入决定....

控制这种状态变化的脚...呵呵...还是叫EN....
EN为1的时候电路正常工作..输出电压跟随输入电压
EN为0的时候电路表现高阻态

如果我们把两个3态门调个头并连起来...并且让他们的EN相互反向..
这样把左边的信号叫A,右边的信号叫B
EN为1的时候..其中一个3态门工作...B跟随A...另外一个高阻态..当她不存在
EN为0的时候..另外一个工作..因为是反过来的接的..所以A跟随B.原来的那个高阻态...

这样在EN的控制下...就实现了这些引跟发送的过程了.....

也许你注意到我用跟随这个词了...什么意思...
其实她来自"射级跟随器"这个词组...是一种三极管的工作状态...
如果完全不懂电路...忽略好了..呵呵

现在如果有合适的EN信号去控制3态门的话就能完成读取发送的功能了.....

总线控制器在发出总线信号的时候...同样要准备好这个控制信号...不仅仅是自己要用...总线上的设备也得用...
一般得这就是一个READ信号..或者是个WRITE信号...用这个信号去控制那些3态门就能完成

好了...到这里...地址解码逻辑也完了...读取写入控制逻辑也完了
剩下得就是具体得总线通讯协议了....这就是跟每个总线相关的信息了..所谓...implementation dependence....

了解这些基本的知识有助于了解总线协议本身...起码不会感觉自己完全不明白具体操作是怎么进行的

还有一个问题...就是总线的共享问题...
设备都是连接到总线上的..如果某个时候有两个设备都想用这个总线怎么办?....很明显不能让两个设备同时往总线上送信号...不然信号叠加以后就错误了....

这样就需要有一个仲裁器(arbiter)来决定当然谁永远总线的控制权..

设备要使用总线的时候..往arbiter送一个request的信号(或许通过某个特殊的信号线..比如pci的bus master设备使用的)....
arbiter经过裁决以后决定..可以让她占用总线..于是回送一个ack信号(也许也是用一个特殊的信号线回送)....然后设备通过某种方式(比如让request信号线跟ack信号线都保持高电平)告诉总线她正在占用控制权...arbiter让她的3态门全部高阻态..交出总线控制权...如果这个时候还有别的设备要访问总线..她也执行相同的操作...不过arbiter却知道现在不能满足这种要求..或者就一直延时到总线可用为止...占用总线的设备用完了以后..用某种方式通知arbiter(比如同时让request跟ack都为低电平)..arbiter回收总线控制权..或许就会回答刚刚后到的那个request..送ack信号...

这就是总线仲裁的基本逻辑模型...

很明显...上面这些描述都基于并行总线...

下面就来看看串行总线的部分....

串行总线应用的范围(远距离)决定了她要付出更多的劳动..
首当其冲的就是信号的干扰问题...
因为并行总线的长度都非常短(几厘米)..而且都是有足够的电磁保护的..相对的讲..受到的干扰要小很多...
但是串行的就不同了..她的传输距离要比并行的长很多(几米或者几十米).而且环境上也没有并行那么安全..这样就需要某些手段作一些安全措施..

然后还有就是异步串行总线的同步问题...
如果这边已经开始发送数据了...对端却没有反应..或者反应不正常...那肯定坏了...如果两边的时钟有偏差...数据采样点不一样..在小范围内或许还不会很多...但是你想..一次采样差1ms..10次就差10ms..或许你已经漏了好多些的数据了....
所以..必须有某种方式让两边的数据采样同步..要保证在无外界干扰的情况下对方读的数据跟自己写的是一样的..

最简单的方法就是传输同步信号..但是这样就必须要提供多的数据线..

还有的办法就是发送端用某种方式把同步信号叠加到数据信号里面..接收端再把同步信号提取出来...

有模拟电路基础的马上就想到..用载波调制嘛...可惜在数字电路里面这行不通....原因嘛...因为数字信号本身就是方波..根据傅立叶变换..她已经化作了拥有无数频率的正弦波...已经找不到一个合适的载波频率了...这个部分...我承认是在我的大学课程中难度仅次于离散数学的一个部分...傅立叶变换..拉普拉斯变换..卷积积分..激励函数..这些名词....想想都头疼....虽然还比不上所谓...群..半群..域..环...这些词的冲击...呵呵..题外话....

不能用载波..就必须要在信号本身上面想办法
就是所谓的编码....

并行总线之间发送高低电平的方式用在串行总线上的话...如果连续出现多个相同的1或者0...就有可能发送同步的问题...如果信号常常有变化..那么对端就可以在变化的边缘调整时钟.达到同步的目的..因为变化总是发生在时钟的边缘....

基于以上的事实...串行总线里面广泛的运用了NRZI编码...不归零翻转编码...Non Return to Zero Inverted....
简单的说..就是如果输入信号是1..就保持输出信号不变化..如果是0就翻转....她其实还是会产生同步问题...但是如果她跟bit stuffing 结合一起用的话..就能比较好的解决同步的问题...

bit stuffing..位填充...如果在输入信号里面有若干(由通讯协议决定这个若干究竟是几..比如usb是6)个1的话..就插入一个0..这样编码后的信号肯定会反转...这个翻转的变化沿就提供了同步的方法..
接收端接收的时候..如果遇到若干个相同不变化的信号..那么就能肯定下一个信号必须要翻转..她是bit stuffing的结果..并且要从最终的信号里面删除掉这个信号...

为什么这里能插入?上面的却不能插入呢?...因为这里的0,1已经不是原来的含义了...上面的你在发送的时候作了stuffing..接收的时候却没有办法判断究竟这个是stuffing的结果还是本身就是一个0..

光有这些操作还是不够了...串行通讯一般都会携带某种形式的CRC..比如usb...再比如LAN...呵呵..lan也是串行通讯的哟....

数据传输过程中的问题解决了...
下面回答这样一个问题
接收端怎么知道什么时候去读取数据并对数据进行解释?
并不是任何时候总线上的数据都是有意义的呀?

回答这个问题之前..先回想并行总线怎么没有遇到这个问题..
因为芯片在EN信号的控制下工作的..EN信号又是最终由总线控制器提供的...总线控制器在必要的时候让设备去读去写..当然就没有这个问题了...

串行环境下就不同了..没有EN信号..原因就跟没有同步信号一样

解决办法就是用一个特殊的bits string(回想串行是一个bit一个bit 串着发出去的)..来告诉对端的设备要开始一次数据传输了..

设备检测到这个特殊的bits string(一般叫着前导帧)..就开始准备自己的内部状态准备接收数据了...同时的..这个前导帧也会提供一些同步的信息(01交替出现)让对端同步自己的时钟..

数据传输开始的判断问题这样也就解决了...

回过头来..或许你要问...我计算机的信号都是并行的
怎么转串行发送呢?接收的时候怎么办呢?

这个解决办法...有一个电路的名字叫移位寄存器
她能实现串行转并行..并行转串行的方法..
比较简单的同步移位寄存器就是用若干个D触发器级连起来构成

或许你注意到了...我一直用对端..对方这样的词来描述..
而且从来都没有提到并行总线那样的地址解码过程..
这是怎么回事情..

因为串行总线使用的数据线本身就很少..不可能把地址当作特别的信号来发送..只能是把地址当作数据的一部分发送出去..所以地址解析的功能就延后了....

一般的...在前导帧以后就会有设备地址数据...每个收到数据的设备解码这个数据作锁存(方面跟上面并行的一样)..设置设备芯片的EN信号..完成地址解码过程.....

关于设备地址..很好的例子就是Lan...在以太网的帧头包含有对方的mac 地址...每个收到(lan是一种广播模式..每个处于相同的广播域的设备都能收到这份数据)的设备比较mac地址是否是自己的mac地址.(当然还有广播地址与多播地址的比较)...然后决定自己是否响应这个数据包....

usb也是一样的...在某些token里面会携带device的address和endpoint number..这些就是足够的信息用作地址解码的...

地址解码部分有了上面的基础..想起来也就不难了..

接下来是数据传输发送部分....3态门..同上..

最后是总线仲裁部分....
这个部分稍微显得复杂...usb总线仲裁由host完成..因为所有的usb transaction都是由host发起的...不存在由设备申请使用总线的时候

而像lan这种..却是使用一种CSMA/CD的方式...所谓..带冲突检测的多路存取载波侦听....不管什么时候读这个总是觉得别扭..呵呵..

关键的地方就在冲突检测上面....每个要占用总线的设备总是在发送之前侦听网线上的信号512 bits的时间...如果网线上的信号显示由设备在使用..则等待...直到没有人使用了再发送..如果有设备同时发送的话...则发生了一次冲突..作一次随机指数回避..然后再侦听..重复上面的过程....512个bits也就是为什么以太网的帧最小长度是60个字节的原因(4个字节的CRC)....

lan的总线共享方式是一种竞争的方式..并没有一个arbiter在运作..
也是必然...lan的物理结构(大..远)决定了不可能有arbiter存在...

7788写了这么多.....
这次主要的是一些基本的关于总线的知识....
算作是计算机组成原理的一个概括好了....
分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
转发到微博

收藏回复 只看该作者 道具 举报

您需要登录后才可以回帖 登录 | 注册

回顶部