比较EHCI和UHCI(一)

摄影艺术家陈冠希老师告诉我们:好的作品,与器材无关,好的作品,与器官无关.有人说,有了EHCI,Intel的上一代USB主机控制器UHCI似乎就将走到被淘汰的边缘.但实际情况是,EHCIUHCI往往是并存的,它们各有价值,如果说EHCI是一款数码相机,那么UHCI则是陈冠希老师那款记录了无数不朽历史瞬间的手机.中国是摄影大国,大多数摄影爱好者玩着器材,追着器材,一味的追求顶尖的器材,但他们的作品恐怕永远也不能和陈冠希老师比肩.

冠希老师的作品的影响力,将持续久远,我们不会忘记这位对摄影艺术有着执著追求,有着独特品位,有着完美情趣的艺术家.陈冠希老师永远手持手机战斗在床榻前,这种精神感染着我们,此时此刻,我们确实应该反思一下,与其猜测UHCI将被EHCI替代,不如静下心来,分析一下它们各自的特点.

首先,EHCIUHCI的区别之一就在于探寻她们内心世界的方式不同.甭管EHCI还是UHCI,她们都有自己的内心世界.访问外设的内心世界通常有两种方法,一种是Memory-mapped I/O,一种是Port-mapped I/O.EHCIOHCI都是这种Memory-mapped I/O,UHCI则剑走偏锋,选择了Port-mapped I/O.

这两种映射的区别又在于前者需要再一次映射,后者不需要.

Linux内核代码中,我们第一次感受到EHCIUHCI的不同是在usb_hcd_pci_probe(),这是一个公共函数,任何主机控制器的驱动都会调用它.我们可以看到代码中分别对EHCIUHCI做了不同的处理:

89 if (driver->flags & HCD_MEMORY) { // EHCI, OHCI

90 hcd->rsrc_start = pci_resource_start (dev, 0);

91 hcd->rsrc_len = pci_resource_len (dev, 0);

92 if (!request_mem_region (hcd->rsrc_start, hcd->rsrc_len,

93 driver->description)) {

94 dev_dbg (&dev->dev, "controller already in use/n");

95 retval = -EBUSY;

96 goto err2;

97 }

98 hcd->regs = ioremap_nocache (hcd->rsrc_start, hcd->rsrc_len);

99 if (hcd->regs == NULL) {

100 dev_dbg (&dev->dev, "error mapping memory/n");

101 retval = -EFAULT;

102 goto err3;

103 }

104

105 } else { // UHCI

106 int region;

107

108 for (region = 0; region < PCI_ROM_RESOURCE; region++) {

109 if (!(pci_resource_flags (dev, region) &

110 IORESOURCE_IO))

111 continue;

112

113 hcd->rsrc_start = pci_resource_start (dev, region);

114 hcd->rsrc_len = pci_resource_len (dev, region);

115 if (request_region (hcd->rsrc_start, hcd->rsrc_len,

116 driver->description))

117 break;

118 }

119 if (region == PCI_ROM_RESOURCE) {

120 dev_dbg (&dev->dev, "no i/o regions available/n");

121 retval = -EBUSY;

122 goto err1;

123 }

124 }

对于EHCI,首先得映射成物理地址,然后再把物理地址映射成虚拟地址.

第一步,首先我们知道PCI设备的配置空间里可以有六个基址寄存器.如图所示:

<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><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:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></path><lock v:ext="edit" aspectratio="t"></lock></shapetype><shape id="_x0000_i1027" style="WIDTH: 414.75pt; HEIGHT: 255pt" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz" o:title=""></imagedata></shape>

Base Address 0Base Address 5.位置从0x10开始,0x27结束.而我们从EHCI spec中的得到的信息是:

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

可以看到USBBASE就是我们提到的那个基址寄存器.而它位于10-13h,换言之,就是6个基址寄存器中的第一个.(这就是为什么我们传递给pci_resource_start的第二个参数是0,而不是1,2,3,4,5)

这里rsrc_start表示这个空间的起始地址,实际上就是这个基址寄存器的高28.(低四位直接取0.)理由如下,因为低四位有别的用途,用来表征其它一些信息.

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

rsrc_len表示这个寄存器所代表的空间大小.EHCI来说,常见的是0x400.

[root@localhost ~]# cat /proc/iomem

00000000-0009ffff : System RAM

00000000-00000000 : Crash kernel

000a0000-000bffff : Video RAM area

000c0000-000c7fff : Video ROM

000c9000-000c9fff : Adapter ROM

000ca000-000ca1ff : Adapter ROM

000ca800-000cbfff : Adapter ROM

000cc000-000d11ff : Adapter ROM

000f0000-000fffff : System ROM

00100000-cffa7fff : System RAM

00200000-00460c6f : Kernel code

00460c70-00595b07 : Kernel data

cffa8000-cffb7bff : ACPI Tables

cffb7c00-cfffffff : reserved

d0000000-d7ffffff : PCI Bus #10

d0000000-d7ffffff : 0000:10:0d.0

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

fc100000-fc2fffff : PCI Bus #10

fc100000-fc11ffff : 0000:10:0d.0

fc1f0000-fc1fffff : 0000:10:0d.0

fc300000-fc4fffff : PCI Bus #0e

fc380000-fc39ffff : 0000:0e:00.1

fc380000-fc39ffff : e1000

fc3a0000-fc3bffff : 0000:0e:00.1

