blob: 5f48de4163a1674495e012fe4535edc0b016e5c8 [file] [log] [blame]
#include <linux/proc_fs.h>
#include <bcmolt_tr_nl_driver.h>
#include <bcmolt_dev_log_kernel.h>
#include <bcm_dev_log.h>
#include <bcmolt_dev_ctrl.h>
#include <bcmolt_tr_mux.h>
#include <bcmos_system.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> /*copy_from_user*/
#include <linux/proc_fs.h>
#include <bcmolt_dev_ctrl_ioctl.h>
#include <bcmolt_user_utils.h>
#include <bcmolt_host_api.h>
#include <bcmolt_fld.h>
#include <bcmtr_pcie.h>
module_param(bcmos_sys_trace_level, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(bcmos_sys_trace_level, "trace_level");
int maple_dev_ctrl_chrdev_major = 237;
module_param(maple_dev_ctrl_chrdev_major, int, S_IRUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(maple_dev_ctrl_chrdev_major, "maple_dev_ctrl_major");
static char is_chrdev_reg;
static char is_cdev_add;
extern struct proc_dir_entry *bcmolt_dir;
bcmos_errno bcmtr_init(void)
{
return BCM_ERR_OK;
}
/* /proc/bcmolt/trmux read operation */
static int trmux_stats_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
static bcmtrmux_stat prev_stat;
int len;
bcmtrmux_stat stat;
bcmos_errno rc;
bcmolt_devid *device_id = data;
rc = bcmtrmux_stat_get(*device_id, &stat);
if (rc)
{
len = snprintf(page, count, "Can't read trmux statistics rc=%s\n", bcmos_strerror(rc));
return len;
}
len = snprintf(page, count, "trmux statistics\n\n");
len += sprintf(page + len, "tx_remote : %u\n", stat.tx_remote - prev_stat.tx_remote);
len += sprintf(page + len, "tx_local : %u\n", stat.tx_local - prev_stat.tx_local);
len += sprintf(page + len, "tx_disc_remote : %u\n", stat.tx_disc_remote - prev_stat.tx_disc_remote);
len += sprintf(page + len, "tx_disc_local : %u\n", stat.tx_disc_local - prev_stat.tx_disc_local);
len += sprintf(page + len, "rx_remote : %u\n", stat.rx_remote - prev_stat.rx_remote);
len += sprintf(page + len, "rx_local : %u\n", stat.rx_local - prev_stat.rx_local);
len += sprintf(page + len, "rx_auto : %u\n", stat.rx_auto - prev_stat.rx_auto);
len += sprintf(page + len, "rx_disc_remote : %u\n", stat.rx_disc_remote - prev_stat.rx_disc_remote);
len += sprintf(page + len, "rx_disc_local : %u\n", stat.rx_disc_local - prev_stat.rx_disc_local);
len += sprintf(page + len, "rx_disc_auto : %u\n", stat.rx_disc_auto - prev_stat.rx_disc_auto);
len += sprintf(page + len, "ctl_to_host : %u\n", stat.control_to_host - prev_stat.control_to_host);
len += sprintf(page + len, "ctl_to_line : %u\n", stat.control_to_line - prev_stat.control_to_line);
len += sprintf(page + len, "rx_disc_inv_ch : %u\n", stat.rx_disc_inv_ch - prev_stat.rx_disc_inv_ch);
len += sprintf(page + len, "rx_poll_urgent : %u\n", stat.rx_poll_urgent - prev_stat.rx_poll_urgent);
len += sprintf(page + len, "rx_poll_normal : %u\n", stat.rx_poll_normal - prev_stat.rx_poll_normal);
prev_stat = stat;
return len;
}
/* /proc/bcmolt/devctrl read operation */
static int devctrl_debug_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
dev_ctrl_database db;
bcmolt_devid *device_id = data;
dev_ctrl_read_db(*device_id, &db);
/* db contains the entire copy of dev_ctrl_database. add whatever you want to print. */
len = snprintf(page, count, "dev_ctrl debug information\n\n");
len += sprintf(page + len, "task name : %s\n", db.task_info.name);
len += sprintf(page + len, "module name : %s\n", db.module_info.name);
len += sprintf(page + len, "connection state : %s\n",
bcm_str_host_connecting_state(db.connection_info.state));
len += sprintf(page + len, "tod : %s\n",
(db.enable_tod == BCMOLT_CONTROL_STATE_DISABLE) ? "DISABLE" : "ENABLE");
len += sprintf(page + len, "last event : %s\n", bcm_str_device_event(db.last_event));
if (db.conn_fail_reason < BCMOLT_HOST_CONNECTION_FAIL_REASON__NUM_OF)
len += sprintf(page + len, "connection failure reason : %s\n",
bcm_str_host_connection_fail_reason(db.conn_fail_reason));
return len;
}
#ifdef ENABLE_LOG
static bcmos_errno dev_ctrl_logger_init_cb(void)
{
bcmos_errno rc;
/* Create kernel logger */
rc = bcm_dev_log_linux_init();
BUG_ON(rc);
/* For now just map BCMOS_TRACE_XX to dev_log. Later on dev_control
* can define its own log ids
*/
rc = bcm_dev_log_os_trace_init();
BUG_ON(rc);
return rc;
}
#endif
static struct cdev maple_dev_ctrl_chrdev_cdev;
static int maple_dev_ctrl_chrdev_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int maple_dev_ctrl_chrdev_release(struct inode *inode, struct file *filp)
{
return 0;
}
static long maple_dev_ctrl_chrdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
bcmos_errno bcmrc;
int linux_rc = 0;
int rc = 0;
dev_ctrl_ioctl_param params;
/* don't even decode wrong cmds: better returning ENOTTY than EFAULT */
if (_IOC_TYPE(cmd) != MAPLE_DEV_CTRL_IOCTL_MAGIC)
return -ENOTTY;
rc = copy_from_user((char *)&params, (char *)arg, sizeof(params));
if (rc < 0)
return rc;
switch (cmd)
{
case MAPLE_DEV_CTRL_IOCTL_OP_PCI_STAT:
{
bcm_pcied_stat stat;
memset(params.dumpptr, 0, sizeof(bcm_pcied_stat));
bcmrc = bcmtr_pcie_get_statistics(params.device, params.start_index, &stat);
if (bcmrc == BCM_ERR_OK)
memcpy(params.dumpptr, &stat, sizeof(bcm_pcied_stat));
else
linux_rc = (int)bcmrc;
}
break;
case MAPLE_DEV_CTRL_IOCTL_OP_PCI_DUMP_TX:
linux_rc = (int)bcmtr_pcie_tx_dump((char *)params.dumpptr, params.device, params.start_index, params.howmany);
break;
case MAPLE_DEV_CTRL_IOCTL_OP_PCI_DUMP_RX:
linux_rc = (int)bcmtr_pcie_rx_dump((char *)params.dumpptr, params.device, params.start_index, params.howmany);
break;
case MAPLE_DEV_CTRL_IOCTL_OP_HOST_EVENT_WRITE:
linux_rc = (int)bcmolt_dev_ctrl_host_event_write(params.device, params.event);
break;
default:
rc = -ENOTTY;
break;
}
return linux_rc;
}
static ssize_t maple_dev_ctrl_chrdev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
return 0;
}
static struct file_operations maple_dev_ctrl_chrdev_fops =
{
.owner = THIS_MODULE,
.open = maple_dev_ctrl_chrdev_open,
.release = maple_dev_ctrl_chrdev_release,
.write = maple_dev_ctrl_chrdev_write,
.unlocked_ioctl = maple_dev_ctrl_chrdev_ioctl
};
static int maple_dev_ctrl_dev_create_chrdev(void)
{
dev_t dev = MKDEV(maple_dev_ctrl_chrdev_major, 0);
int linux_rc;
is_chrdev_reg = 0;
is_cdev_add = 0;
/*
* Register your major, and accept a dynamic number.
*/
if (!maple_dev_ctrl_chrdev_major)
return -1;
linux_rc = register_chrdev_region(dev, 0, "maple_dev_ctrl");
if (linux_rc < 0)
{
printk("register_chrdev_region()->%d\n", linux_rc);
return -EIO;
}
is_chrdev_reg = 1;
cdev_init(&maple_dev_ctrl_chrdev_cdev, &maple_dev_ctrl_chrdev_fops);
linux_rc = cdev_add(&maple_dev_ctrl_chrdev_cdev, dev, 1);
if (linux_rc < 0)
{
printk("cdev_add()->%d\n", linux_rc);
return -EIO;
}
is_cdev_add = 1;
return 0;
}
#define BCMOLT_PROCFS_NAME_LEN 4 /* device_id is 1-byte, which can be 3 digit in decimal. */
#define PROC_TRMUX_NAME "trmux"
#define PROC_DEVCTRL_NAME "devctrl"
#define PROC_ENTRY_MODE (S_IFREG | S_IWUSR | S_IRUGO)
typedef struct
{
bcmolt_devid device_id;
char name[BCMOLT_PROCFS_NAME_LEN];
} bcmolt_proc_ctx_t;
static bcmolt_proc_ctx_t bcmolt_proc_ctx[BCMTR_MAX_OLTS];
static bcmos_bool bcmdev_bcmolt_proc_initialized;
struct proc_dir_entry *trmux_dir = NULL;
struct proc_dir_entry *devctrl_dir = NULL;
static void bcmdev_bcmolt_proc_init(void)
{
bcmolt_devid devid;
if (!bcmdev_bcmolt_proc_initialized)
{
for (devid = 0; devid < BCMTR_MAX_OLTS; devid++)
{
bcmolt_proc_ctx[devid].device_id = devid;
sprintf(bcmolt_proc_ctx[devid].name, "%d", devid);
}
bcmdev_bcmolt_proc_initialized = BCMOS_TRUE;
}
}
static void bcmdev_module_exit(void)
{
int devid;
if (trmux_dir)
{
for (devid = 0; devid < BCMTR_MAX_OLTS; devid++)
{
remove_proc_entry(bcmolt_proc_ctx[devid].name, trmux_dir);
}
remove_proc_entry(PROC_TRMUX_NAME, bcmolt_dir);
}
if (devctrl_dir)
{
for (devid = 0; devid < BCMTR_MAX_OLTS; devid++)
{
remove_proc_entry(bcmolt_proc_ctx[devid].name, devctrl_dir);
}
remove_proc_entry(PROC_DEVCTRL_NAME, bcmolt_dir);
}
bcmolt_dev_ctrl_exit();
bcmtr_nl_exit();
if (is_cdev_add)
cdev_del(&maple_dev_ctrl_chrdev_cdev);
if (is_chrdev_reg)
unregister_chrdev_region(MKDEV(maple_dev_ctrl_chrdev_major, 0), 1);
#if defined(ENABLE_LOG)
bcm_dev_log_linux_exit();
bcm_dev_log_destroy();
#endif
printk("%s\n", __FUNCTION__);
}
static int bcmdev_module_init(void)
{
bcmos_errno rc;
struct proc_dir_entry *trmux_file;
struct proc_dir_entry *devctrl_file;
bcmolt_devid devid;
bcmolt_host_init_params params =
{
#ifdef ENABLE_LOG
.logger_init_cb = dev_ctrl_logger_init_cb,
#endif
.dev_ctrl_params =
{
.image_read_cb = bcmuser_image_read,
.system_mode_validate_cb = bcmuser_system_mode_validate,
.device_off_cb = bcmuser_device_off,
.device_on_cb = bcmuser_device_on,
.device_is_running_cb = bcmuser_device_is_running,
.host_reset_cb = bcmuser_host_reset,
.pcie_channel_prepare_cb = bcmuser_pcie_channel_prepare,
.pcie_channel_remove_cb = bcmuser_pcie_channel_remove,
}
};
if (!is_cdev_add)
{
if (maple_dev_ctrl_dev_create_chrdev() < 0)
printk("%s error to create dev ctrl cdev \n", __FUNCTION__);
}
printk("%s\n", __FUNCTION__);
rc = bcmolt_host_init(&params);
BUG_ON(rc);
bcmos_trace_level_set(BCMOS_TRACE_LEVEL_DEBUG);
bcmdev_bcmolt_proc_init();
rc = bcmtr_nl_init();
BUG_ON(rc);
rc = bcmuser_pcie_prepare();
if (rc == BCM_ERR_OK)
{
if ((trmux_dir = create_proc_entry(PROC_TRMUX_NAME, S_IFDIR, bcmolt_dir)) == NULL)
{
printk("Unable to create /proc/bcmolt/trmux directory\n");
return -1;
}
if ((devctrl_dir = create_proc_entry(PROC_DEVCTRL_NAME, S_IFDIR, bcmolt_dir)) == NULL)
{
printk("Unable to create /proc/bcmolt/devctrl directory\n");
return -1;
}
for (devid = 0; devid < BCMTR_MAX_OLTS; devid++)
{
if ((trmux_file = create_proc_entry(bcmolt_proc_ctx[devid].name, PROC_ENTRY_MODE, trmux_dir)) == NULL)
{
printk("Unable to create /proc/bcmolt/%s/%s\n", PROC_TRMUX_NAME, bcmolt_proc_ctx[devid].name);
return -1;
}
trmux_file->read_proc = (read_proc_t *)trmux_stats_read;
trmux_file->data = &bcmolt_proc_ctx[devid].device_id;
if ((devctrl_file = create_proc_entry(bcmolt_proc_ctx[devid].name, PROC_ENTRY_MODE, devctrl_dir)) == NULL)
{
printk("Unable to create /proc/bcmolt/%s/%s\n", PROC_DEVCTRL_NAME, bcmolt_proc_ctx[devid].name);
return -1;
}
devctrl_file->read_proc = (read_proc_t *)devctrl_debug_read;
devctrl_file->data = &bcmolt_proc_ctx[devid].device_id;
}
}
else
{
bcmos_usleep(2000000);
bcmdev_module_exit();
}
return rc ? -EINVAL : 0;
}
module_init(bcmdev_module_init);
module_exit(bcmdev_module_exit);
MODULE_DESCRIPTION("device_control");
MODULE_LICENSE("Dual BSD/GPL");