Linux那些事儿之我是EHCI(1) 接口体系

EHCI首先是一个PCI设备,我们可以lspci一下看看。

00:1a:7 USB Controller: Intel Corporation USB2 EHCI Controller #1 (rev 03)

我们与外围硬件打交道,可以把数据用in(out)指令传递给外围硬件,还可以把数据传输到cpu和外围硬件共享的内存里面去。这些都是计算机与硬件的接口。(参见ldd3 第9章)

那么我们的程序如何与EHCI联系,交流呢?EHCI定义了三个接口空间。如图Linux那些事儿之我是EHCI(1) 接口体系

作为一个程序员,我们关心的是如何在代码中读/写这些地方的内容。概念性的东西肯定是LDD3写的最好,我就不赘述了。

1)pci configuration space. (ldd3 第12章)

由于EHCI是一个PCI设备,这里用于系统组件枚举和PCI的电源管理。

以x86为例,读取PCI总线套路是这样的。我们要读取PCI总线上地址为add,长度为4个字节的内容。

outl(add, 0xcf8); // 先把add的out到地址为0xcf8的地方

value = inl(0xcfc); // 然后再读取0xcfc的内容

网上找到了一段程序,大家可以试验一下。

Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系/**//*name:pci.c*/
Linux那些事儿之我是EHCI(1) 接口体系#include
<stdio.h>
Linux那些事儿之我是EHCI(1) 接口体系#include
<assert.h>
Linux那些事儿之我是EHCI(1) 接口体系#include
<sys/io.h>
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系
#defineIO_PORTS11/*ioport<=0x3ff*/
Linux那些事儿之我是EHCI(1) 接口体系
#defineIO_PORTS22/*ioport>0x3ff&&ioport<0xffff*/
Linux那些事儿之我是EHCI(1) 接口体系
#defineIO_PERMOFF0
Linux那些事儿之我是EHCI(1) 接口体系
#defineIO_PERMON1
Linux那些事儿之我是EHCI(1) 接口体系
#defineIO_PERMON23
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系
#defineRW_DELAY10000/*delay100000microsecondsforreadingandwritingI/Oports.*/
Linux那些事儿之我是EHCI(1) 接口体系#ifndefBOOL
Linux那些事儿之我是EHCI(1) 接口体系typedefunsigned
charBOOL;
Linux那些事儿之我是EHCI(1) 接口体系
#endif
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系#ifndefBYTE
Linux那些事儿之我是EHCI(1) 接口体系typedefunsigned
charBYTE;
Linux那些事儿之我是EHCI(1) 接口体系
#endif
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系#ifndefDWORD
Linux那些事儿之我是EHCI(1) 接口体系typedefunsigned
longDWORD;
Linux那些事儿之我是EHCI(1) 接口体系
#endif
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系#ifndefINT
Linux那些事儿之我是EHCI(1) 接口体系typedefunsigned
intINT;
Linux那些事儿之我是EHCI(1) 接口体系
#endif
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系#ifndefULONG
Linux那些事儿之我是EHCI(1) 接口体系typedefunsigned
longULONG;
Linux那些事儿之我是EHCI(1) 接口体系
#endif
Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系#ifndefWORD
Linux那些事儿之我是EHCI(1) 接口体系typedefunsigned
shortWORD;
Linux那些事儿之我是EHCI(1) 接口体系
#endif
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
/**//*
Linux那些事儿之我是EHCI(1) 接口体系**Function:WritethevalueofthespecifiedI/Oportbygivingthelengthandthe
Linux那些事儿之我是EHCI(1) 接口体系**startingaddress.
Linux那些事儿之我是EHCI(1) 接口体系**Parameter:PortAddr:theportaddress
Linux那些事儿之我是EHCI(1) 接口体系**PortVal:thevaluetoset
Linux那些事儿之我是EHCI(1) 接口体系**size:size=1forreading1byte,2forword,4fordoublewords
Linux那些事儿之我是EHCI(1) 接口体系**Return:1returnedifsuccess,or0returned
Linux那些事儿之我是EHCI(1) 接口体系
*/

