LCOV - code coverage report
Current view: top level - drivers/mtd - mtdchar.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 368 0.0 %
Date: 2014-02-18 Functions: 0 18 0.0 %
Branches: 0 268 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
       3                 :            :  *
       4                 :            :  * This program is free software; you can redistribute it and/or modify
       5                 :            :  * it under the terms of the GNU General Public License as published by
       6                 :            :  * the Free Software Foundation; either version 2 of the License, or
       7                 :            :  * (at your option) any later version.
       8                 :            :  *
       9                 :            :  * This program is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU General Public License
      15                 :            :  * along with this program; if not, write to the Free Software
      16                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17                 :            :  *
      18                 :            :  */
      19                 :            : 
      20                 :            : #include <linux/device.h>
      21                 :            : #include <linux/fs.h>
      22                 :            : #include <linux/mm.h>
      23                 :            : #include <linux/err.h>
      24                 :            : #include <linux/init.h>
      25                 :            : #include <linux/kernel.h>
      26                 :            : #include <linux/module.h>
      27                 :            : #include <linux/slab.h>
      28                 :            : #include <linux/sched.h>
      29                 :            : #include <linux/mutex.h>
      30                 :            : #include <linux/backing-dev.h>
      31                 :            : #include <linux/compat.h>
      32                 :            : #include <linux/mount.h>
      33                 :            : #include <linux/blkpg.h>
      34                 :            : #include <linux/magic.h>
      35                 :            : #include <linux/major.h>
      36                 :            : #include <linux/mtd/mtd.h>
      37                 :            : #include <linux/mtd/partitions.h>
      38                 :            : #include <linux/mtd/map.h>
      39                 :            : 
      40                 :            : #include <asm/uaccess.h>
      41                 :            : 
      42                 :            : #include "mtdcore.h"
      43                 :            : 
      44                 :            : static DEFINE_MUTEX(mtd_mutex);
      45                 :            : 
      46                 :            : /*
      47                 :            :  * Data structure to hold the pointer to the mtd device as well
      48                 :            :  * as mode information of various use cases.
      49                 :            :  */
      50                 :            : struct mtd_file_info {
      51                 :            :         struct mtd_info *mtd;
      52                 :            :         struct inode *ino;
      53                 :            :         enum mtd_file_modes mode;
      54                 :            : };
      55                 :            : 
      56                 :          0 : static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig)
      57                 :            : {
      58                 :          0 :         struct mtd_file_info *mfi = file->private_data;
      59                 :          0 :         return fixed_size_llseek(file, offset, orig, mfi->mtd->size);
      60                 :            : }
      61                 :            : 
      62                 :            : static int count;
      63                 :            : static struct vfsmount *mnt;
      64                 :            : static struct file_system_type mtd_inodefs_type;
      65                 :            : 
      66                 :          0 : static int mtdchar_open(struct inode *inode, struct file *file)
      67                 :            : {
      68                 :          0 :         int minor = iminor(inode);
      69                 :          0 :         int devnum = minor >> 1;
      70                 :            :         int ret = 0;
      71                 :            :         struct mtd_info *mtd;
      72                 :            :         struct mtd_file_info *mfi;
      73                 :            :         struct inode *mtd_ino;
      74                 :            : 
      75                 :            :         pr_debug("MTD_open\n");
      76                 :            : 
      77                 :            :         /* You can't open the RO devices RW */
      78 [ #  # ][ #  # ]:          0 :         if ((file->f_mode & FMODE_WRITE) && (minor & 1))
      79                 :            :                 return -EACCES;
      80                 :            : 
      81                 :          0 :         ret = simple_pin_fs(&mtd_inodefs_type, &mnt, &count);
      82         [ #  # ]:          0 :         if (ret)
      83                 :            :                 return ret;
      84                 :            : 
      85                 :          0 :         mutex_lock(&mtd_mutex);
      86                 :          0 :         mtd = get_mtd_device(NULL, devnum);
      87                 :            : 
      88         [ #  # ]:          0 :         if (IS_ERR(mtd)) {
      89                 :            :                 ret = PTR_ERR(mtd);
      90                 :          0 :                 goto out;
      91                 :            :         }
      92                 :            : 
      93         [ #  # ]:          0 :         if (mtd->type == MTD_ABSENT) {
      94                 :            :                 ret = -ENODEV;
      95                 :            :                 goto out1;
      96                 :            :         }
      97                 :            : 
      98                 :          0 :         mtd_ino = iget_locked(mnt->mnt_sb, devnum);
      99         [ #  # ]:          0 :         if (!mtd_ino) {
     100                 :            :                 ret = -ENOMEM;
     101                 :            :                 goto out1;
     102                 :            :         }
     103         [ #  # ]:          0 :         if (mtd_ino->i_state & I_NEW) {
     104                 :          0 :                 mtd_ino->i_private = mtd;
     105                 :          0 :                 mtd_ino->i_mode = S_IFCHR;
     106                 :          0 :                 mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info;
     107                 :          0 :                 unlock_new_inode(mtd_ino);
     108                 :            :         }
     109                 :          0 :         file->f_mapping = mtd_ino->i_mapping;
     110                 :            : 
     111                 :            :         /* You can't open it RW if it's not a writeable device */
     112 [ #  # ][ #  # ]:          0 :         if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
     113                 :            :                 ret = -EACCES;
     114                 :            :                 goto out2;
     115                 :            :         }
     116                 :            : 
     117                 :            :         mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
     118         [ #  # ]:          0 :         if (!mfi) {
     119                 :            :                 ret = -ENOMEM;
     120                 :            :                 goto out2;
     121                 :            :         }
     122                 :          0 :         mfi->ino = mtd_ino;
     123                 :          0 :         mfi->mtd = mtd;
     124                 :          0 :         file->private_data = mfi;
     125                 :          0 :         mutex_unlock(&mtd_mutex);
     126                 :          0 :         return 0;
     127                 :            : 
     128                 :            : out2:
     129                 :          0 :         iput(mtd_ino);
     130                 :            : out1:
     131                 :          0 :         put_mtd_device(mtd);
     132                 :            : out:
     133                 :          0 :         mutex_unlock(&mtd_mutex);
     134                 :          0 :         simple_release_fs(&mnt, &count);
     135                 :          0 :         return ret;
     136                 :            : } /* mtdchar_open */
     137                 :            : 
     138                 :            : /*====================================================================*/
     139                 :            : 
     140                 :          0 : static int mtdchar_close(struct inode *inode, struct file *file)
     141                 :            : {
     142                 :          0 :         struct mtd_file_info *mfi = file->private_data;
     143                 :          0 :         struct mtd_info *mtd = mfi->mtd;
     144                 :            : 
     145                 :            :         pr_debug("MTD_close\n");
     146                 :            : 
     147                 :            :         /* Only sync if opened RW */
     148         [ #  # ]:          0 :         if ((file->f_mode & FMODE_WRITE))
     149                 :            :                 mtd_sync(mtd);
     150                 :            : 
     151                 :          0 :         iput(mfi->ino);
     152                 :            : 
     153                 :          0 :         put_mtd_device(mtd);
     154                 :          0 :         file->private_data = NULL;
     155                 :          0 :         kfree(mfi);
     156                 :          0 :         simple_release_fs(&mnt, &count);
     157                 :            : 
     158                 :          0 :         return 0;
     159                 :            : } /* mtdchar_close */
     160                 :            : 
     161                 :            : /* Back in June 2001, dwmw2 wrote:
     162                 :            :  *
     163                 :            :  *   FIXME: This _really_ needs to die. In 2.5, we should lock the
     164                 :            :  *   userspace buffer down and use it directly with readv/writev.
     165                 :            :  *
     166                 :            :  * The implementation below, using mtd_kmalloc_up_to, mitigates
     167                 :            :  * allocation failures when the system is under low-memory situations
     168                 :            :  * or if memory is highly fragmented at the cost of reducing the
     169                 :            :  * performance of the requested transfer due to a smaller buffer size.
     170                 :            :  *
     171                 :            :  * A more complex but more memory-efficient implementation based on
     172                 :            :  * get_user_pages and iovecs to cover extents of those pages is a
     173                 :            :  * longer-term goal, as intimated by dwmw2 above. However, for the
     174                 :            :  * write case, this requires yet more complex head and tail transfer
     175                 :            :  * handling when those head and tail offsets and sizes are such that
     176                 :            :  * alignment requirements are not met in the NAND subdriver.
     177                 :            :  */
     178                 :            : 
     179                 :          0 : static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count,
     180                 :            :                         loff_t *ppos)
     181                 :            : {
     182                 :          0 :         struct mtd_file_info *mfi = file->private_data;
     183                 :          0 :         struct mtd_info *mtd = mfi->mtd;
     184                 :            :         size_t retlen;
     185                 :            :         size_t total_retlen=0;
     186                 :            :         int ret=0;
     187                 :            :         int len;
     188                 :          0 :         size_t size = count;
     189                 :            :         char *kbuf;
     190                 :            : 
     191                 :            :         pr_debug("MTD_read\n");
     192                 :            : 
     193         [ #  # ]:          0 :         if (*ppos + count > mtd->size)
     194                 :          0 :                 count = mtd->size - *ppos;
     195                 :            : 
     196         [ #  # ]:          0 :         if (!count)
     197                 :            :                 return 0;
     198                 :            : 
     199                 :          0 :         kbuf = mtd_kmalloc_up_to(mtd, &size);
     200         [ #  # ]:          0 :         if (!kbuf)
     201                 :            :                 return -ENOMEM;
     202                 :            : 
     203         [ #  # ]:          0 :         while (count) {
     204                 :          0 :                 len = min_t(size_t, count, size);
     205                 :            : 
     206   [ #  #  #  # ]:          0 :                 switch (mfi->mode) {
     207                 :            :                 case MTD_FILE_MODE_OTP_FACTORY:
     208                 :          0 :                         ret = mtd_read_fact_prot_reg(mtd, *ppos, len,
     209                 :            :                                                      &retlen, kbuf);
     210                 :          0 :                         break;
     211                 :            :                 case MTD_FILE_MODE_OTP_USER:
     212                 :          0 :                         ret = mtd_read_user_prot_reg(mtd, *ppos, len,
     213                 :            :                                                      &retlen, kbuf);
     214                 :          0 :                         break;
     215                 :            :                 case MTD_FILE_MODE_RAW:
     216                 :            :                 {
     217                 :            :                         struct mtd_oob_ops ops;
     218                 :            : 
     219                 :          0 :                         ops.mode = MTD_OPS_RAW;
     220                 :          0 :                         ops.datbuf = kbuf;
     221                 :          0 :                         ops.oobbuf = NULL;
     222                 :          0 :                         ops.len = len;
     223                 :            : 
     224                 :          0 :                         ret = mtd_read_oob(mtd, *ppos, &ops);
     225                 :          0 :                         retlen = ops.retlen;
     226                 :            :                         break;
     227                 :            :                 }
     228                 :            :                 default:
     229                 :          0 :                         ret = mtd_read(mtd, *ppos, len, &retlen, kbuf);
     230                 :            :                 }
     231                 :            :                 /* Nand returns -EBADMSG on ECC errors, but it returns
     232                 :            :                  * the data. For our userspace tools it is important
     233                 :            :                  * to dump areas with ECC errors!
     234                 :            :                  * For kernel internal usage it also might return -EUCLEAN
     235                 :            :                  * to signal the caller that a bitflip has occurred and has
     236                 :            :                  * been corrected by the ECC algorithm.
     237                 :            :                  * Userspace software which accesses NAND this way
     238                 :            :                  * must be aware of the fact that it deals with NAND
     239                 :            :                  */
     240 [ #  # ][ #  # ]:          0 :                 if (!ret || mtd_is_bitflip_or_eccerr(ret)) {
     241                 :          0 :                         *ppos += retlen;
     242         [ #  # ]:          0 :                         if (copy_to_user(buf, kbuf, retlen)) {
     243                 :          0 :                                 kfree(kbuf);
     244                 :          0 :                                 return -EFAULT;
     245                 :            :                         }
     246                 :            :                         else
     247                 :          0 :                                 total_retlen += retlen;
     248                 :            : 
     249                 :          0 :                         count -= retlen;
     250                 :          0 :                         buf += retlen;
     251         [ #  # ]:          0 :                         if (retlen == 0)
     252                 :            :                                 count = 0;
     253                 :            :                 }
     254                 :            :                 else {
     255                 :          0 :                         kfree(kbuf);
     256                 :          0 :                         return ret;
     257                 :            :                 }
     258                 :            : 
     259                 :            :         }
     260                 :            : 
     261                 :          0 :         kfree(kbuf);
     262                 :          0 :         return total_retlen;
     263                 :            : } /* mtdchar_read */
     264                 :            : 
     265                 :          0 : static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t count,
     266                 :            :                         loff_t *ppos)
     267                 :            : {
     268                 :          0 :         struct mtd_file_info *mfi = file->private_data;
     269                 :          0 :         struct mtd_info *mtd = mfi->mtd;
     270                 :          0 :         size_t size = count;
     271                 :            :         char *kbuf;
     272                 :            :         size_t retlen;
     273                 :            :         size_t total_retlen=0;
     274                 :            :         int ret=0;
     275                 :            :         int len;
     276                 :            : 
     277                 :            :         pr_debug("MTD_write\n");
     278                 :            : 
     279         [ #  # ]:          0 :         if (*ppos == mtd->size)
     280                 :            :                 return -ENOSPC;
     281                 :            : 
     282         [ #  # ]:          0 :         if (*ppos + count > mtd->size)
     283                 :          0 :                 count = mtd->size - *ppos;
     284                 :            : 
     285         [ #  # ]:          0 :         if (!count)
     286                 :            :                 return 0;
     287                 :            : 
     288                 :          0 :         kbuf = mtd_kmalloc_up_to(mtd, &size);
     289         [ #  # ]:          0 :         if (!kbuf)
     290                 :            :                 return -ENOMEM;
     291                 :            : 
     292         [ #  # ]:          0 :         while (count) {
     293                 :          0 :                 len = min_t(size_t, count, size);
     294                 :            : 
     295         [ #  # ]:          0 :                 if (copy_from_user(kbuf, buf, len)) {
     296                 :          0 :                         kfree(kbuf);
     297                 :          0 :                         return -EFAULT;
     298                 :            :                 }
     299                 :            : 
     300   [ #  #  #  # ]:          0 :                 switch (mfi->mode) {
     301                 :            :                 case MTD_FILE_MODE_OTP_FACTORY:
     302                 :            :                         ret = -EROFS;
     303                 :            :                         break;
     304                 :            :                 case MTD_FILE_MODE_OTP_USER:
     305                 :          0 :                         ret = mtd_write_user_prot_reg(mtd, *ppos, len,
     306                 :            :                                                       &retlen, kbuf);
     307                 :          0 :                         break;
     308                 :            : 
     309                 :            :                 case MTD_FILE_MODE_RAW:
     310                 :            :                 {
     311                 :            :                         struct mtd_oob_ops ops;
     312                 :            : 
     313                 :          0 :                         ops.mode = MTD_OPS_RAW;
     314                 :          0 :                         ops.datbuf = kbuf;
     315                 :          0 :                         ops.oobbuf = NULL;
     316                 :          0 :                         ops.ooboffs = 0;
     317                 :          0 :                         ops.len = len;
     318                 :            : 
     319                 :          0 :                         ret = mtd_write_oob(mtd, *ppos, &ops);
     320                 :          0 :                         retlen = ops.retlen;
     321                 :            :                         break;
     322                 :            :                 }
     323                 :            : 
     324                 :            :                 default:
     325                 :          0 :                         ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
     326                 :            :                 }
     327         [ #  # ]:          0 :                 if (!ret) {
     328                 :          0 :                         *ppos += retlen;
     329                 :          0 :                         total_retlen += retlen;
     330                 :          0 :                         count -= retlen;
     331                 :          0 :                         buf += retlen;
     332                 :            :                 }
     333                 :            :                 else {
     334                 :          0 :                         kfree(kbuf);
     335                 :          0 :                         return ret;
     336                 :            :                 }
     337                 :            :         }
     338                 :            : 
     339                 :          0 :         kfree(kbuf);
     340                 :          0 :         return total_retlen;
     341                 :            : } /* mtdchar_write */
     342                 :            : 
     343                 :            : /*======================================================================
     344                 :            : 
     345                 :            :     IOCTL calls for getting device parameters.
     346                 :            : 
     347                 :            : ======================================================================*/
     348                 :          0 : static void mtdchar_erase_callback (struct erase_info *instr)
     349                 :            : {
     350                 :          0 :         wake_up((wait_queue_head_t *)instr->priv);
     351                 :          0 : }
     352                 :            : 
     353                 :          0 : static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
     354                 :            : {
     355                 :          0 :         struct mtd_info *mtd = mfi->mtd;
     356                 :            :         size_t retlen;
     357                 :            : 
     358   [ #  #  #  # ]:          0 :         switch (mode) {
     359                 :            :         case MTD_OTP_FACTORY:
     360         [ #  # ]:          0 :                 if (mtd_read_fact_prot_reg(mtd, -1, 0, &retlen, NULL) ==
     361                 :            :                                 -EOPNOTSUPP)
     362                 :            :                         return -EOPNOTSUPP;
     363                 :            : 
     364                 :          0 :                 mfi->mode = MTD_FILE_MODE_OTP_FACTORY;
     365                 :            :                 break;
     366                 :            :         case MTD_OTP_USER:
     367         [ #  # ]:          0 :                 if (mtd_read_user_prot_reg(mtd, -1, 0, &retlen, NULL) ==
     368                 :            :                                 -EOPNOTSUPP)
     369                 :            :                         return -EOPNOTSUPP;
     370                 :            : 
     371                 :          0 :                 mfi->mode = MTD_FILE_MODE_OTP_USER;
     372                 :            :                 break;
     373                 :            :         case MTD_OTP_OFF:
     374                 :          0 :                 mfi->mode = MTD_FILE_MODE_NORMAL;
     375                 :            :                 break;
     376                 :            :         default:
     377                 :            :                 return -EINVAL;
     378                 :            :         }
     379                 :            : 
     380                 :            :         return 0;
     381                 :            : }
     382                 :            : 
     383                 :          0 : static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
     384                 :            :         uint64_t start, uint32_t length, void __user *ptr,
     385                 :            :         uint32_t __user *retp)
     386                 :            : {
     387                 :          0 :         struct mtd_file_info *mfi = file->private_data;
     388                 :            :         struct mtd_oob_ops ops;
     389                 :            :         uint32_t retlen;
     390                 :            :         int ret = 0;
     391                 :            : 
     392         [ #  # ]:          0 :         if (!(file->f_mode & FMODE_WRITE))
     393                 :            :                 return -EPERM;
     394                 :            : 
     395         [ #  # ]:          0 :         if (length > 4096)
     396                 :            :                 return -EINVAL;
     397                 :            : 
     398         [ #  # ]:          0 :         if (!mtd->_write_oob)
     399                 :            :                 ret = -EOPNOTSUPP;
     400                 :            :         else
     401         [ #  # ]:          0 :                 ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
     402                 :            : 
     403         [ #  # ]:          0 :         if (ret)
     404                 :            :                 return ret;
     405                 :            : 
     406                 :          0 :         ops.ooblen = length;
     407                 :          0 :         ops.ooboffs = start & (mtd->writesize - 1);
     408                 :          0 :         ops.datbuf = NULL;
     409         [ #  # ]:          0 :         ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW :
     410                 :            :                 MTD_OPS_PLACE_OOB;
     411                 :            : 
     412 [ #  # ][ #  # ]:          0 :         if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
     413                 :            :                 return -EINVAL;
     414                 :            : 
     415                 :          0 :         ops.oobbuf = memdup_user(ptr, length);
     416         [ #  # ]:          0 :         if (IS_ERR(ops.oobbuf))
     417                 :            :                 return PTR_ERR(ops.oobbuf);
     418                 :            : 
     419                 :          0 :         start &= ~((uint64_t)mtd->writesize - 1);
     420                 :          0 :         ret = mtd_write_oob(mtd, start, &ops);
     421                 :            : 
     422                 :            :         if (ops.oobretlen > 0xFFFFFFFFU)
     423                 :            :                 ret = -EOVERFLOW;
     424                 :          0 :         retlen = ops.oobretlen;
     425         [ #  # ]:          0 :         if (copy_to_user(retp, &retlen, sizeof(length)))
     426                 :            :                 ret = -EFAULT;
     427                 :            : 
     428                 :          0 :         kfree(ops.oobbuf);
     429                 :            :         return ret;
     430                 :            : }
     431                 :            : 
     432                 :          0 : static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
     433                 :            :         uint64_t start, uint32_t length, void __user *ptr,
     434                 :            :         uint32_t __user *retp)
     435                 :            : {
     436                 :          0 :         struct mtd_file_info *mfi = file->private_data;
     437                 :            :         struct mtd_oob_ops ops;
     438                 :            :         int ret = 0;
     439                 :            : 
     440         [ #  # ]:          0 :         if (length > 4096)
     441                 :            :                 return -EINVAL;
     442                 :            : 
     443         [ #  # ]:          0 :         if (!access_ok(VERIFY_WRITE, ptr, length))
     444                 :            :                 return -EFAULT;
     445                 :            : 
     446                 :          0 :         ops.ooblen = length;
     447                 :          0 :         ops.ooboffs = start & (mtd->writesize - 1);
     448                 :          0 :         ops.datbuf = NULL;
     449         [ #  # ]:          0 :         ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW :
     450                 :            :                 MTD_OPS_PLACE_OOB;
     451                 :            : 
     452 [ #  # ][ #  # ]:          0 :         if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
     453                 :            :                 return -EINVAL;
     454                 :            : 
     455                 :          0 :         ops.oobbuf = kmalloc(length, GFP_KERNEL);
     456         [ #  # ]:          0 :         if (!ops.oobbuf)
     457                 :            :                 return -ENOMEM;
     458                 :            : 
     459                 :          0 :         start &= ~((uint64_t)mtd->writesize - 1);
     460                 :          0 :         ret = mtd_read_oob(mtd, start, &ops);
     461                 :            : 
     462         [ #  # ]:          0 :         if (put_user(ops.oobretlen, retp))
     463                 :            :                 ret = -EFAULT;
     464 [ #  # ][ #  # ]:          0 :         else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf,
     465                 :            :                                             ops.oobretlen))
     466                 :            :                 ret = -EFAULT;
     467                 :            : 
     468                 :          0 :         kfree(ops.oobbuf);
     469                 :            : 
     470                 :            :         /*
     471                 :            :          * NAND returns -EBADMSG on ECC errors, but it returns the OOB
     472                 :            :          * data. For our userspace tools it is important to dump areas
     473                 :            :          * with ECC errors!
     474                 :            :          * For kernel internal usage it also might return -EUCLEAN
     475                 :            :          * to signal the caller that a bitflip has occured and has
     476                 :            :          * been corrected by the ECC algorithm.
     477                 :            :          *
     478                 :            :          * Note: currently the standard NAND function, nand_read_oob_std,
     479                 :            :          * does not calculate ECC for the OOB area, so do not rely on
     480                 :            :          * this behavior unless you have replaced it with your own.
     481                 :            :          */
     482         [ #  # ]:          0 :         if (mtd_is_bitflip_or_eccerr(ret))
     483                 :            :                 return 0;
     484                 :            : 
     485                 :            :         return ret;
     486                 :            : }
     487                 :            : 
     488                 :            : /*
     489                 :            :  * Copies (and truncates, if necessary) data from the larger struct,
     490                 :            :  * nand_ecclayout, to the smaller, deprecated layout struct,
     491                 :            :  * nand_ecclayout_user. This is necessary only to support the deprecated
     492                 :            :  * API ioctl ECCGETLAYOUT while allowing all new functionality to use
     493                 :            :  * nand_ecclayout flexibly (i.e. the struct may change size in new
     494                 :            :  * releases without requiring major rewrites).
     495                 :            :  */
     496                 :          0 : static int shrink_ecclayout(const struct nand_ecclayout *from,
     497                 :            :                 struct nand_ecclayout_user *to)
     498                 :            : {
     499                 :            :         int i;
     500                 :            : 
     501         [ #  # ]:          0 :         if (!from || !to)
     502                 :            :                 return -EINVAL;
     503                 :            : 
     504                 :          0 :         memset(to, 0, sizeof(*to));
     505                 :            : 
     506                 :          0 :         to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
     507         [ #  # ]:          0 :         for (i = 0; i < to->eccbytes; i++)
     508                 :          0 :                 to->eccpos[i] = from->eccpos[i];
     509                 :            : 
     510         [ #  # ]:          0 :         for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
     511 [ #  # ][ #  # ]:          0 :                 if (from->oobfree[i].length == 0 &&
     512                 :          0 :                                 from->oobfree[i].offset == 0)
     513                 :            :                         break;
     514                 :          0 :                 to->oobavail += from->oobfree[i].length;
     515                 :          0 :                 to->oobfree[i] = from->oobfree[i];
     516                 :            :         }
     517                 :            : 
     518                 :            :         return 0;
     519                 :            : }
     520                 :            : 
     521                 :          0 : static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
     522                 :            :                            struct blkpg_ioctl_arg __user *arg)
     523                 :            : {
     524                 :            :         struct blkpg_ioctl_arg a;
     525                 :            :         struct blkpg_partition p;
     526                 :            : 
     527         [ #  # ]:          0 :         if (!capable(CAP_SYS_ADMIN))
     528                 :            :                 return -EPERM;
     529                 :            : 
     530         [ #  # ]:          0 :         if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
     531                 :            :                 return -EFAULT;
     532                 :            : 
     533         [ #  # ]:          0 :         if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
     534                 :            :                 return -EFAULT;
     535                 :            : 
     536      [ #  #  # ]:          0 :         switch (a.op) {
     537                 :            :         case BLKPG_ADD_PARTITION:
     538                 :            : 
     539                 :            :                 /* Only master mtd device must be used to add partitions */
     540         [ #  # ]:          0 :                 if (mtd_is_partition(mtd))
     541                 :            :                         return -EINVAL;
     542                 :            : 
     543                 :          0 :                 return mtd_add_partition(mtd, p.devname, p.start, p.length);
     544                 :            : 
     545                 :            :         case BLKPG_DEL_PARTITION:
     546                 :            : 
     547         [ #  # ]:          0 :                 if (p.pno < 0)
     548                 :            :                         return -EINVAL;
     549                 :            : 
     550                 :          0 :                 return mtd_del_partition(mtd, p.pno);
     551                 :            : 
     552                 :            :         default:
     553                 :            :                 return -EINVAL;
     554                 :            :         }
     555                 :            : }
     556                 :            : 
     557                 :          0 : static int mtdchar_write_ioctl(struct mtd_info *mtd,
     558                 :            :                 struct mtd_write_req __user *argp)
     559                 :            : {
     560                 :            :         struct mtd_write_req req;
     561                 :            :         struct mtd_oob_ops ops;
     562                 :            :         void __user *usr_data, *usr_oob;
     563                 :            :         int ret;
     564                 :            : 
     565 [ #  # ][ #  # ]:          0 :         if (copy_from_user(&req, argp, sizeof(req)) ||
     566         [ #  # ]:          0 :                         !access_ok(VERIFY_READ, req.usr_data, req.len) ||
     567                 :          0 :                         !access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
     568                 :            :                 return -EFAULT;
     569         [ #  # ]:          0 :         if (!mtd->_write_oob)
     570                 :            :                 return -EOPNOTSUPP;
     571                 :            : 
     572                 :          0 :         ops.mode = req.mode;
     573                 :          0 :         ops.len = (size_t)req.len;
     574                 :          0 :         ops.ooblen = (size_t)req.ooblen;
     575                 :          0 :         ops.ooboffs = 0;
     576                 :            : 
     577                 :          0 :         usr_data = (void __user *)(uintptr_t)req.usr_data;
     578                 :          0 :         usr_oob = (void __user *)(uintptr_t)req.usr_oob;
     579                 :            : 
     580         [ #  # ]:          0 :         if (req.usr_data) {
     581                 :          0 :                 ops.datbuf = memdup_user(usr_data, ops.len);
     582         [ #  # ]:          0 :                 if (IS_ERR(ops.datbuf))
     583                 :          0 :                         return PTR_ERR(ops.datbuf);
     584                 :            :         } else {
     585                 :          0 :                 ops.datbuf = NULL;
     586                 :            :         }
     587                 :            : 
     588         [ #  # ]:          0 :         if (req.usr_oob) {
     589                 :          0 :                 ops.oobbuf = memdup_user(usr_oob, ops.ooblen);
     590         [ #  # ]:          0 :                 if (IS_ERR(ops.oobbuf)) {
     591                 :          0 :                         kfree(ops.datbuf);
     592                 :          0 :                         return PTR_ERR(ops.oobbuf);
     593                 :            :                 }
     594                 :            :         } else {
     595                 :          0 :                 ops.oobbuf = NULL;
     596                 :            :         }
     597                 :            : 
     598                 :          0 :         ret = mtd_write_oob(mtd, (loff_t)req.start, &ops);
     599                 :            : 
     600                 :          0 :         kfree(ops.datbuf);
     601                 :          0 :         kfree(ops.oobbuf);
     602                 :            : 
     603                 :          0 :         return ret;
     604                 :            : }
     605                 :            : 
     606                 :          0 : static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
     607                 :            : {
     608                 :          0 :         struct mtd_file_info *mfi = file->private_data;
     609                 :          0 :         struct mtd_info *mtd = mfi->mtd;
     610                 :          0 :         void __user *argp = (void __user *)arg;
     611                 :            :         int ret = 0;
     612                 :            :         u_long size;
     613                 :            :         struct mtd_info_user info;
     614                 :            : 
     615                 :            :         pr_debug("MTD_ioctl\n");
     616                 :            : 
     617                 :          0 :         size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
     618         [ #  # ]:          0 :         if (cmd & IOC_IN) {
     619         [ #  # ]:          0 :                 if (!access_ok(VERIFY_READ, argp, size))
     620                 :            :                         return -EFAULT;
     621                 :            :         }
     622         [ #  # ]:          0 :         if (cmd & IOC_OUT) {
     623         [ #  # ]:          0 :                 if (!access_ok(VERIFY_WRITE, argp, size))
     624                 :            :                         return -EFAULT;
     625                 :            :         }
     626                 :            : 
     627   [ #  #  #  #  :          0 :         switch (cmd) {
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     628                 :            :         case MEMGETREGIONCOUNT:
     629         [ #  # ]:          0 :                 if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
     630                 :            :                         return -EFAULT;
     631                 :            :                 break;
     632                 :            : 
     633                 :            :         case MEMGETREGIONINFO:
     634                 :            :         {
     635                 :            :                 uint32_t ur_idx;
     636                 :            :                 struct mtd_erase_region_info *kr;
     637                 :            :                 struct region_info_user __user *ur = argp;
     638                 :            : 
     639         [ #  # ]:          0 :                 if (get_user(ur_idx, &(ur->regionindex)))
     640                 :            :                         return -EFAULT;
     641                 :            : 
     642         [ #  # ]:          0 :                 if (ur_idx >= mtd->numeraseregions)
     643                 :            :                         return -EINVAL;
     644                 :            : 
     645                 :          0 :                 kr = &(mtd->eraseregions[ur_idx]);
     646                 :            : 
     647         [ #  # ]:          0 :                 if (put_user(kr->offset, &(ur->offset))
     648         [ #  # ]:          0 :                     || put_user(kr->erasesize, &(ur->erasesize))
     649         [ #  # ]:          0 :                     || put_user(kr->numblocks, &(ur->numblocks)))
     650                 :            :                         return -EFAULT;
     651                 :            : 
     652                 :            :                 break;
     653                 :            :         }
     654                 :            : 
     655                 :            :         case MEMGETINFO:
     656                 :          0 :                 memset(&info, 0, sizeof(info));
     657                 :          0 :                 info.type       = mtd->type;
     658                 :          0 :                 info.flags      = mtd->flags;
     659                 :          0 :                 info.size       = mtd->size;
     660                 :          0 :                 info.erasesize  = mtd->erasesize;
     661                 :          0 :                 info.writesize  = mtd->writesize;
     662                 :          0 :                 info.oobsize    = mtd->oobsize;
     663                 :            :                 /* The below field is obsolete */
     664                 :          0 :                 info.padding    = 0;
     665         [ #  # ]:          0 :                 if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
     666                 :            :                         return -EFAULT;
     667                 :            :                 break;
     668                 :            : 
     669                 :            :         case MEMERASE:
     670                 :            :         case MEMERASE64:
     671                 :            :         {
     672                 :            :                 struct erase_info *erase;
     673                 :            : 
     674         [ #  # ]:          0 :                 if(!(file->f_mode & FMODE_WRITE))
     675                 :            :                         return -EPERM;
     676                 :            : 
     677                 :            :                 erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
     678         [ #  # ]:          0 :                 if (!erase)
     679                 :            :                         ret = -ENOMEM;
     680                 :            :                 else {
     681                 :            :                         wait_queue_head_t waitq;
     682                 :          0 :                         DECLARE_WAITQUEUE(wait, current);
     683                 :            : 
     684                 :          0 :                         init_waitqueue_head(&waitq);
     685                 :            : 
     686         [ #  # ]:          0 :                         if (cmd == MEMERASE64) {
     687                 :            :                                 struct erase_info_user64 einfo64;
     688                 :            : 
     689         [ #  # ]:          0 :                                 if (copy_from_user(&einfo64, argp,
     690                 :            :                                             sizeof(struct erase_info_user64))) {
     691                 :          0 :                                         kfree(erase);
     692                 :          0 :                                         return -EFAULT;
     693                 :            :                                 }
     694                 :          0 :                                 erase->addr = einfo64.start;
     695                 :          0 :                                 erase->len = einfo64.length;
     696                 :            :                         } else {
     697                 :            :                                 struct erase_info_user einfo32;
     698                 :            : 
     699         [ #  # ]:          0 :                                 if (copy_from_user(&einfo32, argp,
     700                 :            :                                             sizeof(struct erase_info_user))) {
     701                 :          0 :                                         kfree(erase);
     702                 :          0 :                                         return -EFAULT;
     703                 :            :                                 }
     704                 :          0 :                                 erase->addr = einfo32.start;
     705                 :          0 :                                 erase->len = einfo32.length;
     706                 :            :                         }
     707                 :          0 :                         erase->mtd = mtd;
     708                 :          0 :                         erase->callback = mtdchar_erase_callback;
     709                 :          0 :                         erase->priv = (unsigned long)&waitq;
     710                 :            : 
     711                 :            :                         /*
     712                 :            :                           FIXME: Allow INTERRUPTIBLE. Which means
     713                 :            :                           not having the wait_queue head on the stack.
     714                 :            : 
     715                 :            :                           If the wq_head is on the stack, and we
     716                 :            :                           leave because we got interrupted, then the
     717                 :            :                           wq_head is no longer there when the
     718                 :            :                           callback routine tries to wake us up.
     719                 :            :                         */
     720                 :          0 :                         ret = mtd_erase(mtd, erase);
     721         [ #  # ]:          0 :                         if (!ret) {
     722                 :          0 :                                 set_current_state(TASK_UNINTERRUPTIBLE);
     723                 :          0 :                                 add_wait_queue(&waitq, &wait);
     724         [ #  # ]:          0 :                                 if (erase->state != MTD_ERASE_DONE &&
     725                 :            :                                     erase->state != MTD_ERASE_FAILED)
     726                 :          0 :                                         schedule();
     727                 :          0 :                                 remove_wait_queue(&waitq, &wait);
     728                 :          0 :                                 set_current_state(TASK_RUNNING);
     729                 :            : 
     730         [ #  # ]:          0 :                                 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
     731                 :            :                         }
     732                 :          0 :                         kfree(erase);
     733                 :            :                 }
     734                 :            :                 break;
     735                 :            :         }
     736                 :            : 
     737                 :            :         case MEMWRITEOOB:
     738                 :            :         {
     739                 :            :                 struct mtd_oob_buf buf;
     740                 :            :                 struct mtd_oob_buf __user *buf_user = argp;
     741                 :            : 
     742                 :            :                 /* NOTE: writes return length to buf_user->length */
     743         [ #  # ]:          0 :                 if (copy_from_user(&buf, argp, sizeof(buf)))
     744                 :            :                         ret = -EFAULT;
     745                 :            :                 else
     746                 :          0 :                         ret = mtdchar_writeoob(file, mtd, buf.start, buf.length,
     747                 :          0 :                                 buf.ptr, &buf_user->length);
     748                 :            :                 break;
     749                 :            :         }
     750                 :            : 
     751                 :            :         case MEMREADOOB:
     752                 :            :         {
     753                 :            :                 struct mtd_oob_buf buf;
     754                 :            :                 struct mtd_oob_buf __user *buf_user = argp;
     755                 :            : 
     756                 :            :                 /* NOTE: writes return length to buf_user->start */
     757         [ #  # ]:          0 :                 if (copy_from_user(&buf, argp, sizeof(buf)))
     758                 :            :                         ret = -EFAULT;
     759                 :            :                 else
     760                 :          0 :                         ret = mtdchar_readoob(file, mtd, buf.start, buf.length,
     761                 :          0 :                                 buf.ptr, &buf_user->start);
     762                 :            :                 break;
     763                 :            :         }
     764                 :            : 
     765                 :            :         case MEMWRITEOOB64:
     766                 :            :         {
     767                 :            :                 struct mtd_oob_buf64 buf;
     768                 :            :                 struct mtd_oob_buf64 __user *buf_user = argp;
     769                 :            : 
     770         [ #  # ]:          0 :                 if (copy_from_user(&buf, argp, sizeof(buf)))
     771                 :            :                         ret = -EFAULT;
     772                 :            :                 else
     773                 :          0 :                         ret = mtdchar_writeoob(file, mtd, buf.start, buf.length,
     774                 :          0 :                                 (void __user *)(uintptr_t)buf.usr_ptr,
     775                 :          0 :                                 &buf_user->length);
     776                 :            :                 break;
     777                 :            :         }
     778                 :            : 
     779                 :            :         case MEMREADOOB64:
     780                 :            :         {
     781                 :            :                 struct mtd_oob_buf64 buf;
     782                 :            :                 struct mtd_oob_buf64 __user *buf_user = argp;
     783                 :            : 
     784         [ #  # ]:          0 :                 if (copy_from_user(&buf, argp, sizeof(buf)))
     785                 :            :                         ret = -EFAULT;
     786                 :            :                 else
     787                 :          0 :                         ret = mtdchar_readoob(file, mtd, buf.start, buf.length,
     788                 :          0 :                                 (void __user *)(uintptr_t)buf.usr_ptr,
     789                 :          0 :                                 &buf_user->length);
     790                 :            :                 break;
     791                 :            :         }
     792                 :            : 
     793                 :            :         case MEMWRITE:
     794                 :            :         {
     795                 :          0 :                 ret = mtdchar_write_ioctl(mtd,
     796                 :            :                       (struct mtd_write_req __user *)arg);
     797                 :          0 :                 break;
     798                 :            :         }
     799                 :            : 
     800                 :            :         case MEMLOCK:
     801                 :            :         {
     802                 :            :                 struct erase_info_user einfo;
     803                 :            : 
     804         [ #  # ]:          0 :                 if (copy_from_user(&einfo, argp, sizeof(einfo)))
     805                 :          0 :                         return -EFAULT;
     806                 :            : 
     807                 :          0 :                 ret = mtd_lock(mtd, einfo.start, einfo.length);
     808                 :          0 :                 break;
     809                 :            :         }
     810                 :            : 
     811                 :            :         case MEMUNLOCK:
     812                 :            :         {
     813                 :            :                 struct erase_info_user einfo;
     814                 :            : 
     815         [ #  # ]:          0 :                 if (copy_from_user(&einfo, argp, sizeof(einfo)))
     816                 :          0 :                         return -EFAULT;
     817                 :            : 
     818                 :          0 :                 ret = mtd_unlock(mtd, einfo.start, einfo.length);
     819                 :          0 :                 break;
     820                 :            :         }
     821                 :            : 
     822                 :            :         case MEMISLOCKED:
     823                 :            :         {
     824                 :            :                 struct erase_info_user einfo;
     825                 :            : 
     826         [ #  # ]:          0 :                 if (copy_from_user(&einfo, argp, sizeof(einfo)))
     827                 :          0 :                         return -EFAULT;
     828                 :            : 
     829                 :          0 :                 ret = mtd_is_locked(mtd, einfo.start, einfo.length);
     830                 :          0 :                 break;
     831                 :            :         }
     832                 :            : 
     833                 :            :         /* Legacy interface */
     834                 :            :         case MEMGETOOBSEL:
     835                 :            :         {
     836                 :            :                 struct nand_oobinfo oi;
     837                 :            : 
     838         [ #  # ]:          0 :                 if (!mtd->ecclayout)
     839                 :          0 :                         return -EOPNOTSUPP;
     840         [ #  # ]:          0 :                 if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
     841                 :            :                         return -EINVAL;
     842                 :            : 
     843                 :          0 :                 oi.useecc = MTD_NANDECC_AUTOPLACE;
     844                 :          0 :                 memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
     845                 :          0 :                 memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
     846                 :            :                        sizeof(oi.oobfree));
     847                 :          0 :                 oi.eccbytes = mtd->ecclayout->eccbytes;
     848                 :            : 
     849         [ #  # ]:          0 :                 if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
     850                 :            :                         return -EFAULT;
     851                 :          0 :                 break;
     852                 :            :         }
     853                 :            : 
     854                 :            :         case MEMGETBADBLOCK:
     855                 :            :         {
     856                 :            :                 loff_t offs;
     857                 :            : 
     858         [ #  # ]:          0 :                 if (copy_from_user(&offs, argp, sizeof(loff_t)))
     859                 :            :                         return -EFAULT;
     860                 :          0 :                 return mtd_block_isbad(mtd, offs);
     861                 :            :                 break;
     862                 :            :         }
     863                 :            : 
     864                 :            :         case MEMSETBADBLOCK:
     865                 :            :         {
     866                 :            :                 loff_t offs;
     867                 :            : 
     868         [ #  # ]:          0 :                 if (copy_from_user(&offs, argp, sizeof(loff_t)))
     869                 :            :                         return -EFAULT;
     870                 :          0 :                 return mtd_block_markbad(mtd, offs);
     871                 :            :                 break;
     872                 :            :         }
     873                 :            : 
     874                 :            :         case OTPSELECT:
     875                 :            :         {
     876                 :            :                 int mode;
     877         [ #  # ]:          0 :                 if (copy_from_user(&mode, argp, sizeof(int)))
     878                 :          0 :                         return -EFAULT;
     879                 :            : 
     880                 :          0 :                 mfi->mode = MTD_FILE_MODE_NORMAL;
     881                 :            : 
     882                 :          0 :                 ret = otp_select_filemode(mfi, mode);
     883                 :            : 
     884                 :          0 :                 file->f_pos = 0;
     885                 :          0 :                 break;
     886                 :            :         }
     887                 :            : 
     888                 :            :         case OTPGETREGIONCOUNT:
     889                 :            :         case OTPGETREGIONINFO:
     890                 :            :         {
     891                 :            :                 struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
     892         [ #  # ]:          0 :                 if (!buf)
     893                 :            :                         return -ENOMEM;
     894      [ #  #  # ]:          0 :                 switch (mfi->mode) {
     895                 :            :                 case MTD_FILE_MODE_OTP_FACTORY:
     896                 :          0 :                         ret = mtd_get_fact_prot_info(mtd, buf, 4096);
     897                 :          0 :                         break;
     898                 :            :                 case MTD_FILE_MODE_OTP_USER:
     899                 :          0 :                         ret = mtd_get_user_prot_info(mtd, buf, 4096);
     900                 :          0 :                         break;
     901                 :            :                 default:
     902                 :            :                         ret = -EINVAL;
     903                 :            :                         break;
     904                 :            :                 }
     905         [ #  # ]:          0 :                 if (ret >= 0) {
     906         [ #  # ]:          0 :                         if (cmd == OTPGETREGIONCOUNT) {
     907                 :          0 :                                 int nbr = ret / sizeof(struct otp_info);
     908                 :          0 :                                 ret = copy_to_user(argp, &nbr, sizeof(int));
     909                 :            :                         } else
     910                 :          0 :                                 ret = copy_to_user(argp, buf, ret);
     911         [ #  # ]:          0 :                         if (ret)
     912                 :            :                                 ret = -EFAULT;
     913                 :            :                 }
     914                 :          0 :                 kfree(buf);
     915                 :          0 :                 break;
     916                 :            :         }
     917                 :            : 
     918                 :            :         case OTPLOCK:
     919                 :            :         {
     920                 :            :                 struct otp_info oinfo;
     921                 :            : 
     922         [ #  # ]:          0 :                 if (mfi->mode != MTD_FILE_MODE_OTP_USER)
     923                 :          0 :                         return -EINVAL;
     924         [ #  # ]:          0 :                 if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
     925                 :            :                         return -EFAULT;
     926                 :          0 :                 ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
     927                 :          0 :                 break;
     928                 :            :         }
     929                 :            : 
     930                 :            :         /* This ioctl is being deprecated - it truncates the ECC layout */
     931                 :            :         case ECCGETLAYOUT:
     932                 :            :         {
     933                 :            :                 struct nand_ecclayout_user *usrlay;
     934                 :            : 
     935         [ #  # ]:          0 :                 if (!mtd->ecclayout)
     936                 :            :                         return -EOPNOTSUPP;
     937                 :            : 
     938                 :            :                 usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
     939         [ #  # ]:          0 :                 if (!usrlay)
     940                 :            :                         return -ENOMEM;
     941                 :            : 
     942                 :          0 :                 shrink_ecclayout(mtd->ecclayout, usrlay);
     943                 :            : 
     944         [ #  # ]:          0 :                 if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
     945                 :            :                         ret = -EFAULT;
     946                 :          0 :                 kfree(usrlay);
     947                 :          0 :                 break;
     948                 :            :         }
     949                 :            : 
     950                 :            :         case ECCGETSTATS:
     951                 :            :         {
     952         [ #  # ]:          0 :                 if (copy_to_user(argp, &mtd->ecc_stats,
     953                 :            :                                  sizeof(struct mtd_ecc_stats)))
     954                 :            :                         return -EFAULT;
     955                 :            :                 break;
     956                 :            :         }
     957                 :            : 
     958                 :            :         case MTDFILEMODE:
     959                 :            :         {
     960                 :          0 :                 mfi->mode = 0;
     961                 :            : 
     962   [ #  #  #  # ]:          0 :                 switch(arg) {
     963                 :            :                 case MTD_FILE_MODE_OTP_FACTORY:
     964                 :            :                 case MTD_FILE_MODE_OTP_USER:
     965                 :          0 :                         ret = otp_select_filemode(mfi, arg);
     966                 :          0 :                         break;
     967                 :            : 
     968                 :            :                 case MTD_FILE_MODE_RAW:
     969         [ #  # ]:          0 :                         if (!mtd_has_oob(mtd))
     970                 :            :                                 return -EOPNOTSUPP;
     971                 :          0 :                         mfi->mode = arg;
     972                 :            : 
     973                 :            :                 case MTD_FILE_MODE_NORMAL:
     974                 :            :                         break;
     975                 :            :                 default:
     976                 :            :                         ret = -EINVAL;
     977                 :            :                 }
     978                 :          0 :                 file->f_pos = 0;
     979                 :          0 :                 break;
     980                 :            :         }
     981                 :            : 
     982                 :            :         case BLKPG:
     983                 :            :         {
     984                 :          0 :                 ret = mtdchar_blkpg_ioctl(mtd,
     985                 :            :                       (struct blkpg_ioctl_arg __user *)arg);
     986                 :          0 :                 break;
     987                 :            :         }
     988                 :            : 
     989                 :            :         case BLKRRPART:
     990                 :            :         {
     991                 :            :                 /* No reread partition feature. Just return ok */
     992                 :            :                 ret = 0;
     993                 :            :                 break;
     994                 :            :         }
     995                 :            : 
     996                 :            :         default:
     997                 :            :                 ret = -ENOTTY;
     998                 :            :         }
     999                 :            : 
    1000                 :          0 :         return ret;
    1001                 :            : } /* memory_ioctl */
    1002                 :            : 
    1003                 :          0 : static long mtdchar_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
    1004                 :            : {
    1005                 :            :         int ret;
    1006                 :            : 
    1007                 :          0 :         mutex_lock(&mtd_mutex);
    1008                 :          0 :         ret = mtdchar_ioctl(file, cmd, arg);
    1009                 :          0 :         mutex_unlock(&mtd_mutex);
    1010                 :            : 
    1011                 :          0 :         return ret;
    1012                 :            : }
    1013                 :            : 
    1014                 :            : #ifdef CONFIG_COMPAT
    1015                 :            : 
    1016                 :            : struct mtd_oob_buf32 {
    1017                 :            :         u_int32_t start;
    1018                 :            :         u_int32_t length;
    1019                 :            :         compat_caddr_t ptr;     /* unsigned char* */
    1020                 :            : };
    1021                 :            : 
    1022                 :            : #define MEMWRITEOOB32           _IOWR('M', 3, struct mtd_oob_buf32)
    1023                 :            : #define MEMREADOOB32            _IOWR('M', 4, struct mtd_oob_buf32)
    1024                 :            : 
    1025                 :            : static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd,
    1026                 :            :         unsigned long arg)
    1027                 :            : {
    1028                 :            :         struct mtd_file_info *mfi = file->private_data;
    1029                 :            :         struct mtd_info *mtd = mfi->mtd;
    1030                 :            :         void __user *argp = compat_ptr(arg);
    1031                 :            :         int ret = 0;
    1032                 :            : 
    1033                 :            :         mutex_lock(&mtd_mutex);
    1034                 :            : 
    1035                 :            :         switch (cmd) {
    1036                 :            :         case MEMWRITEOOB32:
    1037                 :            :         {
    1038                 :            :                 struct mtd_oob_buf32 buf;
    1039                 :            :                 struct mtd_oob_buf32 __user *buf_user = argp;
    1040                 :            : 
    1041                 :            :                 if (copy_from_user(&buf, argp, sizeof(buf)))
    1042                 :            :                         ret = -EFAULT;
    1043                 :            :                 else
    1044                 :            :                         ret = mtdchar_writeoob(file, mtd, buf.start,
    1045                 :            :                                 buf.length, compat_ptr(buf.ptr),
    1046                 :            :                                 &buf_user->length);
    1047                 :            :                 break;
    1048                 :            :         }
    1049                 :            : 
    1050                 :            :         case MEMREADOOB32:
    1051                 :            :         {
    1052                 :            :                 struct mtd_oob_buf32 buf;
    1053                 :            :                 struct mtd_oob_buf32 __user *buf_user = argp;
    1054                 :            : 
    1055                 :            :                 /* NOTE: writes return length to buf->start */
    1056                 :            :                 if (copy_from_user(&buf, argp, sizeof(buf)))
    1057                 :            :                         ret = -EFAULT;
    1058                 :            :                 else
    1059                 :            :                         ret = mtdchar_readoob(file, mtd, buf.start,
    1060                 :            :                                 buf.length, compat_ptr(buf.ptr),
    1061                 :            :                                 &buf_user->start);
    1062                 :            :                 break;
    1063                 :            :         }
    1064                 :            :         default:
    1065                 :            :                 ret = mtdchar_ioctl(file, cmd, (unsigned long)argp);
    1066                 :            :         }
    1067                 :            : 
    1068                 :            :         mutex_unlock(&mtd_mutex);
    1069                 :            : 
    1070                 :            :         return ret;
    1071                 :            : }
    1072                 :            : 
    1073                 :            : #endif /* CONFIG_COMPAT */
    1074                 :            : 
    1075                 :            : /*
    1076                 :            :  * try to determine where a shared mapping can be made
    1077                 :            :  * - only supported for NOMMU at the moment (MMU can't doesn't copy private
    1078                 :            :  *   mappings)
    1079                 :            :  */
    1080                 :            : #ifndef CONFIG_MMU
    1081                 :            : static unsigned long mtdchar_get_unmapped_area(struct file *file,
    1082                 :            :                                            unsigned long addr,
    1083                 :            :                                            unsigned long len,
    1084                 :            :                                            unsigned long pgoff,
    1085                 :            :                                            unsigned long flags)
    1086                 :            : {
    1087                 :            :         struct mtd_file_info *mfi = file->private_data;
    1088                 :            :         struct mtd_info *mtd = mfi->mtd;
    1089                 :            :         unsigned long offset;
    1090                 :            :         int ret;
    1091                 :            : 
    1092                 :            :         if (addr != 0)
    1093                 :            :                 return (unsigned long) -EINVAL;
    1094                 :            : 
    1095                 :            :         if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
    1096                 :            :                 return (unsigned long) -EINVAL;
    1097                 :            : 
    1098                 :            :         offset = pgoff << PAGE_SHIFT;
    1099                 :            :         if (offset > mtd->size - len)
    1100                 :            :                 return (unsigned long) -EINVAL;
    1101                 :            : 
    1102                 :            :         ret = mtd_get_unmapped_area(mtd, len, offset, flags);
    1103                 :            :         return ret == -EOPNOTSUPP ? -ENODEV : ret;
    1104                 :            : }
    1105                 :            : #endif
    1106                 :            : 
    1107                 :            : /*
    1108                 :            :  * set up a mapping for shared memory segments
    1109                 :            :  */
    1110                 :          0 : static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
    1111                 :            : {
    1112                 :            : #ifdef CONFIG_MMU
    1113                 :            :         struct mtd_file_info *mfi = file->private_data;
    1114                 :            :         struct mtd_info *mtd = mfi->mtd;
    1115                 :            :         struct map_info *map = mtd->priv;
    1116                 :            : 
    1117                 :            :         /* This is broken because it assumes the MTD device is map-based
    1118                 :            :            and that mtd->priv is a valid struct map_info.  It should be
    1119                 :            :            replaced with something that uses the mtd_get_unmapped_area()
    1120                 :            :            operation properly. */
    1121                 :            :         if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) {
    1122                 :            : #ifdef pgprot_noncached
    1123                 :            :                 if (file->f_flags & O_DSYNC || map->phys >= __pa(high_memory))
    1124                 :            :                         vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    1125                 :            : #endif
    1126                 :            :                 return vm_iomap_memory(vma, map->phys, map->size);
    1127                 :            :         }
    1128                 :            :         return -ENODEV;
    1129                 :            : #else
    1130                 :            :         return vma->vm_flags & VM_SHARED ? 0 : -EACCES;
    1131                 :            : #endif
    1132                 :            : }
    1133                 :            : 
    1134                 :            : static const struct file_operations mtd_fops = {
    1135                 :            :         .owner          = THIS_MODULE,
    1136                 :            :         .llseek         = mtdchar_lseek,
    1137                 :            :         .read           = mtdchar_read,
    1138                 :            :         .write          = mtdchar_write,
    1139                 :            :         .unlocked_ioctl = mtdchar_unlocked_ioctl,
    1140                 :            : #ifdef CONFIG_COMPAT
    1141                 :            :         .compat_ioctl   = mtdchar_compat_ioctl,
    1142                 :            : #endif
    1143                 :            :         .open           = mtdchar_open,
    1144                 :            :         .release        = mtdchar_close,
    1145                 :            :         .mmap           = mtdchar_mmap,
    1146                 :            : #ifndef CONFIG_MMU
    1147                 :            :         .get_unmapped_area = mtdchar_get_unmapped_area,
    1148                 :            : #endif
    1149                 :            : };
    1150                 :            : 
    1151                 :            : static const struct super_operations mtd_ops = {
    1152                 :            :         .drop_inode = generic_delete_inode,
    1153                 :            :         .statfs = simple_statfs,
    1154                 :            : };
    1155                 :            : 
    1156                 :          0 : static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
    1157                 :            :                                 int flags, const char *dev_name, void *data)
    1158                 :            : {
    1159                 :          0 :         return mount_pseudo(fs_type, "mtd_inode:", &mtd_ops, NULL, MTD_INODE_FS_MAGIC);
    1160                 :            : }
    1161                 :            : 
    1162                 :            : static struct file_system_type mtd_inodefs_type = {
    1163                 :            :        .name = "mtd_inodefs",
    1164                 :            :        .mount = mtd_inodefs_mount,
    1165                 :            :        .kill_sb = kill_anon_super,
    1166                 :            : };
    1167                 :            : MODULE_ALIAS_FS("mtd_inodefs");
    1168                 :            : 
    1169                 :          0 : int __init init_mtdchar(void)
    1170                 :            : {
    1171                 :            :         int ret;
    1172                 :            : 
    1173                 :          0 :         ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
    1174                 :            :                                    "mtd", &mtd_fops);
    1175         [ #  # ]:          0 :         if (ret < 0) {
    1176                 :          0 :                 pr_err("Can't allocate major number %d for MTD\n",
    1177                 :            :                        MTD_CHAR_MAJOR);
    1178                 :          0 :                 return ret;
    1179                 :            :         }
    1180                 :            : 
    1181                 :          0 :         ret = register_filesystem(&mtd_inodefs_type);
    1182         [ #  # ]:          0 :         if (ret) {
    1183                 :          0 :                 pr_err("Can't register mtd_inodefs filesystem, error %d\n",
    1184                 :            :                        ret);
    1185                 :            :                 goto err_unregister_chdev;
    1186                 :            :         }
    1187                 :            : 
    1188                 :            :         return ret;
    1189                 :            : 
    1190                 :            : err_unregister_chdev:
    1191                 :          0 :         __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
    1192                 :          0 :         return ret;
    1193                 :            : }
    1194                 :            : 
    1195                 :          0 : void __exit cleanup_mtdchar(void)
    1196                 :            : {
    1197                 :          0 :         unregister_filesystem(&mtd_inodefs_type);
    1198                 :          0 :         __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
    1199                 :          0 : }
    1200                 :            : 
    1201                 :            : MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);

Generated by: LCOV version 1.9