diff options
Diffstat (limited to 'drivers/misc/tifm_core.c')
-rw-r--r-- | drivers/misc/tifm_core.c | 305 |
1 files changed, 182 insertions, 123 deletions
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 6b10ebe9d93..d195fb088f4 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c | |||
@@ -14,71 +14,124 @@ | |||
14 | #include <linux/idr.h> | 14 | #include <linux/idr.h> |
15 | 15 | ||
16 | #define DRIVER_NAME "tifm_core" | 16 | #define DRIVER_NAME "tifm_core" |
17 | #define DRIVER_VERSION "0.7" | 17 | #define DRIVER_VERSION "0.8" |
18 | 18 | ||
19 | static struct workqueue_struct *workqueue; | ||
19 | static DEFINE_IDR(tifm_adapter_idr); | 20 | static DEFINE_IDR(tifm_adapter_idr); |
20 | static DEFINE_SPINLOCK(tifm_adapter_lock); | 21 | static DEFINE_SPINLOCK(tifm_adapter_lock); |
21 | 22 | ||
22 | static tifm_media_id *tifm_device_match(tifm_media_id *ids, | 23 | static const char *tifm_media_type_name(unsigned char type, unsigned char nt) |
23 | struct tifm_dev *dev) | ||
24 | { | 24 | { |
25 | while (*ids) { | 25 | const char *card_type_name[3][3] = { |
26 | if (dev->media_id == *ids) | 26 | { "SmartMedia/xD", "MemoryStick", "MMC/SD" }, |
27 | return ids; | 27 | { "XD", "MS", "SD"}, |
28 | ids++; | 28 | { "xd", "ms", "sd"} |
29 | } | 29 | }; |
30 | return NULL; | 30 | |
31 | if (nt > 2 || type < 1 || type > 3) | ||
32 | return NULL; | ||
33 | return card_type_name[nt][type - 1]; | ||
31 | } | 34 | } |
32 | 35 | ||
33 | static int tifm_match(struct device *dev, struct device_driver *drv) | 36 | static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id) |
34 | { | 37 | { |
35 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 38 | if (sock->type == id->type) |
36 | struct tifm_driver *fm_drv; | ||
37 | |||
38 | fm_drv = container_of(drv, struct tifm_driver, driver); | ||
39 | if (!fm_drv->id_table) | ||
40 | return -EINVAL; | ||
41 | if (tifm_device_match(fm_drv->id_table, fm_dev)) | ||
42 | return 1; | 39 | return 1; |
43 | return -ENODEV; | 40 | return 0; |
41 | } | ||
42 | |||
43 | static int tifm_bus_match(struct device *dev, struct device_driver *drv) | ||
44 | { | ||
45 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
46 | struct tifm_driver *fm_drv = container_of(drv, struct tifm_driver, | ||
47 | driver); | ||
48 | struct tifm_device_id *ids = fm_drv->id_table; | ||
49 | |||
50 | if (ids) { | ||
51 | while (ids->type) { | ||
52 | if (tifm_dev_match(sock, ids)) | ||
53 | return 1; | ||
54 | ++ids; | ||
55 | } | ||
56 | } | ||
57 | return 0; | ||
44 | } | 58 | } |
45 | 59 | ||
46 | static int tifm_uevent(struct device *dev, char **envp, int num_envp, | 60 | static int tifm_uevent(struct device *dev, char **envp, int num_envp, |
47 | char *buffer, int buffer_size) | 61 | char *buffer, int buffer_size) |
48 | { | 62 | { |
49 | struct tifm_dev *fm_dev; | 63 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
50 | int i = 0; | 64 | int i = 0; |
51 | int length = 0; | 65 | int length = 0; |
52 | const char *card_type_name[] = {"INV", "SM", "MS", "SD"}; | ||
53 | 66 | ||
54 | if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev))) | ||
55 | return -ENODEV; | ||
56 | if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, | 67 | if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, |
57 | "TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id])) | 68 | "TIFM_CARD_TYPE=%s", |
69 | tifm_media_type_name(sock->type, 1))) | ||
58 | return -ENOMEM; | 70 | return -ENOMEM; |
59 | 71 | ||
60 | return 0; | 72 | return 0; |
61 | } | 73 | } |
62 | 74 | ||
75 | static int tifm_device_probe(struct device *dev) | ||
76 | { | ||
77 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
78 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, | ||
79 | driver); | ||
80 | int rc = -ENODEV; | ||
81 | |||
82 | get_device(dev); | ||
83 | if (dev->driver && drv->probe) { | ||
84 | rc = drv->probe(sock); | ||
85 | if (!rc) | ||
86 | return 0; | ||
87 | } | ||
88 | put_device(dev); | ||
89 | return rc; | ||
90 | } | ||
91 | |||
92 | static void tifm_dummy_event(struct tifm_dev *sock) | ||
93 | { | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | static int tifm_device_remove(struct device *dev) | ||
98 | { | ||
99 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
100 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, | ||
101 | driver); | ||
102 | |||
103 | if (dev->driver && drv->remove) { | ||
104 | sock->card_event = tifm_dummy_event; | ||
105 | sock->data_event = tifm_dummy_event; | ||
106 | drv->remove(sock); | ||
107 | sock->dev.driver = NULL; | ||
108 | } | ||
109 | |||
110 | put_device(dev); | ||
111 | return 0; | ||
112 | } | ||
113 | |||
63 | #ifdef CONFIG_PM | 114 | #ifdef CONFIG_PM |
64 | 115 | ||
65 | static int tifm_device_suspend(struct device *dev, pm_message_t state) | 116 | static int tifm_device_suspend(struct device *dev, pm_message_t state) |
66 | { | 117 | { |
67 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 118 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
68 | struct tifm_driver *drv = fm_dev->drv; | 119 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, |
120 | driver); | ||
69 | 121 | ||
70 | if (drv && drv->suspend) | 122 | if (dev->driver && drv->suspend) |
71 | return drv->suspend(fm_dev, state); | 123 | return drv->suspend(sock, state); |
72 | return 0; | 124 | return 0; |
73 | } | 125 | } |
74 | 126 | ||
75 | static int tifm_device_resume(struct device *dev) | 127 | static int tifm_device_resume(struct device *dev) |
76 | { | 128 | { |
77 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 129 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
78 | struct tifm_driver *drv = fm_dev->drv; | 130 | struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, |
131 | driver); | ||
79 | 132 | ||
80 | if (drv && drv->resume) | 133 | if (dev->driver && drv->resume) |
81 | return drv->resume(fm_dev); | 134 | return drv->resume(sock); |
82 | return 0; | 135 | return 0; |
83 | } | 136 | } |
84 | 137 | ||
@@ -89,19 +142,33 @@ static int tifm_device_resume(struct device *dev) | |||
89 | 142 | ||
90 | #endif /* CONFIG_PM */ | 143 | #endif /* CONFIG_PM */ |
91 | 144 | ||
145 | static ssize_t type_show(struct device *dev, struct device_attribute *attr, | ||
146 | char *buf) | ||
147 | { | ||
148 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); | ||
149 | return sprintf(buf, "%x", sock->type); | ||
150 | } | ||
151 | |||
152 | static struct device_attribute tifm_dev_attrs[] = { | ||
153 | __ATTR(type, S_IRUGO, type_show, NULL), | ||
154 | __ATTR_NULL | ||
155 | }; | ||
156 | |||
92 | static struct bus_type tifm_bus_type = { | 157 | static struct bus_type tifm_bus_type = { |
93 | .name = "tifm", | 158 | .name = "tifm", |
94 | .match = tifm_match, | 159 | .dev_attrs = tifm_dev_attrs, |
95 | .uevent = tifm_uevent, | 160 | .match = tifm_bus_match, |
96 | .suspend = tifm_device_suspend, | 161 | .uevent = tifm_uevent, |
97 | .resume = tifm_device_resume | 162 | .probe = tifm_device_probe, |
163 | .remove = tifm_device_remove, | ||
164 | .suspend = tifm_device_suspend, | ||
165 | .resume = tifm_device_resume | ||
98 | }; | 166 | }; |
99 | 167 | ||
100 | static void tifm_free(struct class_device *cdev) | 168 | static void tifm_free(struct class_device *cdev) |
101 | { | 169 | { |
102 | struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); | 170 | struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); |
103 | 171 | ||
104 | kfree(fm->sockets); | ||
105 | kfree(fm); | 172 | kfree(fm); |
106 | } | 173 | } |
107 | 174 | ||
@@ -110,28 +177,25 @@ static struct class tifm_adapter_class = { | |||
110 | .release = tifm_free | 177 | .release = tifm_free |
111 | }; | 178 | }; |
112 | 179 | ||
113 | struct tifm_adapter *tifm_alloc_adapter(void) | 180 | struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets, |
181 | struct device *dev) | ||
114 | { | 182 | { |
115 | struct tifm_adapter *fm; | 183 | struct tifm_adapter *fm; |
116 | 184 | ||
117 | fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL); | 185 | fm = kzalloc(sizeof(struct tifm_adapter) |
186 | + sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL); | ||
118 | if (fm) { | 187 | if (fm) { |
119 | fm->cdev.class = &tifm_adapter_class; | 188 | fm->cdev.class = &tifm_adapter_class; |
120 | spin_lock_init(&fm->lock); | 189 | fm->cdev.dev = dev; |
121 | class_device_initialize(&fm->cdev); | 190 | class_device_initialize(&fm->cdev); |
191 | spin_lock_init(&fm->lock); | ||
192 | fm->num_sockets = num_sockets; | ||
122 | } | 193 | } |
123 | return fm; | 194 | return fm; |
124 | } | 195 | } |
125 | EXPORT_SYMBOL(tifm_alloc_adapter); | 196 | EXPORT_SYMBOL(tifm_alloc_adapter); |
126 | 197 | ||
127 | void tifm_free_adapter(struct tifm_adapter *fm) | 198 | int tifm_add_adapter(struct tifm_adapter *fm) |
128 | { | ||
129 | class_device_put(&fm->cdev); | ||
130 | } | ||
131 | EXPORT_SYMBOL(tifm_free_adapter); | ||
132 | |||
133 | int tifm_add_adapter(struct tifm_adapter *fm, | ||
134 | int (*mediathreadfn)(void *data)) | ||
135 | { | 199 | { |
136 | int rc; | 200 | int rc; |
137 | 201 | ||
@@ -141,59 +205,80 @@ int tifm_add_adapter(struct tifm_adapter *fm, | |||
141 | spin_lock(&tifm_adapter_lock); | 205 | spin_lock(&tifm_adapter_lock); |
142 | rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); | 206 | rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); |
143 | spin_unlock(&tifm_adapter_lock); | 207 | spin_unlock(&tifm_adapter_lock); |
144 | if (!rc) { | 208 | if (rc) |
145 | snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); | 209 | return rc; |
146 | fm->media_switcher = kthread_create(mediathreadfn, | ||
147 | fm, "tifm/%u", fm->id); | ||
148 | |||
149 | if (!IS_ERR(fm->media_switcher)) | ||
150 | return class_device_add(&fm->cdev); | ||
151 | 210 | ||
211 | snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); | ||
212 | rc = class_device_add(&fm->cdev); | ||
213 | if (rc) { | ||
152 | spin_lock(&tifm_adapter_lock); | 214 | spin_lock(&tifm_adapter_lock); |
153 | idr_remove(&tifm_adapter_idr, fm->id); | 215 | idr_remove(&tifm_adapter_idr, fm->id); |
154 | spin_unlock(&tifm_adapter_lock); | 216 | spin_unlock(&tifm_adapter_lock); |
155 | rc = -ENOMEM; | ||
156 | } | 217 | } |
218 | |||
157 | return rc; | 219 | return rc; |
158 | } | 220 | } |
159 | EXPORT_SYMBOL(tifm_add_adapter); | 221 | EXPORT_SYMBOL(tifm_add_adapter); |
160 | 222 | ||
161 | void tifm_remove_adapter(struct tifm_adapter *fm) | 223 | void tifm_remove_adapter(struct tifm_adapter *fm) |
162 | { | 224 | { |
163 | class_device_del(&fm->cdev); | 225 | unsigned int cnt; |
226 | |||
227 | flush_workqueue(workqueue); | ||
228 | for (cnt = 0; cnt < fm->num_sockets; ++cnt) { | ||
229 | if (fm->sockets[cnt]) | ||
230 | device_unregister(&fm->sockets[cnt]->dev); | ||
231 | } | ||
164 | 232 | ||
165 | spin_lock(&tifm_adapter_lock); | 233 | spin_lock(&tifm_adapter_lock); |
166 | idr_remove(&tifm_adapter_idr, fm->id); | 234 | idr_remove(&tifm_adapter_idr, fm->id); |
167 | spin_unlock(&tifm_adapter_lock); | 235 | spin_unlock(&tifm_adapter_lock); |
236 | class_device_del(&fm->cdev); | ||
168 | } | 237 | } |
169 | EXPORT_SYMBOL(tifm_remove_adapter); | 238 | EXPORT_SYMBOL(tifm_remove_adapter); |
170 | 239 | ||
171 | void tifm_free_device(struct device *dev) | 240 | void tifm_free_adapter(struct tifm_adapter *fm) |
172 | { | 241 | { |
173 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 242 | class_device_put(&fm->cdev); |
174 | kfree(fm_dev); | ||
175 | } | 243 | } |
176 | EXPORT_SYMBOL(tifm_free_device); | 244 | EXPORT_SYMBOL(tifm_free_adapter); |
177 | 245 | ||
178 | static void tifm_dummy_signal_irq(struct tifm_dev *sock, | 246 | void tifm_free_device(struct device *dev) |
179 | unsigned int sock_irq_status) | ||
180 | { | 247 | { |
181 | return; | 248 | struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); |
249 | kfree(sock); | ||
182 | } | 250 | } |
251 | EXPORT_SYMBOL(tifm_free_device); | ||
183 | 252 | ||
184 | struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) | 253 | struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id, |
254 | unsigned char type) | ||
185 | { | 255 | { |
186 | struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); | 256 | struct tifm_dev *sock = NULL; |
187 | 257 | ||
188 | if (dev) { | 258 | if (!tifm_media_type_name(type, 0)) |
189 | spin_lock_init(&dev->lock); | 259 | return sock; |
190 | 260 | ||
191 | dev->dev.parent = fm->dev; | 261 | sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); |
192 | dev->dev.bus = &tifm_bus_type; | 262 | if (sock) { |
193 | dev->dev.release = tifm_free_device; | 263 | spin_lock_init(&sock->lock); |
194 | dev->signal_irq = tifm_dummy_signal_irq; | 264 | sock->type = type; |
265 | sock->socket_id = id; | ||
266 | sock->card_event = tifm_dummy_event; | ||
267 | sock->data_event = tifm_dummy_event; | ||
268 | |||
269 | sock->dev.parent = fm->cdev.dev; | ||
270 | sock->dev.bus = &tifm_bus_type; | ||
271 | sock->dev.dma_mask = fm->cdev.dev->dma_mask; | ||
272 | sock->dev.release = tifm_free_device; | ||
273 | |||
274 | snprintf(sock->dev.bus_id, BUS_ID_SIZE, | ||
275 | "tifm_%s%u:%u", tifm_media_type_name(type, 2), | ||
276 | fm->id, id); | ||
277 | printk(KERN_INFO DRIVER_NAME | ||
278 | ": %s card detected in socket %u:%u\n", | ||
279 | tifm_media_type_name(type, 0), fm->id, id); | ||
195 | } | 280 | } |
196 | return dev; | 281 | return sock; |
197 | } | 282 | } |
198 | EXPORT_SYMBOL(tifm_alloc_device); | 283 | EXPORT_SYMBOL(tifm_alloc_device); |
199 | 284 | ||
@@ -218,54 +303,15 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, | |||
218 | } | 303 | } |
219 | EXPORT_SYMBOL(tifm_unmap_sg); | 304 | EXPORT_SYMBOL(tifm_unmap_sg); |
220 | 305 | ||
221 | static int tifm_device_probe(struct device *dev) | 306 | void tifm_queue_work(struct work_struct *work) |
222 | { | ||
223 | struct tifm_driver *drv; | ||
224 | struct tifm_dev *fm_dev; | ||
225 | int rc = 0; | ||
226 | const tifm_media_id *id; | ||
227 | |||
228 | drv = container_of(dev->driver, struct tifm_driver, driver); | ||
229 | fm_dev = container_of(dev, struct tifm_dev, dev); | ||
230 | get_device(dev); | ||
231 | if (!fm_dev->drv && drv->probe && drv->id_table) { | ||
232 | rc = -ENODEV; | ||
233 | id = tifm_device_match(drv->id_table, fm_dev); | ||
234 | if (id) | ||
235 | rc = drv->probe(fm_dev); | ||
236 | if (rc >= 0) { | ||
237 | rc = 0; | ||
238 | fm_dev->drv = drv; | ||
239 | } | ||
240 | } | ||
241 | if (rc) | ||
242 | put_device(dev); | ||
243 | return rc; | ||
244 | } | ||
245 | |||
246 | static int tifm_device_remove(struct device *dev) | ||
247 | { | 307 | { |
248 | struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); | 308 | queue_work(workqueue, work); |
249 | struct tifm_driver *drv = fm_dev->drv; | ||
250 | |||
251 | if (drv) { | ||
252 | fm_dev->signal_irq = tifm_dummy_signal_irq; | ||
253 | if (drv->remove) | ||
254 | drv->remove(fm_dev); | ||
255 | fm_dev->drv = NULL; | ||
256 | } | ||
257 | |||
258 | put_device(dev); | ||
259 | return 0; | ||
260 | } | 309 | } |
310 | EXPORT_SYMBOL(tifm_queue_work); | ||
261 | 311 | ||
262 | int tifm_register_driver(struct tifm_driver *drv) | 312 | int tifm_register_driver(struct tifm_driver *drv) |
263 | { | 313 | { |
264 | drv->driver.bus = &tifm_bus_type; | 314 | drv->driver.bus = &tifm_bus_type; |
265 | drv->driver.probe = tifm_device_probe; | ||
266 | drv->driver.remove = tifm_device_remove; | ||
267 | drv->driver.suspend = tifm_device_suspend; | ||
268 | drv->driver.resume = tifm_device_resume; | ||
269 | 315 | ||
270 | return driver_register(&drv->driver); | 316 | return driver_register(&drv->driver); |
271 | } | 317 | } |
@@ -279,13 +325,25 @@ EXPORT_SYMBOL(tifm_unregister_driver); | |||
279 | 325 | ||
280 | static int __init tifm_init(void) | 326 | static int __init tifm_init(void) |
281 | { | 327 | { |
282 | int rc = bus_register(&tifm_bus_type); | 328 | int rc; |
283 | 329 | ||
284 | if (!rc) { | 330 | workqueue = create_freezeable_workqueue("tifm"); |
285 | rc = class_register(&tifm_adapter_class); | 331 | if (!workqueue) |
286 | if (rc) | 332 | return -ENOMEM; |
287 | bus_unregister(&tifm_bus_type); | 333 | |
288 | } | 334 | rc = bus_register(&tifm_bus_type); |
335 | |||
336 | if (rc) | ||
337 | goto err_out_wq; | ||
338 | |||
339 | rc = class_register(&tifm_adapter_class); | ||
340 | if (!rc) | ||
341 | return 0; | ||
342 | |||
343 | bus_unregister(&tifm_bus_type); | ||
344 | |||
345 | err_out_wq: | ||
346 | destroy_workqueue(workqueue); | ||
289 | 347 | ||
290 | return rc; | 348 | return rc; |
291 | } | 349 | } |
@@ -294,6 +352,7 @@ static void __exit tifm_exit(void) | |||
294 | { | 352 | { |
295 | class_unregister(&tifm_adapter_class); | 353 | class_unregister(&tifm_adapter_class); |
296 | bus_unregister(&tifm_bus_type); | 354 | bus_unregister(&tifm_bus_type); |
355 | destroy_workqueue(workqueue); | ||
297 | } | 356 | } |
298 | 357 | ||
299 | subsys_initcall(tifm_init); | 358 | subsys_initcall(tifm_init); |