in mtdchar.c [688:1087]
static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct mtd_info *master = mtd_get_master(mtd);
void __user *argp = (void __user *)arg;
int ret = 0;
struct mtd_info_user info;
pr_debug("MTD_ioctl\n");
/*
* Check the file mode to require "dangerous" commands to have write
* permissions.
*/
switch (cmd) {
/* "safe" commands */
case MEMGETREGIONCOUNT:
case MEMGETREGIONINFO:
case MEMGETINFO:
case MEMREADOOB:
case MEMREADOOB64:
case MEMISLOCKED:
case MEMGETOOBSEL:
case MEMGETBADBLOCK:
case OTPSELECT:
case OTPGETREGIONCOUNT:
case OTPGETREGIONINFO:
case ECCGETLAYOUT:
case ECCGETSTATS:
case MTDFILEMODE:
case BLKPG:
case BLKRRPART:
break;
/* "dangerous" commands */
case MEMERASE:
case MEMERASE64:
case MEMLOCK:
case MEMUNLOCK:
case MEMSETBADBLOCK:
case MEMWRITEOOB:
case MEMWRITEOOB64:
case MEMWRITE:
case OTPLOCK:
case OTPERASE:
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
break;
default:
return -ENOTTY;
}
switch (cmd) {
case MEMGETREGIONCOUNT:
if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
return -EFAULT;
break;
case MEMGETREGIONINFO:
{
uint32_t ur_idx;
struct mtd_erase_region_info *kr;
struct region_info_user __user *ur = argp;
if (get_user(ur_idx, &(ur->regionindex)))
return -EFAULT;
if (ur_idx >= mtd->numeraseregions)
return -EINVAL;
kr = &(mtd->eraseregions[ur_idx]);
if (put_user(kr->offset, &(ur->offset))
|| put_user(kr->erasesize, &(ur->erasesize))
|| put_user(kr->numblocks, &(ur->numblocks)))
return -EFAULT;
break;
}
case MEMGETINFO:
memset(&info, 0, sizeof(info));
info.type = mtd->type;
info.flags = mtd->flags;
info.size = mtd->size;
info.erasesize = mtd->erasesize;
info.writesize = mtd->writesize;
info.oobsize = mtd->oobsize;
/* The below field is obsolete */
info.padding = 0;
if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
return -EFAULT;
break;
case MEMERASE:
case MEMERASE64:
{
struct erase_info *erase;
erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
if (!erase)
ret = -ENOMEM;
else {
if (cmd == MEMERASE64) {
struct erase_info_user64 einfo64;
if (copy_from_user(&einfo64, argp,
sizeof(struct erase_info_user64))) {
kfree(erase);
return -EFAULT;
}
erase->addr = einfo64.start;
erase->len = einfo64.length;
} else {
struct erase_info_user einfo32;
if (copy_from_user(&einfo32, argp,
sizeof(struct erase_info_user))) {
kfree(erase);
return -EFAULT;
}
erase->addr = einfo32.start;
erase->len = einfo32.length;
}
ret = mtd_erase(mtd, erase);
kfree(erase);
}
break;
}
case MEMWRITEOOB:
{
struct mtd_oob_buf buf;
struct mtd_oob_buf __user *buf_user = argp;
/* NOTE: writes return length to buf_user->length */
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtdchar_writeoob(file, mtd, buf.start, buf.length,
buf.ptr, &buf_user->length);
break;
}
case MEMREADOOB:
{
struct mtd_oob_buf buf;
struct mtd_oob_buf __user *buf_user = argp;
/* NOTE: writes return length to buf_user->start */
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtdchar_readoob(file, mtd, buf.start, buf.length,
buf.ptr, &buf_user->start);
break;
}
case MEMWRITEOOB64:
{
struct mtd_oob_buf64 buf;
struct mtd_oob_buf64 __user *buf_user = argp;
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtdchar_writeoob(file, mtd, buf.start, buf.length,
(void __user *)(uintptr_t)buf.usr_ptr,
&buf_user->length);
break;
}
case MEMREADOOB64:
{
struct mtd_oob_buf64 buf;
struct mtd_oob_buf64 __user *buf_user = argp;
if (copy_from_user(&buf, argp, sizeof(buf)))
ret = -EFAULT;
else
ret = mtdchar_readoob(file, mtd, buf.start, buf.length,
(void __user *)(uintptr_t)buf.usr_ptr,
&buf_user->length);
break;
}
case MEMWRITE:
{
ret = mtdchar_write_ioctl(mtd,
(struct mtd_write_req __user *)arg);
break;
}
case MEMLOCK:
{
struct erase_info_user einfo;
if (copy_from_user(&einfo, argp, sizeof(einfo)))
return -EFAULT;
ret = mtd_lock(mtd, einfo.start, einfo.length);
break;
}
case MEMUNLOCK:
{
struct erase_info_user einfo;
if (copy_from_user(&einfo, argp, sizeof(einfo)))
return -EFAULT;
ret = mtd_unlock(mtd, einfo.start, einfo.length);
break;
}
case MEMISLOCKED:
{
struct erase_info_user einfo;
if (copy_from_user(&einfo, argp, sizeof(einfo)))
return -EFAULT;
ret = mtd_is_locked(mtd, einfo.start, einfo.length);
break;
}
/* Legacy interface */
case MEMGETOOBSEL:
{
struct nand_oobinfo oi;
if (!master->ooblayout)
return -EOPNOTSUPP;
ret = get_oobinfo(mtd, &oi);
if (ret)
return ret;
if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
return -EFAULT;
break;
}
case MEMGETBADBLOCK:
{
loff_t offs;
if (copy_from_user(&offs, argp, sizeof(loff_t)))
return -EFAULT;
return mtd_block_isbad(mtd, offs);
}
case MEMSETBADBLOCK:
{
loff_t offs;
if (copy_from_user(&offs, argp, sizeof(loff_t)))
return -EFAULT;
return mtd_block_markbad(mtd, offs);
}
case OTPSELECT:
{
int mode;
if (copy_from_user(&mode, argp, sizeof(int)))
return -EFAULT;
mfi->mode = MTD_FILE_MODE_NORMAL;
ret = otp_select_filemode(mfi, mode);
file->f_pos = 0;
break;
}
case OTPGETREGIONCOUNT:
case OTPGETREGIONINFO:
{
struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
size_t retlen;
if (!buf)
return -ENOMEM;
switch (mfi->mode) {
case MTD_FILE_MODE_OTP_FACTORY:
ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf);
break;
case MTD_FILE_MODE_OTP_USER:
ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf);
break;
default:
ret = -EINVAL;
break;
}
if (!ret) {
if (cmd == OTPGETREGIONCOUNT) {
int nbr = retlen / sizeof(struct otp_info);
ret = copy_to_user(argp, &nbr, sizeof(int));
} else
ret = copy_to_user(argp, buf, retlen);
if (ret)
ret = -EFAULT;
}
kfree(buf);
break;
}
case OTPLOCK:
case OTPERASE:
{
struct otp_info oinfo;
if (mfi->mode != MTD_FILE_MODE_OTP_USER)
return -EINVAL;
if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
return -EFAULT;
if (cmd == OTPLOCK)
ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
else
ret = mtd_erase_user_prot_reg(mtd, oinfo.start, oinfo.length);
break;
}
/* This ioctl is being deprecated - it truncates the ECC layout */
case ECCGETLAYOUT:
{
struct nand_ecclayout_user *usrlay;
if (!master->ooblayout)
return -EOPNOTSUPP;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
if (!usrlay)
return -ENOMEM;
shrink_ecclayout(mtd, usrlay);
if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
ret = -EFAULT;
kfree(usrlay);
break;
}
case ECCGETSTATS:
{
if (copy_to_user(argp, &mtd->ecc_stats,
sizeof(struct mtd_ecc_stats)))
return -EFAULT;
break;
}
case MTDFILEMODE:
{
mfi->mode = 0;
switch(arg) {
case MTD_FILE_MODE_OTP_FACTORY:
case MTD_FILE_MODE_OTP_USER:
ret = otp_select_filemode(mfi, arg);
break;
case MTD_FILE_MODE_RAW:
if (!mtd_has_oob(mtd))
return -EOPNOTSUPP;
mfi->mode = arg;
break;
case MTD_FILE_MODE_NORMAL:
break;
default:
ret = -EINVAL;
}
file->f_pos = 0;
break;
}
case BLKPG:
{
struct blkpg_ioctl_arg __user *blk_arg = argp;
struct blkpg_ioctl_arg a;
if (copy_from_user(&a, blk_arg, sizeof(a)))
ret = -EFAULT;
else
ret = mtdchar_blkpg_ioctl(mtd, &a);
break;
}
case BLKRRPART:
{
/* No reread partition feature. Just return ok */
ret = 0;
break;
}
}
return ret;
} /* memory_ioctl */