| #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 *)¶ms, (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(¶ms); |
| 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"); |