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

文摘   2024-08-22 22:57   湖南  

.前言

前面介绍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的如下图中可以看到, 128KBCIA(Function0)寄存器区域(Common I/O Area),划分了0x001000~0x017FFF,92KBCIS。同时CCCRFBR中出发有一些箭头指向了CIS区域,这里的箭头的意思是CCCRFBR中的寄存器值确定CIS的位置。

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

所有Function共享一个commonCIS,同时每个Function1-7还有自己独立的CIS。如上图所示,所有CIS的位置都在0x001000~0x017FFF内,但是每个CIS的基地址则由CCCRFBRn中的寄存器来确定。CCCR中的寄存器确定commonCIS的位置,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(nFunction)

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

其中Function 0对应的CISCommon CIS

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

其中sdio_io_rw_directCMD52单寄存器读写的实现。

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开始,后面接着其他FunctionCIS

.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_CODECISTPL_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支持以下CODEO表示可选实现,R表示建议实现,M表示强制实现,n/a表示无需实现。

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

对于其他Function1-7只有FUNCIDFUNCEEND是强制要实现的。

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

3.2CISTPL_MANFID

MANFIDcommon必须实现,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对于commonfunction不一样,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++)            {                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

其中

Function0CIS有三个元组

[CODE]:0x20

[LINK]:0x04

[DATA]:

4c 02 79 f1

Card manufacturer codemanufacturer information

分别是0x024C0xF179

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

[CODE]:0x21

[LINK]:0x02

[DATA]:

0c 00

CISTPL_FUNCID0x000C表示是SDIO

0c 00

[CODE]:0x22

[LINK]:0x04

[DATA]:

00 08 00 32

CISTPL_FUNCE Function0

Blocksize0x0008

Speed0x32

Function1CIS有两个元组

[CODE]:0x21

[LINK]:0x02

[DATA]:

0c 00

CISTPL_FUNCID0x000C表示是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_FUNCEFunction1  01表示是格式1CISTPL_FUNCE

Blocksize0x0200

.总结

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













嵌入式Lee
嵌入式软硬件技术:RTOS,GUI,FS,协议栈,ARM,总线,嵌入式C,开发环境 and blablaba....多年经验分享,非硬货不发,带你扒开每一个技术背后的根本原理。
 最新文章