前面介紹SDIO寄存器空間時看到CIA(Function0)空間中有一部分是CIS,這一篇就來分享CIS這部分空間。從CIS尋址到結構解析兩步來進行。
參考:[REF1] SD Specifications Part E1 SDIO Simplified Specification Version 3.00 July 25, 2018
[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。
參考[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填充。

SDIO支持以下CODE,O表示可選實現,R表示建議實現,M表示強制實現,n/a表示無需實現。
可以看到對于common只有MANFID和END是強制要實現的,
對于其他Function1-7只有FUNCID,FUNCE,END是強制要實現的。
對于申明支持SDIO接口的強制要實現SDIO_STD。

MANFID是common必須實現,function可選實現的。
4字節數據,
低2字節為Card manufacturer code,對于有JEDEC ID的可以使用JEDEC廠商編碼的低8位,高8位為0.
高2字節為manufacturer information,廠商自定義可以保存標志和修訂信息等。

對于Function是強制的,common不是強制的。
為了識別SDIO卡,CISTPL_FUNCID元組應存在于所有CIS區域中。Common和每個function中都應有一個。格式如下

TPLFID_FUNCTION Card function code (0Ch)用于表示是SDIO卡,
該元組后面接其他的Function Extension Tuple
Function Extension Tuple對于common和function不一樣,function又分兩種格式。
通過02h位置來決定是哪一種,02h處為0x00則為common,為0x01則為function的格式1,為0x02則為function的格式2.









支持SDIO的才有


保留供SDIO卡將來使用,V3.00暫時未定義具體格式。

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? ? ? ? ? ? {? ? ? ? ? ? ? ? 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信息,具體含義可以根據規格書去看。