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定义了三个接口空间。如图

作为一个程序员,我们关心的是如何在代码中读/写这些地方的内容。概念性的东西肯定是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的内容

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

/*  name: pci.c   */
#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)影射成虚拟地址后再使用。

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

    #include <asm/io.h>
static   int  hello_init( void )
{
        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.

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值