blob: 9282fc20a47f0b2ce392ca19256c83f578f8a35c [file] [log] [blame]
#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");