cleanup
[linux-2.4.21-pre4.git] / drivers / mtd / mtdcore.c
1 /*
2  * $Id: mtdcore.c,v 1.1.1.1 2005/04/11 02:50:25 jack Exp $
3  *
4  * Core registration and callback routines for MTD
5  * drivers and users.
6  *
7  */
8
9 #include <linux/config.h>
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/sched.h>
13 #include <linux/ptrace.h>
14 #include <linux/slab.h>
15 #include <linux/string.h>
16 #include <linux/timer.h>
17 #include <linux/major.h>
18 #include <linux/fs.h>
19 #include <linux/ioctl.h>
20 #include <linux/mtd/compatmac.h>
21 #ifdef CONFIG_PROC_FS
22 #include <linux/proc_fs.h>
23 #endif
24
25 #include <linux/mtd/mtd.h>
26
27 static DECLARE_MUTEX(mtd_table_mutex);
28 static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
29 static struct mtd_notifier *mtd_notifiers = NULL;
30
31 /**
32  *      add_mtd_device - register an MTD device
33  *      @mtd: pointer to new MTD device info structure
34  *
35  *      Add a device to the list of MTD devices present in the system, and
36  *      notify each currently active MTD 'user' of its arrival. Returns
37  *      zero on success or 1 on failure, which currently will only happen
38  *      if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
39  */
40
41 int add_mtd_device(struct mtd_info *mtd)
42 {
43         int i;
44
45         down(&mtd_table_mutex);
46
47         for (i=0; i< MAX_MTD_DEVICES; i++)
48                 if (!mtd_table[i])
49                 {
50                         struct mtd_notifier *not=mtd_notifiers;
51
52                         mtd_table[i] = mtd;
53                         mtd->index = i;
54                         DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
55                         while (not)
56                         {
57                                 (*(not->add))(mtd);
58                                 not = not->next;
59                         }
60                         up(&mtd_table_mutex);
61                         MOD_INC_USE_COUNT;
62                         return 0;
63                 }
64         
65         up(&mtd_table_mutex);
66         return 1;
67 }
68
69 /**
70  *      del_mtd_device - unregister an MTD device
71  *      @mtd: pointer to MTD device info structure
72  *
73  *      Remove a device from the list of MTD devices present in the system,
74  *      and notify each currently active MTD 'user' of its departure.
75  *      Returns zero on success or 1 on failure, which currently will happen
76  *      if the requested device does not appear to be present in the list.
77  */
78
79 int del_mtd_device (struct mtd_info *mtd)
80 {
81         struct mtd_notifier *not=mtd_notifiers;
82         int i;
83         
84         down(&mtd_table_mutex);
85
86         for (i=0; i < MAX_MTD_DEVICES; i++)
87         {
88                 if (mtd_table[i] == mtd)
89                 {
90                         while (not)
91                         {
92                                 (*(not->remove))(mtd);
93                                 not = not->next;
94                         }
95                         mtd_table[i] = NULL;
96                         up (&mtd_table_mutex);
97                         MOD_DEC_USE_COUNT;
98                         return 0;
99                 }
100         }
101
102         up(&mtd_table_mutex);
103         return 1;
104 }
105
106 /**
107  *      register_mtd_user - register a 'user' of MTD devices.
108  *      @new: pointer to notifier info structure
109  *
110  *      Registers a pair of callbacks function to be called upon addition
111  *      or removal of MTD devices. Causes the 'add' callback to be immediately
112  *      invoked for each MTD device currently present in the system.
113  */
114
115 void register_mtd_user (struct mtd_notifier *new)
116 {
117         int i;
118
119         down(&mtd_table_mutex);
120
121         new->next = mtd_notifiers;
122         mtd_notifiers = new;
123
124         MOD_INC_USE_COUNT;
125         
126         for (i=0; i< MAX_MTD_DEVICES; i++)
127                 if (mtd_table[i])
128                         new->add(mtd_table[i]);
129
130         up(&mtd_table_mutex);
131 }
132
133 /**
134  *      register_mtd_user - unregister a 'user' of MTD devices.
135  *      @new: pointer to notifier info structure
136  *
137  *      Removes a callback function pair from the list of 'users' to be
138  *      notified upon addition or removal of MTD devices. Causes the
139  *      'remove' callback to be immediately invoked for each MTD device
140  *      currently present in the system.
141  */
142
143 int unregister_mtd_user (struct mtd_notifier *old)
144 {
145         struct mtd_notifier **prev = &mtd_notifiers;
146         struct mtd_notifier *cur;
147         int i;
148
149         down(&mtd_table_mutex);
150
151         while ((cur = *prev)) {
152                 if (cur == old) {
153                         *prev = cur->next;
154
155                         MOD_DEC_USE_COUNT;
156
157                         for (i=0; i< MAX_MTD_DEVICES; i++)
158                                 if (mtd_table[i])
159                                         old->remove(mtd_table[i]);
160                         
161                         up(&mtd_table_mutex);
162                         return 0;
163                 }
164                 prev = &cur->next;
165         }
166         up(&mtd_table_mutex);
167         return 1;
168 }
169
170
171 /**
172  *      __get_mtd_device - obtain a validated handle for an MTD device
173  *      @mtd: last known address of the required MTD device
174  *      @num: internal device number of the required MTD device
175  *
176  *      Given a number and NULL address, return the num'th entry in the device
177  *      table, if any.  Given an address and num == -1, search the device table
178  *      for a device with that address and return if it's still present. Given
179  *      both, return the num'th driver only if its address matches. Return NULL
180  *      if not. get_mtd_device() increases the use count, but
181  *      __get_mtd_device() doesn't - you should generally use get_mtd_device().
182  */
183         
184 struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num)
185 {
186         struct mtd_info *ret = NULL;
187         int i;
188
189         down(&mtd_table_mutex);
190
191         if (num == -1) {
192                 for (i=0; i< MAX_MTD_DEVICES; i++)
193                         if (mtd_table[i] == mtd)
194                                 ret = mtd_table[i];
195         } else if (num < MAX_MTD_DEVICES) {
196                 ret = mtd_table[num];
197                 if (mtd && mtd != ret)
198                         ret = NULL;
199         }
200         
201         up(&mtd_table_mutex);
202         return ret;
203 }
204
205
206 /* default_mtd_writev - default mtd writev method for MTD devices that
207  *                      dont implement their own
208  */
209
210 int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
211                        unsigned long count, loff_t to, size_t *retlen)
212 {
213         unsigned long i;
214         size_t totlen = 0, thislen;
215         int ret = 0;
216
217         if(!mtd->write) {
218                 ret = -EROFS;
219         } else {
220                 for (i=0; i<count; i++) {
221                         if (!vecs[i].iov_len)
222                                 continue;
223                         ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
224                         totlen += thislen;
225                         if (ret || thislen != vecs[i].iov_len)
226                                 break;
227                         to += vecs[i].iov_len;
228                 }
229         }
230         if (retlen)
231                 *retlen = totlen;
232         return ret;
233 }
234
235
236 /* default_mtd_readv - default mtd readv method for MTD devices that dont
237  *                     implement their own
238  */
239
240 int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
241                       unsigned long count, loff_t from, size_t *retlen)
242 {
243         unsigned long i;
244         size_t totlen = 0, thislen;
245         int ret = 0;
246
247         if(!mtd->read) {
248                 ret = -EIO;
249         } else {
250                 for (i=0; i<count; i++) {
251                         if (!vecs[i].iov_len)
252                                 continue;
253                         ret = mtd->read(mtd, from, vecs[i].iov_len, &thislen, vecs[i].iov_base);
254                         totlen += thislen;
255                         if (ret || thislen != vecs[i].iov_len)
256                                 break;
257                         from += vecs[i].iov_len;
258                 }
259         }
260         if (retlen)
261                 *retlen = totlen;
262         return ret;
263 }
264
265
266 EXPORT_SYMBOL(add_mtd_device);
267 EXPORT_SYMBOL(del_mtd_device);
268 EXPORT_SYMBOL(__get_mtd_device);
269 EXPORT_SYMBOL(register_mtd_user);
270 EXPORT_SYMBOL(unregister_mtd_user);
271 EXPORT_SYMBOL(default_mtd_writev);
272 EXPORT_SYMBOL(default_mtd_readv);
273
274 /*====================================================================*/
275 /* Power management code */
276
277 #ifdef CONFIG_PM
278
279 #include <linux/pm.h>
280
281 static struct pm_dev *mtd_pm_dev = NULL;
282
283 static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
284 {
285         int ret = 0, i;
286
287         if (down_trylock(&mtd_table_mutex))
288                 return -EAGAIN;
289         if (rqst == PM_SUSPEND) {
290                 for (i = 0; ret == 0 && i < MAX_MTD_DEVICES; i++) {
291                         if (mtd_table[i] && mtd_table[i]->suspend)
292                                 ret = mtd_table[i]->suspend(mtd_table[i]);
293                 }
294         } else i = MAX_MTD_DEVICES-1;
295
296         if (rqst == PM_RESUME || ret) {
297                 for ( ; i >= 0; i--) {
298                         if (mtd_table[i] && mtd_table[i]->resume)
299                                 mtd_table[i]->resume(mtd_table[i]);
300                 }
301         }
302         up(&mtd_table_mutex);
303         return ret;
304 }
305 #endif
306
307 /*====================================================================*/
308 /* Support for /proc/mtd */
309
310 #ifdef CONFIG_PROC_FS
311
312 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
313 static struct proc_dir_entry *proc_mtd;
314 #endif
315
316 static inline int mtd_proc_info (char *buf, int i)
317 {
318         struct mtd_info *this = mtd_table[i];
319
320         if (!this)
321                 return 0;
322
323         return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size,
324                        this->erasesize, this->name);
325 }
326
327 static int mtd_read_proc ( char *page, char **start, off_t off,int count
328 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
329                        ,int *eof, void *data_unused
330 #else
331                         ,int unused
332 #endif
333                         )
334 {
335         int len, l, i;
336         off_t   begin = 0;
337
338         down(&mtd_table_mutex);
339
340         len = sprintf(page, "dev:    size   erasesize  name\n");
341         for (i=0; i< MAX_MTD_DEVICES; i++) {
342
343                 l = mtd_proc_info(page + len, i);
344                 len += l;
345                 if (len+begin > off+count)
346                         goto done;
347                 if (len+begin < off) {
348                         begin += len;
349                         len = 0;
350                 }
351         }
352
353 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
354         *eof = 1;
355 #endif
356
357 done:
358         up(&mtd_table_mutex);
359         if (off >= len+begin)
360                 return 0;
361         *start = page + (off-begin);
362         return ((count < begin+len-off) ? count : begin+len-off);
363 }
364
365 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
366 struct proc_dir_entry mtd_proc_entry = {
367         0,                 /* low_ino: the inode -- dynamic */
368         3, "mtd",     /* len of name and name */
369         S_IFREG | S_IRUGO, /* mode */
370         1, 0, 0,           /* nlinks, owner, group */
371         0, NULL,           /* size - unused; operations -- use default */
372         &mtd_read_proc,   /* function used to read data */
373         /* nothing more */
374     };
375 #endif
376
377 #endif /* CONFIG_PROC_FS */
378
379 /*====================================================================*/
380 /* Init code */
381
382 int __init init_mtd(void)
383 {
384 //REX:
385 //printk("Enter: init_mtd...\n");
386 #ifdef CONFIG_PROC_FS
387 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
388         if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
389           proc_mtd->read_proc = mtd_read_proc;
390 #else
391         proc_register_dynamic(&proc_root,&mtd_proc_entry);
392 #endif
393 #endif
394
395 #if LINUX_VERSION_CODE < 0x20212
396         init_mtd_devices();
397 #endif
398
399 #ifdef CONFIG_PM
400         mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback);
401 #endif
402         return 0;
403 }
404
405 static void __exit cleanup_mtd(void)
406 {
407 #ifdef CONFIG_PM
408         if (mtd_pm_dev) {
409                 pm_unregister(mtd_pm_dev);
410                 mtd_pm_dev = NULL;
411         }
412 #endif
413
414 #ifdef CONFIG_PROC_FS
415 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
416         if (proc_mtd)
417           remove_proc_entry( "mtd", 0);
418 #else
419         proc_unregister(&proc_root,mtd_proc_entry.low_ino);
420 #endif
421 #endif
422 }
423
424 module_init(init_mtd);
425 module_exit(cleanup_mtd);
426
427
428 MODULE_LICENSE("GPL");
429 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
430 MODULE_DESCRIPTION("Core MTD registration and access routines");