SDIO接口WIFI驱动之四:CIS获取解析-以RTL8189FTV为例

SDIO接口WIFI驱动之四:CIS获取解析-以RTL8189FTV为例-电子工程专辑

 

一.前言

 

前面介绍SDIO寄存器空间时看到CIA(Function0)空间中有一部分是CIS,这一篇就来分享CIS这部分空间。从CIS寻址到结构解析两步来进行。

 

 

参考:[REF1] SD Specifications Part E1 SDIO Simplified Specification Version 3.00 July 25, 2018

 

 

二.CIS寻址

 

[REF1]的章节6.7 SDIO Fixed Internal Map的如下图中可以看到, 128KB的CIA(Function0)寄存器区域(即Common I/O Area),划分了0x001000~0x017FFF,92KB给CIS。同时CCCR和FBR中出发有一些箭头指向了CIS区域,这里的箭头的意思是CCCR和FBR中的寄存器值确定CIS的位置。

 

 

CIS即Card Information Structure卡信息结构,提供关于卡和各个Function的更完整的信息。

 

 

所有Function共享一个common的CIS,同时每个Function1-7还有自己独立的CIS。如上图所示,所有CIS的位置都在0x001000~0x017FFF内,但是每个CIS的基地址则由CCCR和FBRn中的寄存器来确定。CCCR中的寄存器确定common的CIS的位置,FBRn中的寄存器来确定Function1-7对应的CIS的位置。

 

 

[REF1]的Table 6-1 : Card Common Control Registers (CCCR)可以看到common CIS的位置由0x09-0x0B位置的寄存器的值确定

 

 

[REF1]的Table 6-3 : Function Basic Information Registers (FBR)可以看到,Function1-7对应的CIS的位置由FBR偏移0x09~0x0B位置的寄存器的值确定,FBR本身的偏移地址是n*0x100(n为Function号)。

 

 

所以可以统一为Function n对应的CIS位置,由寄存器n*0x100+0x09~n*0x100+0x0B的值决定(3个寄存器,24位地址,小端)。

 

 

其中Function 0对应的CIS即Common CIS。

 

 

所以需要实现一个根据Function号,查找CIS位置的函数。

 

 

其中sdio_io_rw_direct是CMD52单寄存器读写的实现。

int sdio_get_cis_addr(sdio_host_common_st* host, uint8_t fn, uint32_t* addr){    uint32_t tmp;    uint32_t regaddr;    uint32_t getaddr = 0;    regaddr = ((uint32_t)fn<<8)+0x09;    tmp = 0;    if(0 == sdio_io_rw_direct(host,0, 0, 0, regaddr, &tmp))    {        getaddr = (uint32_t)(tmp & 0xFF);        regaddr += 1;        if(0 == sdio_io_rw_direct(host,0, 0, 0, regaddr, &tmp))        {            getaddr |= (uint32_t)(tmp & 0xFF)<<8;            regaddr += 1;            if(0 == sdio_io_rw_direct(host,0, 0, 0, regaddr, &tmp))            {                getaddr |= (uint32_t)(tmp & 0xFF)<<16;                *addr = getaddr;                return 0;            }            else            {                return -3;            }        }        else        {            return -2;        }    }    else    {        return -1;    }}

测试获取CIS地址

 

 

for(int i=0; i<=host->fn; i++)    {        uint32_t cisaddr;        if(0 == (res = sdio_get_cis_addr(host,i,&cisaddr)))        {            SDIO_WIFI_INFO_LOG(("[CIS:%01d]0x%05x\r\n", i, cisaddr));        }        else        {            SDIO_WIFI_ERR_LOG(("get function %d cis addr err\n", i, res));        }    }

 

打印如下

 

 

[CIS:0]0x01000

 

 

[CIS:1]0x01100

 

 

都在范围0x001000~0x017FFF内,且common CIS一般都是从偏移0开始,后面接着其他Function的CIS。

 

 

三.CIS结构

 

参考[REF1]的6.11 Card Information Structure (CIS)章节。

 

 

CIS结构基于 PCMCIA的标准,见《P C C A R D  S TA N D A R D  Volume 4 Metaformat Specification》。

 

 

