Linux那些事儿之我是EHCI(1) 接口体系
来源:互联网 发布:武汉软件开发公司 编辑:程序博客网 时间:2024/05/06 17:44
EHCI首先是一个PCI设备,我们可以lspci一下看看。
00:1a:7 USB Controller: Intel Corporation USB2 EHCI Controller #1 (rev 03)
我们与外围硬件打交道,可以把数据用in(out)指令传递给外围硬件,还可以把数据传输到cpu和外围硬件共享的内存里面去。这些都是计算机与硬件的接口。(参见ldd3 第9章)
那么我们的程序如何与EHCI联系,交流呢?EHCI定义了三个接口空间。如图
作为一个程序员,我们关心的是如何在代码中读/写这些地方的内容。概念性的东西肯定是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的内容
网上找到了一段程序,大家可以试验一下。
#include <stdio.h>
#include <assert.h>
#include <sys/io.h>
#define IO_PORTS1 1 /* ioport <= 0x3ff */
#define IO_PORTS2 2 /* ioport >0x3ff && ioport < 0xffff */
#define IO_PERMOFF 0
#define IO_PERMON 1
#define IO_PERMON2 3
#define RW_DELAY 10000 /*delay 100000 microseconds for reading and writing I/O ports. */
#ifndef BOOL
typedef unsigned char BOOL;
#endif
#ifndef BYTE
typedef unsigned char BYTE;
#endif
#ifndef DWORD
typedef unsigned long DWORD;
#endif
#ifndef INT
typedef unsigned int INT;
#endif
#ifndef ULONG
typedef unsigned long ULONG;
#endif
#ifndef WORD
typedef unsigned short WORD;
#endif
/*
** Function : Write the value of the specified I/O port by giving the length and the
** starting address.
** Parameter: PortAddr: the port address
** PortVal : the value to set
** size : size = 1 for reading 1 byte, 2 for word, 4 for double words
** Return : 1 returned if success, or 0 returned
*/
BOOL SetPortVal(WORD PortAddr, DWORD PortVal, BYTE size)
{
BOOL Ret = 0;
INT tmpRet = 0;
ULONG numperm = 1;
INT privilege = 0;
assert(PortAddr>0);
if(PortAddr <=0x3ff)
{
tmpRet = ioperm((ULONG)PortAddr, numperm, IO_PERMON);
privilege = IO_PORTS1;
}
else if( PortAddr > 0x3ff)
{
tmpRet = iopl(IO_PERMON2);
privilege = IO_PORTS2;
}
else
return Ret;
if(tmpRet<0)
{
fprintf(stderr, "can't set the io port permission for setting ! ");
return Ret;
}
else
{
switch(size)
{
case 1: /*write one byte to the port */
outb(PortVal, PortAddr);
break;
case 2: /*write one word to the port */
outw(PortVal, PortAddr);
break;
case 4: /*write double words to the port */
outl(PortVal, PortAddr);
break;
default:
Ret = 0;
break;
}
usleep(RW_DELAY);
Ret = 1;
}
if( privilege == IO_PORTS1 )
ioperm((ULONG)PortAddr, numperm, IO_PERMOFF);
else if(privilege == IO_PORTS2 )
iopl(IO_PERMOFF);
return Ret;
}
/*
** Function : Read the value of the specified I/O port by giving the lenght and the
** starting address.
** Parameter: PortAddr : the port address
** PortVal : value from port
** size : size = 1 for reading 1 byte, 2 for word, 4 for double words
** Return : 1 returned if success, or 0 returned.
*/
BOOL GetPortVal(WORD PortAddr, DWORD * PortVal, BYTE size)
{
BOOL Ret = 0;
int tmpRet = 0;
unsigned long numperm = 1;
int privilege = 0;
assert(PortAddr>0);
assert(PortVal!=NULL);
if(PortAddr <=0x3ff)
{
tmpRet = ioperm((unsigned long)PortAddr, numperm, IO_PERMON);
privilege = IO_PORTS1;
}
else if( PortAddr > 0x3ff)
{
tmpRet = iopl(IO_PERMON2);
privilege = IO_PORTS2;
}
else
return Ret;
if(tmpRet<0)
{
fprintf(stderr, "can't set the io port permission for reading ! ");
return Ret;
}
else
{
switch(size)
{
case 1: /*read one byte from the port */
*PortVal = inb(PortAddr);
break;
case 2: /*read one word from the port */
*PortVal = inw(PortAddr);
break;
case 4: /*read double words from the port */
*PortVal = inl(PortAddr);
break;
default:
Ret = 0;
break;
}
usleep(RW_DELAY);
Ret = 1;
}
if( privilege == IO_PORTS1 )
ioperm( (unsigned long)PortAddr, numperm, IO_PERMOFF );
else if( privilege == IO_PORTS2 )
iopl(IO_PERMOFF);
return Ret;
}
int main (int argc, char * argv[])
{
WORD add_port = 0xcf8;
WORD data_port = 0xcfc;
DWORD addr = 0x80000000;
DWORD port_value;
BYTE size = 4;
int input;
printf("Please select the option number as follow: ");
printf("1--bus 0:dev:0 fun:0 as address 0x80000000 ");
printf("2--bus 0:dev:1 fun:0 as address 0x80000800 ");
printf("3--input your own defined address value: ");
scanf("%d",&input);
switch(input)
{
case 1:
addr=0x80000000;
break;
case 2:
addr=0x80000800;
break;
case 3:
printf("please input the 32 bits address in Hex format(such as 80007800): ");
scanf ("%x", &addr);
break;
default:
printf("input invalid option num, exit program. ");
return -1;
}
printf ("The addr is :%X ", addr);
printf ("The add_port is : %X ", add_port);
printf ("The data_port is : %X ", data_port);
if (SetPortVal(add_port, addr, size))
{
if (GetPortVal(data_port, &port_value, size))
{
printf("port value is :%08X ", port_value);
return 0;
}
}
return -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和地址的转换关系是这样的:
我们只要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)影射成虚拟地址后再使用。
我写了一段程序。大家可以试验一下。
{
unsigned long port_value, mem_value;
void __iomem *add;
int i;
printk(KERN_ALERT "Hello, world ");
add = ioremap(0xfe226400, 0x400);
for(i = 0; i < 100; i++){
mem_value = ioread32(add+i*4);
printk("%08X mem value is :%08X ", add+i*4, mem_value);
}
iounmap(add);
return 0;
}
以上是基于ldd3中那个最简单的模块hello.ko改的。主要是为了可以在内核空间运行。大家可以把打印出来的内容与ehci spec 2.2对照一下。
3)Schedule Interface Space.
这里就是普通的内存。我们直接就可以访问它。
- Linux那些事儿之我是EHCI(1) 接口体系
- Linux那些事儿之我是EHCI(1) 接口体系
- Linux那些事儿之我是EHCI(1) 接口体系
- Linux那些事儿之我是EHCI 引子
- Linux那些事儿之我是EHCI 引子
- Linux那些事儿之我是EHCI 引子
- Linux那些事儿之我是EHCI(2) 套路
- Linux那些事儿之我是EHCI(2) 套路
- Linux那些事儿之我是EHCI(2) 套路
- Linux那些事儿之我是EHCI(2) 套路
- Linux那些事儿之我是EHCI(4) data structure of ehci driver and device
- Linux那些事儿之我是EHCI(4) data structure of ehci driver and device
- Linux那些事儿之我是EHCI(4) data structure of ehci driver and device
- Linux那些事儿之我是EHCI(3) pci match 和 probe
- Linux那些事儿之我是EHCI(5) 2008年的这一场雪
- Linux那些事儿之我是EHCI(3) pci match 和 probe
- Linux那些事儿之我是EHCI(5) 2008年的这一场雪
- Linux那些事儿之我是EHCI(3) pci match 和 probe
- Linux那些事儿之我是Block层(11)传说中的内存映射(上)
- 通过俄罗斯方块浅谈游戏中的AI(七)提升
- Linux那些事儿之我是Block层(12)传说中的内存映射(下)
- svn批量增加新文件
- Linux那些事儿之我是EHCI 引子
- Linux那些事儿之我是EHCI(1) 接口体系
- Linux那些事儿之我是EHCI(2) 套路
- Linux那些事儿之我是EHCI(3) pci match 和 probe
- 支付宝接口
- Linux那些事儿之我是EHCI(4) data structure of ehci driver and device
- Linux那些事儿之我是EHCI(5) 2008年的这一场雪
- Linux那些事儿之我是Hub(引子)
- Ogre学习笔记系列-4:异形入侵
- 支付宝接口 有源码下载地址