📄 mtdchar.c
字号:
struct region_info_user *ur = (struct region_info_user *) argp; if (get_user(ur_idx, &(ur->regionindex))) return -EFAULT; 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: 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 fields are obsolete */ info.ecctype = -1; info.eccsize = 0; if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) return -EFAULT; break; case MEMERASE: { struct erase_info *erase; if(!(file->f_mode & FMODE_WRITE)) return -EPERM; erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); if (!erase) ret = -ENOMEM; else { wait_queue_head_t waitq; DECLARE_WAITQUEUE(wait, current); init_waitqueue_head(&waitq); if (copy_from_user(&erase->addr, argp, sizeof(struct erase_info_user))) { kfree(erase); return -EFAULT; } erase->mtd = mtd; erase->callback = mtdchar_erase_callback; erase->priv = (unsigned long)&waitq; /* FIXME: Allow INTERRUPTIBLE. Which means not having the wait_queue head on the stack. If the wq_head is on the stack, and we leave because we got interrupted, then the wq_head is no longer there when the callback routine tries to wake us up. */ ret = mtd->erase(mtd, erase); if (!ret) { set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&waitq, &wait); if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) schedule(); remove_wait_queue(&waitq, &wait); set_current_state(TASK_RUNNING); ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; } kfree(erase); } break; } case MEMWRITEOOB: { struct mtd_oob_buf buf; struct mtd_oob_ops ops; struct mtd_oob_buf __user *user_buf = argp; uint32_t retlen; if(!(file->f_mode & FMODE_WRITE)) return -EPERM; if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; if (buf.length > 4096) return -EINVAL; if (!mtd->write_oob) ret = -EOPNOTSUPP; else ret = access_ok(VERIFY_READ, buf.ptr, buf.length) ? 0 : EFAULT; if (ret) return ret; ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); if (!ops.oobbuf) return -ENOMEM; if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { kfree(ops.oobbuf); return -EFAULT; } buf.start &= ~(mtd->oobsize - 1); ret = mtd->write_oob(mtd, buf.start, &ops); if (ops.oobretlen > 0xFFFFFFFFU) ret = -EOVERFLOW; retlen = ops.oobretlen; if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length))) ret = -EFAULT; kfree(ops.oobbuf); break; } case MEMREADOOB: { struct mtd_oob_buf buf; struct mtd_oob_ops ops; if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; if (buf.length > 4096) return -EINVAL; if (!mtd->read_oob) ret = -EOPNOTSUPP; else ret = access_ok(VERIFY_WRITE, buf.ptr, buf.length) ? 0 : -EFAULT; if (ret) return ret; ops.ooblen = buf.length; ops.ooboffs = buf.start & (mtd->oobsize - 1); ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); if (!ops.oobbuf) return -ENOMEM; buf.start &= ~(mtd->oobsize - 1); ret = mtd->read_oob(mtd, buf.start, &ops); if (put_user(ops.oobretlen, (uint32_t __user *)argp)) ret = -EFAULT; else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, ops.oobretlen)) ret = -EFAULT; kfree(ops.oobbuf); break; } case MEMLOCK: { struct erase_info_user einfo; if (copy_from_user(&einfo, argp, sizeof(einfo))) return -EFAULT; if (!mtd->lock) ret = -EOPNOTSUPP; else 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; if (!mtd->unlock) ret = -EOPNOTSUPP; else ret = mtd->unlock(mtd, einfo.start, einfo.length); break; } /* Legacy interface */ case MEMGETOOBSEL: { struct nand_oobinfo oi; if (!mtd->ecclayout) return -EOPNOTSUPP; if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) return -EINVAL; oi.useecc = MTD_NANDECC_AUTOPLACE; memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); memcpy(&oi.oobfree, mtd->ecclayout->oobfree, sizeof(oi.oobfree)); oi.eccbytes = mtd->ecclayout->eccbytes; 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; if (!mtd->block_isbad) ret = -EOPNOTSUPP; else return mtd->block_isbad(mtd, offs); break; } case MEMSETBADBLOCK: { loff_t offs; if (copy_from_user(&offs, argp, sizeof(loff_t))) return -EFAULT; if (!mtd->block_markbad) ret = -EOPNOTSUPP; else return mtd->block_markbad(mtd, offs); break; }#ifdef CONFIG_HAVE_MTD_OTP case OTPSELECT: { int mode; if (copy_from_user(&mode, argp, sizeof(int))) return -EFAULT; mfi->mode = MTD_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); if (!buf) return -ENOMEM; ret = -EOPNOTSUPP; switch (mfi->mode) { case MTD_MODE_OTP_FACTORY: if (mtd->get_fact_prot_info) ret = mtd->get_fact_prot_info(mtd, buf, 4096); break; case MTD_MODE_OTP_USER: if (mtd->get_user_prot_info) ret = mtd->get_user_prot_info(mtd, buf, 4096); break; default: break; } if (ret >= 0) { if (cmd == OTPGETREGIONCOUNT) { int nbr = ret / sizeof(struct otp_info); ret = copy_to_user(argp, &nbr, sizeof(int)); } else ret = copy_to_user(argp, buf, ret); if (ret) ret = -EFAULT; } kfree(buf); break; } case OTPLOCK: { struct otp_info oinfo; if (mfi->mode != MTD_MODE_OTP_USER) return -EINVAL; if (copy_from_user(&oinfo, argp, sizeof(oinfo))) return -EFAULT; if (!mtd->lock_user_prot_reg) return -EOPNOTSUPP; ret = mtd->lock_user_prot_reg(mtd, oinfo.start, oinfo.length); break; }#endif case ECCGETLAYOUT: { if (!mtd->ecclayout) return -EOPNOTSUPP; if (copy_to_user(argp, mtd->ecclayout, sizeof(struct nand_ecclayout))) return -EFAULT; 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_MODE_OTP_FACTORY: case MTD_MODE_OTP_USER: ret = otp_select_filemode(mfi, arg); break; case MTD_MODE_RAW: if (!mtd->read_oob || !mtd->write_oob) return -EOPNOTSUPP; mfi->mode = arg; case MTD_MODE_NORMAL: break; default: ret = -EINVAL; } file->f_pos = 0; break; } default: ret = -ENOTTY; } return ret;} /* memory_ioctl */static const struct file_operations mtd_fops = { .owner = THIS_MODULE, .llseek = mtd_lseek, .read = mtd_read, .write = mtd_write, .ioctl = mtd_ioctl, .open = mtd_open, .release = mtd_close,};static int __init init_mtdchar(void){ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", MTD_CHAR_MAJOR); return -EAGAIN; } mtd_class = class_create(THIS_MODULE, "mtd"); if (IS_ERR(mtd_class)) { printk(KERN_ERR "Error creating mtd class.\n"); unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); return PTR_ERR(mtd_class); } register_mtd_user(¬ifier); return 0;}static void __exit cleanup_mtdchar(void){ unregister_mtd_user(¬ifier); class_destroy(mtd_class); unregister_chrdev(MTD_CHAR_MAJOR, "mtd");}module_init(init_mtdchar);module_exit(cleanup_mtdchar);MODULE_LICENSE("GPL");MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");MODULE_DESCRIPTION("Direct character-device access to MTD devices");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -