xref: /openbsd-src/sys/dev/sdmmc/sdmmc_cis.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
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