tifm: simplify bus match and uevent handlers
[linux-2.6.git] / drivers / misc / tifm_core.c
1 /*
2  *  tifm_core.c - TI FlashMedia driver
3  *
4  *  Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11
12 #include <linux/tifm.h>
13 #include <linux/init.h>
14 #include <linux/idr.h>
15
16 #define DRIVER_NAME "tifm_core"
17 #define DRIVER_VERSION "0.8"
18
19 static DEFINE_IDR(tifm_adapter_idr);
20 static DEFINE_SPINLOCK(tifm_adapter_lock);
21
22 static const char *tifm_media_type_name(unsigned char type, unsigned char nt)
23 {
24         const char *card_type_name[3][3] = {
25                 { "SmartMedia/xD", "MemoryStick", "MMC/SD" },
26                 { "XD", "MS", "SD"},
27                 { "xd", "ms", "sd"}
28         };
29
30         if (nt > 2 || type < 1 || type > 3)
31                 return NULL;
32         return card_type_name[nt][type - 1];
33 }
34
35 static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id)
36 {
37         if (sock->type == id->type)
38                 return 1;
39         return 0;
40 }
41
42 static int tifm_bus_match(struct device *dev, struct device_driver *drv)
43 {
44         struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
45         struct tifm_driver *fm_drv = container_of(drv, struct tifm_driver,
46                                                   driver);
47         struct tifm_device_id *ids = fm_drv->id_table;
48
49         if (ids) {
50                 while (ids->type) {
51                         if (tifm_dev_match(sock, ids))
52                                 return 1;
53                         ++ids;
54                 }
55         }
56         return 0;
57 }
58
59 static int tifm_uevent(struct device *dev, char **envp, int num_envp,
60                        char *buffer, int buffer_size)
61 {
62         struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
63         int i = 0;
64         int length = 0;
65
66         if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
67                            "TIFM_CARD_TYPE=%s",
68                            tifm_media_type_name(sock->type, 1)))
69                 return -ENOMEM;
70
71         return 0;
72 }
73
74 static int tifm_device_probe(struct device *dev)
75 {
76         struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
77         struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
78                                                driver);
79         int rc = -ENODEV;
80
81         get_device(dev);
82         if (dev->driver && drv->probe) {
83                 rc = drv->probe(sock);
84                 if (!rc)
85                         return 0;
86         }
87         put_device(dev);
88         return rc;
89 }
90
91 static void tifm_dummy_event(struct tifm_dev *sock)
92 {
93         return;
94 }
95
96 static int tifm_device_remove(struct device *dev)
97 {
98         struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
99         struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
100                                                driver);
101
102         if (dev->driver && drv->remove) {
103                 sock->card_event = tifm_dummy_event;
104                 sock->data_event = tifm_dummy_event;
105                 drv->remove(sock);
106                 sock->dev.driver = NULL;
107         }
108
109         put_device(dev);
110         return 0;
111 }
112
113 #ifdef CONFIG_PM
114
115 static int tifm_device_suspend(struct device *dev, pm_message_t state)
116 {
117         struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
118         struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
119                                                driver);
120
121         if (dev->driver && drv->suspend)
122                 return drv->suspend(fm_dev, state);
123         return 0;
124 }
125
126 static int tifm_device_resume(struct device *dev)
127 {
128         struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
129         struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver,
130                                                driver);
131
132         if (dev->driver && drv->resume)
133                 return drv->resume(fm_dev);
134         return 0;
135 }
136
137 #else
138
139 #define tifm_device_suspend NULL
140 #define tifm_device_resume NULL
141
142 #endif /* CONFIG_PM */
143
144 static struct bus_type tifm_bus_type = {
145         .name    = "tifm",
146         .match   = tifm_bus_match,
147         .uevent  = tifm_uevent,
148         .probe   = tifm_device_probe,
149         .remove  = tifm_device_remove,
150         .suspend = tifm_device_suspend,
151         .resume  = tifm_device_resume
152 };
153
154 static void tifm_free(struct class_device *cdev)
155 {
156         struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev);
157
158         kfree(fm->sockets);
159         kfree(fm);
160 }
161
162 static struct class tifm_adapter_class = {
163         .name    = "tifm_adapter",
164         .release = tifm_free
165 };
166
167 struct tifm_adapter *tifm_alloc_adapter(void)
168 {
169         struct tifm_adapter *fm;
170
171         fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL);
172         if (fm) {
173                 fm->cdev.class = &tifm_adapter_class;
174                 spin_lock_init(&fm->lock);
175                 class_device_initialize(&fm->cdev);
176         }
177         return fm;
178 }
179 EXPORT_SYMBOL(tifm_alloc_adapter);
180
181 void tifm_free_adapter(struct tifm_adapter *fm)
182 {
183         class_device_put(&fm->cdev);
184 }
185 EXPORT_SYMBOL(tifm_free_adapter);
186
187 int tifm_add_adapter(struct tifm_adapter *fm,
188                      int (*mediathreadfn)(void *data))
189 {
190         int rc;
191
192         if (!idr_pre_get(&tifm_adapter_idr, GFP_KERNEL))
193                 return -ENOMEM;
194
195         spin_lock(&tifm_adapter_lock);
196         rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id);
197         spin_unlock(&tifm_adapter_lock);
198         if (!rc) {
199                 snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id);
200                 fm->media_switcher = kthread_create(mediathreadfn,
201                                                     fm, "tifm/%u", fm->id);
202
203                 if (!IS_ERR(fm->media_switcher))
204                         return class_device_add(&fm->cdev);
205
206                 spin_lock(&tifm_adapter_lock);
207                 idr_remove(&tifm_adapter_idr, fm->id);
208                 spin_unlock(&tifm_adapter_lock);
209                 rc = -ENOMEM;
210         }
211         return rc;
212 }
213 EXPORT_SYMBOL(tifm_add_adapter);
214
215 void tifm_remove_adapter(struct tifm_adapter *fm)
216 {
217         class_device_del(&fm->cdev);
218
219         spin_lock(&tifm_adapter_lock);
220         idr_remove(&tifm_adapter_idr, fm->id);
221         spin_unlock(&tifm_adapter_lock);
222 }
223 EXPORT_SYMBOL(tifm_remove_adapter);
224
225 void tifm_free_device(struct device *dev)
226 {
227         struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev);
228         kfree(fm_dev);
229 }
230 EXPORT_SYMBOL(tifm_free_device);
231
232 struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm)
233 {
234         struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL);
235
236         if (dev) {
237                 spin_lock_init(&dev->lock);
238
239                 dev->dev.parent = fm->dev;
240                 dev->dev.bus = &tifm_bus_type;
241                 dev->dev.release = tifm_free_device;
242                 dev->card_event = tifm_dummy_event;
243                 dev->data_event = tifm_dummy_event;
244         }
245         return dev;
246 }
247 EXPORT_SYMBOL(tifm_alloc_device);
248
249 void tifm_eject(struct tifm_dev *sock)
250 {
251         struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);
252         fm->eject(fm, sock);
253 }
254 EXPORT_SYMBOL(tifm_eject);
255
256 int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
257                 int direction)
258 {
259         return pci_map_sg(to_pci_dev(sock->dev.parent), sg, nents, direction);
260 }
261 EXPORT_SYMBOL(tifm_map_sg);
262
263 void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
264                    int direction)
265 {
266         pci_unmap_sg(to_pci_dev(sock->dev.parent), sg, nents, direction);
267 }
268 EXPORT_SYMBOL(tifm_unmap_sg);
269
270 int tifm_register_driver(struct tifm_driver *drv)
271 {
272         drv->driver.bus = &tifm_bus_type;
273
274         return driver_register(&drv->driver);
275 }
276 EXPORT_SYMBOL(tifm_register_driver);
277
278 void tifm_unregister_driver(struct tifm_driver *drv)
279 {
280         driver_unregister(&drv->driver);
281 }
282 EXPORT_SYMBOL(tifm_unregister_driver);
283
284 static int __init tifm_init(void)
285 {
286         int rc = bus_register(&tifm_bus_type);
287
288         if (!rc) {
289                 rc = class_register(&tifm_adapter_class);
290                 if (rc)
291                         bus_unregister(&tifm_bus_type);
292         }
293
294         return rc;
295 }
296
297 static void __exit tifm_exit(void)
298 {
299         class_unregister(&tifm_adapter_class);
300         bus_unregister(&tifm_bus_type);
301 }
302
303 subsys_initcall(tifm_init);
304 module_exit(tifm_exit);
305
306 MODULE_LICENSE("GPL");
307 MODULE_AUTHOR("Alex Dubov");
308 MODULE_DESCRIPTION("TI FlashMedia core driver");
309 MODULE_LICENSE("GPL");
310 MODULE_VERSION(DRIVER_VERSION);