参考[REF1]的16. CIS Formats章节,详细介绍了SDIO中支持的结构。

 

 

CIS以块(或者元组Tuple)为单位存储信息,元组依次存放。

 

 

每个元组都以以下形式组织,

 

 

一个字节的TPL_CODE,用于表示该元组的类型,如果TPL_CODE=CISTPL_END 0xFF表示结束,后续没有元组了,且本元组也只有CODE0xFF,无需后面的TPL_LINK和数据,TPL_CODE为CISTPL_NULL=0x00时表示本元组为空,元组无后面的TPL_LINK和数据,但是后面还有其他元组。

 

 

一个字节的TPL_LINK,用于表示下一个元组的偏移,TPL_LINK可以为0表示没有数据,如果为0xFF也表示本元组是最后一个元组。所以有两种方式表示元组结束,一个是TPL_CODE=0xFF,一个是TPL_LINK=0xFF,建议使用前者。元组最大长度就是1+1+255=257字节,即255字节数据的元组也表示元组的结束。

 

 

n个字节的数据, 理论上TPL_LINK可以大于n,这样n个数据之后有间隙,再继续放下一个元组,但是一般TPL_LINK就等于n,即每个元组依次放不留间隙。当然TPL_LINK不能小于n。

 

 

多字节数据按照小端模式,固定长字符串以NULL填充。

 

 

3.1TPL_CODE

 

SDIO支持以下CODE,O表示可选实现,R表示建议实现,M表示强制实现,n/a表示无需实现。

 

 

可以看到对于common只有MANFID和END是强制要实现的,

 

 

对于其他Function1-7只有FUNCID,FUNCE,END是强制要实现的。

 

 

对于申明支持SDIO接口的强制要实现SDIO_STD。

3.2CISTPL_MANFID

 

MANFID是common必须实现,function可选实现的。

 

 

4字节数据,

 

 

低2字节为Card manufacturer code,对于有JEDEC ID的可以使用JEDEC厂商编码的低8位,高8位为0.

 

 

高2字节为manufacturer information,厂商自定义可以保存标志和修订信息等。

 

 

3.3CISTPL_FUNCID

 

对于Function是强制的,common不是强制的。

 

 

为了识别SDIO卡,CISTPL_FUNCID元组应存在于所有CIS区域中。Common和每个function中都应有一个。格式如下

 

 

TPLFID_FUNCTION Card function code (0Ch)用于表示是SDIO卡,

 

 

该元组后面接其他的Function Extension Tuple

 

 

3.4CISTPL_FUNCE

 

Function Extension Tuple对于common和function不一样,function又分两种格式。

 

 

通过02h位置来决定是哪一种,02h处为0x00则为common,为0x01则为function的格式1,为0x02则为function的格式2.

 

 

3.4.1Function 0格式如下

 

3.4.2Function1-7格式1如下

 

 

 

3.4.3Function1-7格式2如下

 

3.5CISTPL_SDIO_STD

 

支持SDIO的才有

 

 

3.6CISTPL_SDIO_EXT

 

保留供SDIO卡将来使用,V3.00暂时未定义具体格式。

 

 

3.7 实现一个scan扫描解析CIS的函数。

 

