`
dato0123
  • 浏览: 913552 次
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是UHCI(4)IO内存和IO端口

 
阅读更多

usb_bus_init来自drivers/usb/core/hcd.c,很显然,它就是初始化struct usb_bus结构体指针.而这个结构体变量hcd->self的内存已经在刚才为hcd申请内存的时候一并申请了.

688 /**

689 * usb_bus_init - shared initialization code

690 * @bus: the bus structure being initialized

691 *

692 * This code is used to initialize a usb_bus structure, memory for which is

693 * separately managed.

694 */

695 static void usb_bus_init (struct usb_bus *bus)

696 {

697 memset (&bus->devmap, 0, sizeof(struct usb_devmap));

698

699 bus->devnum_next = 1;

700

701 bus->root_hub = NULL;

702 bus->busnum = -1;

703 bus->bandwidth_allocated = 0;

704 bus->bandwidth_int_reqs = 0;

705 bus->bandwidth_isoc_reqs = 0;

706

707 INIT_LIST_HEAD (&bus->bus_list);

708 }

我相信你早已忘记了,当初在hub驱动中我就讲过,devnum_next在总线初始化的时候会被设为1,说的就是这里.现在证明当初我没有忽悠你吧.这里其它的赋值就不多说了,用到的时候我会告诉你的,相信我,这是同志的信任.

回到usb_create_hcd中来,又是几行赋值,飘过.

倒是1511行引起了我的注意,又是可恶的时间机制,init_timer,这个函数我们也见过多次了,usb-storage里见过,hub里见过,斑驳的陌生终于在时间的抚摸下变成了今日的熟悉.这里我们设置的函数是rh_timer_func,而传递给这个函数的参数是hcd.这个函数具体做什么我们走着瞧,不过你放心,咱们这个故事里会多次接触到这个timer,想逃是逃不掉的,躲得过初一躲不过十五.

1515,INIT_WORK咱们也在hub驱动里见过了,这里这个hcd_resume_work什么时候会被调用咱们也到时候再看.

剩下两行赋值,1518行没啥好说的,struct usb_hcd有一个struct hc_driver的结构体指针成员,所以就这样把它和咱们这个uhci_driver给联系起来了.而在uhci_driver中我们看到,其中有一个product_desc被赋值为"UHCI Host Controller",所以这里也赋给hcd->product_desc,因为struct hc_driverstruct usb_hcd这两个结构体中都有一个成员const char *product_desc,你说这不浪费吗?就一个破字符串,还得保存在两个地方,这些家伙九年制义务教育怎么学的?长此以往,国将不国矣!

至此,usb_create_hcd结束了,返回了这个申请好赋好值的hcd.我们继续回到probe函数中来.

89124这个if-else的确让我开心了一把,因为if里说的是EHCIOHCI的情况,else里针对的才是UHCI,鉴于EHCI将由某人来写,OHCIUHCI性质一样,我们不会讲,所以这就意味着这个if-else我只要从105行开始看,即直接看UHCI那部分的代码.!

不过我们也得知道这里为何要判断HCD_MEMORY,这个宏的意思是表明该HC的寄存器是使用memory,而没有设置这个flagHC的寄存器是使用I/O.这些寄存器俗称I/O端口,或者说I/O ports,这个I/O端口可以被映射在Memory Space,也可以被映射在I/O Space.UHCI是属于后者,EHCI/OHCI属于前者.

这里看上去必须多说几句,否则很难说清楚.以我们家Intel为代表的i386系列处理器中,内存和外部IO是独立编址独立寻址的,于是有一个地址空间叫做内存空间,另有一个地址空间叫做I/O空间.也就是说,从处理器的角度来说,i386提供了一些单独的指令用来访问I/O空间.换言之,访问I/O空间和访问普通的内存得使用不同的指令.而在一些玩嵌入式的处理器中,比如PowerPC,他们家就只使用一个空间,那就是内存空间,那像这种情况,外设的I/O端口的物理地址就被映射到内存地址空间中,这就是传说中的Memory-mapped,内存映射.而我们家那种情况,外设的I/O端口的物理地址就被映射到I/O地址空间中,这就是传说中的I/O-mapped,I/O映射.

那么EHCI/OHCI,它们除了有寄存器以外,还有内存,而它们把这些统统映射到Memory Space中去,UHCI只使用寄存器来通信,所以它只需要映射寄存器,I/O端口,而它的spec规定,它是映射到I/O空间.LinuxI/O MemoryI/O ports都被视作一种资源,它们分别被记录在/proc/iomem/proc/ioports.

所以我们可以在这里看到uhci-hcd,

localhost:~ # cat /proc/ioports

0000-001f : dma1

0020-0021 : pic1

(此处省略若干行)

bca0-bcbf : 0000:00:1d.2

bca0-bcbf : uhci_hcd

bcc0-bcdf : 0000:00:1d.1

bcc0-bcdf : uhci_hcd

bce0-bcff : 0000:00:1d.0

bce0-bcff : uhci_hcd

c000-cfff : PCI Bus #10

cc00-ccff : 0000:10:0d.0

d000-dfff : PCI Bus #0e

dcc0-dcdf : 0000:0e:00.1

dcc0-dcdf : e1000

dce0-dcff : 0000:0e:00.0

dce0-dcff : e1000

e000-efff : PCI Bus #0c

e800-e8ff : 0000:0c:00.1

e800-e8ff : qla2xxx

ec00-ecff : 0000:0c:00.0

ec00-ecff : qla2xxx

fc00-fc0f : 0000:00:1f.1

fc00-fc07 : ide0

而在这里看到ehci-hcd,

localhost:~ # cat /proc/iomem

00000000-0009ffff : System RAM

00000000-00000000 : Crash kernel

(此处省略若干行)

d8000000-d80fffff : PCI Bus #01

d8000000-d80fffff : PCI Bus #02

d80f0000-d80fffff : 0000:02:0e.0

d80f0000-d80fffff : megasas: LSI Logic

d8100000-d81fffff : PCI Bus #0c

d8100000-d813ffff : 0000:0c:00.1

e0000000-efffffff : reserved

f2000000-f7ffffff : PCI Bus #06

f4000000-f7ffffff : PCI Bus #07

f4000000-f7ffffff : PCI Bus #08

f4000000-f7ffffff : PCI Bus #09

f4000000-f5ffffff : 0000:09:00.0

f4000000-f5ffffff : bnx2

f8000000-fbffffff : PCI Bus #04

f8000000-fbffffff : PCI Bus #05

f8000000-f9ffffff : 0000:05:00.0

f8000000-f9ffffff : bnx2

(此处省略若干行)

fca00400-fca007ff : 0000:00:1d.7

fca00400-fca007ff : ehci_hcd

fe000000-ffffffff : reserved

100000000-22fffffff : System RAM

要使用I/O内存首先要申请,然后要映射,而要使用I/O端口首先要申请,或者叫请求,对于I/O端口的请求意思是让内核知道你要访问这个端口,这样内核知道了以后它就不会再让别人也访问这个端口了.毕竟这个世界僧多粥少啊.申请I/O端口的函数是request_region,这个函数来自include/linux/ioport.h,

116 /* Convenience shorthand with allocation */

117 #define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name))

118 #define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name))

119 #define rename_region(region, newname) do { (region)->name = (newname); } while (0)

120

121 extern struct resource * __request_region(struct resource *,

122 resource_size_t start,

123 resource_size_t n, const char *name);

这里我们看到的那个request_mem_region是申请I/O内存用的.申请了之后,还需要使用ioremap或者ioremap_nocache函数来映射.

对于request_region,三个参数start,n,name表示你想使用从start开始的sizenI/O port资源,name自然就是你的名字了.这三个概念在我们刚才贴出来的cat /proc/ioports里面显示的很清楚,name就是uhci-hcd.

那么对于uhci-hcd,我们究竟需要请求哪些地址,需要多少空间呢?,又要提到那张上坟图了.PCI设备本身有一堆的地址空间,内存空间和IO空间.那么如何把这些空间映射到总线上来呢?用什么?寄存器.看上坟图中的那几个Base Address 0,1,2,3,4,5,即每个设备都有6个地址空间,这叫做六个基址寄存器,有的设备还有一个ROM,所以又有一个Expansion ROM Base Address,它对应第七个区间,或者说区间6,而在上坟图上对应的就叫做扩展ROM基址寄存器.每个寄存器都是四个字节.而我们在include/linux/pci.h中定义了,

227 /*

228 * For PCI devices, the region numbers are assigned this way:

229 *

230 * 0-5 standard PCI regions

231 * 6 expansion ROM

232 * 7-10 bridges: address space assigned to buses behind the bridge

233 */

234

235 #define PCI_ROM_RESOURCE 6

所以在我们的代码中我们看到循环条件就是从0PCI_ROM_RESOURCE之前,即循环六次,因为有六个区间,区间也叫region.那么这些寄存器究竟取的什么值呢?这就是在PCI总线初始化的时候做的事情了,它会把你每个基址寄存器赋上值,而实际上就是映射于总线上的地址,总线驱动的作用就是让各个设备都需要的地址资源都得到满足,并且没有设备与设备之间的地址发生冲突.PCI总线驱动做了这些之后,我们PCI设备驱动就简单了,在需要使用的时候直接请求即可,正如这里的request_region.那么我们传递给request_region的具体参数是什么呢?

两个函数,pci_resource_startpci_resource_len,就是去获得一个区间的起始地址和长度,所以我们就很好理解这段代码了.至于109行这个if判断,pci_resource_flags是用来判断一个资源是哪种类型的,include/linux/ioport.h中一共定义了四种资源:

36 #define IORESOURCE_IO 0x00000100 /* Resource type */

37 #define IORESOURCE_MEM 0x00000200

38 #define IORESOURCE_IRQ 0x00000400

39 #define IORESOURCE_DMA 0x00000800

它们是IO,Memory,中断,DMA.对应我们在/proc下看到的ioports,iomem,interrupt,dma四个文件.所以这里的意思就是判断说如果不是IO Port资源,那么就不予理睬.因为UHCI主机控制器只需要理财I/O Port.

request_region函数如果成功将返回非NULL,失败了才返回NULL.所以代码的意思就是一旦成功就跳出循环.反之,如果循环都结束了还未能请求到,那就说明出错了.那么你说为何一旦成功就跳出循环?老实说,这个问题足足困扰了我13秒钟,别小看13秒钟,有这么长时间刘翔都已经完成一次110米跨栏了.spec来告诉你.

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 137.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

看到没有,20-23h,四个字节,这里正好对应UHCIIO空间基址寄存器.换言之,UHCI就定义了一个基址寄存器,所以我们只需要使用一个基址寄存器就可以映射我们需要的地址了.所以,成功一次我们就可以结束循环了.

又一次,我们回到了usb_hcd_pci_probe,126,pci_set_master函数.还是看那张上坟图,注意到第三个寄存器,叫做Command Reg.,其实就是命令寄存器.让我们用PCI spec来告诉你这个命令寄存器的格局:

<shape id="_x0000_i1026" style="WIDTH: 386.25pt; HEIGHT: 225pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image003.emz"></imagedata></shape>

看到其中有一位叫做Bus Master了么?没错,就是那个Bit 2.用毛德操先生的话说就是PCI设备要进行DMA操作就得具有竞争成为总线主的能力.而这个Bus Master位就是用来打开或关闭PCI设备竞争成为总线主的能力的.在完成PCI总线的初始化时,所有PCI设备的DMA功能都是关闭的,所以这里要调用pci_set_master启用USB主机控制器竞争成为总线主的能力.就是说PCI设备有没有这么一种能力是可以设置的.

USB spec 2.010.2.9节在讲到USB Host Interface的时候,说了USB HC是应该具备这种能力的:

The Host Controller provides a high-speed bus-mastering interface to and from main system memory. The physical transfer between memory and the USB wire is performed automatically by the Host Controller.

同时在UHCI spec里面我们也能找到这么一句话,For the implementation example in this document, the Host Controller is a PCI device. PCI Bus master capability in the Host Controller permits high performance data transfers to system memory.

所以我们需要启用这种能力.

不过你要问了,究竟什么是PCIBus Master?从我们读电子的人的角度来看,连接到PCI总线上的设备有两种,即主控设备和目标设备.或者说一个是master设备,一个是target-only设备,用我们专业的术语来看,这两者最直接的区别就是target-only最少需要47pin,master最少需要49pin,就是说它们所必须支持的总线信号就是不一样的.PCI设备如果是以一个target-only的方式工作,那么它就完全是在主机的CPU的控制之下工作,比如设备接收到某一个外部事件,然后中断主机,然后主机CPU读写设备,这样设备就可以工作了.这样的设备也被一小撮人称为slave设备,或者说从设备.这就是典型的奴才型的设备,主说什么就是什么,完全没有自己的见解.master类型的设备就比这个要复杂了,master设备能够不在主机CPU的干预下访问主机的地址空间,包括主存和其它PCI设备,很显然,DMA就属于这种情况,即不需要主机CPU干涉的情况下USB主机控制器通过DMA直接读写内存.所以我们需要打开这种能力.不过PCI总线在同一时刻只能供一对设备完成传输.至于有了竞争力能不能竞争得到Master,那就得看人品了.上天给了你一幅天使的面孔和魔鬼的身材,但你能不能成为明星就得看造化了,当然只要你遵守圈中的潜规则,你离成功就不远了.

分享到:
评论

相关推荐

    Linux那些事儿之我是UHCI

    写一下UHCI吧,也顺便怀念一下Intel,以及Intel的那几个女同事们,好久没联系了,你们可好? UHCI是 Intel提出来的.虽然离开 Intel一年多了,但我总觉得也许有一天我还会回到 Intel.所 以关于 Intel的东西,我多少会关注...

    Linux那些事儿

    Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我是Sysfs 今天本人将9个单独的文档整理出来,做成了一...

    Linux那些事儿1-9合集

    由复旦fudan_abc写的,风趣的文笔,深入浅...linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿之我是Sysfs

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Block层.pdf Linux那些事儿之我是SCSI硬盘.pdf Linux那些事儿之我是Sysfs.pdf Linux那些事儿之我是PCI.pdf ...Linux那些事儿之我是UHCI.pdf

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI ...Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事全集

    Linux那些事儿之我是UHCI Linux那些事儿之我是Sysfs Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是PCI Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿.rar

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    Linux那些事儿系列

    本人整理的fudan_abc的专栏中以完结的文章,在此向原作者表示感谢,给...内容包括:linux那些事儿之我是U盘,linux那些事儿之我是USB,linux那些事儿之我是HUB,linux那些事儿之我是UHCI,linux那些事儿之我是Sysfs。

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs...

    Linux那些事系列

    Linux那些事儿之我是Hub Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘 Linux那些事之我是HUB1

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    微信生活缴费商业项目标准版

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    usb那些事的全集

    usb那些事的全集,包含: ...4 Linux那些事儿之我是PCI 5 Linux那些事儿之我是SCSI硬盘 6 Linux那些事儿之我是Sysfs 7 Linux那些事儿之我是UHCI 8 Linux那些事儿之我是USB+core 9 Linux那些事儿之我是U盘

    Linux那些事儿[完整版]自己整理

    我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内核 戏说USB 说的是2.6.22的内核 我是Hub/UHCI/EHCI 说的是2.6.22.1的内核

    LINUX那些事儿 linux经典之作

    我是Hub/UHCI/EHCI 说的是2.6.22.1的内核 其中我是U盘属于基础性的.这一阶段会遇到一些问题.比如urb提交之后究竟怎么处理的?用户空间究竟是如何访问U盘的?DMA究竟怎么回事. 这之后可以开始看Hub.这一阶段你会明白一...

    linux那些事儿.rar

    linux那些事儿,Block层,EHCI主机控制器,Hub,PCI,SCSI硬盘,Sysfs,UHCI,USB core,U盘等文档。

    Linux那些事儿(包括Hub, Sysfs, UHCI, USB, U盘5个部分)

    一个很经典的东东,里面的很多东西都很有启发性,值得看看

Global site tag (gtag.js) - Google Analytics