diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/sdio_cis.c | 65 |
1 files changed, 49 insertions, 16 deletions
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index 6636354b48ce..e1035c895808 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c | |||
@@ -98,6 +98,22 @@ static const unsigned char speed_val[16] = | |||
98 | static const unsigned int speed_unit[8] = | 98 | static const unsigned int speed_unit[8] = |
99 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; | 99 | { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; |
100 | 100 | ||
101 | /* FUNCE tuples with these types get passed to SDIO drivers */ | ||
102 | static const unsigned char funce_type_whitelist[] = { | ||
103 | 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */ | ||
104 | }; | ||
105 | |||
106 | static int cistpl_funce_whitelisted(unsigned char type) | ||
107 | { | ||
108 | int i; | ||
109 | |||
110 | for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) { | ||
111 | if (funce_type_whitelist[i] == type) | ||
112 | return 1; | ||
113 | } | ||
114 | return 0; | ||
115 | } | ||
116 | |||
101 | static int cistpl_funce_common(struct mmc_card *card, | 117 | static int cistpl_funce_common(struct mmc_card *card, |
102 | const unsigned char *buf, unsigned size) | 118 | const unsigned char *buf, unsigned size) |
103 | { | 119 | { |
@@ -120,6 +136,10 @@ static int cistpl_funce_func(struct sdio_func *func, | |||
120 | unsigned vsn; | 136 | unsigned vsn; |
121 | unsigned min_size; | 137 | unsigned min_size; |
122 | 138 | ||
139 | /* let SDIO drivers take care of whitelisted FUNCE tuples */ | ||
140 | if (cistpl_funce_whitelisted(buf[0])) | ||
141 | return -EILSEQ; | ||
142 | |||
123 | vsn = func->card->cccr.sdio_vsn; | 143 | vsn = func->card->cccr.sdio_vsn; |
124 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; | 144 | min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; |
125 | 145 | ||
@@ -154,13 +174,12 @@ static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, | |||
154 | else | 174 | else |
155 | ret = cistpl_funce_common(card, buf, size); | 175 | ret = cistpl_funce_common(card, buf, size); |
156 | 176 | ||
157 | if (ret) { | 177 | if (ret && ret != -EILSEQ) { |
158 | printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " | 178 | printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " |
159 | "type %u\n", mmc_hostname(card->host), size, buf[0]); | 179 | "type %u\n", mmc_hostname(card->host), size, buf[0]); |
160 | return ret; | ||
161 | } | 180 | } |
162 | 181 | ||
163 | return 0; | 182 | return ret; |
164 | } | 183 | } |
165 | 184 | ||
166 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, | 185 | typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, |
@@ -253,21 +272,12 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) | |||
253 | for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) | 272 | for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) |
254 | if (cis_tpl_list[i].code == tpl_code) | 273 | if (cis_tpl_list[i].code == tpl_code) |
255 | break; | 274 | break; |
256 | if (i >= ARRAY_SIZE(cis_tpl_list)) { | 275 | if (i < ARRAY_SIZE(cis_tpl_list)) { |
257 | /* this tuple is unknown to the core */ | ||
258 | this->next = NULL; | ||
259 | this->code = tpl_code; | ||
260 | this->size = tpl_link; | ||
261 | *prev = this; | ||
262 | prev = &this->next; | ||
263 | printk(KERN_DEBUG | ||
264 | "%s: queuing CIS tuple 0x%02x length %u\n", | ||
265 | mmc_hostname(card->host), tpl_code, tpl_link); | ||
266 | } else { | ||
267 | const struct cis_tpl *tpl = cis_tpl_list + i; | 276 | const struct cis_tpl *tpl = cis_tpl_list + i; |
268 | if (tpl_link < tpl->min_size) { | 277 | if (tpl_link < tpl->min_size) { |
269 | printk(KERN_ERR | 278 | printk(KERN_ERR |
270 | "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", | 279 | "%s: bad CIS tuple 0x%02x" |
280 | " (length = %u, expected >= %u)\n", | ||
271 | mmc_hostname(card->host), | 281 | mmc_hostname(card->host), |
272 | tpl_code, tpl_link, tpl->min_size); | 282 | tpl_code, tpl_link, tpl->min_size); |
273 | ret = -EINVAL; | 283 | ret = -EINVAL; |
@@ -275,7 +285,30 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) | |||
275 | ret = tpl->parse(card, func, | 285 | ret = tpl->parse(card, func, |
276 | this->data, tpl_link); | 286 | this->data, tpl_link); |
277 | } | 287 | } |
278 | kfree(this); | 288 | /* |
289 | * We don't need the tuple anymore if it was | ||
290 | * successfully parsed by the SDIO core or if it is | ||
291 | * not going to be parsed by SDIO drivers. | ||
292 | */ | ||
293 | if (!ret || ret != -EILSEQ) | ||
294 | kfree(this); | ||
295 | } else { | ||
296 | /* unknown tuple */ | ||
297 | ret = -EILSEQ; | ||
298 | } | ||
299 | |||
300 | if (ret == -EILSEQ) { | ||
301 | /* this tuple is unknown to the core or whitelisted */ | ||
302 | this->next = NULL; | ||
303 | this->code = tpl_code; | ||
304 | this->size = tpl_link; | ||
305 | *prev = this; | ||
306 | prev = &this->next; | ||
307 | printk(KERN_DEBUG | ||
308 | "%s: queuing CIS tuple 0x%02x length %u\n", | ||
309 | mmc_hostname(card->host), tpl_code, tpl_link); | ||
310 | /* keep on analyzing tuples */ | ||
311 | ret = 0; | ||
279 | } | 312 | } |
280 | 313 | ||
281 | ptr += tpl_link; | 314 | ptr += tpl_link; |