static int sdio_scan_cis(sdio_host_common_st* host, uint32_t addr){    uint32_t regaddr = addr;    uint32_t tmp;    uint8_t code;    uint8_t link;    while(1)    {        tmp = 0;        if(0 == sdio_io_rw_drirect(host,0, 0, 0, regaddr, &tmp))        {            /* 读CODE */            code = (tmp&0xFF);            if(code == 0xFF)            {                return 0;   /* CODE=0xFF表示结束 */            }            if(code == 0x00)            {                regaddr++;                continue;  /* CODE=0x00表示NULL,空元组,继续 */            }            SDIO_WIFI_INFO_LOG(("[CODE]:0x%02x\n",code));            /* 读LINK */            tmp = 0;            regaddr++;            if(0 == sdio_io_rw_drirect(host,0, 0, 0, regaddr, &tmp))            {                link = (tmp&0xFF);                SDIO_WIFI_INFO_LOG(("[LINK]:0x%02x\n",link));                if(link == 0x00)                {                    regaddr++;                    continue;  /* LINK=0 无数据 */                }            }            else            {                return -2;            }            /* 读DATA */            SDIO_WIFI_INFO_LOG(("[DATA]:\r\n"));            for(uint8_t i=0;i<link;i++)< span="">            {                tmp = 0;                regaddr++;                if((i%16==0) && (i!=0))                {                    SDIO_WIFI_INFO_LOG(("\r\n"));                }                if(0 == sdio_io_rw_drirect(host,0, 0, 0, regaddr, &tmp))                {                    SDIO_WIFI_INFO_LOG(("%02x ",tmp&0xFF));                }                else                {                    return -3;                }            } regaddr++;            SDIO_WIFI_INFO_LOG(("\r\n"));        }        else        {            return -1;        }    }}

    /* 解析CIS信息 */    for(int i=0; i<=host->fn; i++)    {        uint32_t cisaddr;        if(0 == (res = sdio_get_cis_addr(host,i,&cisaddr)))        {            SDIO_WIFI_INFO_LOG(("[CIS:%01d]0x%05x\r\n", i, cisaddr));            sdio_scan_cis(host, cisaddr);        }        else        {            SDIO_WIFI_ERR_LOG(("get function %d cis addr err\n", i, res));        }    }

打印如下

 

 

[CIS:0]0x01000

 

 

[CODE]:0x20

 

 

[LINK]:0x04

 

 

[DATA]:

 

 

4c 02 79 f1

 

 

[CODE]:0x21

 

 

[LINK]:0x02

 

 

[DATA]:

 

 

0c 00

 

 

[CODE]:0x22

 

 

[LINK]:0x04

 

 

[DATA]:

 

 

00 08 00 32

 

 

[CIS:1]0x01100

 

 

[CODE]:0x21

 

 

[LINK]:0x02

 

 

[DATA]:

 

 

0c 00

 

 

[CODE]:0x22

 

 

[LINK]:0x2a

 

 

[DATA]:

 

 

01 01 00 00 00 00 00 00 00 00 00 00 00 02 00 ff

 

 

ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

 

 

00 00 eb 00 6e 01 00 00 00 00

 

 

其中

 

 

Function0的CIS有三个元组

 

 

[CODE]:0x20

 

 

[LINK]:0x04

 

 

[DATA]:

 

 

4c 02 79 f1

 

 

Card manufacturer code和manufacturer information

 

 

分别是0x024C,0xF179

 

 

从官方linux驱动代码可以看到,对应的是RTL8188F,实际我这里是RTL8189FTV,属于一个系列。

 

 

[CODE]:0x21

 

 

[LINK]:0x02

 

 

[DATA]:

 

 

0c 00

 

 

CISTPL_FUNCID为0x000C表示是SDIO卡

 

 

0c 00

 

 

[CODE]:0x22

 

 

[LINK]:0x04

 

 

[DATA]:

 

 

00 08 00 32

 

 

CISTPL_FUNCE Function0

 

 

Blocksize为0x0008

 

 

Speed为0x32

 

 

Function1的CIS有两个元组

 

 

[CODE]:0x21

 

 

[LINK]:0x02

 

 

[DATA]:

 

 

0c 00

 

 

CISTPL_FUNCID为0x000C表示是SDIO卡

 

 

0c 00

 

 

[CODE]:0x22

 

 

[LINK]:0x2a

 

 

[DATA]:

 

 

01 01 00 00 00 00 00 00 00 00 00 00 00 02 00 ff

 

 

ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

 

 

00 00 eb 00 6e 01 00 00 00 00

 

 

CISTPL_FUNCE,Function1  01表示是格式1的CISTPL_FUNCE

 

 

Blocksize为0x0200

 

 

四.总结

 

以上介绍了CIS的寻址与结构解析,并实现获取地址和解析的函数,以RTL8189FTV为例获取了其CIS信息,具体含义可以根据规格书去看。

 

posted @ 2025-06-27 16:18  tianxincode  阅读(24)  评论(0)    收藏  举报