blob: 0c4c0c64930b23bed18f83f44f1ab598a17b15b8 [file] [log] [blame]
/*******************************************************************************
* °æÈ¨ËùÓÐ (C)2016, ÖÐÐËͨѶ¹É·ÝÓÐÏÞ¹«Ë¾¡£
*
* ÎļþÃû³Æ: vsim.c
* Îļþ±êʶ: vsim.c
* ÄÚÈÝÕªÒª: vsim
* ʹÓ÷½·¨:
*
* ÐÞ¸ÄÈÕÆÚ °æ±¾ºÅ Ð޸ıê¼Ç ÐÞ¸ÄÈË ÐÞ¸ÄÄÚÈÝ
* ------------------------------------------------------------------------------
* 2016/6/10 V1.0 Create zjk
*
*******************************************************************************/
/*******************************************************************************
* Í·Îļþ *
*******************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/mm.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/soc/zte/rpm/rpmsg.h>
/*******************************************************************************
* Íⲿº¯ÊýÉùÃ÷ *
*******************************************************************************/
/* icp api */
extern int zDrvRpMsg_CreateChannel(
T_ZDrvRpMsg_ActorID actorID,
T_ZDrvRpMsg_ChID chID,
unsigned int size);
extern int zDrvRpMsg_Write(const T_ZDrvRpMsg_Msg *pMsg);
extern int zDrvRpMsg_Read(const T_ZDrvRpMsg_Msg *pMsg);
/*******************************************************************************
* ºê¶¨Òå *
*******************************************************************************/
#define VSIM_MAJOR (220)
#define VSIM_ICP_CHANNEL_SIZE (10*1024)
#define VSIM_ICP_WRITE_MAX_SIZE (5*1024)
#define VSIM_ICP_CHANNEL (ICP_CHANNEL_VSIM)
#define VSIM_ICP_MSG_HEADER_SIZE (16)
#define VSIM_ICP_BUF_SIZE (VSIM_ICP_WRITE_MAX_SIZE-VSIM_ICP_MSG_HEADER_SIZE)
/***********************************1024********************************************
* Êý¾ÝÀàÐͶ¨Òå *
*******************************************************************************/
typedef struct
{
unsigned long addr;
void * buf;
unsigned long len;
} data_spec_write_t;
enum
{
RECOVERY = 0,
WRITE_SPECIFY = 1,
};
typedef struct
{
unsigned long addr;
int len;
char buf[VSIM_ICP_BUF_SIZE];
} icp_msg_data_t;
typedef struct
{
unsigned long cmd;
unsigned long total;
icp_msg_data_t data;
} icp_msg_t;
enum
{
VSIM_CMD_RECOVERY = 1,
VSIM_CMD_WRITE_FACTORY,
VSIM_CMD_WRITE_SPECIAL,
};
/*******************************************************************************
* È«¾Ö±äÁ¿ÉùÃ÷ *
*******************************************************************************/
static const char *vsim_name = "vsim";
static int vsim_major = VSIM_MAJOR;
static struct class *nv_class;
static struct mutex vsim_lock;
/*******************************************************************************
* È«¾Öº¯ÊýÉùÃ÷ *
*******************************************************************************/
/*******************************************************************************
* ¾Ö²¿º¯ÊýʵÏÖ *
*******************************************************************************/
/*
* Open the device; in fact, there's nothing to do here.
*/
static int vsim_open (struct inode *inode, struct file *file)
{
return 0;
}
/*
* Closing is just as open.
*/
static int vsim_release(struct inode *inode, struct file *file)
{
return 0;
}
/*
* read.
*/
static ssize_t vsim_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
return -EPERM;
}
/*
* vsim_icp_split_package
*/
static int vsim_icp_split_package(char *buf, size_t len, icp_msg_t *vsim_msg)
{
int ret = -EPERM;
int read_buf;
char *buf_tmp;
int len_tmp;
T_ZDrvRpMsg_Msg pMsg;
if (buf == NULL || len == 0)
return -EINVAL;
pMsg.actorID = PS_ID;
pMsg.chID = VSIM_ICP_CHANNEL;
len_tmp = len;
buf_tmp = buf;
mutex_lock(&vsim_lock);
while (len_tmp > 0)
{
vsim_msg->data.len = min(len_tmp, VSIM_ICP_BUF_SIZE);
if (copy_from_user(vsim_msg->data.buf, buf_tmp, vsim_msg->data.len))
panic("[ZXIC]VSIM copy_from_user failed\n");
pMsg.buf = vsim_msg;
pMsg.len = sizeof(icp_msg_t);
pMsg.flag = RPMSG_WRITE_INT;
ret = zDrvRpMsg_Write(&pMsg);
if (ret <= 0)
panic("[ZXIC]VSIM zDrvRpMsg_Write failed\n");
len_tmp -= vsim_msg->data.len;
buf_tmp += vsim_msg->data.len;
read_buf = 0;
pMsg.buf = &read_buf;
pMsg.len = sizeof(read_buf);
pMsg.flag = ~RPMSG_READ_POLL;
ret = zDrvRpMsg_Read(&pMsg);
if (ret <= 0)
panic("[ZXIC]VSIM zDrvRpMsg_Read failed\n");
}
read_buf = 0;
pMsg.buf = &read_buf;
pMsg.len = sizeof(read_buf);
pMsg.flag = ~RPMSG_READ_POLL;
ret = zDrvRpMsg_Read(&pMsg);
if (ret <= 0)
panic("[ZXIC]VSIM zDrvRpMsg_Read failed\n");
mutex_unlock(&vsim_lock);
return read_buf;
}
/*
* write fac.
*/
static ssize_t vsim_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int ret = -EPERM;
icp_msg_t *vsim_msg = NULL;
if (*ppos || buf == NULL) //shoud start at offset 0;
return -EINVAL;
vsim_msg = kmalloc(sizeof(icp_msg_t), GFP_KERNEL);
if (vsim_msg == NULL)
return -ENOMEM;
vsim_msg->cmd = VSIM_CMD_WRITE_FACTORY;
vsim_msg->total = count;
ret = vsim_icp_split_package(buf, count, vsim_msg);
kfree(vsim_msg);
return ret;
}
/*
* ioctl to control write.
*/
static ssize_t vsim_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = -EPERM;
int read_buf;
icp_msg_t *vsim_msg = NULL;
T_ZDrvRpMsg_Msg pMsg;
vsim_msg = kmalloc(sizeof(icp_msg_t), GFP_KERNEL);
if (vsim_msg == NULL)
return -ENOMEM;
switch (cmd)
{
case RECOVERY:
mutex_lock(&vsim_lock);
vsim_msg->cmd = VSIM_CMD_RECOVERY;
vsim_msg->total = vsim_msg->data.len = 0;
pMsg.actorID = PS_ID;
pMsg.chID = VSIM_ICP_CHANNEL;
pMsg.buf = vsim_msg;
pMsg.len = sizeof(icp_msg_t);
pMsg.flag = RPMSG_WRITE_INT;
ret = zDrvRpMsg_Write(&pMsg);
if (ret <= 0)
panic("[ZXIC]VSIM zDrvRpMsg_Write failed\n");
pMsg.buf = &read_buf;
pMsg.len = sizeof(read_buf);
pMsg.flag = ~RPMSG_READ_POLL;
ret = zDrvRpMsg_Read(&pMsg);
if (ret <= 0)
panic("[ZXIC]VSIM zDrvRpMsg_Read failed\n");
mutex_unlock(&vsim_lock);
break;
case WRITE_SPECIFY:
if (arg != 0)
{
vsim_msg->cmd = VSIM_CMD_WRITE_SPECIAL;
vsim_msg->total = ((data_spec_write_t *)arg)->len;
vsim_msg->data.addr = ((data_spec_write_t *)arg)->addr;
ret = vsim_icp_split_package(((data_spec_write_t *)arg)->buf,((data_spec_write_t *)arg)->len, vsim_msg);
}
break;
default :
break;
}
kfree(vsim_msg);
return (ret >= 0) ? 0 : -EPERM;
}
static struct file_operations vsim_fops = {
.owner = THIS_MODULE,
.open = vsim_open,
.release = vsim_release,
.read = vsim_read,
.write = vsim_write,
.unlocked_ioctl = vsim_ioctl,
};
static char *vsim_devnode(struct device *dev, umode_t *mode)
{
return NULL;
}
/*
* Module init.
*/
static int vsim_init(void)
{
int ret;
ret = register_chrdev(vsim_major, vsim_name, &vsim_fops);
if (ret < 0)
{
printk(KERN_WARNING "vsim: unable to get major %d\n", vsim_major);
return ret;
}
if (vsim_major == 0)
vsim_major = ret; /* dynamic major number*/
nv_class = class_create(THIS_MODULE, "nv");
if (IS_ERR(nv_class))
return PTR_ERR(nv_class);
nv_class->devnode = vsim_devnode;
device_create(nv_class, NULL, MKDEV(vsim_major, 0), NULL, vsim_name);
/* lock init */
mutex_init(&vsim_lock);
/* icp init */
ret = zDrvRpMsg_CreateChannel(PS_ID, VSIM_ICP_CHANNEL, VSIM_ICP_CHANNEL_SIZE);
if (ret != RPMSG_SUCCESS)
printk(KERN_WARNING "[ZXIC] os extend remote call lib init failed, err is %d\n", ret);
return 0;
}
static void vsim_cleanup(void)
{
unregister_chrdev(vsim_major, vsim_name);
}
module_init(vsim_init);
module_exit(vsim_cleanup);
MODULE_LICENSE("Dual BSD/GPL");