mtdblock.c revision f4a43cfcecfcaeeaa40a9dbc1d1378298c22446e
197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Direct MTD block device access 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * $Id: mtdblock.c,v 1.68 2005/11/07 11:14:20 gleixner Exp $ 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (C) 2000-2003 Nicolas Pitre <nico@cam.org> 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/config.h> 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h> 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 1315fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/kernel.h> 1415fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/module.h> 1515fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/sched.h> 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 1715fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner#include <linux/types.h> 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h> 1915fdc52f35b853e3fa550087987b5ee4ffbd199bThomas Gleixner 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h> 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/blktrans.h> 2248b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar#include <linux/mutex.h> 2348b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtdblk_dev { 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd; 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int count; 2848b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar struct mutex cache_mutex; 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *cache_data; 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long cache_offset; 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int cache_size; 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} *mtdblks[MAX_MTD_DEVICES]; 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Cache stuff... 3797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Since typical flash erasable sectors are much larger than what Linux's 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * buffer cache can handle, we must implement read-modify-write on flash 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * sectors for each block write requests. To avoid over-erasing flash sectors 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and to speed things up, we locally cache a whole flash sector while it is 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * being written to until a different sector is required. 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void erase_callback(struct erase_info *done) 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(wait_q); 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5197894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int erase_write (struct mtd_info *mtd, unsigned long pos, 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int len, const char *buf) 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct erase_info erase; 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_queue_head_t wait_q; 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t retlen; 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * First, let's erase the flash block. 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds init_waitqueue_head(&wait_q); 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.mtd = mtd; 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.callback = erase_callback; 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.addr = pos; 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.len = len; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase.priv = (u_long)&wait_q; 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&wait_q, &wait); 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 74f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner ret = mtd->erase(mtd, &erase); 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) { 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_RUNNING); 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&wait_q, &wait); 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "on \"%s\" failed\n", 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pos, len, mtd->name); 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); /* Wait for erase to finish. */ 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&wait_q, &wait); 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Next, writhe data to flash. 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 91f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner ret = mtd->write(mtd, pos, len, &retlen, buf); 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen != len) 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int write_cached_data (struct mtdblk_dev *mtdblk) 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd = mtdblk->mtd; 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state != STATE_DIRTY) 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" " 10997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner "at 0x%lx, size 0x%x\n", mtd->name, 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset, mtdblk->cache_size); 11197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 11297894cda5773e59bd13e87b72077751099419a9fThomas Gleixner ret = erase_write (mtd, mtdblk->cache_offset, 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_size, mtdblk->cache_data); 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Here we could argubly set the cache state to STATE_CLEAN. 11997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * However this could lead to inconsistency since we will not 12097894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * be notified if this content is altered on the flash by other 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * means. Let's declare it empty and leave buffering tasks to 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the buffer cache instead. 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_EMPTY; 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12997894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int len, const char *buf) 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd = mtdblk->mtd; 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int sect_size = mtdblk->cache_size; 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t retlen; 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n", 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name, pos, len); 13997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!sect_size) 141f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner return mtd->write(mtd, pos, len, &retlen, buf); 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len > 0) { 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long sect_start = (pos/sect_size)*sect_size; 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int offset = pos - sect_start; 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int size = sect_size - offset; 14797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner if( size > len ) 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = len; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size == sect_size) { 15197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner /* 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We are covering a whole sector. Thus there is no 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * need to bother with the cache while it may still be 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * useful for other partial writes. 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = erase_write (mtd, pos, size, buf); 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Partial sector: need to use the cache */ 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state == STATE_DIRTY && 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset != sect_start) { 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = write_cached_data(mtdblk); 16597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner if (ret) 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state == STATE_EMPTY || 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset != sect_start) { 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* fill the cache with the current sector */ 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_EMPTY; 173f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner ret = mtd->read(mtd, sect_start, sect_size, 174f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner &retlen, mtdblk->cache_data); 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen != sect_size) 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset = sect_start; 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_size = sect_size; 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_CLEAN; 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* write data to our local cache */ 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy (mtdblk->cache_data + offset, buf, size); 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_DIRTY; 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf += size; 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pos += size; 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= size; 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 19997894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int len, char *buf) 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd = mtdblk->mtd; 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int sect_size = mtdblk->cache_size; 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t retlen; 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 20797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n", 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name, pos, len); 20997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!sect_size) 211f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner return mtd->read(mtd, pos, len, &retlen, buf); 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len > 0) { 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long sect_start = (pos/sect_size)*sect_size; 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int offset = pos - sect_start; 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int size = sect_size - offset; 21797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner if (size > len) 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = len; 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check if the requested data is already cached 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read the requested amount of data from our internal cache if it 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * contains what we want, otherwise we read the data directly 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * from flash. 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->cache_state != STATE_EMPTY && 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_offset == sect_start) { 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy (buf, mtdblk->cache_data + offset, size); 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 230f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner ret = mtd->read(mtd, pos, size, &retlen, buf); 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen != size) 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf += size; 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pos += size; 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= size; 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_readsect(struct mtd_blktrans_dev *dev, 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long block, char *buf) 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return do_cached_read(mtdblk, block<<9, 512, buf); 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_writesect(struct mtd_blktrans_dev *dev, 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long block, char *buf) 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!mtdblk->cache_data) 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINTR; 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* -EINTR is not really correct, but it is the best match 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * documented in man 2 write for all cases. We could also 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * return -EAGAIN sometimes, but why bother? 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return do_cached_write(mtdblk, block<<9, 512, buf); 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_open(struct mtd_blktrans_dev *mbd) 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtdblk_dev *mtdblk; 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd = mbd->mtd; 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int dev = mbd->devnum; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); 27597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblks[dev]) { 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblks[dev]->count++; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 28097894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK, it's not open. Create cache info for it */ 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!mtdblk) 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(mtdblk, 0, sizeof(*mtdblk)); 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->count = 1; 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->mtd = mtd; 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 29048b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_init(&mtdblk->cache_mutex); 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_state = STATE_EMPTY; 2927f7c08dfdf5bc0a7c906285d9d97b932f83e8979Jörn Engel if (mtdblk->mtd->type != MTD_RAM && mtdblk->mtd->erasesize) { 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_size = mtdblk->mtd->erasesize; 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->cache_data = NULL; 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblks[dev] = mtdblk; 29897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_release(struct mtd_blktrans_dev *mbd) 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int dev = mbd->devnum; 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtdblk_dev *mtdblk = mtdblks[dev]; 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 31148b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_lock(&mtdblk->cache_mutex); 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds write_cached_data(mtdblk); 31348b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_unlock(&mtdblk->cache_mutex); 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!--mtdblk->count) { 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* It was the last usage. Free the device */ 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblks[dev] = NULL; 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->mtd->sync) 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->mtd->sync(mtdblk->mtd); 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vfree(mtdblk->cache_data); 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtdblk); 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 32697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner} 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mtdblock_flush(struct mtd_blktrans_dev *dev) 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 33248b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_lock(&mtdblk->cache_mutex); 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds write_cached_data(mtdblk); 33448b192686dd20cb1576ae1d8ccd17a07971ef24aIngo Molnar mutex_unlock(&mtdblk->cache_mutex); 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtdblk->mtd->sync) 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtdblk->mtd->sync(mtdblk->mtd); 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev) 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(dev, 0, sizeof(*dev)); 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->mtd = mtd; 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->devnum = mtd->index; 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->blksize = 512; 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->size = mtd->size >> 9; 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->tr = tr; 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!(mtd->flags & MTD_WRITEABLE)) 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dev->readonly = 1; 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_mtd_blktrans_dev(dev); 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds del_mtd_blktrans_dev(dev); 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(dev); 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_blktrans_ops mtdblock_tr = { 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "mtdblock", 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .major = 31, 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .part_bits = 0, 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = mtdblock_open, 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .flush = mtdblock_flush, 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .release = mtdblock_release, 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .readsect = mtdblock_readsect, 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .writesect = mtdblock_writesect, 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .add_mtd = mtdblock_add_mtd, 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .remove_dev = mtdblock_remove_dev, 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .owner = THIS_MODULE, 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_mtdblock(void) 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return register_mtd_blktrans(&mtdblock_tr); 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_mtdblock(void) 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds deregister_mtd_blktrans(&mtdblock_tr); 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_mtdblock); 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_mtdblock); 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Nicolas Pitre <nico@cam.org> et al."); 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices"); 399