linux/drivers/mtd/mtdblock.c
<<
>>
Prefs
   1/*
   2 * Direct MTD block device access
   3 *
   4 * (C) 2000-2003 Nicolas Pitre <nico@fluxnic.net>
   5 * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
   6 */
   7
   8#include <linux/fs.h>
   9#include <linux/init.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/sched.h>
  13#include <linux/slab.h>
  14#include <linux/types.h>
  15#include <linux/vmalloc.h>
  16
  17#include <linux/mtd/mtd.h>
  18#include <linux/mtd/blktrans.h>
  19#include <linux/mutex.h>
  20
  21
  22static struct mtdblk_dev {
  23        struct mtd_info *mtd;
  24        int count;
  25        struct mutex cache_mutex;
  26        unsigned char *cache_data;
  27        unsigned long cache_offset;
  28        unsigned int cache_size;
  29        enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
  30} *mtdblks[MAX_MTD_DEVICES];
  31
  32static struct mutex mtdblks_lock;
  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
  44static 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
  50static 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, write the 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
  99static 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
 128static 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
 198static 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
 244static 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
 251static 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
 267static 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        mutex_lock(&mtdblks_lock);
 276        if (mtdblks[dev]) {
 277                mtdblks[dev]->count++;
 278                mutex_unlock(&mtdblks_lock);
 279                return 0;
 280        }
 281
 282        /* OK, it's not open. Create cache info for it */
 283        mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
 284        if (!mtdblk) {
 285                mutex_unlock(&mtdblks_lock);
 286                return -ENOMEM;
 287        }
 288
 289        mtdblk->count = 1;
 290        mtdblk->mtd = mtd;
 291
 292        mutex_init(&mtdblk->cache_mutex);
 293        mtdblk->cache_state = STATE_EMPTY;
 294        if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
 295                mtdblk->cache_size = mtdblk->mtd->erasesize;
 296                mtdblk->cache_data = NULL;
 297        }
 298
 299        mtdblks[dev] = mtdblk;
 300        mutex_unlock(&mtdblks_lock);
 301
 302        DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
 303
 304        return 0;
 305}
 306
 307static int mtdblock_release(struct mtd_blktrans_dev *mbd)
 308{
 309        int dev = mbd->devnum;
 310        struct mtdblk_dev *mtdblk = mtdblks[dev];
 311
 312        DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
 313
 314        mutex_lock(&mtdblks_lock);
 315
 316        mutex_lock(&mtdblk->cache_mutex);
 317        write_cached_data(mtdblk);
 318        mutex_unlock(&mtdblk->cache_mutex);
 319
 320        if (!--mtdblk->count) {
 321                /* It was the last usage. Free the device */
 322                mtdblks[dev] = NULL;
 323                if (mtdblk->mtd->sync)
 324                        mtdblk->mtd->sync(mtdblk->mtd);
 325                vfree(mtdblk->cache_data);
 326                kfree(mtdblk);
 327        }
 328
 329        mutex_unlock(&mtdblks_lock);
 330
 331        DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
 332
 333        return 0;
 334}
 335
 336static int mtdblock_flush(struct mtd_blktrans_dev *dev)
 337{
 338        struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
 339
 340        mutex_lock(&mtdblk->cache_mutex);
 341        write_cached_data(mtdblk);
 342        mutex_unlock(&mtdblk->cache_mutex);
 343
 344        if (mtdblk->mtd->sync)
 345                mtdblk->mtd->sync(mtdblk->mtd);
 346        return 0;
 347}
 348
 349static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 350{
 351        struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 352
 353        if (!dev)
 354                return;
 355
 356        dev->mtd = mtd;
 357        dev->devnum = mtd->index;
 358
 359        dev->size = mtd->size >> 9;
 360        dev->tr = tr;
 361
 362        if (!(mtd->flags & MTD_WRITEABLE))
 363                dev->readonly = 1;
 364
 365        add_mtd_blktrans_dev(dev);
 366}
 367
 368static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
 369{
 370        del_mtd_blktrans_dev(dev);
 371        kfree(dev);
 372}
 373
 374static struct mtd_blktrans_ops mtdblock_tr = {
 375        .name           = "mtdblock",
 376        .major          = 31,
 377        .part_bits      = 0,
 378        .blksize        = 512,
 379        .open           = mtdblock_open,
 380        .flush          = mtdblock_flush,
 381        .release        = mtdblock_release,
 382        .readsect       = mtdblock_readsect,
 383        .writesect      = mtdblock_writesect,
 384        .add_mtd        = mtdblock_add_mtd,
 385        .remove_dev     = mtdblock_remove_dev,
 386        .owner          = THIS_MODULE,
 387};
 388
 389static int __init init_mtdblock(void)
 390{
 391        mutex_init(&mtdblks_lock);
 392
 393        return register_mtd_blktrans(&mtdblock_tr);
 394}
 395
 396static void __exit cleanup_mtdblock(void)
 397{
 398        deregister_mtd_blktrans(&mtdblock_tr);
 399}
 400
 401module_init(init_mtdblock);
 402module_exit(cleanup_mtdblock);
 403
 404
 405MODULE_LICENSE("GPL");
 406MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net> et al.");
 407MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices");
 408