1 /* $OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Routines to decode the Card Information Structure of SD I/O cards */ 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 24 #include <dev/sdmmc/sdmmc_ioreg.h> 25 #include <dev/sdmmc/sdmmcdevs.h> 26 #include <dev/sdmmc/sdmmcvar.h> 27 28 u_int32_t sdmmc_cisptr(struct sdmmc_function *); 29 30 #ifdef SDMMC_DEBUG 31 #define DPRINTF(s) printf s 32 #else 33 #define DPRINTF(s) /**/ 34 #endif 35 36 u_int32_t 37 sdmmc_cisptr(struct sdmmc_function *sf) 38 { 39 u_int32_t cisptr = 0; 40 41 /* XXX where is the per-function CIS pointer register? */ 42 if (sf->number != 0) 43 return SD_IO_CIS_START; 44 45 /* XXX is the CIS pointer stored in little-endian format? */ 46 cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+0) << 0; 47 cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+1) << 8; 48 cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+2) << 16; 49 return cisptr; 50 } 51 52 int 53 sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis) 54 { 55 int reg; 56 u_int8_t tplcode; 57 u_int8_t tpllen; 58 59 bzero(cis, sizeof *cis); 60 61 /* XXX read per-function CIS */ 62 if (sf->number != 0) 63 return 1; 64 65 reg = (int)sdmmc_cisptr(sf); 66 if (reg < SD_IO_CIS_START || 67 reg >= (SD_IO_CIS_START+SD_IO_CIS_SIZE-16)) { 68 printf("%s: bad CIS ptr %#x\n", SDMMCDEVNAME(sf->sc), reg); 69 return 1; 70 } 71 72 for (;;) { 73 tplcode = sdmmc_io_read_1(sf, reg++); 74 tpllen = sdmmc_io_read_1(sf, reg++); 75 76 if (tplcode == 0xff || tpllen == 0) { 77 if (tplcode != 0xff) 78 printf("%s: CIS parse error at %d, " 79 "tuple code %#x, length %d\n", 80 SDMMCDEVNAME(sf->sc), reg, tplcode, tpllen); 81 break; 82 } 83 84 switch (tplcode) { 85 case SD_IO_CISTPL_FUNCID: 86 if (tpllen < 2) { 87 printf("%s: bad CISTPL_FUNCID length\n", 88 SDMMCDEVNAME(sf->sc)); 89 reg += tpllen; 90 break; 91 } 92 cis->function = sdmmc_io_read_1(sf, reg); 93 reg += tpllen; 94 break; 95 case SD_IO_CISTPL_MANFID: 96 if (tpllen < 4) { 97 printf("%s: bad CISTPL_MANFID length\n", 98 SDMMCDEVNAME(sf->sc)); 99 reg += tpllen; 100 break; 101 } 102 cis->manufacturer = sdmmc_io_read_1(sf, reg++); 103 cis->manufacturer |= sdmmc_io_read_1(sf, reg++) << 8; 104 cis->product = sdmmc_io_read_1(sf, reg++); 105 cis->product |= sdmmc_io_read_1(sf, reg++) << 8; 106 break; 107 case SD_IO_CISTPL_VERS_1: 108 if (tpllen < 2) { 109 printf("%s: CISTPL_VERS_1 too short\n", 110 SDMMCDEVNAME(sf->sc)); 111 reg += tpllen; 112 break; 113 } 114 { 115 int start, i, ch, count; 116 117 cis->cis1_major = sdmmc_io_read_1(sf, reg++); 118 cis->cis1_minor = sdmmc_io_read_1(sf, reg++); 119 120 for (count = 0, start = 0, i = 0; 121 (count < 4) && ((i + 4) < 256); i++) { 122 ch = sdmmc_io_read_1(sf, reg + i); 123 if (ch == 0xff) 124 break; 125 cis->cis1_info_buf[i] = ch; 126 if (ch == 0) { 127 cis->cis1_info[count] = 128 cis->cis1_info_buf + start; 129 start = i + 1; 130 count++; 131 } 132 } 133 134 reg += tpllen - 2; 135 } 136 break; 137 default: 138 DPRINTF(("%s: unknown tuple code %#x, length %d\n", 139 SDMMCDEVNAME(sf->sc), tplcode, tpllen)); 140 reg += tpllen; 141 break; 142 } 143 } 144 return 0; 145 } 146 147 void 148 sdmmc_print_cis(struct sdmmc_function *sf) 149 { 150 struct sdmmc_cis *cis = &sf->cis; 151 int i; 152 153 printf("%s: CIS version %d.%d\n", SDMMCDEVNAME(sf->sc), 154 cis->cis1_major, cis->cis1_minor); 155 156 printf("%s: CIS info: ", SDMMCDEVNAME(sf->sc)); 157 for (i = 0; i < 4; i++) { 158 if (cis->cis1_info[i] == NULL) 159 break; 160 if (i) 161 printf(", "); 162 printf("%s", cis->cis1_info[i]); 163 } 164 printf("\n"); 165 166 printf("%s: Manufacturer code 0x%x, product 0x%x\n", 167 SDMMCDEVNAME(sf->sc), cis->manufacturer, cis->product); 168 169 printf("%s: function %d: ", SDMMCDEVNAME(sf->sc), sf->number); 170 switch (sf->cis.function) { 171 case SDMMC_FUNCTION_WLAN: 172 printf("wireless network adapter"); 173 break; 174 default: 175 printf("unknown (%d)", sf->cis.function); 176 break; 177 } 178 printf("\n"); 179 } 180 181 void 182 sdmmc_check_cis_quirks(struct sdmmc_function *sf) 183 { 184 if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC && 185 sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) { 186 /* This card lacks the VERS_1 tuple. */ 187 sf->cis.cis1_major = 0x01; 188 sf->cis.cis1_minor = 0x00; 189 sf->cis.cis1_info[0] = "Spectec"; 190 sf->cis.cis1_info[1] = "SDIO WLAN Card"; 191 sf->cis.cis1_info[2] = "SDW-820"; 192 sf->cis.cis1_info[3] = ""; 193 } 194 } 195