xref: /linux/drivers/mtd/devices/block2mtd.c (revision b604387411ec6a072e95910099262616edd2bd2f)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * block2mtd.c - create an mtd from a block device
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2001,2002	Simon Evans <spse@secret.org.uk>
52b54aaefSJoern Engel  * Copyright (C) 2004-2006	Joern Engel <joern@wh.fh-wedel.de>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Licence: GPL
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds #include <linux/fs.h>
111da177e4SLinus Torvalds #include <linux/blkdev.h>
121da177e4SLinus Torvalds #include <linux/bio.h>
131da177e4SLinus Torvalds #include <linux/pagemap.h>
141da177e4SLinus Torvalds #include <linux/list.h>
151da177e4SLinus Torvalds #include <linux/init.h>
161da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
1748b19268SIngo Molnar #include <linux/mutex.h>
18c4e7fb31SVille Herva #include <linux/mount.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args)
221da177e4SLinus Torvalds #define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args)
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /* Info for the block device */
261da177e4SLinus Torvalds struct block2mtd_dev {
271da177e4SLinus Torvalds 	struct list_head list;
281da177e4SLinus Torvalds 	struct block_device *blkdev;
291da177e4SLinus Torvalds 	struct mtd_info mtd;
3048b19268SIngo Molnar 	struct mutex write_mutex;
311da177e4SLinus Torvalds };
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds /* Static info about the MTD, used in cleanup_module */
351da177e4SLinus Torvalds static LIST_HEAD(blkmtd_device_list);
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 
3821d31f1fSJoern Engel static struct page *page_read(struct address_space *mapping, int index)
391da177e4SLinus Torvalds {
406fe6900eSNick Piggin 	return read_mapping_page(mapping, index, NULL);
411da177e4SLinus Torvalds }
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds /* erase a specified part of the device */
441da177e4SLinus Torvalds static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
471da177e4SLinus Torvalds 	struct page *page;
481da177e4SLinus Torvalds 	int index = to >> PAGE_SHIFT;	// page index
491da177e4SLinus Torvalds 	int pages = len >> PAGE_SHIFT;
501da177e4SLinus Torvalds 	u_long *p;
511da177e4SLinus Torvalds 	u_long *max;
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	while (pages) {
5421d31f1fSJoern Engel 		page = page_read(mapping, index);
551da177e4SLinus Torvalds 		if (!page)
561da177e4SLinus Torvalds 			return -ENOMEM;
571da177e4SLinus Torvalds 		if (IS_ERR(page))
581da177e4SLinus Torvalds 			return PTR_ERR(page);
591da177e4SLinus Torvalds 
600ffb74ccSJoern Engel 		max = page_address(page) + PAGE_SIZE;
610ffb74ccSJoern Engel 		for (p=page_address(page); p<max; p++)
621da177e4SLinus Torvalds 			if (*p != -1UL) {
631da177e4SLinus Torvalds 				lock_page(page);
641da177e4SLinus Torvalds 				memset(page_address(page), 0xff, PAGE_SIZE);
651da177e4SLinus Torvalds 				set_page_dirty(page);
661da177e4SLinus Torvalds 				unlock_page(page);
671da177e4SLinus Torvalds 				break;
681da177e4SLinus Torvalds 			}
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 		page_cache_release(page);
711da177e4SLinus Torvalds 		pages--;
721da177e4SLinus Torvalds 		index++;
731da177e4SLinus Torvalds 	}
741da177e4SLinus Torvalds 	return 0;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
791da177e4SLinus Torvalds 	size_t from = instr->addr;
801da177e4SLinus Torvalds 	size_t len = instr->len;
811da177e4SLinus Torvalds 	int err;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	instr->state = MTD_ERASING;
8448b19268SIngo Molnar 	mutex_lock(&dev->write_mutex);
851da177e4SLinus Torvalds 	err = _block2mtd_erase(dev, from, len);
8648b19268SIngo Molnar 	mutex_unlock(&dev->write_mutex);
871da177e4SLinus Torvalds 	if (err) {
881da177e4SLinus Torvalds 		ERROR("erase failed err = %d", err);
891da177e4SLinus Torvalds 		instr->state = MTD_ERASE_FAILED;
901da177e4SLinus Torvalds 	} else
911da177e4SLinus Torvalds 		instr->state = MTD_ERASE_DONE;
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	mtd_erase_callback(instr);
941da177e4SLinus Torvalds 	return err;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
991da177e4SLinus Torvalds 		size_t *retlen, u_char *buf)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
1021da177e4SLinus Torvalds 	struct page *page;
1031da177e4SLinus Torvalds 	int index = from >> PAGE_SHIFT;
104711c11b7SJoern Engel 	int offset = from & (PAGE_SIZE-1);
1051da177e4SLinus Torvalds 	int cpylen;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	if (from > mtd->size)
1081da177e4SLinus Torvalds 		return -EINVAL;
1091da177e4SLinus Torvalds 	if (from + len > mtd->size)
1101da177e4SLinus Torvalds 		len = mtd->size - from;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	if (retlen)
1131da177e4SLinus Torvalds 		*retlen = 0;
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	while (len) {
1161da177e4SLinus Torvalds 		if ((offset + len) > PAGE_SIZE)
1171da177e4SLinus Torvalds 			cpylen = PAGE_SIZE - offset;	// multiple pages
1181da177e4SLinus Torvalds 		else
1191da177e4SLinus Torvalds 			cpylen = len;	// this page
1201da177e4SLinus Torvalds 		len = len - cpylen;
1211da177e4SLinus Torvalds 
12221d31f1fSJoern Engel 		page = page_read(dev->blkdev->bd_inode->i_mapping, index);
1231da177e4SLinus Torvalds 		if (!page)
1241da177e4SLinus Torvalds 			return -ENOMEM;
1251da177e4SLinus Torvalds 		if (IS_ERR(page))
1261da177e4SLinus Torvalds 			return PTR_ERR(page);
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 		memcpy(buf, page_address(page) + offset, cpylen);
1291da177e4SLinus Torvalds 		page_cache_release(page);
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 		if (retlen)
1321da177e4SLinus Torvalds 			*retlen += cpylen;
1331da177e4SLinus Torvalds 		buf += cpylen;
1341da177e4SLinus Torvalds 		offset = 0;
1351da177e4SLinus Torvalds 		index++;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 	return 0;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds /* write data to the underlying device */
1421da177e4SLinus Torvalds static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
1431da177e4SLinus Torvalds 		loff_t to, size_t len, size_t *retlen)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	struct page *page;
1461da177e4SLinus Torvalds 	struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
1471da177e4SLinus Torvalds 	int index = to >> PAGE_SHIFT;	// page index
1481da177e4SLinus Torvalds 	int offset = to & ~PAGE_MASK;	// page offset
1491da177e4SLinus Torvalds 	int cpylen;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	if (retlen)
1521da177e4SLinus Torvalds 		*retlen = 0;
1531da177e4SLinus Torvalds 	while (len) {
1541da177e4SLinus Torvalds 		if ((offset+len) > PAGE_SIZE)
1551da177e4SLinus Torvalds 			cpylen = PAGE_SIZE - offset;	// multiple pages
1561da177e4SLinus Torvalds 		else
1571da177e4SLinus Torvalds 			cpylen = len;			// this page
1581da177e4SLinus Torvalds 		len = len - cpylen;
1591da177e4SLinus Torvalds 
16021d31f1fSJoern Engel 		page = page_read(mapping, index);
1611da177e4SLinus Torvalds 		if (!page)
1621da177e4SLinus Torvalds 			return -ENOMEM;
1631da177e4SLinus Torvalds 		if (IS_ERR(page))
1641da177e4SLinus Torvalds 			return PTR_ERR(page);
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 		if (memcmp(page_address(page)+offset, buf, cpylen)) {
1671da177e4SLinus Torvalds 			lock_page(page);
1681da177e4SLinus Torvalds 			memcpy(page_address(page) + offset, buf, cpylen);
1691da177e4SLinus Torvalds 			set_page_dirty(page);
1701da177e4SLinus Torvalds 			unlock_page(page);
1711da177e4SLinus Torvalds 		}
1721da177e4SLinus Torvalds 		page_cache_release(page);
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 		if (retlen)
1751da177e4SLinus Torvalds 			*retlen += cpylen;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 		buf += cpylen;
1781da177e4SLinus Torvalds 		offset = 0;
1791da177e4SLinus Torvalds 		index++;
1801da177e4SLinus Torvalds 	}
1811da177e4SLinus Torvalds 	return 0;
1821da177e4SLinus Torvalds }
183c4e7fb31SVille Herva 
184c4e7fb31SVille Herva 
1851da177e4SLinus Torvalds static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
1861da177e4SLinus Torvalds 		size_t *retlen, const u_char *buf)
1871da177e4SLinus Torvalds {
1881da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
1891da177e4SLinus Torvalds 	int err;
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	if (!len)
1921da177e4SLinus Torvalds 		return 0;
1931da177e4SLinus Torvalds 	if (to >= mtd->size)
1941da177e4SLinus Torvalds 		return -ENOSPC;
1951da177e4SLinus Torvalds 	if (to + len > mtd->size)
1961da177e4SLinus Torvalds 		len = mtd->size - to;
1971da177e4SLinus Torvalds 
19848b19268SIngo Molnar 	mutex_lock(&dev->write_mutex);
1991da177e4SLinus Torvalds 	err = _block2mtd_write(dev, buf, to, len, retlen);
20048b19268SIngo Molnar 	mutex_unlock(&dev->write_mutex);
2011da177e4SLinus Torvalds 	if (err > 0)
2021da177e4SLinus Torvalds 		err = 0;
2031da177e4SLinus Torvalds 	return err;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds /* sync the device - wait until the write queue is empty */
2081da177e4SLinus Torvalds static void block2mtd_sync(struct mtd_info *mtd)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
2111da177e4SLinus Torvalds 	sync_blockdev(dev->blkdev);
2121da177e4SLinus Torvalds 	return;
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds static void block2mtd_free_device(struct block2mtd_dev *dev)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	if (!dev)
2191da177e4SLinus Torvalds 		return;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	kfree(dev->mtd.name);
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	if (dev->blkdev) {
224fc0ecff6SAndrew Morton 		invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping,
225fc0ecff6SAndrew Morton 					0, -1);
226e525fd89STejun Heo 		blkdev_put(dev->blkdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
2271da177e4SLinus Torvalds 	}
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	kfree(dev);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds /* FIXME: ensure that mtd->size % erase_size == 0 */
2341da177e4SLinus Torvalds static struct block2mtd_dev *add_device(char *devname, int erase_size)
2351da177e4SLinus Torvalds {
236e525fd89STejun Heo 	const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
2371da177e4SLinus Torvalds 	struct block_device *bdev;
2381da177e4SLinus Torvalds 	struct block2mtd_dev *dev;
239eadcf0d7SGreg Kroah-Hartman 	char *name;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	if (!devname)
2421da177e4SLinus Torvalds 		return NULL;
2431da177e4SLinus Torvalds 
24495b93a0cSBurman Yan 	dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
2451da177e4SLinus Torvalds 	if (!dev)
2461da177e4SLinus Torvalds 		return NULL;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	/* Get a handle on the device */
249d4d77629STejun Heo 	bdev = blkdev_get_by_path(devname, mode, dev);
250c4e7fb31SVille Herva #ifndef MODULE
251c4e7fb31SVille Herva 	if (IS_ERR(bdev)) {
252c4e7fb31SVille Herva 
253c4e7fb31SVille Herva 		/* We might not have rootfs mounted at this point. Try
254c4e7fb31SVille Herva 		   to resolve the device name by other means. */
255c4e7fb31SVille Herva 
2568870530aSJoern Engel 		dev_t devt = name_to_dev_t(devname);
257e525fd89STejun Heo 		if (devt)
258d4d77629STejun Heo 			bdev = blkdev_get_by_dev(devt, mode, dev);
259c4e7fb31SVille Herva 	}
260c4e7fb31SVille Herva #endif
261c4e7fb31SVille Herva 
2621da177e4SLinus Torvalds 	if (IS_ERR(bdev)) {
2631da177e4SLinus Torvalds 		ERROR("error: cannot open device %s", devname);
2641da177e4SLinus Torvalds 		goto devinit_err;
2651da177e4SLinus Torvalds 	}
2661da177e4SLinus Torvalds 	dev->blkdev = bdev;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
2691da177e4SLinus Torvalds 		ERROR("attempting to use an MTD device as a block device");
2701da177e4SLinus Torvalds 		goto devinit_err;
2711da177e4SLinus Torvalds 	}
2721da177e4SLinus Torvalds 
27348b19268SIngo Molnar 	mutex_init(&dev->write_mutex);
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 	/* Setup the MTD structure */
2761da177e4SLinus Torvalds 	/* make the name contain the block device in */
2774d682420SJulia Lawall 	name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
278eadcf0d7SGreg Kroah-Hartman 	if (!name)
2791da177e4SLinus Torvalds 		goto devinit_err;
2801da177e4SLinus Torvalds 
281eadcf0d7SGreg Kroah-Hartman 	dev->mtd.name = name;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
2841da177e4SLinus Torvalds 	dev->mtd.erasesize = erase_size;
28517ffc7baSArtem B. Bityutskiy 	dev->mtd.writesize = 1;
286*b6043874SArtem Bityutskiy 	dev->mtd.writebufsize = PAGE_SIZE;
28721c8db9eSDavid Woodhouse 	dev->mtd.type = MTD_RAM;
2881da177e4SLinus Torvalds 	dev->mtd.flags = MTD_CAP_RAM;
2893c3c10bbSArtem Bityutskiy 	dev->mtd._erase = block2mtd_erase;
2903c3c10bbSArtem Bityutskiy 	dev->mtd._write = block2mtd_write;
2913c3c10bbSArtem Bityutskiy 	dev->mtd._writev = mtd_writev;
2923c3c10bbSArtem Bityutskiy 	dev->mtd._sync = block2mtd_sync;
2933c3c10bbSArtem Bityutskiy 	dev->mtd._read = block2mtd_read;
2941da177e4SLinus Torvalds 	dev->mtd.priv = dev;
2951da177e4SLinus Torvalds 	dev->mtd.owner = THIS_MODULE;
2961da177e4SLinus Torvalds 
297ee0e87b1SJamie Iles 	if (mtd_device_register(&dev->mtd, NULL, 0)) {
29825985edcSLucas De Marchi 		/* Device didn't get added, so free the entry */
2991da177e4SLinus Torvalds 		goto devinit_err;
3001da177e4SLinus Torvalds 	}
3011da177e4SLinus Torvalds 	list_add(&dev->list, &blkmtd_device_list);
3021da177e4SLinus Torvalds 	INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
3030bc88c59SStephane Chazelas 			dev->mtd.name + strlen("block2mtd: "),
3041da177e4SLinus Torvalds 			dev->mtd.erasesize >> 10, dev->mtd.erasesize);
3051da177e4SLinus Torvalds 	return dev;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds devinit_err:
3081da177e4SLinus Torvalds 	block2mtd_free_device(dev);
3091da177e4SLinus Torvalds 	return NULL;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 
313954c2422SJoern Engel /* This function works similar to reguler strtoul.  In addition, it
314954c2422SJoern Engel  * allows some suffixes for a more human-readable number format:
315954c2422SJoern Engel  * ki, Ki, kiB, KiB	- multiply result with 1024
316954c2422SJoern Engel  * Mi, MiB		- multiply result with 1024^2
317954c2422SJoern Engel  * Gi, GiB		- multiply result with 1024^3
318954c2422SJoern Engel  */
3191da177e4SLinus Torvalds static int ustrtoul(const char *cp, char **endp, unsigned int base)
3201da177e4SLinus Torvalds {
3211da177e4SLinus Torvalds 	unsigned long result = simple_strtoul(cp, endp, base);
3221da177e4SLinus Torvalds 	switch (**endp) {
3231da177e4SLinus Torvalds 	case 'G' :
3241da177e4SLinus Torvalds 		result *= 1024;
3251da177e4SLinus Torvalds 	case 'M':
3261da177e4SLinus Torvalds 		result *= 1024;
327954c2422SJoern Engel 	case 'K':
3281da177e4SLinus Torvalds 	case 'k':
3291da177e4SLinus Torvalds 		result *= 1024;
3301da177e4SLinus Torvalds 	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
331954c2422SJoern Engel 		if ((*endp)[1] == 'i') {
332954c2422SJoern Engel 			if ((*endp)[2] == 'B')
333954c2422SJoern Engel 				(*endp) += 3;
334954c2422SJoern Engel 			else
3351da177e4SLinus Torvalds 				(*endp) += 2;
3361da177e4SLinus Torvalds 		}
337954c2422SJoern Engel 	}
3381da177e4SLinus Torvalds 	return result;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 
342cc71229fSThomas Gleixner static int parse_num(size_t *num, const char *token)
3431da177e4SLinus Torvalds {
3441da177e4SLinus Torvalds 	char *endp;
345cc71229fSThomas Gleixner 	size_t n;
3461da177e4SLinus Torvalds 
347cc71229fSThomas Gleixner 	n = (size_t) ustrtoul(token, &endp, 0);
3481da177e4SLinus Torvalds 	if (*endp)
3491da177e4SLinus Torvalds 		return -EINVAL;
3501da177e4SLinus Torvalds 
351cc71229fSThomas Gleixner 	*num = n;
3521da177e4SLinus Torvalds 	return 0;
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds static inline void kill_final_newline(char *str)
3571da177e4SLinus Torvalds {
3581da177e4SLinus Torvalds 	char *newline = strrchr(str, '\n');
3591da177e4SLinus Torvalds 	if (newline && !newline[1])
3601da177e4SLinus Torvalds 		*newline = 0;
3611da177e4SLinus Torvalds }
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds #define parse_err(fmt, args...) do {	\
3650bc88c59SStephane Chazelas 	ERROR(fmt, ## args);		\
3661da177e4SLinus Torvalds 	return 0;			\
3671da177e4SLinus Torvalds } while (0)
3681da177e4SLinus Torvalds 
369c4e7fb31SVille Herva #ifndef MODULE
370c4e7fb31SVille Herva static int block2mtd_init_called = 0;
3714839f048SAdrian Bunk static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
372c4e7fb31SVille Herva #endif
373c4e7fb31SVille Herva 
374c4e7fb31SVille Herva 
375c4e7fb31SVille Herva static int block2mtd_setup2(const char *val)
3761da177e4SLinus Torvalds {
377a6550e57SJesper Juhl 	char buf[80 + 12]; /* 80 for device, 12 for erase size */
378a6550e57SJesper Juhl 	char *str = buf;
3791da177e4SLinus Torvalds 	char *token[2];
3801da177e4SLinus Torvalds 	char *name;
381cc71229fSThomas Gleixner 	size_t erase_size = PAGE_SIZE;
3821da177e4SLinus Torvalds 	int i, ret;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	if (strnlen(val, sizeof(buf)) >= sizeof(buf))
3851da177e4SLinus Torvalds 		parse_err("parameter too long");
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	strcpy(str, val);
3881da177e4SLinus Torvalds 	kill_final_newline(str);
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	for (i = 0; i < 2; i++)
3911da177e4SLinus Torvalds 		token[i] = strsep(&str, ",");
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	if (str)
3941da177e4SLinus Torvalds 		parse_err("too many arguments");
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	if (!token[0])
3971da177e4SLinus Torvalds 		parse_err("no argument");
3981da177e4SLinus Torvalds 
399c4e7fb31SVille Herva 	name = token[0];
400c4e7fb31SVille Herva 	if (strlen(name) + 1 > 80)
401c4e7fb31SVille Herva 		parse_err("device name too long");
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	if (token[1]) {
404cc71229fSThomas Gleixner 		ret = parse_num(&erase_size, token[1]);
405a6550e57SJesper Juhl 		if (ret) {
4061da177e4SLinus Torvalds 			parse_err("illegal erase size");
4071da177e4SLinus Torvalds 		}
408a6550e57SJesper Juhl 	}
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	add_device(name, erase_size);
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	return 0;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 
416c4e7fb31SVille Herva static int block2mtd_setup(const char *val, struct kernel_param *kp)
417c4e7fb31SVille Herva {
418c4e7fb31SVille Herva #ifdef MODULE
419c4e7fb31SVille Herva 	return block2mtd_setup2(val);
420c4e7fb31SVille Herva #else
421c4e7fb31SVille Herva 	/* If more parameters are later passed in via
422c4e7fb31SVille Herva 	   /sys/module/block2mtd/parameters/block2mtd
423c4e7fb31SVille Herva 	   and block2mtd_init() has already been called,
424c4e7fb31SVille Herva 	   we can parse the argument now. */
425c4e7fb31SVille Herva 
426c4e7fb31SVille Herva 	if (block2mtd_init_called)
427c4e7fb31SVille Herva 		return block2mtd_setup2(val);
428c4e7fb31SVille Herva 
429c4e7fb31SVille Herva 	/* During early boot stage, we only save the parameters
430c4e7fb31SVille Herva 	   here. We must parse them later: if the param passed
431c4e7fb31SVille Herva 	   from kernel boot command line, block2mtd_setup() is
432c4e7fb31SVille Herva 	   called so early that it is not possible to resolve
433c4e7fb31SVille Herva 	   the device (even kmalloc() fails). Deter that work to
434c4e7fb31SVille Herva 	   block2mtd_setup2(). */
435c4e7fb31SVille Herva 
436c4e7fb31SVille Herva 	strlcpy(block2mtd_paramline, val, sizeof(block2mtd_paramline));
437c4e7fb31SVille Herva 
438c4e7fb31SVille Herva 	return 0;
439c4e7fb31SVille Herva #endif
440c4e7fb31SVille Herva }
441c4e7fb31SVille Herva 
442c4e7fb31SVille Herva 
4431da177e4SLinus Torvalds module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
4441da177e4SLinus Torvalds MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\"");
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds static int __init block2mtd_init(void)
4471da177e4SLinus Torvalds {
448c4e7fb31SVille Herva 	int ret = 0;
449c4e7fb31SVille Herva 
450c4e7fb31SVille Herva #ifndef MODULE
451c4e7fb31SVille Herva 	if (strlen(block2mtd_paramline))
452c4e7fb31SVille Herva 		ret = block2mtd_setup2(block2mtd_paramline);
453c4e7fb31SVille Herva 	block2mtd_init_called = 1;
454c4e7fb31SVille Herva #endif
455c4e7fb31SVille Herva 
456c4e7fb31SVille Herva 	return ret;
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds static void __devexit block2mtd_exit(void)
4611da177e4SLinus Torvalds {
4621da177e4SLinus Torvalds 	struct list_head *pos, *next;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	/* Remove the MTD devices */
4651da177e4SLinus Torvalds 	list_for_each_safe(pos, next, &blkmtd_device_list) {
4661da177e4SLinus Torvalds 		struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
4671da177e4SLinus Torvalds 		block2mtd_sync(&dev->mtd);
468ee0e87b1SJamie Iles 		mtd_device_unregister(&dev->mtd);
4691da177e4SLinus Torvalds 		INFO("mtd%d: [%s] removed", dev->mtd.index,
4700bc88c59SStephane Chazelas 				dev->mtd.name + strlen("block2mtd: "));
4711da177e4SLinus Torvalds 		list_del(&dev->list);
4721da177e4SLinus Torvalds 		block2mtd_free_device(dev);
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds module_init(block2mtd_init);
4781da177e4SLinus Torvalds module_exit(block2mtd_exit);
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds MODULE_LICENSE("GPL");
4812b54aaefSJoern Engel MODULE_AUTHOR("Joern Engel <joern@lazybastard.org>");
4821da177e4SLinus Torvalds MODULE_DESCRIPTION("Emulate an MTD using a block device");
483