fc3a0000-fc3bffff : e1000

fc3c0000-fc3dffff : 0000:0e:00.0

fc3c0000-fc3dffff : e1000

fc3e0000-fc3fffff : 0000:0e:00.0

fc3e0000-fc3fffff : e1000

fc500000-fc6fffff : PCI Bus #0c

fc5f8000-fc5fbfff : 0000:0c:00.1

fc5f8000-fc5fbfff : qla2xxx

fc5fc000-fc5fffff : 0000:0c:00.0

fc5fc000-fc5fffff : qla2xxx

fc600000-fc63ffff : 0000:0c:00.0

fc700000-fc9fffff : PCI Bus #01

fc800000-fc9fffff : PCI Bus #02

fc8e0000-fc8fffff : 0000:02:0e.0

fc8e0000-fc8fffff : megasas: LSI Logic

fc900000-fc907fff : 0000:02:0e.0

fca00000-fca003ff : 0000:00:1d.7

fca00000-fca003ff : ehci_hcd

fe000000-ffffffff : reserved

100000000-22fffffff : System RAM

这里我们看到fca00000-fca003ff,大小就是0x400.那么这个0x400怎么得到的呢?如何得到这个值是PCI spec明确规定了的:

Size calculation can be done from the 32-bit value read by first clearing encoding information bits (bit 0 for I/O, bits 0-3 for memory), inverting all 32 bits (logical NOT), then incrementing by 1. The resultant 32-bit value is the memory/I/O range size decoded by the register. Note that the upper 16 bits of the result is ignored if the Base Address register is for I/O and bits 16-31 returned zero upon read.

具体完成这一工作的是pci_read_bases()函数,在设备枚举的时候这个函数会被调用.枚举的时候,每个pci设备会有一个struct pci_dev *dev结构体指针,该结构体中有这样一个成员:

170 struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */

struct resource定义于include/linux/ioport.h:

17 struct resource {

18 resource_size_t start;

19 resource_size_t end;

20 const char *name;

21 unsigned long flags;

22 struct resource *parent, *sibling, *child;

23 };

实际上就是表示一段资源,或者或一段资源区间.枚举的时候就会把这个pci_dev的这个成员赋上值,而咱们这里pci_resource_start以及pci_resouce_len无非就是通过这个结构体来得到资源区间的起始地址和它的长度.

知道了我们应该申请什么资源之后我们就可以正式申请了.为此我们可以调用request_mem_region().这样做就是让申请正式生效,从而也让内核知道咱们要用到这么一段区域.以免别人还对这段区域虎视眈眈.

然而,上面这两个rsrc_startrsrc_len是来自硬件本身,它们指的都是物理地址的概念.那么从驱动程序里来说应该如何访问呢?直接访问物理地址是不行的.调用一个ioremap()函数或者ioremap_nocache()函数就可以了,这叫做重映射,或者如它的名字那样remap.这个函数的返回值我们赋给了hcd->regs,从此以后,hcd->regs就代表了这段寄存器空间,它将指引我们通向EHCI控制器内心世界.

对于UHCI,它基址寄存器所代表的空间大小通常是32.或者说0x20.不信您看:

[root@localhost ~]# cat /proc/ioports

0000-001f : dma1

0020-0021 : pic1

0040-0043 : timer0

0050-0053 : timer1

0060-006f : keyboard

0070-0077 : rtc

0080-008f : dma page reg

00a0-00a1 : pic2

00c0-00df : dma2

00f0-00ff : fpu

01f0-01f7 : ide0

02f8-02ff : serial

03c0-03df : vga+

03f6-03f6 : ide0

03f8-03ff : serial

0800-0803 : ACPI PM1a_EVT_BLK

0804-0805 : ACPI PM1a_CNT_BLK

0808-080b : ACPI PM_TMR

0828-082f : ACPI GPE0_BLK

0880-08bf : pnp 00:08

08c0-08df : pnp 00:08

08e0-08e3 : pnp 00:08

0c00-0c7f : pnp 00:08

0ca0-0ca7 : pnp 00:08

0ca8-0ca8 : pnp 00:09

0ca9-0cab : pnp 00:08

0cac-0cac : pnp 00:09

0cad-0caf : pnp 00:08

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

不管是0xbca0-0xbcbf,还是0xbce0-0xbcff,都是占据了0x20的空间.网友只寻花不问柳问到,那么这个0x20有什么依据呢?为啥要取这么一个值?,您别急,这还真是有讲究的,您看下面这幅图,摘自uhci-spec,人家uhci-spec定义了这么些个I/O寄存器,这里占用的地址就是00h13h,不到20h,不过在党的科学发展观的英明指导下,设计者们还是多留出几个地址有备无患,这样即使您设计的硬件需要更多的寄存器那也能行,比如说这里定义的是两个端口,而如果您觉得不够用,多设计几个,那也没问题,PORTSC1,PORTSC2之后您还可以接上PORTSC3,PORTSC4.直到19h也没问题.

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

对于UHCI来说,获得rsrc_startrsrc_len的方法和上面EHCI是一样的.但之后,需要调用request_region()函数正式申请.并且由于Port-mapped IO我们是直接用专用IO指令访问的,所以不需要映射成虚拟地址.做到这一步就足够了.从此以后我们将通过hcd->rsrc_start来访问UHCI内部的寄存器.(正如我们在uhci_init()函数中看到的那样.)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值