xref: /linux/drivers/mtd/devices/block2mtd.c (revision c4e7fb313771ac03dfdca26d30e8b721731c562b)
11da177e4SLinus Torvalds /*
22b9175c1SAdrian Bunk  * $Id: block2mtd.c,v 1.30 2005/11/29 14:48:32 gleixner Exp $
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * block2mtd.c - create an mtd from a block device
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 2001,2002	Simon Evans <spse@secret.org.uk>
7151e7659SDavid Woodhouse  * Copyright (C) 2004-2006	Jörn Engel <joern@wh.fh-wedel.de>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Licence: GPL
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/fs.h>
131da177e4SLinus Torvalds #include <linux/blkdev.h>
141da177e4SLinus Torvalds #include <linux/bio.h>
151da177e4SLinus Torvalds #include <linux/pagemap.h>
161da177e4SLinus Torvalds #include <linux/list.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
191da177e4SLinus Torvalds #include <linux/buffer_head.h>
2048b19268SIngo Molnar #include <linux/mutex.h>
21*c4e7fb31SVille Herva #include <linux/mount.h>
221da177e4SLinus Torvalds 
232b9175c1SAdrian Bunk #define VERSION "$Revision: 1.30 $"
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args)
271da177e4SLinus Torvalds #define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args)
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds /* Info for the block device */
311da177e4SLinus Torvalds struct block2mtd_dev {
321da177e4SLinus Torvalds 	struct list_head list;
331da177e4SLinus Torvalds 	struct block_device *blkdev;
341da177e4SLinus Torvalds 	struct mtd_info mtd;
3548b19268SIngo Molnar 	struct mutex write_mutex;
361da177e4SLinus Torvalds };
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds /* Static info about the MTD, used in cleanup_module */
401da177e4SLinus Torvalds static LIST_HEAD(blkmtd_device_list);
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define PAGE_READAHEAD 64
442b9175c1SAdrian Bunk static void cache_readahead(struct address_space *mapping, int index)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	filler_t *filler = (filler_t*)mapping->a_ops->readpage;
471da177e4SLinus Torvalds 	int i, pagei;
481da177e4SLinus Torvalds 	unsigned ret = 0;
491da177e4SLinus Torvalds 	unsigned long end_index;
501da177e4SLinus Torvalds 	struct page *page;
511da177e4SLinus Torvalds 	LIST_HEAD(page_pool);
521da177e4SLinus Torvalds 	struct inode *inode = mapping->host;
531da177e4SLinus Torvalds 	loff_t isize = i_size_read(inode);
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	if (!isize) {
561da177e4SLinus Torvalds 		INFO("iSize=0 in cache_readahead\n");
571da177e4SLinus Torvalds 		return;
581da177e4SLinus Torvalds 	}
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	end_index = ((isize - 1) >> PAGE_CACHE_SHIFT);
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	read_lock_irq(&mapping->tree_lock);
631da177e4SLinus Torvalds 	for (i = 0; i < PAGE_READAHEAD; i++) {
641da177e4SLinus Torvalds 		pagei = index + i;
651da177e4SLinus Torvalds 		if (pagei > end_index) {
661da177e4SLinus Torvalds 			INFO("Overrun end of disk in cache readahead\n");
671da177e4SLinus Torvalds 			break;
681da177e4SLinus Torvalds 		}
691da177e4SLinus Torvalds 		page = radix_tree_lookup(&mapping->page_tree, pagei);
701da177e4SLinus Torvalds 		if (page && (!i))
711da177e4SLinus Torvalds 			break;
721da177e4SLinus Torvalds 		if (page)
731da177e4SLinus Torvalds 			continue;
741da177e4SLinus Torvalds 		read_unlock_irq(&mapping->tree_lock);
751da177e4SLinus Torvalds 		page = page_cache_alloc_cold(mapping);
761da177e4SLinus Torvalds 		read_lock_irq(&mapping->tree_lock);
771da177e4SLinus Torvalds 		if (!page)
781da177e4SLinus Torvalds 			break;
791da177e4SLinus Torvalds 		page->index = pagei;
801da177e4SLinus Torvalds 		list_add(&page->lru, &page_pool);
811da177e4SLinus Torvalds 		ret++;
821da177e4SLinus Torvalds 	}
831da177e4SLinus Torvalds 	read_unlock_irq(&mapping->tree_lock);
841da177e4SLinus Torvalds 	if (ret)
851da177e4SLinus Torvalds 		read_cache_pages(mapping, &page_pool, filler, NULL);
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds static struct page* page_readahead(struct address_space *mapping, int index)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	filler_t *filler = (filler_t*)mapping->a_ops->readpage;
921da177e4SLinus Torvalds 	cache_readahead(mapping, index);
931da177e4SLinus Torvalds 	return read_cache_page(mapping, index, filler, NULL);
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds /* erase a specified part of the device */
981da177e4SLinus Torvalds static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
1011da177e4SLinus Torvalds 	struct page *page;
1021da177e4SLinus Torvalds 	int index = to >> PAGE_SHIFT;	// page index
1031da177e4SLinus Torvalds 	int pages = len >> PAGE_SHIFT;
1041da177e4SLinus Torvalds 	u_long *p;
1051da177e4SLinus Torvalds 	u_long *max;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	while (pages) {
1081da177e4SLinus Torvalds 		page = page_readahead(mapping, index);
1091da177e4SLinus Torvalds 		if (!page)
1101da177e4SLinus Torvalds 			return -ENOMEM;
1111da177e4SLinus Torvalds 		if (IS_ERR(page))
1121da177e4SLinus Torvalds 			return PTR_ERR(page);
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 		max = (u_long*)page_address(page) + PAGE_SIZE;
1151da177e4SLinus Torvalds 		for (p=(u_long*)page_address(page); p<max; p++)
1161da177e4SLinus Torvalds 			if (*p != -1UL) {
1171da177e4SLinus Torvalds 				lock_page(page);
1181da177e4SLinus Torvalds 				memset(page_address(page), 0xff, PAGE_SIZE);
1191da177e4SLinus Torvalds 				set_page_dirty(page);
1201da177e4SLinus Torvalds 				unlock_page(page);
1211da177e4SLinus Torvalds 				break;
1221da177e4SLinus Torvalds 			}
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 		page_cache_release(page);
1251da177e4SLinus Torvalds 		pages--;
1261da177e4SLinus Torvalds 		index++;
1271da177e4SLinus Torvalds 	}
1281da177e4SLinus Torvalds 	return 0;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
1331da177e4SLinus Torvalds 	size_t from = instr->addr;
1341da177e4SLinus Torvalds 	size_t len = instr->len;
1351da177e4SLinus Torvalds 	int err;
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	instr->state = MTD_ERASING;
13848b19268SIngo Molnar 	mutex_lock(&dev->write_mutex);
1391da177e4SLinus Torvalds 	err = _block2mtd_erase(dev, from, len);
14048b19268SIngo Molnar 	mutex_unlock(&dev->write_mutex);
1411da177e4SLinus Torvalds 	if (err) {
1421da177e4SLinus Torvalds 		ERROR("erase failed err = %d", err);
1431da177e4SLinus Torvalds 		instr->state = MTD_ERASE_FAILED;
1441da177e4SLinus Torvalds 	} else
1451da177e4SLinus Torvalds 		instr->state = MTD_ERASE_DONE;
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds 	instr->state = MTD_ERASE_DONE;
1481da177e4SLinus Torvalds 	mtd_erase_callback(instr);
1491da177e4SLinus Torvalds 	return err;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
1541da177e4SLinus Torvalds 		size_t *retlen, u_char *buf)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
1571da177e4SLinus Torvalds 	struct page *page;
1581da177e4SLinus Torvalds 	int index = from >> PAGE_SHIFT;
159711c11b7SJoern Engel 	int offset = from & (PAGE_SIZE-1);
1601da177e4SLinus Torvalds 	int cpylen;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	if (from > mtd->size)
1631da177e4SLinus Torvalds 		return -EINVAL;
1641da177e4SLinus Torvalds 	if (from + len > mtd->size)
1651da177e4SLinus Torvalds 		len = mtd->size - from;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	if (retlen)
1681da177e4SLinus Torvalds 		*retlen = 0;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	while (len) {
1711da177e4SLinus Torvalds 		if ((offset + len) > PAGE_SIZE)
1721da177e4SLinus Torvalds 			cpylen = PAGE_SIZE - offset;	// multiple pages
1731da177e4SLinus Torvalds 		else
1741da177e4SLinus Torvalds 			cpylen = len;	// this page
1751da177e4SLinus Torvalds 		len = len - cpylen;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 		//      Get page
1781da177e4SLinus Torvalds 		page = page_readahead(dev->blkdev->bd_inode->i_mapping, index);
1791da177e4SLinus Torvalds 		if (!page)
1801da177e4SLinus Torvalds 			return -ENOMEM;
1811da177e4SLinus Torvalds 		if (IS_ERR(page))
1821da177e4SLinus Torvalds 			return PTR_ERR(page);
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 		memcpy(buf, page_address(page) + offset, cpylen);
1851da177e4SLinus Torvalds 		page_cache_release(page);
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 		if (retlen)
1881da177e4SLinus Torvalds 			*retlen += cpylen;
1891da177e4SLinus Torvalds 		buf += cpylen;
1901da177e4SLinus Torvalds 		offset = 0;
1911da177e4SLinus Torvalds 		index++;
1921da177e4SLinus Torvalds 	}
1931da177e4SLinus Torvalds 	return 0;
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /* write data to the underlying device */
1981da177e4SLinus Torvalds static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
1991da177e4SLinus Torvalds 		loff_t to, size_t len, size_t *retlen)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	struct page *page;
2021da177e4SLinus Torvalds 	struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
2031da177e4SLinus Torvalds 	int index = to >> PAGE_SHIFT;	// page index
2041da177e4SLinus Torvalds 	int offset = to & ~PAGE_MASK;	// page offset
2051da177e4SLinus Torvalds 	int cpylen;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	if (retlen)
2081da177e4SLinus Torvalds 		*retlen = 0;
2091da177e4SLinus Torvalds 	while (len) {
2101da177e4SLinus Torvalds 		if ((offset+len) > PAGE_SIZE)
2111da177e4SLinus Torvalds 			cpylen = PAGE_SIZE - offset;	// multiple pages
2121da177e4SLinus Torvalds 		else
2131da177e4SLinus Torvalds 			cpylen = len;			// this page
2141da177e4SLinus Torvalds 		len = len - cpylen;
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 		//	Get page
2171da177e4SLinus Torvalds 		page = page_readahead(mapping, index);
2181da177e4SLinus Torvalds 		if (!page)
2191da177e4SLinus Torvalds 			return -ENOMEM;
2201da177e4SLinus Torvalds 		if (IS_ERR(page))
2211da177e4SLinus Torvalds 			return PTR_ERR(page);
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 		if (memcmp(page_address(page)+offset, buf, cpylen)) {
2241da177e4SLinus Torvalds 			lock_page(page);
2251da177e4SLinus Torvalds 			memcpy(page_address(page) + offset, buf, cpylen);
2261da177e4SLinus Torvalds 			set_page_dirty(page);
2271da177e4SLinus Torvalds 			unlock_page(page);
2281da177e4SLinus Torvalds 		}
2291da177e4SLinus Torvalds 		page_cache_release(page);
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 		if (retlen)
2321da177e4SLinus Torvalds 			*retlen += cpylen;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 		buf += cpylen;
2351da177e4SLinus Torvalds 		offset = 0;
2361da177e4SLinus Torvalds 		index++;
2371da177e4SLinus Torvalds 	}
2381da177e4SLinus Torvalds 	return 0;
2391da177e4SLinus Torvalds }
240*c4e7fb31SVille Herva 
241*c4e7fb31SVille Herva 
2421da177e4SLinus Torvalds static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
2431da177e4SLinus Torvalds 		size_t *retlen, const u_char *buf)
2441da177e4SLinus Torvalds {
2451da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
2461da177e4SLinus Torvalds 	int err;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	if (!len)
2491da177e4SLinus Torvalds 		return 0;
2501da177e4SLinus Torvalds 	if (to >= mtd->size)
2511da177e4SLinus Torvalds 		return -ENOSPC;
2521da177e4SLinus Torvalds 	if (to + len > mtd->size)
2531da177e4SLinus Torvalds 		len = mtd->size - to;
2541da177e4SLinus Torvalds 
25548b19268SIngo Molnar 	mutex_lock(&dev->write_mutex);
2561da177e4SLinus Torvalds 	err = _block2mtd_write(dev, buf, to, len, retlen);
25748b19268SIngo Molnar 	mutex_unlock(&dev->write_mutex);
2581da177e4SLinus Torvalds 	if (err > 0)
2591da177e4SLinus Torvalds 		err = 0;
2601da177e4SLinus Torvalds 	return err;
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds /* sync the device - wait until the write queue is empty */
2651da177e4SLinus Torvalds static void block2mtd_sync(struct mtd_info *mtd)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
2681da177e4SLinus Torvalds 	sync_blockdev(dev->blkdev);
2691da177e4SLinus Torvalds 	return;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds static void block2mtd_free_device(struct block2mtd_dev *dev)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	if (!dev)
2761da177e4SLinus Torvalds 		return;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	kfree(dev->mtd.name);
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	if (dev->blkdev) {
2811da177e4SLinus Torvalds 		invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping);
2821da177e4SLinus Torvalds 		close_bdev_excl(dev->blkdev);
2831da177e4SLinus Torvalds 	}
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	kfree(dev);
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds /* FIXME: ensure that mtd->size % erase_size == 0 */
2901da177e4SLinus Torvalds static struct block2mtd_dev *add_device(char *devname, int erase_size)
2911da177e4SLinus Torvalds {
2921da177e4SLinus Torvalds 	struct block_device *bdev;
2931da177e4SLinus Torvalds 	struct block2mtd_dev *dev;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	if (!devname)
2961da177e4SLinus Torvalds 		return NULL;
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
2991da177e4SLinus Torvalds 	if (!dev)
3001da177e4SLinus Torvalds 		return NULL;
3011da177e4SLinus Torvalds 	memset(dev, 0, sizeof(*dev));
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	/* Get a handle on the device */
3041da177e4SLinus Torvalds 	bdev = open_bdev_excl(devname, O_RDWR, NULL);
305*c4e7fb31SVille Herva #ifndef MODULE
306*c4e7fb31SVille Herva 	if (IS_ERR(bdev)) {
307*c4e7fb31SVille Herva 
308*c4e7fb31SVille Herva 		/* We might not have rootfs mounted at this point. Try
309*c4e7fb31SVille Herva 		   to resolve the device name by other means. */
310*c4e7fb31SVille Herva 
311*c4e7fb31SVille Herva 		dev_t dev = name_to_dev_t(devname);
312*c4e7fb31SVille Herva 		if (dev != 0) {
313*c4e7fb31SVille Herva 			bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ);
314*c4e7fb31SVille Herva 		}
315*c4e7fb31SVille Herva 	}
316*c4e7fb31SVille Herva #endif
317*c4e7fb31SVille Herva 
3181da177e4SLinus Torvalds 	if (IS_ERR(bdev)) {
3191da177e4SLinus Torvalds 		ERROR("error: cannot open device %s", devname);
3201da177e4SLinus Torvalds 		goto devinit_err;
3211da177e4SLinus Torvalds 	}
3221da177e4SLinus Torvalds 	dev->blkdev = bdev;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
3251da177e4SLinus Torvalds 		ERROR("attempting to use an MTD device as a block device");
3261da177e4SLinus Torvalds 		goto devinit_err;
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds 
32948b19268SIngo Molnar 	mutex_init(&dev->write_mutex);
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	/* Setup the MTD structure */
3321da177e4SLinus Torvalds 	/* make the name contain the block device in */
3331da177e4SLinus Torvalds 	dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname),
3341da177e4SLinus Torvalds 			GFP_KERNEL);
3351da177e4SLinus Torvalds 	if (!dev->mtd.name)
3361da177e4SLinus Torvalds 		goto devinit_err;
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	sprintf(dev->mtd.name, "block2mtd: %s", devname);
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
3411da177e4SLinus Torvalds 	dev->mtd.erasesize = erase_size;
34217ffc7baSArtem B. Bityutskiy 	dev->mtd.writesize = 1;
34321c8db9eSDavid Woodhouse 	dev->mtd.type = MTD_RAM;
3441da177e4SLinus Torvalds 	dev->mtd.flags = MTD_CAP_RAM;
3451da177e4SLinus Torvalds 	dev->mtd.erase = block2mtd_erase;
3461da177e4SLinus Torvalds 	dev->mtd.write = block2mtd_write;
3471da177e4SLinus Torvalds 	dev->mtd.writev = default_mtd_writev;
3481da177e4SLinus Torvalds 	dev->mtd.sync = block2mtd_sync;
3491da177e4SLinus Torvalds 	dev->mtd.read = block2mtd_read;
3501da177e4SLinus Torvalds 	dev->mtd.priv = dev;
3511da177e4SLinus Torvalds 	dev->mtd.owner = THIS_MODULE;
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	if (add_mtd_device(&dev->mtd)) {
3541da177e4SLinus Torvalds 		/* Device didnt get added, so free the entry */
3551da177e4SLinus Torvalds 		goto devinit_err;
3561da177e4SLinus Torvalds 	}
3571da177e4SLinus Torvalds 	list_add(&dev->list, &blkmtd_device_list);
3581da177e4SLinus Torvalds 	INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
3591da177e4SLinus Torvalds 			dev->mtd.name + strlen("blkmtd: "),
3601da177e4SLinus Torvalds 			dev->mtd.erasesize >> 10, dev->mtd.erasesize);
3611da177e4SLinus Torvalds 	return dev;
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds devinit_err:
3641da177e4SLinus Torvalds 	block2mtd_free_device(dev);
3651da177e4SLinus Torvalds 	return NULL;
3661da177e4SLinus Torvalds }
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 
369954c2422SJoern Engel /* This function works similar to reguler strtoul.  In addition, it
370954c2422SJoern Engel  * allows some suffixes for a more human-readable number format:
371954c2422SJoern Engel  * ki, Ki, kiB, KiB	- multiply result with 1024
372954c2422SJoern Engel  * Mi, MiB		- multiply result with 1024^2
373954c2422SJoern Engel  * Gi, GiB		- multiply result with 1024^3
374954c2422SJoern Engel  */
3751da177e4SLinus Torvalds static int ustrtoul(const char *cp, char **endp, unsigned int base)
3761da177e4SLinus Torvalds {
3771da177e4SLinus Torvalds 	unsigned long result = simple_strtoul(cp, endp, base);
3781da177e4SLinus Torvalds 	switch (**endp) {
3791da177e4SLinus Torvalds 	case 'G' :
3801da177e4SLinus Torvalds 		result *= 1024;
3811da177e4SLinus Torvalds 	case 'M':
3821da177e4SLinus Torvalds 		result *= 1024;
383954c2422SJoern Engel 	case 'K':
3841da177e4SLinus Torvalds 	case 'k':
3851da177e4SLinus Torvalds 		result *= 1024;
3861da177e4SLinus Torvalds 	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
387954c2422SJoern Engel 		if ((*endp)[1] == 'i') {
388954c2422SJoern Engel 			if ((*endp)[2] == 'B')
389954c2422SJoern Engel 				(*endp) += 3;
390954c2422SJoern Engel 			else
3911da177e4SLinus Torvalds 				(*endp) += 2;
3921da177e4SLinus Torvalds 		}
393954c2422SJoern Engel 	}
3941da177e4SLinus Torvalds 	return result;
3951da177e4SLinus Torvalds }
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 
398cc71229fSThomas Gleixner static int parse_num(size_t *num, const char *token)
3991da177e4SLinus Torvalds {
4001da177e4SLinus Torvalds 	char *endp;
401cc71229fSThomas Gleixner 	size_t n;
4021da177e4SLinus Torvalds 
403cc71229fSThomas Gleixner 	n = (size_t) ustrtoul(token, &endp, 0);
4041da177e4SLinus Torvalds 	if (*endp)
4051da177e4SLinus Torvalds 		return -EINVAL;
4061da177e4SLinus Torvalds 
407cc71229fSThomas Gleixner 	*num = n;
4081da177e4SLinus Torvalds 	return 0;
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds static inline void kill_final_newline(char *str)
4131da177e4SLinus Torvalds {
4141da177e4SLinus Torvalds 	char *newline = strrchr(str, '\n');
4151da177e4SLinus Torvalds 	if (newline && !newline[1])
4161da177e4SLinus Torvalds 		*newline = 0;
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds #define parse_err(fmt, args...) do {		\
4211da177e4SLinus Torvalds 	ERROR("block2mtd: " fmt "\n", ## args);	\
4221da177e4SLinus Torvalds 	return 0;				\
4231da177e4SLinus Torvalds } while (0)
4241da177e4SLinus Torvalds 
425*c4e7fb31SVille Herva #ifndef MODULE
426*c4e7fb31SVille Herva static int block2mtd_init_called = 0;
427*c4e7fb31SVille Herva static __initdata char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
428*c4e7fb31SVille Herva #endif
429*c4e7fb31SVille Herva 
430*c4e7fb31SVille Herva 
431*c4e7fb31SVille Herva static int block2mtd_setup2(const char *val)
4321da177e4SLinus Torvalds {
433a6550e57SJesper Juhl 	char buf[80 + 12]; /* 80 for device, 12 for erase size */
434a6550e57SJesper Juhl 	char *str = buf;
4351da177e4SLinus Torvalds 	char *token[2];
4361da177e4SLinus Torvalds 	char *name;
437cc71229fSThomas Gleixner 	size_t erase_size = PAGE_SIZE;
4381da177e4SLinus Torvalds 	int i, ret;
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 	if (strnlen(val, sizeof(buf)) >= sizeof(buf))
4411da177e4SLinus Torvalds 		parse_err("parameter too long");
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	strcpy(str, val);
4441da177e4SLinus Torvalds 	kill_final_newline(str);
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	for (i = 0; i < 2; i++)
4471da177e4SLinus Torvalds 		token[i] = strsep(&str, ",");
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	if (str)
4501da177e4SLinus Torvalds 		parse_err("too many arguments");
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	if (!token[0])
4531da177e4SLinus Torvalds 		parse_err("no argument");
4541da177e4SLinus Torvalds 
455*c4e7fb31SVille Herva 	name = token[0];
456*c4e7fb31SVille Herva 	if (strlen(name) + 1 > 80)
457*c4e7fb31SVille Herva 		parse_err("device name too long");
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	if (token[1]) {
460cc71229fSThomas Gleixner 		ret = parse_num(&erase_size, token[1]);
461a6550e57SJesper Juhl 		if (ret) {
462a6550e57SJesper Juhl 			kfree(name);
4631da177e4SLinus Torvalds 			parse_err("illegal erase size");
4641da177e4SLinus Torvalds 		}
465a6550e57SJesper Juhl 	}
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	add_device(name, erase_size);
4681da177e4SLinus Torvalds 
4691da177e4SLinus Torvalds 	return 0;
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 
473*c4e7fb31SVille Herva static int block2mtd_setup(const char *val, struct kernel_param *kp)
474*c4e7fb31SVille Herva {
475*c4e7fb31SVille Herva #ifdef MODULE
476*c4e7fb31SVille Herva 	return block2mtd_setup2(val);
477*c4e7fb31SVille Herva #else
478*c4e7fb31SVille Herva 	/* If more parameters are later passed in via
479*c4e7fb31SVille Herva 	   /sys/module/block2mtd/parameters/block2mtd
480*c4e7fb31SVille Herva 	   and block2mtd_init() has already been called,
481*c4e7fb31SVille Herva 	   we can parse the argument now. */
482*c4e7fb31SVille Herva 
483*c4e7fb31SVille Herva 	if (block2mtd_init_called)
484*c4e7fb31SVille Herva 		return block2mtd_setup2(val);
485*c4e7fb31SVille Herva 
486*c4e7fb31SVille Herva 	/* During early boot stage, we only save the parameters
487*c4e7fb31SVille Herva 	   here. We must parse them later: if the param passed
488*c4e7fb31SVille Herva 	   from kernel boot command line, block2mtd_setup() is
489*c4e7fb31SVille Herva 	   called so early that it is not possible to resolve
490*c4e7fb31SVille Herva 	   the device (even kmalloc() fails). Deter that work to
491*c4e7fb31SVille Herva 	   block2mtd_setup2(). */
492*c4e7fb31SVille Herva 
493*c4e7fb31SVille Herva 	strlcpy(block2mtd_paramline, val, sizeof(block2mtd_paramline));
494*c4e7fb31SVille Herva 
495*c4e7fb31SVille Herva 	return 0;
496*c4e7fb31SVille Herva #endif
497*c4e7fb31SVille Herva }
498*c4e7fb31SVille Herva 
499*c4e7fb31SVille Herva 
5001da177e4SLinus Torvalds module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
5011da177e4SLinus Torvalds MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\"");
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds static int __init block2mtd_init(void)
5041da177e4SLinus Torvalds {
505*c4e7fb31SVille Herva 	int ret = 0;
5061da177e4SLinus Torvalds 	INFO("version " VERSION);
507*c4e7fb31SVille Herva 
508*c4e7fb31SVille Herva #ifndef MODULE
509*c4e7fb31SVille Herva 	if (strlen(block2mtd_paramline))
510*c4e7fb31SVille Herva 		ret = block2mtd_setup2(block2mtd_paramline);
511*c4e7fb31SVille Herva 	block2mtd_init_called = 1;
512*c4e7fb31SVille Herva #endif
513*c4e7fb31SVille Herva 
514*c4e7fb31SVille Herva 	return ret;
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds static void __devexit block2mtd_exit(void)
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds 	struct list_head *pos, *next;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	/* Remove the MTD devices */
5231da177e4SLinus Torvalds 	list_for_each_safe(pos, next, &blkmtd_device_list) {
5241da177e4SLinus Torvalds 		struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
5251da177e4SLinus Torvalds 		block2mtd_sync(&dev->mtd);
5261da177e4SLinus Torvalds 		del_mtd_device(&dev->mtd);
5271da177e4SLinus Torvalds 		INFO("mtd%d: [%s] removed", dev->mtd.index,
5281da177e4SLinus Torvalds 				dev->mtd.name + strlen("blkmtd: "));
5291da177e4SLinus Torvalds 		list_del(&dev->list);
5301da177e4SLinus Torvalds 		block2mtd_free_device(dev);
5311da177e4SLinus Torvalds 	}
5321da177e4SLinus Torvalds }
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds module_init(block2mtd_init);
5361da177e4SLinus Torvalds module_exit(block2mtd_exit);
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds MODULE_LICENSE("GPL");
5391da177e4SLinus Torvalds MODULE_AUTHOR("Simon Evans <spse@secret.org.uk> and others");
5401da177e4SLinus Torvalds MODULE_DESCRIPTION("Emulate an MTD using a block device");
541