| #include <linux/module.h> |
| #include <linux/errno.h> |
| #include <linux/proc_fs.h> |
| #include <linux/uaccess.h> |
| #include <linux/cdev.h> |
| #include <linux/cpnv.h> |
| #include <linux/vmalloc.h> |
| #include <linux/string.h> |
| #include <linux/cp_types.h> |
| #include <linux/init.h> |
| #include <linux/device.h> |
| #include <linux/syscalls.h> |
| |
| #ifndef USE_CPPS_KO |
| extern UINT32 zOss_NvItemWrite(UINT32 NvItemID, UINT8 * NvItemData, UINT32 NvItemLen); |
| extern UINT32 zOss_NvItemRead(UINT32 NvItemID, UINT8 * NvItemData, UINT32 NvItemLen); |
| extern UINT32 zOss_ResetNVFactory(VOID); |
| extern UINT32 zOss_NvramFlush(VOID); |
| extern UINT32 zOss_NvItemWriteFactory(UINT32 NvItemID, UINT8 * NvItemData, UINT32 NvItemLen); |
| #endif |
| |
| extern int quick_gc_wait_done(const char *mtd_name); |
| |
| #define CPNV_DEVNAME "cpnv" |
| struct class *class_cpnv; |
| static struct semaphore cpnv_sem; |
| |
| struct cpnv_dev_t { |
| struct cdev cdev; |
| dev_t devt; |
| } cpnv_dev; |
| |
| static int cpnv_open(struct inode *inode, struct file *file) |
| { |
| file->private_data = (void *)(&cpnv_dev); |
| |
| return 0; |
| } |
| |
| static int cpnv_release(struct inode *inode, struct file *file) |
| { |
| return 0; |
| } |
| |
| static ssize_t cpnv_read(struct file *file, char *buf, size_t count, loff_t * ppos) |
| { |
| char tmp[128] = { 0 }; |
| char *itemBuf = tmp; |
| UINT32 NvItemID = 0; |
| ssize_t ret; |
| UINT32 zoss_ret; |
| |
| if (!buf) { |
| printk(KERN_WARNING "Error cpnv : %s %d\n", __FILE__, __LINE__); |
| return -ENODATA; |
| } |
| |
| if (count > sizeof(tmp)) { |
| itemBuf = vmalloc(count); |
| if (NULL == itemBuf) { |
| printk(KERN_ERR "cpnv_read vmalloc fail \n"); |
| return -ENOMEM; |
| } |
| } |
| |
| if (copy_from_user(itemBuf, buf, count)) { |
| printk(KERN_WARNING "cpnv_read() copy_from_user error! \n"); |
| ret = -EFAULT; |
| goto out; |
| } |
| memcpy(&NvItemID, itemBuf, sizeof(NvItemID)); |
| |
| down(&cpnv_sem); |
| zoss_ret = CPPS_FUNC(cpps_callbacks, zOss_NvItemRead)(NvItemID, itemBuf, count); |
| up(&cpnv_sem); |
| if (zoss_ret != ZOSS_SUCCESS) { |
| ret = -EFAULT; |
| goto out; |
| } |
| |
| if (copy_to_user(buf, itemBuf, count)) { |
| ret = -EFAULT; |
| goto out; |
| } |
| ret = count; |
| |
| out: |
| if (itemBuf != tmp) { |
| vfree(itemBuf); |
| } |
| |
| return ret; |
| } |
| |
| static ssize_t cpnv_write(struct file *file, const char *buf, size_t count, loff_t * ppos) |
| { |
| char tmp[128] = { 0 }; |
| char *tmpBuf = tmp; |
| ssize_t ret = 0; |
| UINT32 zoss_ret = ZOSS_ERROR; |
| struct cpnv_readwrite_head *phead = (struct cpnv_readwrite_head *)buf; |
| |
| if (!phead) { |
| printk(KERN_WARNING "Error: %s %d\n", __FILE__, __LINE__); |
| return -ENODATA; |
| } |
| |
| if (count > sizeof(tmp)) { |
| if (!(tmpBuf = vmalloc(count))) { |
| printk(KERN_ERR "cpnv_write() vmalloc fail \n"); |
| return -ENOMEM; |
| } |
| } |
| |
| if (copy_from_user(tmpBuf, buf, count)) { |
| ret = -EFAULT; |
| goto out; |
| } |
| |
| down(&cpnv_sem); |
| switch (phead->direction) { |
| case TO_NVRO: |
| case TO_NVRW: |
| zoss_ret = CPPS_FUNC(cpps_callbacks, zOss_NvItemWrite)(phead->NvItemID, phead->NvItemData, phead->NvItemLen); |
| break; |
| case TO_NVFAC: |
| CPPS_FUNC(cpps_callbacks, zOss_NvItemWriteFactory)(phead->NvItemID, phead->NvItemData, phead->NvItemLen); |
| zoss_ret = 0; |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| up(&cpnv_sem); |
| |
| if (zoss_ret == ZOSS_SUCCESS) |
| ret = count; |
| else { |
| printk(KERN_ERR "cpnv_write error\n"); |
| ret = -EFAULT; |
| } |
| |
| out: |
| if (tmpBuf != tmp) { |
| vfree(tmpBuf); |
| } |
| return ret; |
| } |
| |
| static long cpnv_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| long ret = 0; |
| UINT32 zoss_ret = ZOSS_ERROR; |
| |
| switch (cmd) { |
| case CPNV_IOIOCTL_FLUSH: |
| down(&cpnv_sem); |
| zoss_ret = CPPS_FUNC(cpps_callbacks, zOss_NvramFlush)(); |
| if (ZOSS_SUCCESS != zoss_ret) { |
| ret = -EFAULT; |
| printk(KERN_ERR "cpnv CPNV_IOIOCTL_FLUSH error\n"); |
| } |
| up(&cpnv_sem); |
| break; |
| |
| case CPNV_IOIOCTL_RESETNVFACTORY: |
| down(&cpnv_sem); |
| zoss_ret = CPPS_FUNC(cpps_callbacks, zOss_ResetNVFactory)(); |
| if (ZOSS_SUCCESS != zoss_ret) { |
| ret = -EFAULT; |
| printk(KERN_ERR "cpnv CPNV_IOIOCTL_RESETNVFACTORY error\n"); |
| } |
| up(&cpnv_sem); |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static int cpnv_Remount(char *DevName, char *DirName, char *Type, unsigned int Flag) |
| { |
| int ret = 0; |
| mm_segment_t old_fs = get_fs(); |
| |
| set_fs(KERNEL_DS); |
| ret = sys_mount(DevName, DirName, Type, Flag , 0); |
| if(ret == EBUSY) |
| ret = -1; |
| set_fs(old_fs); |
| |
| return ret; |
| } |
| |
| int cpnv_ChangeNvroAttr(int writable) |
| { |
| int ret = 0; |
| |
| if(writable) { |
| ret = cpnv_Remount("mtd:nvrofs", "/mnt/nvrofs", "jffs2", MS_REMOUNT |MS_VERBOSE); |
| } |
| else { |
| quick_gc_wait_done("nvrofs"); |
| ret = cpnv_Remount("mtd:nvrofs", "/mnt/nvrofs", "jffs2", MS_REMOUNT |MS_VERBOSE|MS_RDONLY); |
| } |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(cpnv_ChangeNvroAttr); |
| |
| struct file_operations cpnv_fops = { |
| .owner = THIS_MODULE, |
| .open = cpnv_open, |
| .release = cpnv_release, |
| .read = cpnv_read, |
| .write = cpnv_write, |
| .unlocked_ioctl = cpnv_ioctl, |
| .mmap = NULL, |
| }; |
| |
| static int cpnv_init(void) |
| { |
| int error = 0; |
| |
| printk(KERN_DEBUG "Load cpnv driver\n"); |
| cdev_init(&cpnv_dev.cdev, &cpnv_fops); |
| error = alloc_chrdev_region(&cpnv_dev.devt, 0, 1, CPNV_DEVNAME); |
| if (error) { |
| printk(KERN_ERR "cpnv alloc_chrdev_region error\n"); |
| return error; |
| } |
| |
| class_cpnv = class_create(THIS_MODULE, "cpnv_class"); |
| if (class_cpnv == NULL) { |
| unregister_chrdev(cpnv_dev.devt, CPNV_DEVNAME); |
| return -1; |
| } |
| |
| if (device_create(class_cpnv, NULL, cpnv_dev.devt, NULL, "cpnv") == NULL) { |
| class_destroy(class_cpnv); |
| unregister_chrdev(cpnv_dev.devt, CPNV_DEVNAME); |
| return -1; |
| } |
| |
| error = cdev_add(&cpnv_dev.cdev, cpnv_dev.devt, 1); |
| if (error) { |
| printk(KERN_ERR "cpnv cdev_add error\n"); |
| device_destroy(class_cpnv, cpnv_dev.devt); |
| class_destroy(class_cpnv); |
| unregister_chrdev(cpnv_dev.devt, CPNV_DEVNAME); |
| return error; |
| } |
| sema_init(&cpnv_sem, 1); |
| |
| return error; |
| } |
| |
| static void cpnv_exit(void) |
| { |
| printk(KERN_DEBUG "cpnv driver exit\n"); |
| device_destroy(class_cpnv, cpnv_dev.devt); |
| class_destroy(class_cpnv); |
| unregister_chrdev(cpnv_dev.devt, CPNV_DEVNAME); |
| cdev_del(&cpnv_dev.cdev); |
| } |
| |
| late_initcall(cpnv_init); |
| module_exit(cpnv_exit); |
| MODULE_LICENSE("GPLv2"); |