| /******************************************************************************* |
| * °æÈ¨ËùÓÐ (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"); |
| |