diff options
Diffstat (limited to 'linux-2.4.x/drivers/mtd/mtdchar.c')
-rw-r--r-- | linux-2.4.x/drivers/mtd/mtdchar.c | 485 |
1 files changed, 280 insertions, 205 deletions
diff --git a/linux-2.4.x/drivers/mtd/mtdchar.c b/linux-2.4.x/drivers/mtd/mtdchar.c index 1780055..fdb7e66 100644 --- a/linux-2.4.x/drivers/mtd/mtdchar.c +++ b/linux-2.4.x/drivers/mtd/mtdchar.c @@ -1,66 +1,113 @@ /* - * $Id: mtdchar.c,v 1.44 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.80 2006/02/22 00:26:36 kmpark Exp $ * * Character-device access to raw MTD devices. - * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c * */ #include <linux/config.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mtd/mtd.h> #include <linux/slab.h> +#include <linux/sched.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/compatmac.h> + +#include <asm/uaccess.h> + +static struct class *mtd_class; + +static void mtd_notify_add(struct mtd_info* mtd) +{ + if (!mtd) + return; -#ifdef CONFIG_DEVFS_FS -#include <linux/devfs_fs_kernel.h> -static void mtd_notify_add(struct mtd_info* mtd); -static void mtd_notify_remove(struct mtd_info* mtd); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) + /* + * In 2.6.13 and 2.6.14 kernels class_device_create() had different + * prototype (they had no 'parent' paremater). + * No idea how to fix it in compatmac.h, so this is a hotfix. Must go sometime. + */ + class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); + + class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); +#else + class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); + + class_device_create(mtd_class, NULL, + MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); +#endif +} + +static void mtd_notify_remove(struct mtd_info* mtd) +{ + if (!mtd) + return; + + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); +} static struct mtd_notifier notifier = { - add: mtd_notify_add, - remove: mtd_notify_remove, + .add = mtd_notify_add, + .remove = mtd_notify_remove, }; -static devfs_handle_t devfs_dir_handle; -static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; -static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES]; -#endif +/* + * We use file->private_data to store a pointer to the MTDdevice. + * Since alighment is at least 32 bits, we have 2 bits free for OTP + * modes as well. + */ + +#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) + +#define MTD_MODE_OTP_FACT 1 +#define MTD_MODE_OTP_USER 2 +#define MTD_MODE(file) ((long)((file)->private_data) & 3) + +#define SET_MTD_MODE(file, mode) \ + do { long __p = (long)((file)->private_data); \ + (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd=(struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); switch (orig) { case 0: /* SEEK_SET */ - file->f_pos = offset; break; case 1: /* SEEK_CUR */ - file->f_pos += offset; + offset += file->f_pos; break; case 2: /* SEEK_END */ - file->f_pos =mtd->size + offset; + offset += mtd->size; break; default: return -EINVAL; } - if (file->f_pos < 0) - file->f_pos = 0; - else if (file->f_pos >= mtd->size) - file->f_pos = mtd->size - 1; + if (offset >= 0 && offset < mtd->size) + return file->f_pos = offset; - return file->f_pos; + return -EINVAL; } static int mtd_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = iminor(inode); int devnum = minor >> 1; struct mtd_info *mtd; @@ -74,23 +121,23 @@ static int mtd_open(struct inode *inode, struct file *file) return -EACCES; mtd = get_mtd_device(NULL, devnum); - + if (!mtd) return -ENODEV; - + if (MTD_ABSENT == mtd->type) { put_mtd_device(mtd); return -ENODEV; } file->private_data = mtd; - + /* You can't open it RW if it's not a writeable device */ if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { put_mtd_device(mtd); return -EACCES; } - + return 0; } /* mtd_open */ @@ -102,11 +149,11 @@ static int mtd_close(struct inode *inode, struct file *file) DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = (struct mtd_info *)file->private_data; - + mtd = TO_MTD(file); + if (mtd->sync) mtd->sync(mtd); - + put_mtd_device(mtd); return 0; @@ -117,15 +164,15 @@ static int mtd_close(struct inode *inode, struct file *file) */ #define MAX_KMALLOC_SIZE 0x20000 -static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) +static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); size_t retlen=0; size_t total_retlen=0; int ret=0; int len; char *kbuf; - + DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); if (*ppos + count > mtd->size) @@ -133,11 +180,11 @@ static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) if (!count) return 0; - + /* FIXME: Use kiovec in 2.5 to lock down the user's buffers and pass them directly to the MTD functions */ while (count) { - if (count > MAX_KMALLOC_SIZE) + if (count > MAX_KMALLOC_SIZE) len = MAX_KMALLOC_SIZE; else len = count; @@ -145,9 +192,24 @@ static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) kbuf=kmalloc(len,GFP_KERNEL); if (!kbuf) return -ENOMEM; - - ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); - if (!ret) { + + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + case MTD_MODE_OTP_USER: + ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: + ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + } + /* Nand returns -EBADMSG on ecc errors, but it returns + * the data. For our userspace tools it is important + * to dump areas with ecc errors ! + * Userspace software which accesses NAND this way + * must be aware of the fact that it deals with NAND + */ + if (!ret || (ret == -EBADMSG)) { *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); @@ -158,21 +220,23 @@ static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) count -= retlen; buf += retlen; + if (retlen == 0) + count = 0; } else { kfree(kbuf); return ret; } - + kfree(kbuf); } - + return total_retlen; } /* mtd_read */ -static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos) +static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); char *kbuf; size_t retlen; size_t total_retlen=0; @@ -180,10 +244,10 @@ static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t int len; DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); - + if (*ppos == mtd->size) return -ENOSPC; - + if (*ppos + count > mtd->size) count = mtd->size - *ppos; @@ -191,7 +255,7 @@ static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t return 0; while (count) { - if (count > MAX_KMALLOC_SIZE) + if (count > MAX_KMALLOC_SIZE) len = MAX_KMALLOC_SIZE; else len = count; @@ -206,8 +270,21 @@ static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t kfree(kbuf); return -EFAULT; } - - ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = -EROFS; + break; + case MTD_MODE_OTP_USER: + if (!mtd->write_user_prot_reg) { + ret = -EOPNOTSUPP; + break; + } + ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: + ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + } if (!ret) { *ppos += retlen; total_retlen += retlen; @@ -218,7 +295,7 @@ static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t kfree(kbuf); return ret; } - + kfree(kbuf); } @@ -230,7 +307,7 @@ static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t IOCTL calls for getting device parameters. ======================================================================*/ -static void mtd_erase_callback (struct erase_info *instr) +static void mtdchar_erase_callback (struct erase_info *instr) { wake_up((wait_queue_head_t *)instr->priv); } @@ -238,25 +315,26 @@ static void mtd_erase_callback (struct erase_info *instr) static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); + void __user *argp = (void __user *)arg; int ret = 0; u_long size; - + DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { - ret = verify_area(VERIFY_READ, (char *)arg, size); - if (ret) return ret; + if (!access_ok(VERIFY_READ, argp, size)) + return -EFAULT; } if (cmd & IOC_OUT) { - ret = verify_area(VERIFY_WRITE, (char *)arg, size); - if (ret) return ret; + if (!access_ok(VERIFY_WRITE, argp, size)) + return -EFAULT; } - + switch (cmd) { case MEMGETREGIONCOUNT: - if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int))) + if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) return -EFAULT; break; @@ -264,30 +342,30 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { struct region_info_user ur; - if (copy_from_user( &ur, - (struct region_info_user *)arg, - sizeof(struct region_info_user))) { + if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) return -EFAULT; - } if (ur.regionindex >= mtd->numeraseregions) return -EINVAL; - if (copy_to_user((struct mtd_erase_region_info *) arg, - &(mtd->eraseregions[ur.regionindex]), + if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), sizeof(struct mtd_erase_region_info))) return -EFAULT; break; } case MEMGETINFO: - if (copy_to_user((struct mtd_info *)arg, mtd, - sizeof(struct mtd_info_user))) + if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user))) return -EFAULT; break; case MEMERASE: { - struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); + struct erase_info *erase; + + if(!(file->f_mode & 2)) + return -EPERM; + + erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); if (!erase) ret = -ENOMEM; else { @@ -297,19 +375,19 @@ static int mtd_ioctl(struct inode *inode, struct file *file, init_waitqueue_head(&waitq); memset (erase,0,sizeof(struct erase_info)); - if (copy_from_user(&erase->addr, (u_long *)arg, - 2 * sizeof(u_long))) { + if (copy_from_user(&erase->addr, argp, + sizeof(struct erase_info_user))) { kfree(erase); return -EFAULT; } erase->mtd = mtd; - erase->callback = mtd_erase_callback; + 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 @@ -337,17 +415,21 @@ static int mtd_ioctl(struct inode *inode, struct file *file, struct mtd_oob_buf buf; void *databuf; ssize_t retlen; - - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + + if(!(file->f_mode & 2)) + return -EPERM; + + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; - + if (buf.length > 0x4096) return -EINVAL; if (!mtd->write_oob) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); + ret = access_ok(VERIFY_READ, buf.ptr, + buf.length) ? 0 : EFAULT; if (ret) return ret; @@ -355,7 +437,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, databuf = kmalloc(buf.length, GFP_KERNEL); if (!databuf) return -ENOMEM; - + if (copy_from_user(databuf, buf.ptr, buf.length)) { kfree(databuf); return -EFAULT; @@ -363,7 +445,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) + if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) ret = -EFAULT; kfree(databuf); @@ -377,16 +459,17 @@ static int mtd_ioctl(struct inode *inode, struct file *file, void *databuf; ssize_t retlen; - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; - + if (buf.length > 0x4096) return -EINVAL; if (!mtd->read_oob) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); + ret = access_ok(VERIFY_WRITE, buf.ptr, + buf.length) ? 0 : -EFAULT; if (ret) return ret; @@ -394,123 +477,161 @@ static int mtd_ioctl(struct inode *inode, struct file *file, databuf = kmalloc(buf.length, GFP_KERNEL); if (!databuf) return -ENOMEM; - + ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) + if (put_user(retlen, (uint32_t __user *)argp)) ret = -EFAULT; else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) ret = -EFAULT; - + kfree(databuf); break; } case MEMLOCK: { - unsigned long adrs[2]; + struct erase_info_user info; - if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long))) + if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; if (!mtd->lock) ret = -EOPNOTSUPP; else - ret = mtd->lock(mtd, adrs[0], adrs[1]); + ret = mtd->lock(mtd, info.start, info.length); break; } case MEMUNLOCK: { - unsigned long adrs[2]; + struct erase_info_user info; - if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long))) + if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; if (!mtd->unlock) ret = -EOPNOTSUPP; else - ret = mtd->unlock(mtd, adrs[0], adrs[1]); + ret = mtd->unlock(mtd, info.start, info.length); break; } - case MEMWRITEDATA: + case MEMSETOOBSEL: { - struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen; - - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo))) return -EFAULT; - - if (buf.length > 0x4096) - return -EINVAL; - - if (!mtd->write_ecc) - ret = -EOPNOTSUPP; - else - ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); - - if (ret) - return ret; + break; + } - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) - return -ENOMEM; - - if (copy_from_user(databuf, buf.ptr, buf.length)) { - kfree(databuf); + case MEMGETOOBSEL: + { + if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) return -EFAULT; - } - - ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL); + break; + } - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) - ret = -EFAULT; + case MEMGETBADBLOCK: + { + loff_t offs; - kfree(databuf); + 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 MEMREADDATA: + case MEMSETBADBLOCK: { - struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen = 0; + loff_t offs; - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&offs, argp, sizeof(loff_t))) return -EFAULT; - - if (buf.length > 0x4096) - return -EINVAL; - - if (!mtd->read_ecc) + if (!mtd->block_markbad) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); + return mtd->block_markbad(mtd, offs); + break; + } - if (ret) - return ret; +#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) + case OTPSELECT: + { + int mode; + if (copy_from_user(&mode, argp, sizeof(int))) + return -EFAULT; + SET_MTD_MODE(file, 0); + switch (mode) { + case MTD_OTP_FACTORY: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_FACT); + break; + case MTD_OTP_USER: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_USER); + break; + default: + ret = -EINVAL; + case MTD_OTP_OFF: + break; + } + file->f_pos = 0; + break; + } - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) + case OTPGETREGIONCOUNT: + case OTPGETREGIONINFO: + { + struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + if (!buf) return -ENOMEM; - - ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL); + ret = -EOPNOTSUPP; + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + 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; + } + 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; + } - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) - ret = -EFAULT; - else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) - ret = -EFAULT; - - kfree(databuf); + case OTPLOCK: + { + struct otp_info info; + + if (MTD_MODE(file) != MTD_MODE_OTP_USER) + return -EINVAL; + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + if (!mtd->lock_user_prot_reg) + return -EOPNOTSUPP; + ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); break; } +#endif - default: - DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %lx)\n", cmd, MEMGETINFO); ret = -ENOTTY; } @@ -518,86 +639,40 @@ static int mtd_ioctl(struct inode *inode, struct file *file, } /* memory_ioctl */ static struct file_operations mtd_fops = { - owner: THIS_MODULE, - llseek: mtd_lseek, /* lseek */ - read: mtd_read, /* read */ - write: mtd_write, /* write */ - ioctl: mtd_ioctl, /* ioctl */ - open: mtd_open, /* open */ - release: mtd_close, /* release */ + .owner = THIS_MODULE, + .llseek = mtd_lseek, + .read = mtd_read, + .write = mtd_write, + .ioctl = mtd_ioctl, + .open = mtd_open, + .release = mtd_close, }; - -#ifdef CONFIG_DEVFS_FS -/* Notification that a new device has been added. Create the devfs entry for - * it. */ - -static void mtd_notify_add(struct mtd_info* mtd) -{ - char name[8]; - - if (!mtd) - return; - - sprintf(name, "%d", mtd->index); - devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, - DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2, - S_IFCHR | S_IRUGO | S_IWUGO, - &mtd_fops, NULL); - - sprintf(name, "%dro", mtd->index); - devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name, - DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1, - S_IFCHR | S_IRUGO | S_IWUGO, - &mtd_fops, NULL); -} - -static void mtd_notify_remove(struct mtd_info* mtd) -{ - if (!mtd) - return; - - devfs_unregister(devfs_rw_handle[mtd->index]); - devfs_unregister(devfs_ro_handle[mtd->index]); -} -#endif - static int __init init_mtdchar(void) { -#ifdef CONFIG_DEVFS_FS - if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) - { + 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; } - devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); + mtd_class = class_create(THIS_MODULE, "mtd"); - register_mtd_user(¬ifier); -#else - 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; + if (IS_ERR(mtd_class)) { + printk(KERN_ERR "Error creating mtd class.\n"); + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + return PTR_ERR(mtd_class); } -#endif - DEBUG(MTD_DEBUG_LEVEL3, - "init_mtdchar: allocated major number %d.\n", MTD_CHAR_MAJOR); + register_mtd_user(¬ifier); return 0; } static void __exit cleanup_mtdchar(void) { -#ifdef CONFIG_DEVFS_FS unregister_mtd_user(¬ifier); - devfs_unregister(devfs_dir_handle); - devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); -#else + class_destroy(mtd_class); unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); -#endif } module_init(init_mtdchar); |