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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [mtd/] [mtdblock.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * Direct MTD block device access
3
 *
4
 * $Id: mtdblock.c,v 1.68 2005/11/07 11:14:20 gleixner Exp $
5
 *
6
 * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
7
 * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
8
 */
9
 
10
#include <linux/fs.h>
11
#include <linux/init.h>
12
#include <linux/kernel.h>
13
#include <linux/module.h>
14
#include <linux/sched.h>
15
#include <linux/slab.h>
16
#include <linux/types.h>
17
#include <linux/vmalloc.h>
18
 
19
#include <linux/mtd/mtd.h>
20
#include <linux/mtd/blktrans.h>
21
#include <linux/mutex.h>
22
 
23
 
24
static struct mtdblk_dev {
25
        struct mtd_info *mtd;
26
        int count;
27
        struct mutex cache_mutex;
28
        unsigned char *cache_data;
29
        unsigned long cache_offset;
30
        unsigned int cache_size;
31
        enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
32
} *mtdblks[MAX_MTD_DEVICES];
33
 
34
/*
35
 * Cache stuff...
36
 *
37
 * Since typical flash erasable sectors are much larger than what Linux's
38
 * buffer cache can handle, we must implement read-modify-write on flash
39
 * sectors for each block write requests.  To avoid over-erasing flash sectors
40
 * and to speed things up, we locally cache a whole flash sector while it is
41
 * being written to until a different sector is required.
42
 */
43
 
44
static void erase_callback(struct erase_info *done)
45
{
46
        wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
47
        wake_up(wait_q);
48
}
49
 
50
static int erase_write (struct mtd_info *mtd, unsigned long pos,
51
                        int len, const char *buf)
52
{
53
        struct erase_info erase;
54
        DECLARE_WAITQUEUE(wait, current);
55
        wait_queue_head_t wait_q;
56
        size_t retlen;
57
        int ret;
58
 
59
        /*
60
         * First, let's erase the flash block.
61
         */
62
 
63
        init_waitqueue_head(&wait_q);
64
        erase.mtd = mtd;
65
        erase.callback = erase_callback;
66
        erase.addr = pos;
67
        erase.len = len;
68
        erase.priv = (u_long)&wait_q;
69
 
70
        set_current_state(TASK_INTERRUPTIBLE);
71
        add_wait_queue(&wait_q, &wait);
72
 
73
        ret = mtd->erase(mtd, &erase);
74
        if (ret) {
75
                set_current_state(TASK_RUNNING);
76
                remove_wait_queue(&wait_q, &wait);
77
                printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
78
                                     "on \"%s\" failed\n",
79
                        pos, len, mtd->name);
80
                return ret;
81
        }
82
 
83
        schedule();  /* Wait for erase to finish. */
84
        remove_wait_queue(&wait_q, &wait);
85
 
86
        /*
87
         * Next, writhe data to flash.
88
         */
89
 
90
        ret = mtd->write(mtd, pos, len, &retlen, buf);
91
        if (ret)
92
                return ret;
93
        if (retlen != len)
94
                return -EIO;
95
        return 0;
96
}
97
 
98
 
99
static int write_cached_data (struct mtdblk_dev *mtdblk)
100
{
101
        struct mtd_info *mtd = mtdblk->mtd;
102
        int ret;
103
 
104
        if (mtdblk->cache_state != STATE_DIRTY)
105
                return 0;
106
 
107
        DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" "
108
                        "at 0x%lx, size 0x%x\n", mtd->name,
109
                        mtdblk->cache_offset, mtdblk->cache_size);
110
 
111
        ret = erase_write (mtd, mtdblk->cache_offset,
112
                           mtdblk->cache_size, mtdblk->cache_data);
113
        if (ret)
114
                return ret;
115
 
116
        /*
117
         * Here we could argubly set the cache state to STATE_CLEAN.
118
         * However this could lead to inconsistency since we will not
119
         * be notified if this content is altered on the flash by other
120
         * means.  Let's declare it empty and leave buffering tasks to
121
         * the buffer cache instead.
122
         */
123
        mtdblk->cache_state = STATE_EMPTY;
124
        return 0;
125
}
126
 
127
 
128
static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
129
                            int len, const char *buf)
