diff options
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 3 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_cis.c | 83 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_cis.h | 1 | ||||
-rw-r--r-- | include/linux/mmc/sdio_func.h | 12 |
4 files changed, 70 insertions, 29 deletions
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index fa488cea8594..78e0381f55ac 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/mmc/card.h> | 17 | #include <linux/mmc/card.h> |
18 | #include <linux/mmc/sdio_func.h> | 18 | #include <linux/mmc/sdio_func.h> |
19 | 19 | ||
20 | #include "sdio_cis.h" | ||
20 | #include "sdio_bus.h" | 21 | #include "sdio_bus.h" |
21 | 22 | ||
22 | #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) | 23 | #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) |
@@ -94,6 +95,8 @@ static void sdio_release_func(struct device *dev) | |||
94 | { | 95 | { |
95 | struct sdio_func *func = dev_to_sdio_func(dev); | 96 | struct sdio_func *func = dev_to_sdio_func(dev); |
96 | 97 | ||
98 | sdio_free_cis(func); | ||
99 | |||
97 | kfree(func); | 100 | kfree(func); |
98 | } | 101 | } |
99 | 102 | ||
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index 114b600cd788..b6c7342572c1 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c | |||
@@ -5,6 +5,8 @@ | |||
5 | * Created: June 11, 2007 | 5 | * Created: June 11, 2007 |
6 | * Copyright: MontaVista Software Inc. | 6 | * Copyright: MontaVista Software Inc. |
7 | * | 7 | * |
8 | * Copyright 2007 Pierre Ossman | ||
9 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 11 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or (at | 12 | * the Free Software Foundation; either version 2 of the License, or (at |
@@ -49,7 +51,7 @@ static const struct cis_tpl cis_tpl_list[] = { | |||
49 | int sdio_read_cis(struct sdio_func *func) | 51 | int sdio_read_cis(struct sdio_func *func) |
50 | { | 52 | { |
51 | int ret; | 53 | int ret; |
52 | unsigned char *buf; | 54 | struct sdio_func_tuple *this, **prev; |
53 | unsigned i, ptr = 0; | 55 | unsigned i, ptr = 0; |
54 | 56 | ||
55 | for (i = 0; i < 3; i++) { | 57 | for (i = 0; i < 3; i++) { |
@@ -61,13 +63,11 @@ int sdio_read_cis(struct sdio_func *func) | |||
61 | ptr |= x << (i * 8); | 63 | ptr |= x << (i * 8); |
62 | } | 64 | } |
63 | 65 | ||
64 | buf = kmalloc(256, GFP_KERNEL); | 66 | /* find the list tail */ |
65 | if (!buf) | 67 | for (prev = &func->tuples; *prev; prev = &(*prev)->next); |
66 | return -ENOMEM; | ||
67 | 68 | ||
68 | do { | 69 | do { |
69 | unsigned char tpl_code, tpl_link; | 70 | unsigned char tpl_code, tpl_link; |
70 | const struct cis_tpl *tpl; | ||
71 | 71 | ||
72 | ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code); | 72 | ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code); |
73 | if (ret) | 73 | if (ret) |
@@ -81,39 +81,64 @@ int sdio_read_cis(struct sdio_func *func) | |||
81 | if (ret) | 81 | if (ret) |
82 | break; | 82 | break; |
83 | 83 | ||
84 | for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) | 84 | this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); |
85 | if (cis_tpl_list[i].code == tpl_code) | 85 | if (!this) |
86 | return -ENOMEM; | ||
87 | |||
88 | for (i = 0; i < tpl_link; i++) { | ||
89 | ret = mmc_io_rw_direct(func->card, 0, 0, | ||
90 | ptr + i, 0, &this->data[i]); | ||
91 | if (ret) | ||
86 | break; | 92 | break; |
87 | if (i >= ARRAY_SIZE(cis_tpl_list)) { | ||
88 | printk(KERN_WARNING | ||
89 | "%s: unknown CIS tuple 0x%02x of length %u\n", | ||
90 | sdio_func_id(func), tpl_code, tpl_link); | ||
91 | ptr += tpl_link; | ||
92 | continue; | ||
93 | } | 93 | } |
94 | tpl = cis_tpl_list + i; | 94 | if (ret) { |
95 | 95 | kfree(this); | |
96 | if (tpl_link < tpl->min_size) { | ||
97 | printk(KERN_ERR | ||
98 | "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u\n", | ||
99 | sdio_func_id(func), tpl_code, tpl_link, tpl->min_size); | ||
100 | ret = -EINVAL; | ||
101 | break; | 96 | break; |
102 | } | 97 | } |
103 | 98 | ||
104 | for (i = 0; i < tpl_link; i++) { | 99 | for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) |
105 | ret = mmc_io_rw_direct(func->card, 0, 0, ptr + i, 0, &buf[i]); | 100 | if (cis_tpl_list[i].code == tpl_code) |
106 | if (ret) | ||
107 | break; | 101 | break; |
102 | if (i >= ARRAY_SIZE(cis_tpl_list)) { | ||
103 | /* this tuple is unknown to the core */ | ||
104 | this->next = NULL; | ||
105 | this->code = tpl_code; | ||
106 | this->size = tpl_link; | ||
107 | *prev = this; | ||
108 | prev = &this->next; | ||
109 | printk(KERN_DEBUG | ||
110 | "%s: queuing CIS tuple 0x%02x length %u\n", | ||
111 | sdio_func_id(func), tpl_code, tpl_link); | ||
112 | } else { | ||
113 | const struct cis_tpl *tpl = cis_tpl_list + i; | ||
114 | if (tpl_link < tpl->min_size) { | ||
115 | printk(KERN_ERR | ||
116 | "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", | ||
117 | sdio_func_id(func), tpl_code, tpl_link, tpl->min_size); | ||
118 | ret = -EINVAL; | ||
119 | } else if (tpl->parse) | ||
120 | ret = tpl->parse(func, this->data, tpl_link); | ||
121 | kfree(this); | ||
108 | } | 122 | } |
109 | if (ret) | ||
110 | break; | ||
111 | ptr += tpl_link; | ||
112 | 123 | ||
113 | if (tpl->parse) | 124 | ptr += tpl_link; |
114 | ret = tpl->parse(func, buf, tpl_link); | ||
115 | } while (!ret); | 125 | } while (!ret); |
116 | 126 | ||
117 | kfree(buf); | ||
118 | return ret; | 127 | return ret; |
119 | } | 128 | } |
129 | |||
130 | void sdio_free_cis(struct sdio_func *func) | ||
131 | { | ||
132 | struct sdio_func_tuple *tuple, *victim; | ||
133 | |||
134 | tuple = func->tuples; | ||
135 | |||
136 | while (tuple) { | ||
137 | victim = tuple; | ||
138 | tuple = tuple->next; | ||
139 | kfree(victim); | ||
140 | } | ||
141 | |||
142 | func->tuples = NULL; | ||
143 | } | ||
144 | |||
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h index df21c495d133..863d3d516371 100644 --- a/drivers/mmc/core/sdio_cis.h +++ b/drivers/mmc/core/sdio_cis.h | |||
@@ -15,5 +15,6 @@ | |||
15 | #define _MMC_SDIO_CIS_H | 15 | #define _MMC_SDIO_CIS_H |
16 | 16 | ||
17 | int sdio_read_cis(struct sdio_func *func); | 17 | int sdio_read_cis(struct sdio_func *func); |
18 | void sdio_free_cis(struct sdio_func *func); | ||
18 | 19 | ||
19 | #endif | 20 | #endif |
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 4164809a8e63..269067663c8d 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h | |||
@@ -15,6 +15,16 @@ | |||
15 | struct mmc_card; | 15 | struct mmc_card; |
16 | 16 | ||
17 | /* | 17 | /* |
18 | * SDIO function CIS tuple (unknown to the core) | ||
19 | */ | ||
20 | struct sdio_func_tuple { | ||
21 | struct sdio_func_tuple *next; | ||
22 | unsigned char code; | ||
23 | unsigned char size; | ||
24 | unsigned char data[0]; | ||
25 | }; | ||
26 | |||
27 | /* | ||
18 | * SDIO function devices | 28 | * SDIO function devices |
19 | */ | 29 | */ |
20 | struct sdio_func { | 30 | struct sdio_func { |
@@ -28,6 +38,8 @@ struct sdio_func { | |||
28 | 38 | ||
29 | unsigned int state; /* function state */ | 39 | unsigned int state; /* function state */ |
30 | #define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ | 40 | #define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ |
41 | |||
42 | struct sdio_func_tuple *tuples; | ||
31 | }; | 43 | }; |
32 | 44 | ||
33 | #define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT) | 45 | #define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT) |