diff options
-rw-r--r-- | drivers/mmc/core/sdio_cis.c | 167 |
1 files changed, 93 insertions, 74 deletions
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index f85dcd536508..9538389783c1 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c | |||
@@ -97,26 +97,56 @@ static const unsigned char speed_val[16] = | |||
97 | static const unsigned int speed_unit[8] = | 97 | static const unsigned int speed_unit[8] = |
98 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; | 98 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; |
99 | 99 | ||
100 | /* FUNCE tuples with these types get passed to SDIO drivers */ | 100 | |
101 | static const unsigned char funce_type_whitelist[] = { | 101 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, |
102 | 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */ | 102 | const unsigned char *, unsigned); |
103 | |||
104 | struct cis_tpl { | ||
105 | unsigned char code; | ||
106 | unsigned char min_size; | ||
107 | tpl_parse_t *parse; | ||
103 | }; | 108 | }; |
104 | 109 | ||
105 | static int cistpl_funce_whitelisted(unsigned char type) | 110 | static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func, |
111 | const char *tpl_descr, | ||
112 | const struct cis_tpl *tpl, int tpl_count, | ||
113 | unsigned char code, | ||
114 | const unsigned char *buf, unsigned size) | ||
106 | { | 115 | { |
107 | int i; | 116 | int i, ret; |
108 | 117 | ||
109 | for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) { | 118 | /* look for a matching code in the table */ |
110 | if (funce_type_whitelist[i] == type) | 119 | for (i = 0; i < tpl_count; i++, tpl++) { |
111 | return 1; | 120 | if (tpl->code == code) |
121 | break; | ||
112 | } | 122 | } |
113 | return 0; | 123 | if (i < tpl_count) { |
124 | if (size >= tpl->min_size) { | ||
125 | if (tpl->parse) | ||
126 | ret = tpl->parse(card, func, buf, size); | ||
127 | else | ||
128 | ret = -EILSEQ; /* known tuple, not parsed */ | ||
129 | } else { | ||
130 | /* invalid tuple */ | ||
131 | ret = -EINVAL; | ||
132 | } | ||
133 | if (ret && ret != -EILSEQ && ret != -ENOENT) { | ||
134 | printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n", | ||
135 | mmc_hostname(card->host), tpl_descr, code, size); | ||
136 | } | ||
137 | } else { | ||
138 | /* unknown tuple */ | ||
139 | ret = -ENOENT; | ||
140 | } | ||
141 | |||
142 | return ret; | ||
114 | } | 143 | } |
115 | 144 | ||
116 | static int cistpl_funce_common(struct mmc_card *card, | 145 | static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func, |
117 | const unsigned char *buf, unsigned size) | 146 | const unsigned char *buf, unsigned size) |
118 | { | 147 | { |
119 | if (size < 0x04 || buf[0] != 0) | 148 | /* Only valid for the common CIS (function 0) */ |
149 | if (func) | ||
120 | return -EINVAL; | 150 | return -EINVAL; |
121 | 151 | ||
122 | /* TPLFE_FN0_BLK_SIZE */ | 152 | /* TPLFE_FN0_BLK_SIZE */ |
@@ -129,20 +159,24 @@ static int cistpl_funce_common(struct mmc_card *card, | |||
129 | return 0; | 159 | return 0; |
130 | } | 160 | } |
131 | 161 | ||
132 | static int cistpl_funce_func(struct sdio_func *func, | 162 | static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func, |
133 | const unsigned char *buf, unsigned size) | 163 | const unsigned char *buf, unsigned size) |
134 | { | 164 | { |
135 | unsigned vsn; | 165 | unsigned vsn; |
136 | unsigned min_size; | 166 | unsigned min_size; |
137 | 167 | ||
138 | /* let SDIO drivers take care of whitelisted FUNCE tuples */ | 168 | /* Only valid for the individual function's CIS (1-7) */ |
139 | if (cistpl_funce_whitelisted(buf[0])) | 169 | if (!func) |
140 | return -EILSEQ; | 170 | return -EINVAL; |
141 | 171 | ||
172 | /* | ||
173 | * This tuple has a different length depending on the SDIO spec | ||
174 | * version. | ||
175 | */ | ||
142 | vsn = func->card->cccr.sdio_vsn; | 176 | vsn = func->card->cccr.sdio_vsn; |
143 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; | 177 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; |
144 | 178 | ||
145 | if (size < min_size || buf[0] != 1) | 179 | if (size < min_size) |
146 | return -EINVAL; | 180 | return -EINVAL; |
147 | 181 | ||
148 | /* TPLFE_MAX_BLK_SIZE */ | 182 | /* TPLFE_MAX_BLK_SIZE */ |
@@ -157,39 +191,32 @@ static int cistpl_funce_func(struct sdio_func *func, | |||
157 | return 0; | 191 | return 0; |
158 | } | 192 | } |
159 | 193 | ||
194 | /* | ||
195 | * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples. | ||
196 | * | ||
197 | * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending | ||
198 | * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO | ||
199 | * TPLFID_FUNCTION is always hardcoded to 0x0C. | ||
200 | */ | ||
201 | static const struct cis_tpl cis_tpl_funce_list[] = { | ||
202 | { 0x00, 4, cistpl_funce_common }, | ||
203 | { 0x01, 0, cistpl_funce_func }, | ||
204 | { 0x04, 1+1+6, /* CISTPL_FUNCE_LAN_NODE_ID */ }, | ||
205 | }; | ||
206 | |||
160 | static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, | 207 | static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, |
161 | const unsigned char *buf, unsigned size) | 208 | const unsigned char *buf, unsigned size) |
162 | { | 209 | { |
163 | int ret; | 210 | if (size < 1) |
164 | 211 | return -EINVAL; | |
165 | /* | ||
166 | * There should be two versions of the CISTPL_FUNCE tuple, | ||
167 | * one for the common CIS (function 0) and a version used by | ||
168 | * the individual function's CIS (1-7). Yet, the later has a | ||
169 | * different length depending on the SDIO spec version. | ||
170 | */ | ||
171 | if (func) | ||
172 | ret = cistpl_funce_func(func, buf, size); | ||
173 | else | ||
174 | ret = cistpl_funce_common(card, buf, size); | ||
175 | |||
176 | if (ret && ret != -EILSEQ) { | ||
177 | printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " | ||
178 | "type %u\n", mmc_hostname(card->host), size, buf[0]); | ||
179 | } | ||
180 | 212 | ||
181 | return ret; | 213 | return cis_tpl_parse(card, func, "CISTPL_FUNCE", |
214 | cis_tpl_funce_list, | ||
215 | ARRAY_SIZE(cis_tpl_funce_list), | ||
216 | buf[0], buf, size); | ||
182 | } | 217 | } |
183 | 218 | ||
184 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, | 219 | /* Known TPL_CODEs table for CIS tuples */ |
185 | const unsigned char *, unsigned); | ||
186 | |||
187 | struct cis_tpl { | ||
188 | unsigned char code; | ||
189 | unsigned char min_size; | ||
190 | tpl_parse_t *parse; | ||
191 | }; | ||
192 | |||
193 | static const struct cis_tpl cis_tpl_list[] = { | 220 | static const struct cis_tpl cis_tpl_list[] = { |
194 | { 0x15, 3, cistpl_vers_1 }, | 221 | { 0x15, 3, cistpl_vers_1 }, |
195 | { 0x20, 4, cistpl_manfid }, | 222 | { 0x20, 4, cistpl_manfid }, |
@@ -268,46 +295,38 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) | |||
268 | break; | 295 | break; |
269 | } | 296 | } |
270 | 297 | ||
271 | for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) | 298 | /* Try to parse the CIS tuple */ |
272 | if (cis_tpl_list[i].code == tpl_code) | 299 | ret = cis_tpl_parse(card, func, "CIS", |
273 | break; | 300 | cis_tpl_list, ARRAY_SIZE(cis_tpl_list), |
274 | if (i < ARRAY_SIZE(cis_tpl_list)) { | 301 | tpl_code, this->data, tpl_link); |
275 | const struct cis_tpl *tpl = cis_tpl_list + i; | 302 | if (ret == -EILSEQ || ret == -ENOENT) { |
276 | if (tpl_link < tpl->min_size) { | ||
277 | printk(KERN_ERR | ||
278 | "%s: bad CIS tuple 0x%02x" | ||
279 | " (length = %u, expected >= %u)\n", | ||
280 | mmc_hostname(card->host), | ||
281 | tpl_code, tpl_link, tpl->min_size); | ||
282 | ret = -EINVAL; | ||
283 | } else if (tpl->parse) { | ||
284 | ret = tpl->parse(card, func, | ||
285 | this->data, tpl_link); | ||
286 | } | ||
287 | /* | 303 | /* |
288 | * We don't need the tuple anymore if it was | 304 | * The tuple is unknown or known but not parsed. |
289 | * successfully parsed by the SDIO core or if it is | 305 | * Queue the tuple for the function driver. |
290 | * not going to be parsed by SDIO drivers. | ||
291 | */ | 306 | */ |
292 | if (!ret || ret != -EILSEQ) | ||
293 | kfree(this); | ||
294 | } else { | ||
295 | /* unknown tuple */ | ||
296 | ret = -EILSEQ; | ||
297 | } | ||
298 | |||
299 | if (ret == -EILSEQ) { | ||
300 | /* this tuple is unknown to the core or whitelisted */ | ||
301 | this->next = NULL; | 307 | this->next = NULL; |
302 | this->code = tpl_code; | 308 | this->code = tpl_code; |
303 | this->size = tpl_link; | 309 | this->size = tpl_link; |
304 | *prev = this; | 310 | *prev = this; |
305 | prev = &this->next; | 311 | prev = &this->next; |
306 | printk(KERN_DEBUG | 312 | |
307 | "%s: queuing CIS tuple 0x%02x length %u\n", | 313 | if (ret == -ENOENT) { |
308 | mmc_hostname(card->host), tpl_code, tpl_link); | 314 | /* warn about unknown tuples */ |
315 | printk(KERN_WARNING "%s: queuing unknown" | ||
316 | " CIS tuple 0x%02x (%u bytes)\n", | ||
317 | mmc_hostname(card->host), | ||
318 | tpl_code, tpl_link); | ||
319 | } | ||
320 | |||
309 | /* keep on analyzing tuples */ | 321 | /* keep on analyzing tuples */ |
310 | ret = 0; | 322 | ret = 0; |
323 | } else { | ||
324 | /* | ||
325 | * We don't need the tuple anymore if it was | ||
326 | * successfully parsed by the SDIO core or if it is | ||
327 | * not going to be queued for a driver. | ||
328 | */ | ||
329 | kfree(this); | ||
311 | } | 330 | } |
312 | 331 | ||
313 | ptr += tpl_link; | 332 | ptr += tpl_link; |