1 /* $OpenBSD: sdmmc_cis.c,v 1.2 2009/10/03 18:42:36 kettenis 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/device.h> 23 #include <sys/systm.h> 24 25 #include <dev/sdmmc/sdmmc_ioreg.h> 26 #include <dev/sdmmc/sdmmcdevs.h> 27 #include <dev/sdmmc/sdmmcvar.h> 28 29 u_int32_t sdmmc_cisptr(struct sdmmc_function *); 30 31 #ifdef SDMMC_DEBUG 32 #define DPRINTF(s) printf s 33 #else 34 #define DPRINTF(s) /**/ 35 #endif 36 37 u_int32_t 38 sdmmc_cisptr(struct sdmmc_function *sf) 39 { 40 u_int32_t cisptr = 0; 41 42 /* XXX where is the per-function CIS pointer register? */ 43 if (sf->number != 0) 44 return SD_IO_CIS_START; 45 46 /* XXX is the CIS pointer stored in little-endian format? */ 47 cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+0) << 0; 48 cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+1) << 8; 49 cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+2) << 16; 50 return cisptr; 51 } 52 53 int 54 sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis) 55 { 56 int reg; 57 u_int8_t tplcode; 58 u_int8_t tpllen; 59 60 bzero(cis, sizeof *cis); 61 62 /* XXX read per-function CIS */ 63 if (sf->number != 0) 64 return 1; 65 66 reg = (int)sdmmc_cisptr(sf); 67 if (reg < SD_IO_CIS_START || 68 reg >= (SD_IO_CIS_START+SD_IO_CIS_SIZE-16)) { 69 printf("%s: bad CIS ptr %#x\n", SDMMCDEVNAME(sf->sc), reg); 70 return 1; 71 } 72 73 for (;;) { 74 tplcode = sdmmc_io_read_1(sf, reg++); 75 tpllen = sdmmc_io_read_1(sf, reg++); 76 77 if (tplcode == 0xff || tpllen == 0) { 78 if (tplcode != 0xff) 79 printf("%s: CIS parse error at %d, " 80 "tuple code %#x, length %d\n", 81 SDMMCDEVNAME(sf->sc), reg, tplcode, tpllen); 82 break; 83 } 84 85 switch (tplcode) { 86 case SD_IO_CISTPL_FUNCID: 87 if (tpllen < 2) { 88 printf("%s: bad CISTPL_FUNCID length\n", 89 SDMMCDEVNAME(sf->sc)); 90 reg += tpllen; 91 break; 92 } 93 cis->function = sdmmc_io_read_1(sf, reg); 94 reg += tpllen; 95 break; 96 case SD_IO_CISTPL_MANFID: 97 if (tpllen < 4) { 98 printf("%s: bad CISTPL_MANFID length\n", 99 SDMMCDEVNAME(sf->sc)); 100 reg += tpllen; 101 break; 102 } 103 cis->manufacturer = sdmmc_io_read_1(sf, reg++); 104 cis->manufacturer |= sdmmc_io_read_1(sf, reg++) << 8; 105 cis->product = sdmmc_io_read_1(sf, reg++); 106 cis->product |= sdmmc_io_read_1(sf, reg++) << 8; 107 break; 108 case SD_IO_CISTPL_VERS_1: 109 if (tpllen < 2) { 110 printf("%s: CISTPL_VERS_1 too short\n", 111 SDMMCDEVNAME(sf->sc)); 112 reg += tpllen; 113 break; 114 } 115 { 116 int start, i, ch, count; 117 118 cis->cis1_major = sdmmc_io_read_1(sf, reg++); 119 cis->cis1_minor = sdmmc_io_read_1(sf, reg++); 120 121 for (count = 0, start = 0, i = 0; 122 (count < 4) && ((i + 4) < 256); i++) { 123 ch = sdmmc_io_read_1(sf, reg + i); 124 if (ch == 0xff) 125 break; 126 cis->cis1_info_buf[i] = ch; 127 if (ch == 0) { 128 cis->cis1_info[count] = 129 cis->cis1_info_buf + start; 130 start = i + 1; 131 count++; 132 } 133 } 134 135 reg += tpllen - 2; 136 } 137 break; 138 default: 139 DPRINTF(("%s: unknown tuple code %#x, length %d\n", 140 SDMMCDEVNAME(sf->sc), tplcode, tpllen)); 141 reg += tpllen; 142 break; 143 } 144 } 145 return 0; 146 } 147 148 void 149 sdmmc_print_cis(struct sdmmc_function *sf) 150 { 151 struct sdmmc_cis *cis = &sf->cis; 152 int i; 153 154 printf("%s: CIS version %d.%d\n", SDMMCDEVNAME(sf->sc), 155 cis->cis1_major, cis->cis1_minor); 156 157 printf("%s: CIS info: ", SDMMCDEVNAME(sf->sc)); 158 for (i = 0; i < 4; i++) { 159 if (cis->cis1_info[i] == NULL) 160 break; 161 if (i) 162 printf(", "); 163 printf("%s", cis->cis1_info[i]); 164 } 165 printf("\n"); 166 167 printf("%s: Manufacturer code 0x%x, product 0x%x\n", 168 SDMMCDEVNAME(sf->sc), cis->manufacturer, cis->product); 169 170 printf("%s: function %d: ", SDMMCDEVNAME(sf->sc), sf->number); 171 switch (sf->cis.function) { 172 case SDMMC_FUNCTION_WLAN: 173 printf("wireless network adapter"); 174 break; 175 default: 176 printf("unknown (%d)", sf->cis.function); 177 break; 178 } 179 printf("\n"); 180 } 181 182 void 183 sdmmc_check_cis_quirks(struct sdmmc_function *sf) 184 { 185 if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC && 186 sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) { 187 /* This card lacks the VERS_1 tuple. */ 188 sf->cis.cis1_major = 0x01; 189 sf->cis.cis1_minor = 0x00; 190 sf->cis.cis1_info[0] = "Spectec"; 191 sf->cis.cis1_info[1] = "SDIO WLAN Card"; 192 sf->cis.cis1_info[2] = "SDW-820"; 193 sf->cis.cis1_info[3] = ""; 194 } 195 } 196