130
{
131
        struct mtd_info *mtd = mtdblk->mtd;
132
        unsigned int sect_size = mtdblk->cache_size;
133
        size_t retlen;
134
        int ret;
135
 
136
        DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
137
                mtd->name, pos, len);
138
 
139
        if (!sect_size)
140
                return mtd->write(mtd, pos, len, &retlen, buf);
141
 
142
        while (len > 0) {
143
                unsigned long sect_start = (pos/sect_size)*sect_size;
144
                unsigned int offset = pos - sect_start;
145
                unsigned int size = sect_size - offset;
146
                if( size > len )
147
                        size = len;
148
 
149
                if (size == sect_size) {
150
                        /*
151
                         * We are covering a whole sector.  Thus there is no
152
                         * need to bother with the cache while it may still be
153
                         * useful for other partial writes.
154
                         */
155
                        ret = erase_write (mtd, pos, size, buf);
156
                        if (ret)
157
                                return ret;
158
                } else {
159
                        /* Partial sector: need to use the cache */
160
 
161
                        if (mtdblk->cache_state == STATE_DIRTY &&
162
                            mtdblk->cache_offset != sect_start) {
163
                                ret = write_cached_data(mtdblk);
164
                                if (ret)
165
                                        return ret;
166
                        }
167
 
168
                        if (mtdblk->cache_state == STATE_EMPTY ||
169
                            mtdblk->cache_offset != sect_start) {
170
                                /* fill the cache with the current sector */
171
                                mtdblk->cache_state = STATE_EMPTY;
172
                                ret = mtd->read(mtd, sect_start, sect_size,
173
                                                &retlen, mtdblk->cache_data);
174
                                if (ret)
175
                                        return ret;
176
                                if (retlen != sect_size)
177
                                        return -EIO;
178
 
179
                                mtdblk->cache_offset = sect_start;
180
                                mtdblk->cache_size = sect_size;
181
                                mtdblk->cache_state = STATE_CLEAN;
182
                        }
183
 
184
                        /* write data to our local cache */
185
                        memcpy (mtdblk->cache_data + offset, buf, size);
186
                        mtdblk->cache_state = STATE_DIRTY;
187
                }
188
 
189
                buf += size;
190
                pos += size;
191
                len -= size;
192
        }
193
 
194
        return 0;
195
}
196
 
197
 
198
static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
199
                           int len, char *buf)
200
{
201
        struct mtd_info *mtd = mtdblk->mtd;
202
        unsigned int sect_size = mtdblk->cache_size;
203
        size_t retlen;
204
        int ret;
205
 
206
        DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
207
                        mtd->name, pos, len);
208
 
209
        if (!sect_size)
210
                return mtd->read(mtd, pos, len, &retlen, buf);
211
 
212
        while (len > 0) {
213
                unsigned long sect_start = (pos/sect_size)*sect_size;
214
                unsigned int offset = pos - sect_start;
215
                unsigned int size = sect_size - offset;
216
                if (size > len)
217
                        size = len;
218
 
219
                /*
220
                 * Check if the requested data is already cached
221
                 * Read the requested amount of data from our internal cache if it
222
                 * contains what we want, otherwise we read the data directly
223
                 * from flash.
224
                 */
225
                if (mtdblk->cache_state != STATE_EMPTY &&
226
                    mtdblk->cache_offset == sect_start) {
227
                        memcpy (buf, mtdblk->cache_data + offset, size);
228
                } else {
229
                        ret = mtd->read(mtd, pos, size, &retlen, buf);
230
                        if (ret)
231
                                return ret;
232
                        if (retlen != size)
233
                                return -EIO;
234
                }
235
 
236
                buf += size;
237
                pos += size;
238
                len -= size;
239
        }
240
 
241
        return 0;
242
}
243
 
244
static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
245
                              unsigned long block, char *buf)
246
{
247
        struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
248
        return do_cached_read(mtdblk, block<<9, 512, buf);
249
}
250
 
251
static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
252
                              unsigned long block, char *buf)
253
{
254
        struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
255
        if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
256
                mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
257
                if (!mtdblk->cache_data)
258
                        return -EINTR;
259
                /* -EINTR is not really correct, but it is the best match
260
                 * documented in man 2 write for all cases.  We could also
261
                 * return -EAGAIN sometimes, but why bother?
262
                 */
263
        }
