OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [mtd/] [mtdcore.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * $Id: mtdcore.c,v 1.1.1.1 2004-04-15 01:51:42 phoenix 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
#ifdef CONFIG_PROC_FS
385
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
386
        if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
387
          proc_mtd->read_proc = mtd_read_proc;
388
#else
389
        proc_register_dynamic(&proc_root,&mtd_proc_entry);
390
#endif
391
#endif
392
 
393
#if LINUX_VERSION_CODE < 0x20212
394
        init_mtd_devices();
395
#endif
396
 
397
#ifdef CONFIG_PM
398
        mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback);
399
#endif
400
        return 0;
401
}
402
 
403
static void __exit cleanup_mtd(void)
404
{
405
#ifdef CONFIG_PM
406
        if (mtd_pm_dev) {
407
                pm_unregister(mtd_pm_dev);
408
                mtd_pm_dev = NULL;
409
        }
410
#endif
411
 
412
#ifdef CONFIG_PROC_FS
413
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
414
        if (proc_mtd)
415
          remove_proc_entry( "mtd", 0);
416
#else
417
        proc_unregister(&proc_root,mtd_proc_entry.low_ino);
418
#endif
419
#endif
420
}
421
 
422
module_init(init_mtd);
423
module_exit(cleanup_mtd);
424
 
425
 
426
MODULE_LICENSE("GPL");
427
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
428
MODULE_DESCRIPTION("Core MTD registration and access routines");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.