11da177e4SLinus Torvalds /* 2a1452a37SDavid Woodhouse * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> 3a1452a37SDavid Woodhouse * 4a1452a37SDavid Woodhouse * This program is free software; you can redistribute it and/or modify 5a1452a37SDavid Woodhouse * it under the terms of the GNU General Public License as published by 6a1452a37SDavid Woodhouse * the Free Software Foundation; either version 2 of the License, or 7a1452a37SDavid Woodhouse * (at your option) any later version. 8a1452a37SDavid Woodhouse * 9a1452a37SDavid Woodhouse * This program is distributed in the hope that it will be useful, 10a1452a37SDavid Woodhouse * but WITHOUT ANY WARRANTY; without even the implied warranty of 11a1452a37SDavid Woodhouse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12a1452a37SDavid Woodhouse * GNU General Public License for more details. 13a1452a37SDavid Woodhouse * 14a1452a37SDavid Woodhouse * You should have received a copy of the GNU General Public License 15a1452a37SDavid Woodhouse * along with this program; if not, write to the Free Software 16a1452a37SDavid Woodhouse * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds */ 191da177e4SLinus Torvalds 2015fdc52fSThomas Gleixner #include <linux/device.h> 2115fdc52fSThomas Gleixner #include <linux/fs.h> 220c1eafdbSAndrew Morton #include <linux/mm.h> 239c74034fSArtem Bityutskiy #include <linux/err.h> 2415fdc52fSThomas Gleixner #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/kernel.h> 261da177e4SLinus Torvalds #include <linux/module.h> 2715fdc52fSThomas Gleixner #include <linux/slab.h> 2815fdc52fSThomas Gleixner #include <linux/sched.h> 295aa82940SArnd Bergmann #include <linux/mutex.h> 30402d3265SDavid Howells #include <linux/backing-dev.h> 3197718540SKevin Cernekee #include <linux/compat.h> 32cd874237SKirill A. Shutemov #include <linux/mount.h> 33d0f7959eSRoman Tereshonkov #include <linux/blkpg.h> 34b502bd11SMuthu Kumar #include <linux/magic.h> 35f83c3838SEzequiel Garcia #include <linux/major.h> 361da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 37d0f7959eSRoman Tereshonkov #include <linux/mtd/partitions.h> 38dd02b67dSAnatolij Gustschin #include <linux/mtd/map.h> 391da177e4SLinus Torvalds 4015fdc52fSThomas Gleixner #include <asm/uaccess.h> 419bc7b387STodd Poynor 42660685d9SArtem Bityutskiy #include "mtdcore.h" 43660685d9SArtem Bityutskiy 445aa82940SArnd Bergmann static DEFINE_MUTEX(mtd_mutex); 451da177e4SLinus Torvalds 46045e9a5dSNicolas Pitre /* 47f1a28c02SThomas Gleixner * Data structure to hold the pointer to the mtd device as well 4892394b5cSBrian Norris * as mode information of various use cases. 49045e9a5dSNicolas Pitre */ 50f1a28c02SThomas Gleixner struct mtd_file_info { 51f1a28c02SThomas Gleixner struct mtd_info *mtd; 52f1a28c02SThomas Gleixner enum mtd_file_modes mode; 53f1a28c02SThomas Gleixner }; 5431f4233bSNicolas Pitre 55969e57adSArtem Bityutskiy static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig) 561da177e4SLinus Torvalds { 57f1a28c02SThomas Gleixner struct mtd_file_info *mfi = file->private_data; 58b959957fSAl Viro return fixed_size_llseek(file, offset, orig, mfi->mtd->size); 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 61969e57adSArtem Bityutskiy static int mtdchar_open(struct inode *inode, struct file *file) 621da177e4SLinus Torvalds { 631da177e4SLinus Torvalds int minor = iminor(inode); 641da177e4SLinus Torvalds int devnum = minor >> 1; 656071239eSJonathan Corbet int ret = 0; 661da177e4SLinus Torvalds struct mtd_info *mtd; 67f1a28c02SThomas Gleixner struct mtd_file_info *mfi; 681da177e4SLinus Torvalds 69289c0522SBrian Norris pr_debug("MTD_open\n"); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* You can't open the RO devices RW */ 72aeb5d727SAl Viro if ((file->f_mode & FMODE_WRITE) && (minor & 1)) 731da177e4SLinus Torvalds return -EACCES; 741da177e4SLinus Torvalds 755aa82940SArnd Bergmann mutex_lock(&mtd_mutex); 761da177e4SLinus Torvalds mtd = get_mtd_device(NULL, devnum); 771da177e4SLinus Torvalds 786071239eSJonathan Corbet if (IS_ERR(mtd)) { 796071239eSJonathan Corbet ret = PTR_ERR(mtd); 806071239eSJonathan Corbet goto out; 816071239eSJonathan Corbet } 821da177e4SLinus Torvalds 83402d3265SDavid Howells if (mtd->type == MTD_ABSENT) { 846071239eSJonathan Corbet ret = -ENODEV; 85c65390f4SAl Viro goto out1; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* You can't open it RW if it's not a writeable device */ 89aeb5d727SAl Viro if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { 906071239eSJonathan Corbet ret = -EACCES; 91b4caecd4SChristoph Hellwig goto out1; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 94f1a28c02SThomas Gleixner mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); 95f1a28c02SThomas Gleixner if (!mfi) { 966071239eSJonathan Corbet ret = -ENOMEM; 97b4caecd4SChristoph Hellwig goto out1; 98f1a28c02SThomas Gleixner } 99f1a28c02SThomas Gleixner mfi->mtd = mtd; 100f1a28c02SThomas Gleixner file->private_data = mfi; 101c65390f4SAl Viro mutex_unlock(&mtd_mutex); 102c65390f4SAl Viro return 0; 103f1a28c02SThomas Gleixner 104c65390f4SAl Viro out1: 105c65390f4SAl Viro put_mtd_device(mtd); 1066071239eSJonathan Corbet out: 1075aa82940SArnd Bergmann mutex_unlock(&mtd_mutex); 1086071239eSJonathan Corbet return ret; 109969e57adSArtem Bityutskiy } /* mtdchar_open */ 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds /*====================================================================*/ 1121da177e4SLinus Torvalds 113969e57adSArtem Bityutskiy static int mtdchar_close(struct inode *inode, struct file *file) 1141da177e4SLinus Torvalds { 115f1a28c02SThomas Gleixner struct mtd_file_info *mfi = file->private_data; 116f1a28c02SThomas Gleixner struct mtd_info *mtd = mfi->mtd; 1171da177e4SLinus Torvalds 118289c0522SBrian Norris pr_debug("MTD_close\n"); 1191da177e4SLinus Torvalds 1207eafaed5SJoakim Tjernlund /* Only sync if opened RW */ 121327cf292SArtem Bityutskiy if ((file->f_mode & FMODE_WRITE)) 12285f2f2a8SArtem Bityutskiy mtd_sync(mtd); 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds put_mtd_device(mtd); 125f1a28c02SThomas Gleixner file->private_data = NULL; 126f1a28c02SThomas Gleixner kfree(mfi); 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds return 0; 129969e57adSArtem Bityutskiy } /* mtdchar_close */ 1301da177e4SLinus Torvalds 1313e45cf5eSGrant Erickson /* Back in June 2001, dwmw2 wrote: 1323e45cf5eSGrant Erickson * 1333e45cf5eSGrant Erickson * FIXME: This _really_ needs to die. In 2.5, we should lock the 1343e45cf5eSGrant Erickson * userspace buffer down and use it directly with readv/writev. 1353e45cf5eSGrant Erickson * 1363e45cf5eSGrant Erickson * The implementation below, using mtd_kmalloc_up_to, mitigates 1373e45cf5eSGrant Erickson * allocation failures when the system is under low-memory situations 1383e45cf5eSGrant Erickson * or if memory is highly fragmented at the cost of reducing the 1393e45cf5eSGrant Erickson * performance of the requested transfer due to a smaller buffer size. 1403e45cf5eSGrant Erickson * 1413e45cf5eSGrant Erickson * A more complex but more memory-efficient implementation based on 1423e45cf5eSGrant Erickson * get_user_pages and iovecs to cover extents of those pages is a 1433e45cf5eSGrant Erickson * longer-term goal, as intimated by dwmw2 above. However, for the 1443e45cf5eSGrant Erickson * write case, this requires yet more complex head and tail transfer 1453e45cf5eSGrant Erickson * handling when those head and tail offsets and sizes are such that 1463e45cf5eSGrant Erickson * alignment requirements are not met in the NAND subdriver. 1471da177e4SLinus Torvalds */ 1481da177e4SLinus Torvalds 149969e57adSArtem Bityutskiy static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count, 150969e57adSArtem Bityutskiy loff_t *ppos) 1511da177e4SLinus Torvalds { 152f1a28c02SThomas Gleixner struct mtd_file_info *mfi = file->private_data; 153f1a28c02SThomas Gleixner struct mtd_info *mtd = mfi->mtd; 15430fa9848SArtem Bityutskiy size_t retlen; 1551da177e4SLinus Torvalds size_t total_retlen=0; 1561da177e4SLinus Torvalds int ret=0; 1571da177e4SLinus Torvalds int len; 1583e45cf5eSGrant Erickson size_t size = count; 1591da177e4SLinus Torvalds char *kbuf; 1601da177e4SLinus Torvalds 161289c0522SBrian Norris pr_debug("MTD_read\n"); 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds if (*ppos + count > mtd->size) 1641da177e4SLinus Torvalds count = mtd->size - *ppos; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds if (!count) 1671da177e4SLinus Torvalds return 0; 1681da177e4SLinus Torvalds 1693e45cf5eSGrant Erickson kbuf = mtd_kmalloc_up_to(mtd, &size); 170b802c074SThago Galesi if (!kbuf) 171b802c074SThago Galesi return -ENOMEM; 172b802c074SThago Galesi 1731da177e4SLinus Torvalds while (count) { 1743e45cf5eSGrant Erickson len = min_t(size_t, count, size); 1751da177e4SLinus Torvalds 176f1a28c02SThomas Gleixner switch (mfi->mode) { 177beb133fcSBrian Norris case MTD_FILE_MODE_OTP_FACTORY: 178d264f72aSArtem Bityutskiy ret = mtd_read_fact_prot_reg(mtd, *ppos, len, 179d264f72aSArtem Bityutskiy &retlen, kbuf); 18031f4233bSNicolas Pitre break; 181beb133fcSBrian Norris case MTD_FILE_MODE_OTP_USER: 1824ea1cabbSArtem Bityutskiy ret = mtd_read_user_prot_reg(mtd, *ppos, len, 1834ea1cabbSArtem Bityutskiy &retlen, kbuf); 18431f4233bSNicolas Pitre break; 185beb133fcSBrian Norris case MTD_FILE_MODE_RAW: 186f1a28c02SThomas Gleixner { 187f1a28c02SThomas Gleixner struct mtd_oob_ops ops; 188f1a28c02SThomas Gleixner 1890612b9ddSBrian Norris ops.mode = MTD_OPS_RAW; 190f1a28c02SThomas Gleixner ops.datbuf = kbuf; 191f1a28c02SThomas Gleixner ops.oobbuf = NULL; 192f1a28c02SThomas Gleixner ops.len = len; 193f1a28c02SThomas Gleixner 194fd2819bbSArtem Bityutskiy ret = mtd_read_oob(mtd, *ppos, &ops); 195f1a28c02SThomas Gleixner retlen = ops.retlen; 196f1a28c02SThomas Gleixner break; 197f1a28c02SThomas Gleixner } 19831f4233bSNicolas Pitre default: 199329ad399SArtem Bityutskiy ret = mtd_read(mtd, *ppos, len, &retlen, kbuf); 20031f4233bSNicolas Pitre } 2017854d3f7SBrian Norris /* Nand returns -EBADMSG on ECC errors, but it returns 2021da177e4SLinus Torvalds * the data. For our userspace tools it is important 2037854d3f7SBrian Norris * to dump areas with ECC errors! 2049a1fcdfdSThomas Gleixner * For kernel internal usage it also might return -EUCLEAN 20525985edcSLucas De Marchi * to signal the caller that a bitflip has occurred and has 2069a1fcdfdSThomas Gleixner * been corrected by the ECC algorithm. 2071da177e4SLinus Torvalds * Userspace software which accesses NAND this way 2081da177e4SLinus Torvalds * must be aware of the fact that it deals with NAND 2091da177e4SLinus Torvalds */ 210d57f4054SBrian Norris if (!ret || mtd_is_bitflip_or_eccerr(ret)) { 2111da177e4SLinus Torvalds *ppos += retlen; 2121da177e4SLinus Torvalds if (copy_to_user(buf, kbuf, retlen)) { 2131da177e4SLinus Torvalds kfree(kbuf); 2141da177e4SLinus Torvalds return -EFAULT; 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds else 2171da177e4SLinus Torvalds total_retlen += retlen; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds count -= retlen; 2201da177e4SLinus Torvalds buf += retlen; 22131f4233bSNicolas Pitre if (retlen == 0) 22231f4233bSNicolas Pitre count = 0; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds else { 2251da177e4SLinus Torvalds kfree(kbuf); 2261da177e4SLinus Torvalds return ret; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds } 2301da177e4SLinus Torvalds 231b802c074SThago Galesi kfree(kbuf); 2321da177e4SLinus Torvalds return total_retlen; 233969e57adSArtem Bityutskiy } /* mtdchar_read */ 2341da177e4SLinus Torvalds 235969e57adSArtem Bityutskiy static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t count, 236969e57adSArtem Bityutskiy loff_t *ppos) 2371da177e4SLinus Torvalds { 238f1a28c02SThomas Gleixner struct mtd_file_info *mfi = file->private_data; 239f1a28c02SThomas Gleixner struct mtd_info *mtd = mfi->mtd; 2403e45cf5eSGrant Erickson size_t size = count; 2411da177e4SLinus Torvalds char *kbuf; 2421da177e4SLinus Torvalds size_t retlen; 2431da177e4SLinus Torvalds size_t total_retlen=0; 2441da177e4SLinus Torvalds int ret=0; 2451da177e4SLinus Torvalds int len; 2461da177e4SLinus Torvalds 247289c0522SBrian Norris pr_debug("MTD_write\n"); 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds if (*ppos == mtd->size) 2501da177e4SLinus Torvalds return -ENOSPC; 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds if (*ppos + count > mtd->size) 2531da177e4SLinus Torvalds count = mtd->size - *ppos; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds if (!count) 2561da177e4SLinus Torvalds return 0; 2571da177e4SLinus Torvalds 2583e45cf5eSGrant Erickson kbuf = mtd_kmalloc_up_to(mtd, &size); 259b802c074SThago Galesi if (!kbuf) 260b802c074SThago Galesi return -ENOMEM; 261b802c074SThago Galesi 2621da177e4SLinus Torvalds while (count) { 2633e45cf5eSGrant Erickson len = min_t(size_t, count, size); 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds if (copy_from_user(kbuf, buf, len)) { 2661da177e4SLinus Torvalds kfree(kbuf); 2671da177e4SLinus Torvalds return -EFAULT; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 270f1a28c02SThomas Gleixner switch (mfi->mode) { 271beb133fcSBrian Norris case MTD_FILE_MODE_OTP_FACTORY: 27231f4233bSNicolas Pitre ret = -EROFS; 27331f4233bSNicolas Pitre break; 274beb133fcSBrian Norris case MTD_FILE_MODE_OTP_USER: 275482b43adSArtem Bityutskiy ret = mtd_write_user_prot_reg(mtd, *ppos, len, 276482b43adSArtem Bityutskiy &retlen, kbuf); 27731f4233bSNicolas Pitre break; 278f1a28c02SThomas Gleixner 279beb133fcSBrian Norris case MTD_FILE_MODE_RAW: 280f1a28c02SThomas Gleixner { 281f1a28c02SThomas Gleixner struct mtd_oob_ops ops; 282f1a28c02SThomas Gleixner 2830612b9ddSBrian Norris ops.mode = MTD_OPS_RAW; 284f1a28c02SThomas Gleixner ops.datbuf = kbuf; 285f1a28c02SThomas Gleixner ops.oobbuf = NULL; 286bf514081SPeter Wippich ops.ooboffs = 0; 287f1a28c02SThomas Gleixner ops.len = len; 288f1a28c02SThomas Gleixner 289a2cc5ba0SArtem Bityutskiy ret = mtd_write_oob(mtd, *ppos, &ops); 290f1a28c02SThomas Gleixner retlen = ops.retlen; 291f1a28c02SThomas Gleixner break; 292f1a28c02SThomas Gleixner } 293f1a28c02SThomas Gleixner 29431f4233bSNicolas Pitre default: 295eda95cbfSArtem Bityutskiy ret = mtd_write(mtd, *ppos, len, &retlen, kbuf); 29631f4233bSNicolas Pitre } 2979a78bc83SChristian Riesch 2989a78bc83SChristian Riesch /* 2999a78bc83SChristian Riesch * Return -ENOSPC only if no data could be written at all. 3009a78bc83SChristian Riesch * Otherwise just return the number of bytes that actually 3019a78bc83SChristian Riesch * have been written. 3029a78bc83SChristian Riesch */ 3039a78bc83SChristian Riesch if ((ret == -ENOSPC) && (total_retlen)) 3049a78bc83SChristian Riesch break; 3059a78bc83SChristian Riesch 3061da177e4SLinus Torvalds if (!ret) { 3071da177e4SLinus Torvalds *ppos += retlen; 3081da177e4SLinus Torvalds total_retlen += retlen; 3091da177e4SLinus Torvalds count -= retlen; 3101da177e4SLinus Torvalds buf += retlen; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds else { 3131da177e4SLinus Torvalds kfree(kbuf); 3141da177e4SLinus Torvalds return ret; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 318b802c074SThago Galesi kfree(kbuf); 3191da177e4SLinus Torvalds return total_retlen; 320969e57adSArtem Bityutskiy } /* mtdchar_write */ 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds /*====================================================================== 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds IOCTL calls for getting device parameters. 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds ======================================================================*/ 3271da177e4SLinus Torvalds static void mtdchar_erase_callback (struct erase_info *instr) 3281da177e4SLinus Torvalds { 3291da177e4SLinus Torvalds wake_up((wait_queue_head_t *)instr->priv); 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds 332f1a28c02SThomas Gleixner static int otp_select_filemode(struct mtd_file_info *mfi, int mode) 333f1a28c02SThomas Gleixner { 334f1a28c02SThomas Gleixner struct mtd_info *mtd = mfi->mtd; 335b6de3d6cSArtem Bityutskiy size_t retlen; 336b6de3d6cSArtem Bityutskiy 337f1a28c02SThomas Gleixner switch (mode) { 338f1a28c02SThomas Gleixner case MTD_OTP_FACTORY: 3395dc63fa2SUwe Kleine-König if (mtd_read_fact_prot_reg(mtd, -1, 0, &retlen, NULL) == 3405dc63fa2SUwe Kleine-König -EOPNOTSUPP) 3415dc63fa2SUwe Kleine-König return -EOPNOTSUPP; 3425dc63fa2SUwe Kleine-König 343beb133fcSBrian Norris mfi->mode = MTD_FILE_MODE_OTP_FACTORY; 344f1a28c02SThomas Gleixner break; 345f1a28c02SThomas Gleixner case MTD_OTP_USER: 3465dc63fa2SUwe Kleine-König if (mtd_read_user_prot_reg(mtd, -1, 0, &retlen, NULL) == 3475dc63fa2SUwe Kleine-König -EOPNOTSUPP) 3485dc63fa2SUwe Kleine-König return -EOPNOTSUPP; 3495dc63fa2SUwe Kleine-König 350beb133fcSBrian Norris mfi->mode = MTD_FILE_MODE_OTP_USER; 351f1a28c02SThomas Gleixner break; 352f1a28c02SThomas Gleixner case MTD_OTP_OFF: 3535dc63fa2SUwe Kleine-König mfi->mode = MTD_FILE_MODE_NORMAL; 354f1a28c02SThomas Gleixner break; 3555dc63fa2SUwe Kleine-König default: 3565dc63fa2SUwe Kleine-König return -EINVAL; 357f1a28c02SThomas Gleixner } 3585dc63fa2SUwe Kleine-König 3595dc63fa2SUwe Kleine-König return 0; 360f1a28c02SThomas Gleixner } 361f1a28c02SThomas Gleixner 362969e57adSArtem Bityutskiy static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, 36397718540SKevin Cernekee uint64_t start, uint32_t length, void __user *ptr, 36497718540SKevin Cernekee uint32_t __user *retp) 36597718540SKevin Cernekee { 3669ce244b3SBrian Norris struct mtd_file_info *mfi = file->private_data; 36797718540SKevin Cernekee struct mtd_oob_ops ops; 36897718540SKevin Cernekee uint32_t retlen; 36997718540SKevin Cernekee int ret = 0; 37097718540SKevin Cernekee 37197718540SKevin Cernekee if (!(file->f_mode & FMODE_WRITE)) 37297718540SKevin Cernekee return -EPERM; 37397718540SKevin Cernekee 37497718540SKevin Cernekee if (length > 4096) 37597718540SKevin Cernekee return -EINVAL; 37697718540SKevin Cernekee 3773c3c10bbSArtem Bityutskiy if (!mtd->_write_oob) 37897718540SKevin Cernekee ret = -EOPNOTSUPP; 37997718540SKevin Cernekee else 3800040476bSRoel Kluin ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT; 38197718540SKevin Cernekee 38297718540SKevin Cernekee if (ret) 38397718540SKevin Cernekee return ret; 38497718540SKevin Cernekee 38597718540SKevin Cernekee ops.ooblen = length; 386305b93f1SBrian Norris ops.ooboffs = start & (mtd->writesize - 1); 38797718540SKevin Cernekee ops.datbuf = NULL; 388beb133fcSBrian Norris ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW : 3890612b9ddSBrian Norris MTD_OPS_PLACE_OOB; 39097718540SKevin Cernekee 39197718540SKevin Cernekee if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) 39297718540SKevin Cernekee return -EINVAL; 39397718540SKevin Cernekee 394df1f1d1cSJulia Lawall ops.oobbuf = memdup_user(ptr, length); 395df1f1d1cSJulia Lawall if (IS_ERR(ops.oobbuf)) 396df1f1d1cSJulia Lawall return PTR_ERR(ops.oobbuf); 39797718540SKevin Cernekee 398305b93f1SBrian Norris start &= ~((uint64_t)mtd->writesize - 1); 399a2cc5ba0SArtem Bityutskiy ret = mtd_write_oob(mtd, start, &ops); 40097718540SKevin Cernekee 40197718540SKevin Cernekee if (ops.oobretlen > 0xFFFFFFFFU) 40297718540SKevin Cernekee ret = -EOVERFLOW; 40397718540SKevin Cernekee retlen = ops.oobretlen; 40497718540SKevin Cernekee if (copy_to_user(retp, &retlen, sizeof(length))) 40597718540SKevin Cernekee ret = -EFAULT; 40697718540SKevin Cernekee 40797718540SKevin Cernekee kfree(ops.oobbuf); 40897718540SKevin Cernekee return ret; 40997718540SKevin Cernekee } 41097718540SKevin Cernekee 411969e57adSArtem Bityutskiy static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, 412c46f6483SBrian Norris uint64_t start, uint32_t length, void __user *ptr, 413c46f6483SBrian Norris uint32_t __user *retp) 41497718540SKevin Cernekee { 415c46f6483SBrian Norris struct mtd_file_info *mfi = file->private_data; 41697718540SKevin Cernekee struct mtd_oob_ops ops; 41797718540SKevin Cernekee int ret = 0; 41897718540SKevin Cernekee 41997718540SKevin Cernekee if (length > 4096) 42097718540SKevin Cernekee return -EINVAL; 42197718540SKevin Cernekee 422dac2639fSArtem Bityutskiy if (!access_ok(VERIFY_WRITE, ptr, length)) 423dac2639fSArtem Bityutskiy return -EFAULT; 42497718540SKevin Cernekee 42597718540SKevin Cernekee ops.ooblen = length; 426305b93f1SBrian Norris ops.ooboffs = start & (mtd->writesize - 1); 42797718540SKevin Cernekee ops.datbuf = NULL; 428beb133fcSBrian Norris ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW : 4290612b9ddSBrian Norris MTD_OPS_PLACE_OOB; 43097718540SKevin Cernekee 43197718540SKevin Cernekee if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) 43297718540SKevin Cernekee return -EINVAL; 43397718540SKevin Cernekee 43497718540SKevin Cernekee ops.oobbuf = kmalloc(length, GFP_KERNEL); 43597718540SKevin Cernekee if (!ops.oobbuf) 43697718540SKevin Cernekee return -ENOMEM; 43797718540SKevin Cernekee 438305b93f1SBrian Norris start &= ~((uint64_t)mtd->writesize - 1); 439fd2819bbSArtem Bityutskiy ret = mtd_read_oob(mtd, start, &ops); 44097718540SKevin Cernekee 44197718540SKevin Cernekee if (put_user(ops.oobretlen, retp)) 44297718540SKevin Cernekee ret = -EFAULT; 44397718540SKevin Cernekee else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf, 44497718540SKevin Cernekee ops.oobretlen)) 44597718540SKevin Cernekee ret = -EFAULT; 44697718540SKevin Cernekee 44797718540SKevin Cernekee kfree(ops.oobbuf); 448041e4575SBrian Norris 449041e4575SBrian Norris /* 450041e4575SBrian Norris * NAND returns -EBADMSG on ECC errors, but it returns the OOB 451041e4575SBrian Norris * data. For our userspace tools it is important to dump areas 452041e4575SBrian Norris * with ECC errors! 453041e4575SBrian Norris * For kernel internal usage it also might return -EUCLEAN 454041e4575SBrian Norris * to signal the caller that a bitflip has occured and has 455041e4575SBrian Norris * been corrected by the ECC algorithm. 456041e4575SBrian Norris * 457c478d7e4SBrian Norris * Note: currently the standard NAND function, nand_read_oob_std, 458c478d7e4SBrian Norris * does not calculate ECC for the OOB area, so do not rely on 459c478d7e4SBrian Norris * this behavior unless you have replaced it with your own. 460041e4575SBrian Norris */ 461d57f4054SBrian Norris if (mtd_is_bitflip_or_eccerr(ret)) 462041e4575SBrian Norris return 0; 463041e4575SBrian Norris 46497718540SKevin Cernekee return ret; 46597718540SKevin Cernekee } 46697718540SKevin Cernekee 467cc26c3cdSBrian Norris /* 468*aab616e3SBoris Brezillon * Copies (and truncates, if necessary) OOB layout information to the 469*aab616e3SBoris Brezillon * deprecated layout struct, nand_ecclayout_user. This is necessary only to 470*aab616e3SBoris Brezillon * support the deprecated API ioctl ECCGETLAYOUT while allowing all new 471*aab616e3SBoris Brezillon * functionality to use mtd_ooblayout_ops flexibly (i.e. mtd_ooblayout_ops 472*aab616e3SBoris Brezillon * can describe any kind of OOB layout with almost zero overhead from a 473*aab616e3SBoris Brezillon * memory usage point of view). 474cc26c3cdSBrian Norris */ 475c2b78452SBoris Brezillon static int shrink_ecclayout(struct mtd_info *mtd, 476cc26c3cdSBrian Norris struct nand_ecclayout_user *to) 477cc26c3cdSBrian Norris { 478c2b78452SBoris Brezillon struct mtd_oob_region oobregion; 479c2b78452SBoris Brezillon int i, section = 0, ret; 480cc26c3cdSBrian Norris 481c2b78452SBoris Brezillon if (!mtd || !to) 482cc26c3cdSBrian Norris return -EINVAL; 483cc26c3cdSBrian Norris 484cc26c3cdSBrian Norris memset(to, 0, sizeof(*to)); 485cc26c3cdSBrian Norris 486c2b78452SBoris Brezillon to->eccbytes = 0; 487c2b78452SBoris Brezillon for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) { 488c2b78452SBoris Brezillon u32 eccpos; 489c2b78452SBoris Brezillon 490c2b78452SBoris Brezillon ret = mtd_ooblayout_ecc(mtd, section, &oobregion); 491c2b78452SBoris Brezillon if (ret < 0) { 492c2b78452SBoris Brezillon if (ret != -ERANGE) 493c2b78452SBoris Brezillon return ret; 494c2b78452SBoris Brezillon 495c2b78452SBoris Brezillon break; 496c2b78452SBoris Brezillon } 497c2b78452SBoris Brezillon 498c2b78452SBoris Brezillon eccpos = oobregion.offset; 499c2b78452SBoris Brezillon for (; i < MTD_MAX_ECCPOS_ENTRIES && 500c2b78452SBoris Brezillon eccpos < oobregion.offset + oobregion.length; i++) { 501c2b78452SBoris Brezillon to->eccpos[i] = eccpos++; 502c2b78452SBoris Brezillon to->eccbytes++; 503c2b78452SBoris Brezillon } 504c2b78452SBoris Brezillon } 505cc26c3cdSBrian Norris 506cc26c3cdSBrian Norris for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { 507c2b78452SBoris Brezillon ret = mtd_ooblayout_free(mtd, i, &oobregion); 508c2b78452SBoris Brezillon if (ret < 0) { 509c2b78452SBoris Brezillon if (ret != -ERANGE) 510c2b78452SBoris Brezillon return ret; 511c2b78452SBoris Brezillon 512cc26c3cdSBrian Norris break; 513cc26c3cdSBrian Norris } 514cc26c3cdSBrian Norris 515c2b78452SBoris Brezillon to->oobfree[i].offset = oobregion.offset; 516c2b78452SBoris Brezillon to->oobfree[i].length = oobregion.length; 517c2b78452SBoris Brezillon to->oobavail += to->oobfree[i].length; 518c2b78452SBoris Brezillon } 519c2b78452SBoris Brezillon 520c2b78452SBoris Brezillon return 0; 521c2b78452SBoris Brezillon } 522c2b78452SBoris Brezillon 523c2b78452SBoris Brezillon static int get_oobinfo(struct mtd_info *mtd, struct nand_oobinfo *to) 524c2b78452SBoris Brezillon { 525c2b78452SBoris Brezillon struct mtd_oob_region oobregion; 526c2b78452SBoris Brezillon int i, section = 0, ret; 527c2b78452SBoris Brezillon 528c2b78452SBoris Brezillon if (!mtd || !to) 529c2b78452SBoris Brezillon return -EINVAL; 530c2b78452SBoris Brezillon 531c2b78452SBoris Brezillon memset(to, 0, sizeof(*to)); 532c2b78452SBoris Brezillon 533c2b78452SBoris Brezillon to->eccbytes = 0; 534c2b78452SBoris Brezillon for (i = 0; i < ARRAY_SIZE(to->eccpos);) { 535c2b78452SBoris Brezillon u32 eccpos; 536c2b78452SBoris Brezillon 537c2b78452SBoris Brezillon ret = mtd_ooblayout_ecc(mtd, section, &oobregion); 538c2b78452SBoris Brezillon if (ret < 0) { 539c2b78452SBoris Brezillon if (ret != -ERANGE) 540c2b78452SBoris Brezillon return ret; 541c2b78452SBoris Brezillon 542c2b78452SBoris Brezillon break; 543c2b78452SBoris Brezillon } 544c2b78452SBoris Brezillon 545c2b78452SBoris Brezillon if (oobregion.length + i > ARRAY_SIZE(to->eccpos)) 546c2b78452SBoris Brezillon return -EINVAL; 547c2b78452SBoris Brezillon 548c2b78452SBoris Brezillon eccpos = oobregion.offset; 549c2b78452SBoris Brezillon for (; eccpos < oobregion.offset + oobregion.length; i++) { 550c2b78452SBoris Brezillon to->eccpos[i] = eccpos++; 551c2b78452SBoris Brezillon to->eccbytes++; 552c2b78452SBoris Brezillon } 553c2b78452SBoris Brezillon } 554c2b78452SBoris Brezillon 555c2b78452SBoris Brezillon for (i = 0; i < 8; i++) { 556c2b78452SBoris Brezillon ret = mtd_ooblayout_free(mtd, i, &oobregion); 557c2b78452SBoris Brezillon if (ret < 0) { 558c2b78452SBoris Brezillon if (ret != -ERANGE) 559c2b78452SBoris Brezillon return ret; 560c2b78452SBoris Brezillon 561c2b78452SBoris Brezillon break; 562c2b78452SBoris Brezillon } 563c2b78452SBoris Brezillon 564c2b78452SBoris Brezillon to->oobfree[i][0] = oobregion.offset; 565c2b78452SBoris Brezillon to->oobfree[i][1] = oobregion.length; 566c2b78452SBoris Brezillon } 567c2b78452SBoris Brezillon 568c2b78452SBoris Brezillon to->useecc = MTD_NANDECC_AUTOPLACE; 569c2b78452SBoris Brezillon 570cc26c3cdSBrian Norris return 0; 571cc26c3cdSBrian Norris } 572cc26c3cdSBrian Norris 573969e57adSArtem Bityutskiy static int mtdchar_blkpg_ioctl(struct mtd_info *mtd, 57453bb724fSBrian Norris struct blkpg_ioctl_arg *arg) 575d0f7959eSRoman Tereshonkov { 576d0f7959eSRoman Tereshonkov struct blkpg_partition p; 577d0f7959eSRoman Tereshonkov 578d0f7959eSRoman Tereshonkov if (!capable(CAP_SYS_ADMIN)) 579d0f7959eSRoman Tereshonkov return -EPERM; 580d0f7959eSRoman Tereshonkov 58153bb724fSBrian Norris if (copy_from_user(&p, arg->data, sizeof(p))) 582d0f7959eSRoman Tereshonkov return -EFAULT; 583d0f7959eSRoman Tereshonkov 58453bb724fSBrian Norris switch (arg->op) { 585d0f7959eSRoman Tereshonkov case BLKPG_ADD_PARTITION: 586d0f7959eSRoman Tereshonkov 587a7e93dcdSRoman Tereshonkov /* Only master mtd device must be used to add partitions */ 588a7e93dcdSRoman Tereshonkov if (mtd_is_partition(mtd)) 589a7e93dcdSRoman Tereshonkov return -EINVAL; 590a7e93dcdSRoman Tereshonkov 5911cc8d841SBrian Norris /* Sanitize user input */ 5921cc8d841SBrian Norris p.devname[BLKPG_DEVNAMELTH - 1] = '\0'; 5931cc8d841SBrian Norris 594d0f7959eSRoman Tereshonkov return mtd_add_partition(mtd, p.devname, p.start, p.length); 595d0f7959eSRoman Tereshonkov 596d0f7959eSRoman Tereshonkov case BLKPG_DEL_PARTITION: 597d0f7959eSRoman Tereshonkov 598d0f7959eSRoman Tereshonkov if (p.pno < 0) 599d0f7959eSRoman Tereshonkov return -EINVAL; 600d0f7959eSRoman Tereshonkov 601d0f7959eSRoman Tereshonkov return mtd_del_partition(mtd, p.pno); 602d0f7959eSRoman Tereshonkov 603d0f7959eSRoman Tereshonkov default: 604d0f7959eSRoman Tereshonkov return -EINVAL; 605d0f7959eSRoman Tereshonkov } 606d0f7959eSRoman Tereshonkov } 607d0f7959eSRoman Tereshonkov 608969e57adSArtem Bityutskiy static int mtdchar_write_ioctl(struct mtd_info *mtd, 609e99d8b08SBrian Norris struct mtd_write_req __user *argp) 610e99d8b08SBrian Norris { 611e99d8b08SBrian Norris struct mtd_write_req req; 612e99d8b08SBrian Norris struct mtd_oob_ops ops; 613f62cde49SGeert Uytterhoeven const void __user *usr_data, *usr_oob; 614e99d8b08SBrian Norris int ret; 615e99d8b08SBrian Norris 616f62cde49SGeert Uytterhoeven if (copy_from_user(&req, argp, sizeof(req))) 617e99d8b08SBrian Norris return -EFAULT; 618f62cde49SGeert Uytterhoeven 619f62cde49SGeert Uytterhoeven usr_data = (const void __user *)(uintptr_t)req.usr_data; 620f62cde49SGeert Uytterhoeven usr_oob = (const void __user *)(uintptr_t)req.usr_oob; 621f62cde49SGeert Uytterhoeven if (!access_ok(VERIFY_READ, usr_data, req.len) || 622f62cde49SGeert Uytterhoeven !access_ok(VERIFY_READ, usr_oob, req.ooblen)) 623f62cde49SGeert Uytterhoeven return -EFAULT; 624f62cde49SGeert Uytterhoeven 6253c3c10bbSArtem Bityutskiy if (!mtd->_write_oob) 626e99d8b08SBrian Norris return -EOPNOTSUPP; 627e99d8b08SBrian Norris 628e99d8b08SBrian Norris ops.mode = req.mode; 629e99d8b08SBrian Norris ops.len = (size_t)req.len; 630e99d8b08SBrian Norris ops.ooblen = (size_t)req.ooblen; 631e99d8b08SBrian Norris ops.ooboffs = 0; 632e99d8b08SBrian Norris 633f62cde49SGeert Uytterhoeven if (usr_data) { 634e99d8b08SBrian Norris ops.datbuf = memdup_user(usr_data, ops.len); 635e99d8b08SBrian Norris if (IS_ERR(ops.datbuf)) 636e99d8b08SBrian Norris return PTR_ERR(ops.datbuf); 637e99d8b08SBrian Norris } else { 638e99d8b08SBrian Norris ops.datbuf = NULL; 639e99d8b08SBrian Norris } 640e99d8b08SBrian Norris 641f62cde49SGeert Uytterhoeven if (usr_oob) { 642e99d8b08SBrian Norris ops.oobbuf = memdup_user(usr_oob, ops.ooblen); 643e99d8b08SBrian Norris if (IS_ERR(ops.oobbuf)) { 644e99d8b08SBrian Norris kfree(ops.datbuf); 645e99d8b08SBrian Norris return PTR_ERR(ops.oobbuf); 646e99d8b08SBrian Norris } 647e99d8b08SBrian Norris } else { 648e99d8b08SBrian Norris ops.oobbuf = NULL; 649e99d8b08SBrian Norris } 650e99d8b08SBrian Norris 651a2cc5ba0SArtem Bityutskiy ret = mtd_write_oob(mtd, (loff_t)req.start, &ops); 652e99d8b08SBrian Norris 653e99d8b08SBrian Norris kfree(ops.datbuf); 654e99d8b08SBrian Norris kfree(ops.oobbuf); 655e99d8b08SBrian Norris 656e99d8b08SBrian Norris return ret; 657e99d8b08SBrian Norris } 658e99d8b08SBrian Norris 659969e57adSArtem Bityutskiy static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) 6601da177e4SLinus Torvalds { 661f1a28c02SThomas Gleixner struct mtd_file_info *mfi = file->private_data; 662f1a28c02SThomas Gleixner struct mtd_info *mtd = mfi->mtd; 6631da177e4SLinus Torvalds void __user *argp = (void __user *)arg; 6641da177e4SLinus Torvalds int ret = 0; 6651da177e4SLinus Torvalds u_long size; 66673c619eaSJoern Engel struct mtd_info_user info; 6671da177e4SLinus Torvalds 668289c0522SBrian Norris pr_debug("MTD_ioctl\n"); 6691da177e4SLinus Torvalds 6701da177e4SLinus Torvalds size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; 6711da177e4SLinus Torvalds if (cmd & IOC_IN) { 6721da177e4SLinus Torvalds if (!access_ok(VERIFY_READ, argp, size)) 6731da177e4SLinus Torvalds return -EFAULT; 6741da177e4SLinus Torvalds } 6751da177e4SLinus Torvalds if (cmd & IOC_OUT) { 6761da177e4SLinus Torvalds if (!access_ok(VERIFY_WRITE, argp, size)) 6771da177e4SLinus Torvalds return -EFAULT; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds switch (cmd) { 6811da177e4SLinus Torvalds case MEMGETREGIONCOUNT: 6821da177e4SLinus Torvalds if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) 6831da177e4SLinus Torvalds return -EFAULT; 6841da177e4SLinus Torvalds break; 6851da177e4SLinus Torvalds 6861da177e4SLinus Torvalds case MEMGETREGIONINFO: 6871da177e4SLinus Torvalds { 688b67c5f87SZev Weiss uint32_t ur_idx; 689b67c5f87SZev Weiss struct mtd_erase_region_info *kr; 690bcc98a46SH Hartley Sweeten struct region_info_user __user *ur = argp; 6911da177e4SLinus Torvalds 692b67c5f87SZev Weiss if (get_user(ur_idx, &(ur->regionindex))) 6931da177e4SLinus Torvalds return -EFAULT; 6941da177e4SLinus Torvalds 6955e59be1fSDan Carpenter if (ur_idx >= mtd->numeraseregions) 6965e59be1fSDan Carpenter return -EINVAL; 6975e59be1fSDan Carpenter 698b67c5f87SZev Weiss kr = &(mtd->eraseregions[ur_idx]); 699b67c5f87SZev Weiss 700b67c5f87SZev Weiss if (put_user(kr->offset, &(ur->offset)) 701b67c5f87SZev Weiss || put_user(kr->erasesize, &(ur->erasesize)) 702b67c5f87SZev Weiss || put_user(kr->numblocks, &(ur->numblocks))) 7031da177e4SLinus Torvalds return -EFAULT; 704b67c5f87SZev Weiss 7051da177e4SLinus Torvalds break; 7061da177e4SLinus Torvalds } 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds case MEMGETINFO: 709a0c5a394SVasiliy Kulikov memset(&info, 0, sizeof(info)); 71073c619eaSJoern Engel info.type = mtd->type; 71173c619eaSJoern Engel info.flags = mtd->flags; 71273c619eaSJoern Engel info.size = mtd->size; 71373c619eaSJoern Engel info.erasesize = mtd->erasesize; 71473c619eaSJoern Engel info.writesize = mtd->writesize; 71573c619eaSJoern Engel info.oobsize = mtd->oobsize; 71619fb4341SBrian Norris /* The below field is obsolete */ 71719fb4341SBrian Norris info.padding = 0; 71873c619eaSJoern Engel if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) 7191da177e4SLinus Torvalds return -EFAULT; 7201da177e4SLinus Torvalds break; 7211da177e4SLinus Torvalds 7221da177e4SLinus Torvalds case MEMERASE: 7230dc54e9fSKevin Cernekee case MEMERASE64: 7241da177e4SLinus Torvalds { 7251da177e4SLinus Torvalds struct erase_info *erase; 7261da177e4SLinus Torvalds 727aeb5d727SAl Viro if(!(file->f_mode & FMODE_WRITE)) 7281da177e4SLinus Torvalds return -EPERM; 7291da177e4SLinus Torvalds 73095b93a0cSBurman Yan erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); 7311da177e4SLinus Torvalds if (!erase) 7321da177e4SLinus Torvalds ret = -ENOMEM; 7331da177e4SLinus Torvalds else { 7341da177e4SLinus Torvalds wait_queue_head_t waitq; 7351da177e4SLinus Torvalds DECLARE_WAITQUEUE(wait, current); 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds init_waitqueue_head(&waitq); 7381da177e4SLinus Torvalds 7390dc54e9fSKevin Cernekee if (cmd == MEMERASE64) { 7400dc54e9fSKevin Cernekee struct erase_info_user64 einfo64; 7410dc54e9fSKevin Cernekee 7420dc54e9fSKevin Cernekee if (copy_from_user(&einfo64, argp, 7430dc54e9fSKevin Cernekee sizeof(struct erase_info_user64))) { 7440dc54e9fSKevin Cernekee kfree(erase); 7450dc54e9fSKevin Cernekee return -EFAULT; 7460dc54e9fSKevin Cernekee } 7470dc54e9fSKevin Cernekee erase->addr = einfo64.start; 7480dc54e9fSKevin Cernekee erase->len = einfo64.length; 7490dc54e9fSKevin Cernekee } else { 7500dc54e9fSKevin Cernekee struct erase_info_user einfo32; 7510dc54e9fSKevin Cernekee 7520dc54e9fSKevin Cernekee if (copy_from_user(&einfo32, argp, 7531da177e4SLinus Torvalds sizeof(struct erase_info_user))) { 7541da177e4SLinus Torvalds kfree(erase); 7551da177e4SLinus Torvalds return -EFAULT; 7561da177e4SLinus Torvalds } 7570dc54e9fSKevin Cernekee erase->addr = einfo32.start; 7580dc54e9fSKevin Cernekee erase->len = einfo32.length; 7590dc54e9fSKevin Cernekee } 7601da177e4SLinus Torvalds erase->mtd = mtd; 7611da177e4SLinus Torvalds erase->callback = mtdchar_erase_callback; 7621da177e4SLinus Torvalds erase->priv = (unsigned long)&waitq; 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds /* 7651da177e4SLinus Torvalds FIXME: Allow INTERRUPTIBLE. Which means 7661da177e4SLinus Torvalds not having the wait_queue head on the stack. 7671da177e4SLinus Torvalds 7681da177e4SLinus Torvalds If the wq_head is on the stack, and we 7691da177e4SLinus Torvalds leave because we got interrupted, then the 7701da177e4SLinus Torvalds wq_head is no longer there when the 7711da177e4SLinus Torvalds callback routine tries to wake us up. 7721da177e4SLinus Torvalds */ 7737e1f0dc0SArtem Bityutskiy ret = mtd_erase(mtd, erase); 7741da177e4SLinus Torvalds if (!ret) { 7751da177e4SLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 7761da177e4SLinus Torvalds add_wait_queue(&waitq, &wait); 7771da177e4SLinus Torvalds if (erase->state != MTD_ERASE_DONE && 7781da177e4SLinus Torvalds erase->state != MTD_ERASE_FAILED) 7791da177e4SLinus Torvalds schedule(); 7801da177e4SLinus Torvalds remove_wait_queue(&waitq, &wait); 7811da177e4SLinus Torvalds set_current_state(TASK_RUNNING); 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds kfree(erase); 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds break; 7881da177e4SLinus Torvalds } 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds case MEMWRITEOOB: 7911da177e4SLinus Torvalds { 7921da177e4SLinus Torvalds struct mtd_oob_buf buf; 79397718540SKevin Cernekee struct mtd_oob_buf __user *buf_user = argp; 7941da177e4SLinus Torvalds 79597718540SKevin Cernekee /* NOTE: writes return length to buf_user->length */ 79697718540SKevin Cernekee if (copy_from_user(&buf, argp, sizeof(buf))) 7971da177e4SLinus Torvalds ret = -EFAULT; 79897718540SKevin Cernekee else 799969e57adSArtem Bityutskiy ret = mtdchar_writeoob(file, mtd, buf.start, buf.length, 80097718540SKevin Cernekee buf.ptr, &buf_user->length); 8011da177e4SLinus Torvalds break; 8021da177e4SLinus Torvalds } 8031da177e4SLinus Torvalds 8041da177e4SLinus Torvalds case MEMREADOOB: 8051da177e4SLinus Torvalds { 8061da177e4SLinus Torvalds struct mtd_oob_buf buf; 80797718540SKevin Cernekee struct mtd_oob_buf __user *buf_user = argp; 8081da177e4SLinus Torvalds 80997718540SKevin Cernekee /* NOTE: writes return length to buf_user->start */ 81097718540SKevin Cernekee if (copy_from_user(&buf, argp, sizeof(buf))) 81197718540SKevin Cernekee ret = -EFAULT; 8121da177e4SLinus Torvalds else 813969e57adSArtem Bityutskiy ret = mtdchar_readoob(file, mtd, buf.start, buf.length, 81497718540SKevin Cernekee buf.ptr, &buf_user->start); 8151da177e4SLinus Torvalds break; 8161da177e4SLinus Torvalds } 8171da177e4SLinus Torvalds 818aea7cea9SKevin Cernekee case MEMWRITEOOB64: 819aea7cea9SKevin Cernekee { 820aea7cea9SKevin Cernekee struct mtd_oob_buf64 buf; 821aea7cea9SKevin Cernekee struct mtd_oob_buf64 __user *buf_user = argp; 822aea7cea9SKevin Cernekee 823aea7cea9SKevin Cernekee if (copy_from_user(&buf, argp, sizeof(buf))) 824aea7cea9SKevin Cernekee ret = -EFAULT; 825aea7cea9SKevin Cernekee else 826969e57adSArtem Bityutskiy ret = mtdchar_writeoob(file, mtd, buf.start, buf.length, 827aea7cea9SKevin Cernekee (void __user *)(uintptr_t)buf.usr_ptr, 828aea7cea9SKevin Cernekee &buf_user->length); 829aea7cea9SKevin Cernekee break; 830aea7cea9SKevin Cernekee } 831aea7cea9SKevin Cernekee 832aea7cea9SKevin Cernekee case MEMREADOOB64: 833aea7cea9SKevin Cernekee { 834aea7cea9SKevin Cernekee struct mtd_oob_buf64 buf; 835aea7cea9SKevin Cernekee struct mtd_oob_buf64 __user *buf_user = argp; 836aea7cea9SKevin Cernekee 837aea7cea9SKevin Cernekee if (copy_from_user(&buf, argp, sizeof(buf))) 838aea7cea9SKevin Cernekee ret = -EFAULT; 839aea7cea9SKevin Cernekee else 840969e57adSArtem Bityutskiy ret = mtdchar_readoob(file, mtd, buf.start, buf.length, 841aea7cea9SKevin Cernekee (void __user *)(uintptr_t)buf.usr_ptr, 842aea7cea9SKevin Cernekee &buf_user->length); 843aea7cea9SKevin Cernekee break; 844aea7cea9SKevin Cernekee } 845aea7cea9SKevin Cernekee 846e99d8b08SBrian Norris case MEMWRITE: 847e99d8b08SBrian Norris { 848969e57adSArtem Bityutskiy ret = mtdchar_write_ioctl(mtd, 849e99d8b08SBrian Norris (struct mtd_write_req __user *)arg); 850e99d8b08SBrian Norris break; 851e99d8b08SBrian Norris } 852e99d8b08SBrian Norris 8531da177e4SLinus Torvalds case MEMLOCK: 8541da177e4SLinus Torvalds { 855175428b2SHarvey Harrison struct erase_info_user einfo; 8561da177e4SLinus Torvalds 857175428b2SHarvey Harrison if (copy_from_user(&einfo, argp, sizeof(einfo))) 8581da177e4SLinus Torvalds return -EFAULT; 8591da177e4SLinus Torvalds 8607799f9acSArtem Bityutskiy ret = mtd_lock(mtd, einfo.start, einfo.length); 8611da177e4SLinus Torvalds break; 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds case MEMUNLOCK: 8651da177e4SLinus Torvalds { 866175428b2SHarvey Harrison struct erase_info_user einfo; 8671da177e4SLinus Torvalds 868175428b2SHarvey Harrison if (copy_from_user(&einfo, argp, sizeof(einfo))) 8691da177e4SLinus Torvalds return -EFAULT; 8701da177e4SLinus Torvalds 871b66005cdSArtem Bityutskiy ret = mtd_unlock(mtd, einfo.start, einfo.length); 8721da177e4SLinus Torvalds break; 8731da177e4SLinus Torvalds } 8741da177e4SLinus Torvalds 8759938424fSRichard Cochran case MEMISLOCKED: 8769938424fSRichard Cochran { 8779938424fSRichard Cochran struct erase_info_user einfo; 8789938424fSRichard Cochran 8799938424fSRichard Cochran if (copy_from_user(&einfo, argp, sizeof(einfo))) 8809938424fSRichard Cochran return -EFAULT; 8819938424fSRichard Cochran 882e95e9786SArtem Bityutskiy ret = mtd_is_locked(mtd, einfo.start, einfo.length); 8839938424fSRichard Cochran break; 8849938424fSRichard Cochran } 8859938424fSRichard Cochran 8865bd34c09SThomas Gleixner /* Legacy interface */ 8871da177e4SLinus Torvalds case MEMGETOOBSEL: 8881da177e4SLinus Torvalds { 8895bd34c09SThomas Gleixner struct nand_oobinfo oi; 8905bd34c09SThomas Gleixner 891adbbc3bcSBoris Brezillon if (!mtd->ooblayout) 8925bd34c09SThomas Gleixner return -EOPNOTSUPP; 8935bd34c09SThomas Gleixner 894c2b78452SBoris Brezillon ret = get_oobinfo(mtd, &oi); 895c2b78452SBoris Brezillon if (ret) 896c2b78452SBoris Brezillon return ret; 8975bd34c09SThomas Gleixner 8985bd34c09SThomas Gleixner if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) 8991da177e4SLinus Torvalds return -EFAULT; 9001da177e4SLinus Torvalds break; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds case MEMGETBADBLOCK: 9041da177e4SLinus Torvalds { 9051da177e4SLinus Torvalds loff_t offs; 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds if (copy_from_user(&offs, argp, sizeof(loff_t))) 9081da177e4SLinus Torvalds return -EFAULT; 9097086c19dSArtem Bityutskiy return mtd_block_isbad(mtd, offs); 9101da177e4SLinus Torvalds break; 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds case MEMSETBADBLOCK: 9141da177e4SLinus Torvalds { 9151da177e4SLinus Torvalds loff_t offs; 9161da177e4SLinus Torvalds 9171da177e4SLinus Torvalds if (copy_from_user(&offs, argp, sizeof(loff_t))) 9181da177e4SLinus Torvalds return -EFAULT; 9195942ddbcSArtem Bityutskiy return mtd_block_markbad(mtd, offs); 9201da177e4SLinus Torvalds break; 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 92331f4233bSNicolas Pitre case OTPSELECT: 92431f4233bSNicolas Pitre { 92531f4233bSNicolas Pitre int mode; 92631f4233bSNicolas Pitre if (copy_from_user(&mode, argp, sizeof(int))) 92731f4233bSNicolas Pitre return -EFAULT; 928f1a28c02SThomas Gleixner 929beb133fcSBrian Norris mfi->mode = MTD_FILE_MODE_NORMAL; 930f1a28c02SThomas Gleixner 931f1a28c02SThomas Gleixner ret = otp_select_filemode(mfi, mode); 932f1a28c02SThomas Gleixner 93381dba488SNicolas Pitre file->f_pos = 0; 93431f4233bSNicolas Pitre break; 93531f4233bSNicolas Pitre } 93631f4233bSNicolas Pitre 93731f4233bSNicolas Pitre case OTPGETREGIONCOUNT: 93831f4233bSNicolas Pitre case OTPGETREGIONINFO: 93931f4233bSNicolas Pitre { 94031f4233bSNicolas Pitre struct otp_info *buf = kmalloc(4096, GFP_KERNEL); 9414b78fc42SChristian Riesch size_t retlen; 94231f4233bSNicolas Pitre if (!buf) 94331f4233bSNicolas Pitre return -ENOMEM; 944f1a28c02SThomas Gleixner switch (mfi->mode) { 945beb133fcSBrian Norris case MTD_FILE_MODE_OTP_FACTORY: 9464b78fc42SChristian Riesch ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf); 94731f4233bSNicolas Pitre break; 948beb133fcSBrian Norris case MTD_FILE_MODE_OTP_USER: 9494b78fc42SChristian Riesch ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf); 95031f4233bSNicolas Pitre break; 951f1a28c02SThomas Gleixner default: 95287e858a9SArtem Bityutskiy ret = -EINVAL; 953f1a28c02SThomas Gleixner break; 95431f4233bSNicolas Pitre } 9554b78fc42SChristian Riesch if (!ret) { 95631f4233bSNicolas Pitre if (cmd == OTPGETREGIONCOUNT) { 9574b78fc42SChristian Riesch int nbr = retlen / sizeof(struct otp_info); 95831f4233bSNicolas Pitre ret = copy_to_user(argp, &nbr, sizeof(int)); 95931f4233bSNicolas Pitre } else 9604b78fc42SChristian Riesch ret = copy_to_user(argp, buf, retlen); 96131f4233bSNicolas Pitre if (ret) 96231f4233bSNicolas Pitre ret = -EFAULT; 96331f4233bSNicolas Pitre } 96431f4233bSNicolas Pitre kfree(buf); 96531f4233bSNicolas Pitre break; 96631f4233bSNicolas Pitre } 96731f4233bSNicolas Pitre 96831f4233bSNicolas Pitre case OTPLOCK: 96931f4233bSNicolas Pitre { 970175428b2SHarvey Harrison struct otp_info oinfo; 97131f4233bSNicolas Pitre 972beb133fcSBrian Norris if (mfi->mode != MTD_FILE_MODE_OTP_USER) 97331f4233bSNicolas Pitre return -EINVAL; 974175428b2SHarvey Harrison if (copy_from_user(&oinfo, argp, sizeof(oinfo))) 97531f4233bSNicolas Pitre return -EFAULT; 9764403dbfbSArtem Bityutskiy ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length); 97731f4233bSNicolas Pitre break; 97831f4233bSNicolas Pitre } 97931f4233bSNicolas Pitre 9807854d3f7SBrian Norris /* This ioctl is being deprecated - it truncates the ECC layout */ 981f1a28c02SThomas Gleixner case ECCGETLAYOUT: 982f1a28c02SThomas Gleixner { 983cc26c3cdSBrian Norris struct nand_ecclayout_user *usrlay; 984cc26c3cdSBrian Norris 985adbbc3bcSBoris Brezillon if (!mtd->ooblayout) 986f1a28c02SThomas Gleixner return -EOPNOTSUPP; 987f1a28c02SThomas Gleixner 988cc26c3cdSBrian Norris usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); 989cc26c3cdSBrian Norris if (!usrlay) 990cc26c3cdSBrian Norris return -ENOMEM; 991cc26c3cdSBrian Norris 992c2b78452SBoris Brezillon shrink_ecclayout(mtd, usrlay); 993cc26c3cdSBrian Norris 994cc26c3cdSBrian Norris if (copy_to_user(argp, usrlay, sizeof(*usrlay))) 995cc26c3cdSBrian Norris ret = -EFAULT; 996cc26c3cdSBrian Norris kfree(usrlay); 997f1a28c02SThomas Gleixner break; 998f1a28c02SThomas Gleixner } 999f1a28c02SThomas Gleixner 1000f1a28c02SThomas Gleixner case ECCGETSTATS: 1001f1a28c02SThomas Gleixner { 1002f1a28c02SThomas Gleixner if (copy_to_user(argp, &mtd->ecc_stats, 1003f1a28c02SThomas Gleixner sizeof(struct mtd_ecc_stats))) 1004f1a28c02SThomas Gleixner return -EFAULT; 1005f1a28c02SThomas Gleixner break; 1006f1a28c02SThomas Gleixner } 1007f1a28c02SThomas Gleixner 1008f1a28c02SThomas Gleixner case MTDFILEMODE: 1009f1a28c02SThomas Gleixner { 1010f1a28c02SThomas Gleixner mfi->mode = 0; 1011f1a28c02SThomas Gleixner 1012f1a28c02SThomas Gleixner switch(arg) { 1013beb133fcSBrian Norris case MTD_FILE_MODE_OTP_FACTORY: 1014beb133fcSBrian Norris case MTD_FILE_MODE_OTP_USER: 1015f1a28c02SThomas Gleixner ret = otp_select_filemode(mfi, arg); 1016f1a28c02SThomas Gleixner break; 1017f1a28c02SThomas Gleixner 1018beb133fcSBrian Norris case MTD_FILE_MODE_RAW: 1019fc002e3cSArtem Bityutskiy if (!mtd_has_oob(mtd)) 1020f1a28c02SThomas Gleixner return -EOPNOTSUPP; 1021f1a28c02SThomas Gleixner mfi->mode = arg; 1022f1a28c02SThomas Gleixner 1023beb133fcSBrian Norris case MTD_FILE_MODE_NORMAL: 1024f1a28c02SThomas Gleixner break; 1025f1a28c02SThomas Gleixner default: 1026f1a28c02SThomas Gleixner ret = -EINVAL; 1027f1a28c02SThomas Gleixner } 1028f1a28c02SThomas Gleixner file->f_pos = 0; 1029f1a28c02SThomas Gleixner break; 1030f1a28c02SThomas Gleixner } 1031f1a28c02SThomas Gleixner 1032d0f7959eSRoman Tereshonkov case BLKPG: 1033d0f7959eSRoman Tereshonkov { 103453bb724fSBrian Norris struct blkpg_ioctl_arg __user *blk_arg = argp; 103553bb724fSBrian Norris struct blkpg_ioctl_arg a; 103653bb724fSBrian Norris 103753bb724fSBrian Norris if (copy_from_user(&a, blk_arg, sizeof(a))) 103853bb724fSBrian Norris ret = -EFAULT; 103953bb724fSBrian Norris else 104053bb724fSBrian Norris ret = mtdchar_blkpg_ioctl(mtd, &a); 1041d0f7959eSRoman Tereshonkov break; 1042d0f7959eSRoman Tereshonkov } 1043d0f7959eSRoman Tereshonkov 1044d0f7959eSRoman Tereshonkov case BLKRRPART: 1045d0f7959eSRoman Tereshonkov { 1046d0f7959eSRoman Tereshonkov /* No reread partition feature. Just return ok */ 1047d0f7959eSRoman Tereshonkov ret = 0; 1048d0f7959eSRoman Tereshonkov break; 1049d0f7959eSRoman Tereshonkov } 1050d0f7959eSRoman Tereshonkov 10511da177e4SLinus Torvalds default: 10521da177e4SLinus Torvalds ret = -ENOTTY; 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds 10551da177e4SLinus Torvalds return ret; 10561da177e4SLinus Torvalds } /* memory_ioctl */ 10571da177e4SLinus Torvalds 1058969e57adSArtem Bityutskiy static long mtdchar_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) 105955929332SArnd Bergmann { 106055929332SArnd Bergmann int ret; 106155929332SArnd Bergmann 10625aa82940SArnd Bergmann mutex_lock(&mtd_mutex); 1063969e57adSArtem Bityutskiy ret = mtdchar_ioctl(file, cmd, arg); 10645aa82940SArnd Bergmann mutex_unlock(&mtd_mutex); 106555929332SArnd Bergmann 106655929332SArnd Bergmann return ret; 106755929332SArnd Bergmann } 106855929332SArnd Bergmann 106997718540SKevin Cernekee #ifdef CONFIG_COMPAT 107097718540SKevin Cernekee 107197718540SKevin Cernekee struct mtd_oob_buf32 { 107297718540SKevin Cernekee u_int32_t start; 107397718540SKevin Cernekee u_int32_t length; 107497718540SKevin Cernekee compat_caddr_t ptr; /* unsigned char* */ 107597718540SKevin Cernekee }; 107697718540SKevin Cernekee 107797718540SKevin Cernekee #define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32) 107897718540SKevin Cernekee #define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32) 107997718540SKevin Cernekee 1080969e57adSArtem Bityutskiy static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd, 108197718540SKevin Cernekee unsigned long arg) 108297718540SKevin Cernekee { 108397718540SKevin Cernekee struct mtd_file_info *mfi = file->private_data; 108497718540SKevin Cernekee struct mtd_info *mtd = mfi->mtd; 10850b6585ceSDavid Woodhouse void __user *argp = compat_ptr(arg); 108697718540SKevin Cernekee int ret = 0; 108797718540SKevin Cernekee 10885aa82940SArnd Bergmann mutex_lock(&mtd_mutex); 108997718540SKevin Cernekee 109097718540SKevin Cernekee switch (cmd) { 109197718540SKevin Cernekee case MEMWRITEOOB32: 109297718540SKevin Cernekee { 109397718540SKevin Cernekee struct mtd_oob_buf32 buf; 109497718540SKevin Cernekee struct mtd_oob_buf32 __user *buf_user = argp; 109597718540SKevin Cernekee 109697718540SKevin Cernekee if (copy_from_user(&buf, argp, sizeof(buf))) 109797718540SKevin Cernekee ret = -EFAULT; 109897718540SKevin Cernekee else 1099969e57adSArtem Bityutskiy ret = mtdchar_writeoob(file, mtd, buf.start, 110097718540SKevin Cernekee buf.length, compat_ptr(buf.ptr), 110197718540SKevin Cernekee &buf_user->length); 110297718540SKevin Cernekee break; 110397718540SKevin Cernekee } 110497718540SKevin Cernekee 110597718540SKevin Cernekee case MEMREADOOB32: 110697718540SKevin Cernekee { 110797718540SKevin Cernekee struct mtd_oob_buf32 buf; 110897718540SKevin Cernekee struct mtd_oob_buf32 __user *buf_user = argp; 110997718540SKevin Cernekee 111097718540SKevin Cernekee /* NOTE: writes return length to buf->start */ 111197718540SKevin Cernekee if (copy_from_user(&buf, argp, sizeof(buf))) 111297718540SKevin Cernekee ret = -EFAULT; 111397718540SKevin Cernekee else 1114969e57adSArtem Bityutskiy ret = mtdchar_readoob(file, mtd, buf.start, 111597718540SKevin Cernekee buf.length, compat_ptr(buf.ptr), 111697718540SKevin Cernekee &buf_user->start); 111797718540SKevin Cernekee break; 111897718540SKevin Cernekee } 111953bb724fSBrian Norris 112053bb724fSBrian Norris case BLKPG: 112153bb724fSBrian Norris { 112253bb724fSBrian Norris /* Convert from blkpg_compat_ioctl_arg to blkpg_ioctl_arg */ 112353bb724fSBrian Norris struct blkpg_compat_ioctl_arg __user *uarg = argp; 112453bb724fSBrian Norris struct blkpg_compat_ioctl_arg compat_arg; 112553bb724fSBrian Norris struct blkpg_ioctl_arg a; 112653bb724fSBrian Norris 112753bb724fSBrian Norris if (copy_from_user(&compat_arg, uarg, sizeof(compat_arg))) { 112853bb724fSBrian Norris ret = -EFAULT; 112953bb724fSBrian Norris break; 113053bb724fSBrian Norris } 113153bb724fSBrian Norris 113253bb724fSBrian Norris memset(&a, 0, sizeof(a)); 113353bb724fSBrian Norris a.op = compat_arg.op; 113453bb724fSBrian Norris a.flags = compat_arg.flags; 113553bb724fSBrian Norris a.datalen = compat_arg.datalen; 113653bb724fSBrian Norris a.data = compat_ptr(compat_arg.data); 113753bb724fSBrian Norris 113853bb724fSBrian Norris ret = mtdchar_blkpg_ioctl(mtd, &a); 113953bb724fSBrian Norris break; 114053bb724fSBrian Norris } 114153bb724fSBrian Norris 114297718540SKevin Cernekee default: 1143969e57adSArtem Bityutskiy ret = mtdchar_ioctl(file, cmd, (unsigned long)argp); 114497718540SKevin Cernekee } 114597718540SKevin Cernekee 11465aa82940SArnd Bergmann mutex_unlock(&mtd_mutex); 114797718540SKevin Cernekee 114897718540SKevin Cernekee return ret; 114997718540SKevin Cernekee } 115097718540SKevin Cernekee 115197718540SKevin Cernekee #endif /* CONFIG_COMPAT */ 115297718540SKevin Cernekee 1153402d3265SDavid Howells /* 1154402d3265SDavid Howells * try to determine where a shared mapping can be made 1155402d3265SDavid Howells * - only supported for NOMMU at the moment (MMU can't doesn't copy private 1156402d3265SDavid Howells * mappings) 1157402d3265SDavid Howells */ 1158402d3265SDavid Howells #ifndef CONFIG_MMU 1159969e57adSArtem Bityutskiy static unsigned long mtdchar_get_unmapped_area(struct file *file, 1160402d3265SDavid Howells unsigned long addr, 1161402d3265SDavid Howells unsigned long len, 1162402d3265SDavid Howells unsigned long pgoff, 1163402d3265SDavid Howells unsigned long flags) 1164402d3265SDavid Howells { 1165402d3265SDavid Howells struct mtd_file_info *mfi = file->private_data; 1166402d3265SDavid Howells struct mtd_info *mtd = mfi->mtd; 1167402d3265SDavid Howells unsigned long offset; 1168cd621274SArtem Bityutskiy int ret; 1169402d3265SDavid Howells 1170402d3265SDavid Howells if (addr != 0) 1171402d3265SDavid Howells return (unsigned long) -EINVAL; 1172402d3265SDavid Howells 1173402d3265SDavid Howells if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT)) 1174402d3265SDavid Howells return (unsigned long) -EINVAL; 1175402d3265SDavid Howells 1176402d3265SDavid Howells offset = pgoff << PAGE_SHIFT; 1177402d3265SDavid Howells if (offset > mtd->size - len) 1178402d3265SDavid Howells return (unsigned long) -EINVAL; 1179402d3265SDavid Howells 1180cd621274SArtem Bityutskiy ret = mtd_get_unmapped_area(mtd, len, offset, flags); 1181b9995932SVladimir Zapolskiy return ret == -EOPNOTSUPP ? -ENODEV : ret; 1182402d3265SDavid Howells } 1183b4caecd4SChristoph Hellwig 1184b4caecd4SChristoph Hellwig static unsigned mtdchar_mmap_capabilities(struct file *file) 1185b4caecd4SChristoph Hellwig { 1186b4caecd4SChristoph Hellwig struct mtd_file_info *mfi = file->private_data; 1187b4caecd4SChristoph Hellwig 1188b4caecd4SChristoph Hellwig return mtd_mmap_capabilities(mfi->mtd); 1189b4caecd4SChristoph Hellwig } 1190402d3265SDavid Howells #endif 1191402d3265SDavid Howells 1192402d3265SDavid Howells /* 1193402d3265SDavid Howells * set up a mapping for shared memory segments 1194402d3265SDavid Howells */ 1195969e57adSArtem Bityutskiy static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma) 1196402d3265SDavid Howells { 1197402d3265SDavid Howells #ifdef CONFIG_MMU 1198402d3265SDavid Howells struct mtd_file_info *mfi = file->private_data; 1199402d3265SDavid Howells struct mtd_info *mtd = mfi->mtd; 1200dd02b67dSAnatolij Gustschin struct map_info *map = mtd->priv; 1201402d3265SDavid Howells 1202f5cf8f07SDavid Woodhouse /* This is broken because it assumes the MTD device is map-based 1203f5cf8f07SDavid Woodhouse and that mtd->priv is a valid struct map_info. It should be 1204f5cf8f07SDavid Woodhouse replaced with something that uses the mtd_get_unmapped_area() 1205f5cf8f07SDavid Woodhouse operation properly. */ 1206f5cf8f07SDavid Woodhouse if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) { 1207dd02b67dSAnatolij Gustschin #ifdef pgprot_noncached 12088558e4a2SLinus Torvalds if (file->f_flags & O_DSYNC || map->phys >= __pa(high_memory)) 1209dd02b67dSAnatolij Gustschin vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1210dd02b67dSAnatolij Gustschin #endif 12118558e4a2SLinus Torvalds return vm_iomap_memory(vma, map->phys, map->size); 1212dd02b67dSAnatolij Gustschin } 1213b9995932SVladimir Zapolskiy return -ENODEV; 1214402d3265SDavid Howells #else 1215b9995932SVladimir Zapolskiy return vma->vm_flags & VM_SHARED ? 0 : -EACCES; 1216402d3265SDavid Howells #endif 1217402d3265SDavid Howells } 1218402d3265SDavid Howells 1219d54b1fdbSArjan van de Ven static const struct file_operations mtd_fops = { 12201da177e4SLinus Torvalds .owner = THIS_MODULE, 1221969e57adSArtem Bityutskiy .llseek = mtdchar_lseek, 1222969e57adSArtem Bityutskiy .read = mtdchar_read, 1223969e57adSArtem Bityutskiy .write = mtdchar_write, 1224969e57adSArtem Bityutskiy .unlocked_ioctl = mtdchar_unlocked_ioctl, 122597718540SKevin Cernekee #ifdef CONFIG_COMPAT 1226969e57adSArtem Bityutskiy .compat_ioctl = mtdchar_compat_ioctl, 122797718540SKevin Cernekee #endif 1228969e57adSArtem Bityutskiy .open = mtdchar_open, 1229969e57adSArtem Bityutskiy .release = mtdchar_close, 1230969e57adSArtem Bityutskiy .mmap = mtdchar_mmap, 1231402d3265SDavid Howells #ifndef CONFIG_MMU 1232969e57adSArtem Bityutskiy .get_unmapped_area = mtdchar_get_unmapped_area, 1233b4caecd4SChristoph Hellwig .mmap_capabilities = mtdchar_mmap_capabilities, 1234402d3265SDavid Howells #endif 12351da177e4SLinus Torvalds }; 12361da177e4SLinus Torvalds 1237660685d9SArtem Bityutskiy int __init init_mtdchar(void) 1238cd874237SKirill A. Shutemov { 1239cd874237SKirill A. Shutemov int ret; 1240cd874237SKirill A. Shutemov 1241cd874237SKirill A. Shutemov ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, 1242cd874237SKirill A. Shutemov "mtd", &mtd_fops); 1243cd874237SKirill A. Shutemov if (ret < 0) { 124457ae2b60SArtem Bityutskiy pr_err("Can't allocate major number %d for MTD\n", 124557ae2b60SArtem Bityutskiy MTD_CHAR_MAJOR); 1246cd874237SKirill A. Shutemov return ret; 1247cd874237SKirill A. Shutemov } 1248cd874237SKirill A. Shutemov 1249cd874237SKirill A. Shutemov return ret; 12501da177e4SLinus Torvalds } 12511da177e4SLinus Torvalds 1252660685d9SArtem Bityutskiy void __exit cleanup_mtdchar(void) 12531da177e4SLinus Torvalds { 1254dad0db31SBen Hutchings __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); 12551da177e4SLinus Torvalds } 12561da177e4SLinus Torvalds 125790160e13SScott James Remnant MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR); 1258