264
        return do_cached_write(mtdblk, block<<9, 512, buf);
265
}
266
 
267
static int mtdblock_open(struct mtd_blktrans_dev *mbd)
268
{
269
        struct mtdblk_dev *mtdblk;
270
        struct mtd_info *mtd = mbd->mtd;
271
        int dev = mbd->devnum;
272
 
273
        DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
274
 
275
        if (mtdblks[dev]) {
276
                mtdblks[dev]->count++;
277
                return 0;
278
        }
279
 
280
        /* OK, it's not open. Create cache info for it */
281
        mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
282
        if (!mtdblk)
283
                return -ENOMEM;
284
 
285
        mtdblk->count = 1;
286
        mtdblk->mtd = mtd;
287
 
288
        mutex_init(&mtdblk->cache_mutex);
289
        mtdblk->cache_state = STATE_EMPTY;
290
        if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
291
                mtdblk->cache_size = mtdblk->mtd->erasesize;
292
                mtdblk->cache_data = NULL;
293
        }
294
 
295
        mtdblks[dev] = mtdblk;
296
 
297
        DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
298
 
299
        return 0;
300
}
301
 
302
static int mtdblock_release(struct mtd_blktrans_dev *mbd)
303
{
304
        int dev = mbd->devnum;
305
        struct mtdblk_dev *mtdblk = mtdblks[dev];
306
 
307
        DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
308
 
309
        mutex_lock(&mtdblk->cache_mutex);
310
        write_cached_data(mtdblk);
311
        mutex_unlock(&mtdblk->cache_mutex);
312
 
313
        if (!--mtdblk->count) {
314
                /* It was the last usage. Free the device */
315
                mtdblks[dev] = NULL;
316
                if (mtdblk->mtd->sync)
317
                        mtdblk->mtd->sync(mtdblk->mtd);
318
                vfree(mtdblk->cache_data);
319
                kfree(mtdblk);
320
        }
321
        DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
322
 
323
        return 0;
324
}
325
 
326
static int mtdblock_flush(struct mtd_blktrans_dev *dev)
327
{
328
        struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
329
 
330
        mutex_lock(&mtdblk->cache_mutex);
331
        write_cached_data(mtdblk);
332
        mutex_unlock(&mtdblk->cache_mutex);
333
 
334
        if (mtdblk->mtd->sync)
335
                mtdblk->mtd->sync(mtdblk->mtd);
336
        return 0;
337
}
338
 
339
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
340
{
341
        struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
342
 
343
        if (!dev)
344
                return;
345
 
346
        dev->mtd = mtd;
347
        dev->devnum = mtd->index;
348
 
349
        dev->size = mtd->size >> 9;
350
        dev->tr = tr;
351
 
352
        if (!(mtd->flags & MTD_WRITEABLE))
353
                dev->readonly = 1;
354
 
355
        add_mtd_blktrans_dev(dev);
356
}
357
 
358
static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
359
{
360
        del_mtd_blktrans_dev(dev);
361
        kfree(dev);
362
}
363
 
364
static struct mtd_blktrans_ops mtdblock_tr = {
365
        .name           = "mtdblock",
366
        .major          = 31,
367
        .part_bits      = 0,
368
        .blksize        = 512,
369
        .open           = mtdblock_open,
370
        .flush          = mtdblock_flush,
371
        .release        = mtdblock_release,
372
        .readsect       = mtdblock_readsect,
373
        .writesect      = mtdblock_writesect,
374
        .add_mtd        = mtdblock_add_mtd,
375
        .remove_dev     = mtdblock_remove_dev,
376
        .owner          = THIS_MODULE,
377
};
378
 
379
static int __init init_mtdblock(void)
380
{
381
        return register_mtd_blktrans(&mtdblock_tr);
382
}
383
 
384
static void __exit cleanup_mtdblock(void)
385
{
386
        deregister_mtd_blktrans(&mtdblock_tr);
387
}
388
 
389
module_init(init_mtdblock);
390
module_exit(cleanup_mtdblock);
391
 
392
 
393
MODULE_LICENSE("GPL");
394
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org> et al.");
395
MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices");

powered by: WebSVN 2.1.0

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