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 */
9a1c06609SJoe Perches
10a1c06609SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11a1c06609SJoe Perches
12d6a3f017SFelix Fietkau /*
13d6a3f017SFelix Fietkau * When the first attempt at device initialization fails, we may need to
14d6a3f017SFelix Fietkau * wait a little bit and retry. This timeout, by default 3 seconds, gives
15d6a3f017SFelix Fietkau * device time to start up. Required on BCM2708 and a few other chipsets.
16d6a3f017SFelix Fietkau */
17d6a3f017SFelix Fietkau #define MTD_DEFAULT_TIMEOUT 3
18d6a3f017SFelix Fietkau
191da177e4SLinus Torvalds #include <linux/module.h>
20d6a3f017SFelix Fietkau #include <linux/delay.h>
211da177e4SLinus Torvalds #include <linux/fs.h>
221da177e4SLinus Torvalds #include <linux/blkdev.h>
2366114cadSTejun Heo #include <linux/backing-dev.h>
241da177e4SLinus Torvalds #include <linux/bio.h>
251da177e4SLinus Torvalds #include <linux/pagemap.h>
261da177e4SLinus Torvalds #include <linux/list.h>
271da177e4SLinus Torvalds #include <linux/init.h>
281da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
2948b19268SIngo Molnar #include <linux/mutex.h>
30c4e7fb31SVille Herva #include <linux/mount.h>
315a0e3ad6STejun Heo #include <linux/slab.h>
32f83c3838SEzequiel Garcia #include <linux/major.h>
331da177e4SLinus Torvalds
34a04e9653SJoachim Wiberg /* Maximum number of comma-separated items in the 'block2mtd=' parameter */
357b09acdcSJoachim Wiberg #define BLOCK2MTD_PARAM_MAX_COUNT 3
36a04e9653SJoachim Wiberg
371da177e4SLinus Torvalds /* Info for the block device */
381da177e4SLinus Torvalds struct block2mtd_dev {
391da177e4SLinus Torvalds struct list_head list;
401da177e4SLinus Torvalds struct block_device *blkdev;
411da177e4SLinus Torvalds struct mtd_info mtd;
4248b19268SIngo Molnar struct mutex write_mutex;
431da177e4SLinus Torvalds };
441da177e4SLinus Torvalds
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds /* Static info about the MTD, used in cleanup_module */
471da177e4SLinus Torvalds static LIST_HEAD(blkmtd_device_list);
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds
page_read(struct address_space * mapping,pgoff_t index)50c3917a04SLiu Song static struct page *page_read(struct address_space *mapping, pgoff_t index)
511da177e4SLinus Torvalds {
526fe6900eSNick Piggin return read_mapping_page(mapping, index, NULL);
531da177e4SLinus Torvalds }
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds /* erase a specified part of the device */
_block2mtd_erase(struct block2mtd_dev * dev,loff_t to,size_t len)561da177e4SLinus Torvalds static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
591da177e4SLinus Torvalds struct page *page;
60c3917a04SLiu Song pgoff_t index = to >> PAGE_SHIFT; // page index
611da177e4SLinus Torvalds int pages = len >> PAGE_SHIFT;
621da177e4SLinus Torvalds u_long *p;
631da177e4SLinus Torvalds u_long *max;
641da177e4SLinus Torvalds
651da177e4SLinus Torvalds while (pages) {
6621d31f1fSJoern Engel page = page_read(mapping, index);
671da177e4SLinus Torvalds if (IS_ERR(page))
681da177e4SLinus Torvalds return PTR_ERR(page);
691da177e4SLinus Torvalds
700ffb74ccSJoern Engel max = page_address(page) + PAGE_SIZE;
710ffb74ccSJoern Engel for (p=page_address(page); p<max; p++)
721da177e4SLinus Torvalds if (*p != -1UL) {
731da177e4SLinus Torvalds lock_page(page);
741da177e4SLinus Torvalds memset(page_address(page), 0xff, PAGE_SIZE);
751da177e4SLinus Torvalds set_page_dirty(page);
761da177e4SLinus Torvalds unlock_page(page);
77031da73fSNeilBrown balance_dirty_pages_ratelimited(mapping);
781da177e4SLinus Torvalds break;
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds
8109cbfeafSKirill A. Shutemov put_page(page);
821da177e4SLinus Torvalds pages--;
831da177e4SLinus Torvalds index++;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds return 0;
861da177e4SLinus Torvalds }
block2mtd_erase(struct mtd_info * mtd,struct erase_info * instr)871da177e4SLinus Torvalds static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds struct block2mtd_dev *dev = mtd->priv;
901da177e4SLinus Torvalds size_t from = instr->addr;
911da177e4SLinus Torvalds size_t len = instr->len;
921da177e4SLinus Torvalds int err;
931da177e4SLinus Torvalds
9448b19268SIngo Molnar mutex_lock(&dev->write_mutex);
951da177e4SLinus Torvalds err = _block2mtd_erase(dev, from, len);
9648b19268SIngo Molnar mutex_unlock(&dev->write_mutex);
97e7bfb3fdSBoris Brezillon if (err)
98a1c06609SJoe Perches pr_err("erase failed err = %d\n", err);
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds return err;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds
block2mtd_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)1041da177e4SLinus Torvalds static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
1051da177e4SLinus Torvalds size_t *retlen, u_char *buf)
1061da177e4SLinus Torvalds {
1071da177e4SLinus Torvalds struct block2mtd_dev *dev = mtd->priv;
1081da177e4SLinus Torvalds struct page *page;
109c3917a04SLiu Song pgoff_t index = from >> PAGE_SHIFT;
110711c11b7SJoern Engel int offset = from & (PAGE_SIZE-1);
1111da177e4SLinus Torvalds int cpylen;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds while (len) {
1141da177e4SLinus Torvalds if ((offset + len) > PAGE_SIZE)
1151da177e4SLinus Torvalds cpylen = PAGE_SIZE - offset; // multiple pages
1161da177e4SLinus Torvalds else
1171da177e4SLinus Torvalds cpylen = len; // this page
1181da177e4SLinus Torvalds len = len - cpylen;
1191da177e4SLinus Torvalds
12021d31f1fSJoern Engel page = page_read(dev->blkdev->bd_inode->i_mapping, index);
1211da177e4SLinus Torvalds if (IS_ERR(page))
1221da177e4SLinus Torvalds return PTR_ERR(page);
1231da177e4SLinus Torvalds
1241da177e4SLinus Torvalds memcpy(buf, page_address(page) + offset, cpylen);
12509cbfeafSKirill A. Shutemov put_page(page);
1261da177e4SLinus Torvalds
1271da177e4SLinus Torvalds if (retlen)
1281da177e4SLinus Torvalds *retlen += cpylen;
1291da177e4SLinus Torvalds buf += cpylen;
1301da177e4SLinus Torvalds offset = 0;
1311da177e4SLinus Torvalds index++;
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds return 0;
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds /* write data to the underlying device */
_block2mtd_write(struct block2mtd_dev * dev,const u_char * buf,loff_t to,size_t len,size_t * retlen)1381da177e4SLinus Torvalds static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
1391da177e4SLinus Torvalds loff_t to, size_t len, size_t *retlen)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds struct page *page;
1421da177e4SLinus Torvalds struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
143c3917a04SLiu Song pgoff_t index = to >> PAGE_SHIFT; // page index
1441da177e4SLinus Torvalds int offset = to & ~PAGE_MASK; // page offset
1451da177e4SLinus Torvalds int cpylen;
1461da177e4SLinus Torvalds
1471da177e4SLinus Torvalds while (len) {
1481da177e4SLinus Torvalds if ((offset+len) > PAGE_SIZE)
1491da177e4SLinus Torvalds cpylen = PAGE_SIZE - offset; // multiple pages
1501da177e4SLinus Torvalds else
1511da177e4SLinus Torvalds cpylen = len; // this page
1521da177e4SLinus Torvalds len = len - cpylen;
1531da177e4SLinus Torvalds
15421d31f1fSJoern Engel page = page_read(mapping, index);
1551da177e4SLinus Torvalds if (IS_ERR(page))
1561da177e4SLinus Torvalds return PTR_ERR(page);
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds if (memcmp(page_address(page)+offset, buf, cpylen)) {
1591da177e4SLinus Torvalds lock_page(page);
1601da177e4SLinus Torvalds memcpy(page_address(page) + offset, buf, cpylen);
1611da177e4SLinus Torvalds set_page_dirty(page);
1621da177e4SLinus Torvalds unlock_page(page);
163031da73fSNeilBrown balance_dirty_pages_ratelimited(mapping);
1641da177e4SLinus Torvalds }
16509cbfeafSKirill A. Shutemov put_page(page);
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds if (retlen)
1681da177e4SLinus Torvalds *retlen += cpylen;
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds buf += cpylen;
1711da177e4SLinus Torvalds offset = 0;
1721da177e4SLinus Torvalds index++;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds return 0;
1751da177e4SLinus Torvalds }
176c4e7fb31SVille Herva
177c4e7fb31SVille Herva
block2mtd_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)1781da177e4SLinus Torvalds static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
1791da177e4SLinus Torvalds size_t *retlen, const u_char *buf)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds struct block2mtd_dev *dev = mtd->priv;
1821da177e4SLinus Torvalds int err;
1831da177e4SLinus Torvalds
18448b19268SIngo Molnar mutex_lock(&dev->write_mutex);
1851da177e4SLinus Torvalds err = _block2mtd_write(dev, buf, to, len, retlen);
18648b19268SIngo Molnar mutex_unlock(&dev->write_mutex);
1871da177e4SLinus Torvalds if (err > 0)
1881da177e4SLinus Torvalds err = 0;
1891da177e4SLinus Torvalds return err;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds /* sync the device - wait until the write queue is empty */
block2mtd_sync(struct mtd_info * mtd)1941da177e4SLinus Torvalds static void block2mtd_sync(struct mtd_info *mtd)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds struct block2mtd_dev *dev = mtd->priv;
1971da177e4SLinus Torvalds sync_blockdev(dev->blkdev);
1981da177e4SLinus Torvalds return;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds
2011da177e4SLinus Torvalds
block2mtd_free_device(struct block2mtd_dev * dev)2021da177e4SLinus Torvalds static void block2mtd_free_device(struct block2mtd_dev *dev)
2031da177e4SLinus Torvalds {
2041da177e4SLinus Torvalds if (!dev)
2051da177e4SLinus Torvalds return;
2061da177e4SLinus Torvalds
2071da177e4SLinus Torvalds kfree(dev->mtd.name);
2081da177e4SLinus Torvalds
2091da177e4SLinus Torvalds if (dev->blkdev) {
210fc0ecff6SAndrew Morton invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping,
211fc0ecff6SAndrew Morton 0, -1);
2122736e8eeSChristoph Hellwig blkdev_put(dev->blkdev, NULL);
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds kfree(dev);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
2188d03187eSChristoph Hellwig /*
2198d03187eSChristoph Hellwig * This function is marked __ref because it calls the __init marked
2208d03187eSChristoph Hellwig * early_lookup_bdev when called from the early boot code.
2218d03187eSChristoph Hellwig */
mdtblock_early_get_bdev(const char * devname,blk_mode_t mode,int timeout,struct block2mtd_dev * dev)2228d03187eSChristoph Hellwig static struct block_device __ref *mdtblock_early_get_bdev(const char *devname,
223*05bdb996SChristoph Hellwig blk_mode_t mode, int timeout, struct block2mtd_dev *dev)
224b2baa574SChristoph Hellwig {
225b2baa574SChristoph Hellwig struct block_device *bdev = ERR_PTR(-ENODEV);
226b2baa574SChristoph Hellwig #ifndef MODULE
227b2baa574SChristoph Hellwig int i;
228b2baa574SChristoph Hellwig
229b2baa574SChristoph Hellwig /*
2308d03187eSChristoph Hellwig * We can't use early_lookup_bdev from a running system.
2318d03187eSChristoph Hellwig */
2328d03187eSChristoph Hellwig if (system_state >= SYSTEM_RUNNING)
2338d03187eSChristoph Hellwig return bdev;
2348d03187eSChristoph Hellwig
2358d03187eSChristoph Hellwig /*
236b2baa574SChristoph Hellwig * We might not have the root device mounted at this point.
237b2baa574SChristoph Hellwig * Try to resolve the device name by other means.
238b2baa574SChristoph Hellwig */
239b2baa574SChristoph Hellwig for (i = 0; i <= timeout; i++) {
240b2baa574SChristoph Hellwig dev_t devt;
241b2baa574SChristoph Hellwig
242b2baa574SChristoph Hellwig if (i)
243b2baa574SChristoph Hellwig /*
244b2baa574SChristoph Hellwig * Calling wait_for_device_probe in the first loop
245b2baa574SChristoph Hellwig * was not enough, sleep for a bit in subsequent
246b2baa574SChristoph Hellwig * go-arounds.
247b2baa574SChristoph Hellwig */
248b2baa574SChristoph Hellwig msleep(1000);
249b2baa574SChristoph Hellwig wait_for_device_probe();
250b2baa574SChristoph Hellwig
251b2baa574SChristoph Hellwig if (!early_lookup_bdev(devname, &devt)) {
252b2baa574SChristoph Hellwig bdev = blkdev_get_by_dev(devt, mode, dev, NULL);
253b2baa574SChristoph Hellwig if (!IS_ERR(bdev))
254b2baa574SChristoph Hellwig break;
255b2baa574SChristoph Hellwig }
256b2baa574SChristoph Hellwig }
257b2baa574SChristoph Hellwig #endif
258b2baa574SChristoph Hellwig return bdev;
259b2baa574SChristoph Hellwig }
2601da177e4SLinus Torvalds
add_device(char * devname,int erase_size,char * label,int timeout)261d6a3f017SFelix Fietkau static struct block2mtd_dev *add_device(char *devname, int erase_size,
2627b09acdcSJoachim Wiberg char *label, int timeout)
2631da177e4SLinus Torvalds {
264*05bdb996SChristoph Hellwig const blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_WRITE;
265d40a18feSColin Ian King struct block_device *bdev;
2661da177e4SLinus Torvalds struct block2mtd_dev *dev;
267eadcf0d7SGreg Kroah-Hartman char *name;
2681da177e4SLinus Torvalds
2691da177e4SLinus Torvalds if (!devname)
2701da177e4SLinus Torvalds return NULL;
2711da177e4SLinus Torvalds
27295b93a0cSBurman Yan dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
2731da177e4SLinus Torvalds if (!dev)
2741da177e4SLinus Torvalds return NULL;
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds /* Get a handle on the device */
2770718afd4SChristoph Hellwig bdev = blkdev_get_by_path(devname, mode, dev, NULL);
278b2baa574SChristoph Hellwig if (IS_ERR(bdev))
279b2baa574SChristoph Hellwig bdev = mdtblock_early_get_bdev(devname, mode, timeout, dev);
2801da177e4SLinus Torvalds if (IS_ERR(bdev)) {
281a1c06609SJoe Perches pr_err("error: cannot open device %s\n", devname);
2824bed207cSFabian Frederick goto err_free_block2mtd;
2831da177e4SLinus Torvalds }
2841da177e4SLinus Torvalds dev->blkdev = bdev;
2851da177e4SLinus Torvalds
2861da177e4SLinus Torvalds if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
287a1c06609SJoe Perches pr_err("attempting to use an MTD device as a block device\n");
2884bed207cSFabian Frederick goto err_free_block2mtd;
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds
291ea6d833aSFabian Frederick if ((long)dev->blkdev->bd_inode->i_size % erase_size) {
292ea6d833aSFabian Frederick pr_err("erasesize must be a divisor of device size\n");
293ea6d833aSFabian Frederick goto err_free_block2mtd;
294ea6d833aSFabian Frederick }
295ea6d833aSFabian Frederick
29648b19268SIngo Molnar mutex_init(&dev->write_mutex);
2971da177e4SLinus Torvalds
2981da177e4SLinus Torvalds /* Setup the MTD structure */
2991da177e4SLinus Torvalds /* make the name contain the block device in */
3007b09acdcSJoachim Wiberg if (!label)
3014d682420SJulia Lawall name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
3027b09acdcSJoachim Wiberg else
3037b09acdcSJoachim Wiberg name = kstrdup(label, GFP_KERNEL);
304eadcf0d7SGreg Kroah-Hartman if (!name)
3054bed207cSFabian Frederick goto err_destroy_mutex;
3061da177e4SLinus Torvalds
307eadcf0d7SGreg Kroah-Hartman dev->mtd.name = name;
3081da177e4SLinus Torvalds
3091da177e4SLinus Torvalds dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
3101da177e4SLinus Torvalds dev->mtd.erasesize = erase_size;
31117ffc7baSArtem B. Bityutskiy dev->mtd.writesize = 1;
312b6043874SArtem Bityutskiy dev->mtd.writebufsize = PAGE_SIZE;
31321c8db9eSDavid Woodhouse dev->mtd.type = MTD_RAM;
3141da177e4SLinus Torvalds dev->mtd.flags = MTD_CAP_RAM;
3153c3c10bbSArtem Bityutskiy dev->mtd._erase = block2mtd_erase;
3163c3c10bbSArtem Bityutskiy dev->mtd._write = block2mtd_write;
3173c3c10bbSArtem Bityutskiy dev->mtd._sync = block2mtd_sync;
3183c3c10bbSArtem Bityutskiy dev->mtd._read = block2mtd_read;
3191da177e4SLinus Torvalds dev->mtd.priv = dev;
3201da177e4SLinus Torvalds dev->mtd.owner = THIS_MODULE;
3211da177e4SLinus Torvalds
322ee0e87b1SJamie Iles if (mtd_device_register(&dev->mtd, NULL, 0)) {
32325985edcSLucas De Marchi /* Device didn't get added, so free the entry */
3244bed207cSFabian Frederick goto err_destroy_mutex;
3251da177e4SLinus Torvalds }
326d6a3f017SFelix Fietkau
3271da177e4SLinus Torvalds list_add(&dev->list, &blkmtd_device_list);
328a1c06609SJoe Perches pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
329a1c06609SJoe Perches dev->mtd.index,
3307b09acdcSJoachim Wiberg label ? label : dev->mtd.name + strlen("block2mtd: "),
3311da177e4SLinus Torvalds dev->mtd.erasesize >> 10, dev->mtd.erasesize);
3321da177e4SLinus Torvalds return dev;
3331da177e4SLinus Torvalds
3344bed207cSFabian Frederick err_destroy_mutex:
3354bed207cSFabian Frederick mutex_destroy(&dev->write_mutex);
3364bed207cSFabian Frederick err_free_block2mtd:
3371da177e4SLinus Torvalds block2mtd_free_device(dev);
3381da177e4SLinus Torvalds return NULL;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds
3411da177e4SLinus Torvalds
342954c2422SJoern Engel /* This function works similar to reguler strtoul. In addition, it
343954c2422SJoern Engel * allows some suffixes for a more human-readable number format:
344954c2422SJoern Engel * ki, Ki, kiB, KiB - multiply result with 1024
345954c2422SJoern Engel * Mi, MiB - multiply result with 1024^2
346954c2422SJoern Engel * Gi, GiB - multiply result with 1024^3
347954c2422SJoern Engel */
ustrtoul(const char * cp,char ** endp,unsigned int base)3481da177e4SLinus Torvalds static int ustrtoul(const char *cp, char **endp, unsigned int base)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds unsigned long result = simple_strtoul(cp, endp, base);
3511da177e4SLinus Torvalds switch (**endp) {
3521da177e4SLinus Torvalds case 'G' :
3531da177e4SLinus Torvalds result *= 1024;
354025a06c1SMiquel Raynal fallthrough;
3551da177e4SLinus Torvalds case 'M':
3561da177e4SLinus Torvalds result *= 1024;
357025a06c1SMiquel Raynal fallthrough;
358954c2422SJoern Engel case 'K':
3591da177e4SLinus Torvalds case 'k':
3601da177e4SLinus Torvalds result *= 1024;
3611da177e4SLinus Torvalds /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
362954c2422SJoern Engel if ((*endp)[1] == 'i') {
363954c2422SJoern Engel if ((*endp)[2] == 'B')
364954c2422SJoern Engel (*endp) += 3;
365954c2422SJoern Engel else
3661da177e4SLinus Torvalds (*endp) += 2;
3671da177e4SLinus Torvalds }
368954c2422SJoern Engel }
3691da177e4SLinus Torvalds return result;
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds
3721da177e4SLinus Torvalds
parse_num(size_t * num,const char * token)373cc71229fSThomas Gleixner static int parse_num(size_t *num, const char *token)
3741da177e4SLinus Torvalds {
3751da177e4SLinus Torvalds char *endp;
376cc71229fSThomas Gleixner size_t n;
3771da177e4SLinus Torvalds
378cc71229fSThomas Gleixner n = (size_t) ustrtoul(token, &endp, 0);
3791da177e4SLinus Torvalds if (*endp)
3801da177e4SLinus Torvalds return -EINVAL;
3811da177e4SLinus Torvalds
382cc71229fSThomas Gleixner *num = n;
3831da177e4SLinus Torvalds return 0;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds
kill_final_newline(char * str)3871da177e4SLinus Torvalds static inline void kill_final_newline(char *str)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds char *newline = strrchr(str, '\n');
3901da177e4SLinus Torvalds if (newline && !newline[1])
3911da177e4SLinus Torvalds *newline = 0;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds
3941da177e4SLinus Torvalds
395c4e7fb31SVille Herva #ifndef MODULE
396c4e7fb31SVille Herva static int block2mtd_init_called = 0;
397d6a3f017SFelix Fietkau /* 80 for device, 12 for erase size */
398d6a3f017SFelix Fietkau static char block2mtd_paramline[80 + 12];
399c4e7fb31SVille Herva #endif
400c4e7fb31SVille Herva
block2mtd_setup2(const char * val)401c4e7fb31SVille Herva static int block2mtd_setup2(const char *val)
4021da177e4SLinus Torvalds {
403d6a3f017SFelix Fietkau /* 80 for device, 12 for erase size, 80 for name, 8 for timeout */
404d6a3f017SFelix Fietkau char buf[80 + 12 + 80 + 8];
405a6550e57SJesper Juhl char *str = buf;
406a04e9653SJoachim Wiberg char *token[BLOCK2MTD_PARAM_MAX_COUNT];
4071da177e4SLinus Torvalds char *name;
4087b09acdcSJoachim Wiberg char *label = NULL;
409cc71229fSThomas Gleixner size_t erase_size = PAGE_SIZE;
410d6a3f017SFelix Fietkau unsigned long timeout = MTD_DEFAULT_TIMEOUT;
4111da177e4SLinus Torvalds int i, ret;
4121da177e4SLinus Torvalds
413a1c06609SJoe Perches if (strnlen(val, sizeof(buf)) >= sizeof(buf)) {
414a1c06609SJoe Perches pr_err("parameter too long\n");
415a1c06609SJoe Perches return 0;
416a1c06609SJoe Perches }
4171da177e4SLinus Torvalds
4181da177e4SLinus Torvalds strcpy(str, val);
4191da177e4SLinus Torvalds kill_final_newline(str);
4201da177e4SLinus Torvalds
421a04e9653SJoachim Wiberg for (i = 0; i < BLOCK2MTD_PARAM_MAX_COUNT; i++)
4221da177e4SLinus Torvalds token[i] = strsep(&str, ",");
4231da177e4SLinus Torvalds
424a1c06609SJoe Perches if (str) {
425a1c06609SJoe Perches pr_err("too many arguments\n");
426a1c06609SJoe Perches return 0;
427a1c06609SJoe Perches }
4281da177e4SLinus Torvalds
429a1c06609SJoe Perches if (!token[0]) {
430a1c06609SJoe Perches pr_err("no argument\n");
431a1c06609SJoe Perches return 0;
432a1c06609SJoe Perches }
4331da177e4SLinus Torvalds
434c4e7fb31SVille Herva name = token[0];
435a1c06609SJoe Perches if (strlen(name) + 1 > 80) {
436a1c06609SJoe Perches pr_err("device name too long\n");
437a1c06609SJoe Perches return 0;
438a1c06609SJoe Perches }
4391da177e4SLinus Torvalds
4407b09acdcSJoachim Wiberg /* Optional argument when custom label is used */
4417b09acdcSJoachim Wiberg if (token[1] && strlen(token[1])) {
442cc71229fSThomas Gleixner ret = parse_num(&erase_size, token[1]);
443a6550e57SJesper Juhl if (ret) {
444a1c06609SJoe Perches pr_err("illegal erase size\n");
445a1c06609SJoe Perches return 0;
4461da177e4SLinus Torvalds }
447a6550e57SJesper Juhl }
4481da177e4SLinus Torvalds
4497b09acdcSJoachim Wiberg if (token[2]) {
4507b09acdcSJoachim Wiberg label = token[2];
4517b09acdcSJoachim Wiberg pr_info("Using custom MTD label '%s' for dev %s\n", label, name);
4527b09acdcSJoachim Wiberg }
4537b09acdcSJoachim Wiberg
4547b09acdcSJoachim Wiberg add_device(name, erase_size, label, timeout);
4551da177e4SLinus Torvalds
4561da177e4SLinus Torvalds return 0;
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds
4591da177e4SLinus Torvalds
block2mtd_setup(const char * val,const struct kernel_param * kp)460e4dca7b7SKees Cook static int block2mtd_setup(const char *val, const struct kernel_param *kp)
461c4e7fb31SVille Herva {
462c4e7fb31SVille Herva #ifdef MODULE
463c4e7fb31SVille Herva return block2mtd_setup2(val);
464c4e7fb31SVille Herva #else
465c4e7fb31SVille Herva /* If more parameters are later passed in via
466c4e7fb31SVille Herva /sys/module/block2mtd/parameters/block2mtd
467c4e7fb31SVille Herva and block2mtd_init() has already been called,
468c4e7fb31SVille Herva we can parse the argument now. */
469c4e7fb31SVille Herva
470c4e7fb31SVille Herva if (block2mtd_init_called)
471c4e7fb31SVille Herva return block2mtd_setup2(val);
472c4e7fb31SVille Herva
473c4e7fb31SVille Herva /* During early boot stage, we only save the parameters
474c4e7fb31SVille Herva here. We must parse them later: if the param passed
475c4e7fb31SVille Herva from kernel boot command line, block2mtd_setup() is
476c4e7fb31SVille Herva called so early that it is not possible to resolve
477c4e7fb31SVille Herva the device (even kmalloc() fails). Deter that work to
478c4e7fb31SVille Herva block2mtd_setup2(). */
479c4e7fb31SVille Herva
48080b7e928SWolfram Sang strscpy(block2mtd_paramline, val, sizeof(block2mtd_paramline));
481c4e7fb31SVille Herva
482c4e7fb31SVille Herva return 0;
483c4e7fb31SVille Herva #endif
484c4e7fb31SVille Herva }
485c4e7fb31SVille Herva
486c4e7fb31SVille Herva
4871da177e4SLinus Torvalds module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
4887b09acdcSJoachim Wiberg MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,[<erasesize>][,<label>]]\"");
4891da177e4SLinus Torvalds
block2mtd_init(void)4901da177e4SLinus Torvalds static int __init block2mtd_init(void)
4911da177e4SLinus Torvalds {
492c4e7fb31SVille Herva int ret = 0;
493c4e7fb31SVille Herva
494c4e7fb31SVille Herva #ifndef MODULE
495c4e7fb31SVille Herva if (strlen(block2mtd_paramline))
496c4e7fb31SVille Herva ret = block2mtd_setup2(block2mtd_paramline);
497c4e7fb31SVille Herva block2mtd_init_called = 1;
498c4e7fb31SVille Herva #endif
499c4e7fb31SVille Herva
500c4e7fb31SVille Herva return ret;
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds
5031da177e4SLinus Torvalds
block2mtd_exit(void)504810b7e06SBill Pemberton static void block2mtd_exit(void)
5051da177e4SLinus Torvalds {
5061da177e4SLinus Torvalds struct list_head *pos, *next;
5071da177e4SLinus Torvalds
5081da177e4SLinus Torvalds /* Remove the MTD devices */
5091da177e4SLinus Torvalds list_for_each_safe(pos, next, &blkmtd_device_list) {
5101da177e4SLinus Torvalds struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
5111da177e4SLinus Torvalds block2mtd_sync(&dev->mtd);
512ee0e87b1SJamie Iles mtd_device_unregister(&dev->mtd);
5134bed207cSFabian Frederick mutex_destroy(&dev->write_mutex);
514a1c06609SJoe Perches pr_info("mtd%d: [%s] removed\n",
515a1c06609SJoe Perches dev->mtd.index,
5160bc88c59SStephane Chazelas dev->mtd.name + strlen("block2mtd: "));
5171da177e4SLinus Torvalds list_del(&dev->list);
5181da177e4SLinus Torvalds block2mtd_free_device(dev);
5191da177e4SLinus Torvalds }
5201da177e4SLinus Torvalds }
5211da177e4SLinus Torvalds
522d6a3f017SFelix Fietkau late_initcall(block2mtd_init);
5231da177e4SLinus Torvalds module_exit(block2mtd_exit);
5241da177e4SLinus Torvalds
5251da177e4SLinus Torvalds MODULE_LICENSE("GPL");
5262b54aaefSJoern Engel MODULE_AUTHOR("Joern Engel <joern@lazybastard.org>");
5271da177e4SLinus Torvalds MODULE_DESCRIPTION("Emulate an MTD using a block device");
528