blob: b157184958cb70df367bf4a3d233bde29cf60c82 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) Marvell International Ltd. and its affiliates
*
* Marvell GPL License Option
*
* If you received this File from Marvell, you may opt to use, redistribute and/or
* modify this File in accordance with the terms and conditions of the General
* Public License Version 2, June 1991 (the "GPL License"), a copy of which is
* available along with the File in the license.txt file or by writing to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
* on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
* DISCLAIMED. The GPL License provides additional details about this warranty
* disclaimer.
********************************************************************************/
/*******************************************************************************
System head files
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <asm/page.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/version.h>
#include <asm-generic/current.h>
#include <linux/sched.h>
/*******************************************************************************
Local head files
*/
#include <mach/galois_generic.h>
#include"pe_agent_driver.h"
#include "cc_msgq.h"
/*******************************************************************************
Module API defined
*/
#define PE_AGENT_DEVICE_NAME "galois_pe_agent"
#define PE_AGENT_DEVICE_TAG "[Galois][pe_agent_driver] "
#define PE_AGENT_DEVICE_PATH ("/dev/" PE_AGENT_DEVICE_NAME)
#define PE_AGENT_DEVICE_PROCFILE_STATUS "status"
#define MAX_NUM_PE_AGENT_INSTANCES (64)
#define PE_MODULE_MSG_TYPE_AGENT_TERM (0x80000001)
#define PE_MODULE_MSG_ID_AGENT (0x00060011) /*MV_GS_AGENT_Serv */
#define AGENT_IOCTL_SETDEVICETOKEN (0xbeef5001)
#define AGENT_IOCTL_RECORD_HANDLE_MSG (0xbeef5002)
static int pe_agent_driver_open(struct inode *inode, struct file *filp);
static int pe_agent_driver_release(struct inode *inode, struct file *filp);
static long pe_agent_driver_ioctl_unlocked(struct file *filp, unsigned int cmd,
unsigned long arg);
/*******************************************************************************
Macro Defined
*/
#if 0
#define pe_agent_debug(...) printk(KERN_DEBUG PE_AGENT_DEVICE_TAG __VA_ARGS__)
#else
#define pe_agent_debug(...)
#endif
#define pe_agent_trace(...) printk(KERN_WARNING PE_AGENT_DEVICE_TAG __VA_ARGS__)
#define pe_agent_error(...) printk(KERN_ERR PE_AGENT_DEVICE_TAG __VA_ARGS__)
/*******************************************************************************
Module Variable
*/
static struct cdev pe_agent_dev;
static struct class *pe_agent_dev_class;
static struct file_operations pe_agent_ops = {
.open = pe_agent_driver_open,
.release = pe_agent_driver_release,
.unlocked_ioctl = pe_agent_driver_ioctl_unlocked,
.owner = THIS_MODULE,
};
typedef struct _InstanceInfo {
HANDLE handle; /*pe instance handle */
long tgid; /*tgid */
struct file *filp;
long token; /*token betwen agent and server */
} InstanceInfo;
static InstanceInfo instance_list[MAX_NUM_PE_AGENT_INSTANCES];
static int pe_agent_instance_cnt;
static struct proc_dir_entry *pe_agent_driver_procdir;
static DEFINE_SPINLOCK(pe_agent_spinlock);
/*******************************************************************************
Module API
*/
static int pe_agent_driver_open(struct inode *inode, struct file *filp)
{
int i = 0;
MV_CC_MSG_t msg = { 0, };
spin_lock(&pe_agent_spinlock);
if (pe_agent_instance_cnt < MAX_NUM_PE_AGENT_INSTANCES) {
for (i = 0; i < MAX_NUM_PE_AGENT_INSTANCES; i++) {
if (!instance_list[i].filp) {
instance_list[i].filp = filp; /*allocate this instance; */
instance_list[i].tgid = current->tgid;
instance_list[i].handle = 0;
pe_agent_instance_cnt++;
spin_unlock(&pe_agent_spinlock);
pe_agent_debug("open filp:%x tgid:%d\n",
filp, instance_list[i].tgid);
return 0;
}
}
}
spin_unlock(&pe_agent_spinlock);
pe_agent_debug("pe agent instances:%d > %d, open failed\n",
pe_agent_instance_cnt, MAX_NUM_PE_AGENT_INSTANCES);
return -1;
}
static int pe_agent_driver_release(struct inode *inode, struct file *filp)
{
int i;
MV_CC_MSG_t msg = { 0, };
int res = 0;
HANDLE pe_handle = 0;
spin_lock(&pe_agent_spinlock);
for (i = 0; i < MAX_NUM_PE_AGENT_INSTANCES; i++) {
if (instance_list[i].filp == filp) {
msg.m_MsgID = PE_MODULE_MSG_TYPE_AGENT_TERM;
msg.m_Param1 = instance_list[i].handle;
msg.m_Param2 = instance_list[i].tgid;
pe_handle = instance_list[i].handle;
instance_list[i].filp = 0;
instance_list[i].handle = 0;
instance_list[i].tgid = 0;
instance_list[i].token = 0;
pe_agent_instance_cnt--;
spin_unlock(&pe_agent_spinlock);
res =
MV_CC_MsgQ_PostMsgByID(PE_MODULE_MSG_ID_AGENT,
&msg);
pe_agent_debug("close file:%x pid:%d handle:%x\n",
filp, current->tgid, (UINT) pe_handle);
return 0;
}
}
spin_unlock(&pe_agent_spinlock);
return -1;
}
static long pe_agent_driver_ioctl_unlocked(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int i = 0;
MV_CC_MSG_t msg = { 0, };
int res = 0;
switch (cmd) {
case AGENT_IOCTL_SETDEVICETOKEN:
spin_lock(&pe_agent_spinlock);
for (i = 1; i < MAX_NUM_PE_AGENT_INSTANCES; i++) {
if (instance_list[i].filp == filp) {
instance_list[i].token = arg;
spin_unlock(&pe_agent_spinlock);
pe_agent_debug
("filp:%x ioctl:set token:%08x tgid:%d\n",
filp, arg, instance_list[i].tgid);
return 0;
}
}
spin_unlock(&pe_agent_spinlock);
res = -ENODEV;
break;
case AGENT_IOCTL_RECORD_HANDLE_MSG:
if (copy_from_user
(&msg, (int __user *)arg, sizeof(MV_CC_MSG_t)))
return -EFAULT;
spin_lock(&pe_agent_spinlock);
for (i = 0; i < MAX_NUM_PE_AGENT_INSTANCES; i++) {
if (instance_list[i].filp
&& instance_list[i].token == msg.m_MsgID) {
instance_list[i].handle = msg.m_Param1;
spin_unlock(&pe_agent_spinlock);
pe_agent_debug
("filp:%x ioctl:set pe inst:%08x tgid:%d token:%08x\n",
filp, instance_list[i].handle,
instance_list[i].tgid,
instance_list[i].token);
return 0;
}
}
spin_unlock(&pe_agent_spinlock);
pe_agent_debug("not found token holder\n");
res = -ENODEV;
break;
default:
break;
}
return res;
}
static int read_proc_status(char *page, char **start, off_t offset,
int count, int *eof, void *data)
{
int len = 0;
int i = 0;
len +=
sprintf(page + len, "pe_instance_cnt:%d\n", pe_agent_instance_cnt);
len +=
sprintf(page + len,
"----------------------------------------------------------------\n");
len +=
sprintf(page + len,
" No | filp | tgid | PE handle | token\n");
len +=
sprintf(page + len,
"----------------------------------------------------------------\n");
for (i = 0; (i < MAX_NUM_PE_AGENT_INSTANCES) && (len < count - 200);
i++) {
if (instance_list[i].filp) {
len +=
sprintf(page + len,
" %2d | 0x%08x | %08d | 0x%08x | 0x%08x\n",
i, instance_list[i].filp,
instance_list[i].tgid,
instance_list[i].handle,
instance_list[i].token);
}
}
pe_agent_debug("%s OK. (%d / %d)\n", __func__, len, count);
*eof = 1;
return ((count < len) ? count : len);
}
/*******************************************************************************
Module Register API
*/
static int pe_agent_driver_setup_cdev(struct cdev *dev, int major, int minor,
struct file_operations *fops)
{
cdev_init(dev, fops);
dev->owner = THIS_MODULE;
dev->ops = fops;
return cdev_add(dev, MKDEV(major, minor), 1);
}
int __init pe_agent_driver_init(void)
{
int res;
/* Now setup cdevs. */
res =
pe_agent_driver_setup_cdev(&pe_agent_dev, GALOIS_PE_MAJOR,
GALOIS_PE_AGENT_MINOR, &pe_agent_ops);
if (res) {
pe_agent_error("pe_agent_driver_setup_cdev failed.\n");
goto err_add_device;
}
pe_agent_debug("setup cdevs device minor [%d]\n",
GALOIS_PE_AGENT_MINOR);
/* add PE agent devices to sysfs */
pe_agent_dev_class = class_create(THIS_MODULE, PE_AGENT_DEVICE_NAME);
if (IS_ERR(pe_agent_dev_class)) {
pe_agent_error("class_create failed.\n");
res = -ENODEV;
goto err_add_device;
}
device_create(pe_agent_dev_class, NULL,
MKDEV(GALOIS_PE_MAJOR, GALOIS_PE_AGENT_MINOR),
NULL, PE_AGENT_DEVICE_NAME);
pe_agent_debug("create device sysfs [%s]\n", PE_AGENT_DEVICE_NAME);
/* create PE agent device proc file */
pe_agent_driver_procdir = proc_mkdir(PE_AGENT_DEVICE_NAME, NULL);
/*pe_driver_procdir->owner = THIS_MODULE; */
create_proc_read_entry(PE_AGENT_DEVICE_PROCFILE_STATUS, 0,
pe_agent_driver_procdir, read_proc_status, NULL);
pe_agent_instance_cnt = 0;
pe_agent_trace("pe_agent_driver_init OK\n");
return 0;
err_add_device:
cdev_del(&pe_agent_dev);
err_reg_device:
pe_agent_trace("pe_agent_driver_init failed !!! (%d)\n", res);
return res;
}
void __exit pe_agent_driver_exit(void)
{
int res;
/* remove PE agent device proc file */
remove_proc_entry(PE_AGENT_DEVICE_PROCFILE_STATUS,
pe_agent_driver_procdir);
remove_proc_entry(PE_AGENT_DEVICE_NAME, NULL);
/* del sysfs entries */
device_destroy(pe_agent_dev_class,
MKDEV(GALOIS_PE_MAJOR, GALOIS_PE_AGENT_MINOR));
pe_agent_debug("delete device sysfs [%s]\n", PE_AGENT_DEVICE_NAME);
class_destroy(pe_agent_dev_class);
/* del cdev */
cdev_del(&pe_agent_dev);
pe_agent_trace("pe_agent_driver_exit OK\n");
}