Linux那些事儿之我是EHCI(1) 接口体系BOOLSetPortVal(WORDPortAddr,DWORDPortVal,BYTEsize)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系BOOLRet
=0;
Linux那些事儿之我是EHCI(1) 接口体系INTtmpRet
=0;
Linux那些事儿之我是EHCI(1) 接口体系ULONGnumperm
=1;
Linux那些事儿之我是EHCI(1) 接口体系INTprivilege
=0;
Linux那些事儿之我是EHCI(1) 接口体系assert(PortAddr
>0);
Linux那些事儿之我是EHCI(1) 接口体系
if(PortAddr<=0x3ff)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系tmpRet
=ioperm((ULONG)PortAddr,numperm,IO_PERMON);
Linux那些事儿之我是EHCI(1) 接口体系privilege
=IO_PORTS1;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
elseif(PortAddr>0x3ff)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系tmpRet
=iopl(IO_PERMON2);
Linux那些事儿之我是EHCI(1) 接口体系privilege
=IO_PORTS2;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
else
Linux那些事儿之我是EHCI(1) 接口体系
returnRet;
Linux那些事儿之我是EHCI(1) 接口体系
if(tmpRet<0)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系fprintf(stderr,
"can'tsettheioportpermissionforsetting! ");
Linux那些事儿之我是EHCI(1) 接口体系
returnRet;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
else
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系
switch(size)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
case1:/**//*writeonebytetotheport*/
Linux那些事儿之我是EHCI(1) 接口体系outb(PortVal,PortAddr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
case2:/**//*writeonewordtotheport*/
Linux那些事儿之我是EHCI(1) 接口体系outw(PortVal,PortAddr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
case4:/**//*writedoublewordstotheport*/
Linux那些事儿之我是EHCI(1) 接口体系outl(PortVal,PortAddr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系
default:
Linux那些事儿之我是EHCI(1) 接口体系Ret
=0;
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系usleep(RW_DELAY);
Linux那些事儿之我是EHCI(1) 接口体系Ret
=1;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
if(privilege==IO_PORTS1)
Linux那些事儿之我是EHCI(1) 接口体系ioperm((ULONG)PortAddr,numperm,IO_PERMOFF);
Linux那些事儿之我是EHCI(1) 接口体系
elseif(privilege==IO_PORTS2)
Linux那些事儿之我是EHCI(1) 接口体系iopl(IO_PERMOFF);
Linux那些事儿之我是EHCI(1) 接口体系
returnRet;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
/**//*
Linux那些事儿之我是EHCI(1) 接口体系**Function:ReadthevalueofthespecifiedI/Oportbygivingthelenghtandthe
Linux那些事儿之我是EHCI(1) 接口体系**startingaddress.
Linux那些事儿之我是EHCI(1) 接口体系**Parameter:PortAddr:theportaddress
Linux那些事儿之我是EHCI(1) 接口体系**PortVal:valuefromport
Linux那些事儿之我是EHCI(1) 接口体系**size:size=1forreading1byte,2forword,4fordoublewords
Linux那些事儿之我是EHCI(1) 接口体系**Return:1returnedifsuccess,or0returned.
Linux那些事儿之我是EHCI(1) 接口体系
*/

Linux那些事儿之我是EHCI(1) 接口体系BOOLGetPortVal(WORDPortAddr,DWORD
*PortVal,BYTEsize)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系BOOLRet
=0;
Linux那些事儿之我是EHCI(1) 接口体系
inttmpRet=0;
Linux那些事儿之我是EHCI(1) 接口体系unsigned
longnumperm=1;
Linux那些事儿之我是EHCI(1) 接口体系
intprivilege=0;
Linux那些事儿之我是EHCI(1) 接口体系assert(PortAddr
>0);
Linux那些事儿之我是EHCI(1) 接口体系assert(PortVal
!=NULL);
Linux那些事儿之我是EHCI(1) 接口体系
if(PortAddr<=0x3ff)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系tmpRet
=ioperm((unsignedlong)PortAddr,numperm,IO_PERMON);
Linux那些事儿之我是EHCI(1) 接口体系privilege
=IO_PORTS1;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
elseif(PortAddr>0x3ff)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系tmpRet
=iopl(IO_PERMON2);
Linux那些事儿之我是EHCI(1) 接口体系privilege
=IO_PORTS2;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
else
Linux那些事儿之我是EHCI(1) 接口体系
returnRet;
Linux那些事儿之我是EHCI(1) 接口体系
if(tmpRet<0)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系fprintf(stderr,
"can'tsettheioportpermissionforreading! ");
Linux那些事儿之我是EHCI(1) 接口体系
returnRet;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
else
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系
switch(size)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
case1:/**//*readonebytefromtheport*/
Linux那些事儿之我是EHCI(1) 接口体系
*PortVal=inb(PortAddr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
case2:/**//*readonewordfromtheport*/
Linux那些事儿之我是EHCI(1) 接口体系
*PortVal=inw(PortAddr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
case4:/**//*readdoublewordsfromtheport*/
Linux那些事儿之我是EHCI(1) 接口体系
*PortVal=inl(PortAddr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系
default:
Linux那些事儿之我是EHCI(1) 接口体系Ret
=0;
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系usleep(RW_DELAY);
Linux那些事儿之我是EHCI(1) 接口体系Ret
=1;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系
if(privilege==IO_PORTS1)
Linux那些事儿之我是EHCI(1) 接口体系ioperm((unsigned
long)PortAddr,numperm,IO_PERMOFF);
Linux那些事儿之我是EHCI(1) 接口体系
elseif(privilege==IO_PORTS2)
Linux那些事儿之我是EHCI(1) 接口体系iopl(IO_PERMOFF);
Linux那些事儿之我是EHCI(1) 接口体系
returnRet;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系
intmain(intargc,char*argv[])
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系WORDadd_port
=0xcf8;
Linux那些事儿之我是EHCI(1) 接口体系WORDdata_port
=0xcfc;
Linux那些事儿之我是EHCI(1) 接口体系DWORDaddr
=0x80000000;
Linux那些事儿之我是EHCI(1) 接口体系DWORDport_value;
Linux那些事儿之我是EHCI(1) 接口体系BYTEsize
=4;
Linux那些事儿之我是EHCI(1) 接口体系
intinput;
Linux那些事儿之我是EHCI(1) 接口体系printf(
"Pleaseselecttheoptionnumberasfollow: ");
Linux那些事儿之我是EHCI(1) 接口体系printf(
"1--bus0:dev:0fun:0asaddress0x80000000 ");
Linux那些事儿之我是EHCI(1) 接口体系printf(
"2--bus0:dev:1fun:0asaddress0x80000800 ");
Linux那些事儿之我是EHCI(1) 接口体系printf(
"3--inputyourowndefinedaddressvalue: ");
Linux那些事儿之我是EHCI(1) 接口体系scanf(
"%d",&input);
Linux那些事儿之我是EHCI(1) 接口体系
switch(input)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系
case1:
Linux那些事儿之我是EHCI(1) 接口体系addr
=0x80000000;
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系
case2:
Linux那些事儿之我是EHCI(1) 接口体系addr
=0x80000800;
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系
case3:
Linux那些事儿之我是EHCI(1) 接口体系printf(
"pleaseinputthe32bitsaddressinHexformat(suchas80007800):");
Linux那些事儿之我是EHCI(1) 接口体系scanf(
"%x",&addr);
Linux那些事儿之我是EHCI(1) 接口体系
break;
Linux那些事儿之我是EHCI(1) 接口体系
default:
Linux那些事儿之我是EHCI(1) 接口体系printf(
"inputinvalidoptionnum,exitprogram. ");
Linux那些事儿之我是EHCI(1) 接口体系
return-1;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系printf(
"Theaddris:%X ",addr);
Linux那些事儿之我是EHCI(1) 接口体系printf(
"Theadd_portis:%X ",add_port);
Linux那些事儿之我是EHCI(1) 接口体系printf(
"Thedata_portis:%X ",data_port);
Linux那些事儿之我是EHCI(1) 接口体系
if(SetPortVal(add_port,addr,size))
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系
if(GetPortVal(data_port,&port_value,size))
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系printf(
"portvalueis:%08X ",port_value);
Linux那些事儿之我是EHCI(1) 接口体系
return0;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系
return-1;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系

打印出来的内容与(1)用lspci -xxx 命令输出;(2)EHCI spec 2.1章的内容对照一下。

好了,现在问题是我们怎么知道PCI总线上EHCI的地址add。lspci可以看到所有PCI设备的地址。首先,EHCI不管有没有驱动,它这个PCI设备在PCI总线枚举时就被探测到了,这时候它就被分配了地址。每个PCI 外设有一个总线号, 一个设备号, 一个功能号标识号。比如00:1a:7,00总线号,1a设备号,7功能号。这些个号组成了独一无二的ID。ID和地址的转换关系是这样的:

Linux那些事儿之我是EHCI(1) 接口体系

我们只要ID,就知道了外设的地址,然后就可以读写PCI寄存器的内容。另外可以看看

pci_read(),pci_write() //arch/i386/pci/common.c

的内容,这样会有更深的理解。

2)regster space.

这是基于内的i/o寄存器,就是i/o内存。这里包含了Capability Registers和Operational Registers。我们可以读取/proc/iomem 看看io内存的分配情况。我们可以看到ehci的地址是fe226400-fe2267ff。这段内存不可以直接读写,先要调用ioremap(或是ioremap_nocache)影射成虚拟地址后再使用。

我写了一段程序。大家可以试验一下。

#include <asm/io.h>
Linux那些事儿之我是EHCI(1) 接口体系staticinthello_init(void)
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
...{
Linux那些事儿之我是EHCI(1) 接口体系unsigned
longport_value,mem_value;
Linux那些事儿之我是EHCI(1) 接口体系
void__iomem*add;
Linux那些事儿之我是EHCI(1) 接口体系
inti;
Linux那些事儿之我是EHCI(1) 接口体系printk(KERN_ALERT
"Hello,world ");
Linux那些事儿之我是EHCI(1) 接口体系add=ioremap(0xfe226400,0x400);
Linux那些事儿之我是EHCI(1) 接口体系Linux那些事儿之我是EHCI(1) 接口体系
for(i=0;i<100;i++)...{
Linux那些事儿之我是EHCI(1) 接口体系mem_value
=ioread32(add+i*4);
Linux那些事儿之我是EHCI(1) 接口体系printk(
"%08Xmemvalueis:%08X ",add+i*4,mem_value);
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系iounmap(add);Linux那些事儿之我是EHCI(1) 接口体系
Linux那些事儿之我是EHCI(1) 接口体系
return0;
Linux那些事儿之我是EHCI(1) 接口体系}

Linux那些事儿之我是EHCI(1) 接口体系

以上是基于ldd3中那个最简单的模块hello.ko改的。主要是为了可以在内核空间运行。大家可以把打印出来的内容与ehci spec 2.2对照一下。

3)Schedule Interface Space.

这里就是普通的内存。我们直接就可以访问它。