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