/* * transceiver_sysfs.c * * This module create eth kobjects and attributes in /sys/s3ip/transceiver * * History * [Version] [Date] [Description] * * v1.0 2021-08-31 S3IP sysfs */ #include #include "switch.h" #include "transceiver_sysfs.h" static int g_sff_loglevel = 0; #define SFF_INFO(fmt, args...) do { \ if (g_sff_loglevel & INFO) { \ printk(KERN_INFO "[SFF_SYSFS][func:%s line:%d]\n"fmt, __func__, __LINE__, ## args); \ } \ } while (0) #define SFF_ERR(fmt, args...) do { \ if (g_sff_loglevel & ERR) { \ printk(KERN_ERR "[SFF_SYSFS][func:%s line:%d]\n"fmt, __func__, __LINE__, ## args); \ } \ } while (0) #define SFF_DBG(fmt, args...) do { \ if (g_sff_loglevel & DBG) { \ printk(KERN_DEBUG "[SFF_SYSFS][func:%s line:%d]\n"fmt, __func__, __LINE__, ## args); \ } \ } while (0) struct sff_obj_s { struct switch_obj *sff_obj; struct bin_attribute bin; int sff_creat_bin_flag; }; struct sff_s { unsigned int sff_number; struct sff_obj_s *sff; }; static struct sff_s g_sff; static struct switch_obj *g_sff_obj = NULL; static struct s3ip_sysfs_transceiver_drivers_s *g_sff_drv = NULL; static ssize_t transceiver_power_on_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_transceiver_power_on_status); ret = g_sff_drv->get_transceiver_power_on_status(buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get transceiver power on status failed, ret: %d\n", ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t transceiver_power_on_store(struct switch_obj *obj, struct switch_attribute *attr, const char* buf, size_t count) { int ret, value; check_p(g_sff_drv); check_p(g_sff_drv->set_transceiver_power_on_status); sscanf(buf, "%d", &value); if (value < 0 || value > 1) { SFF_ERR("invalid value: %d, can't set transceiver power on status.\n", value); return -EINVAL; } ret = g_sff_drv->set_transceiver_power_on_status(value); if (ret < 0) { SFF_ERR("set transceiver power on status %d failed, ret: %d\n", value, ret); return -EIO; } return count; } static ssize_t eth_power_on_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_power_on_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_power_on_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u power on status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_power_on_store(struct switch_obj *obj, struct switch_attribute *attr, const char* buf, size_t count) { unsigned int eth_index; int ret, value; check_p(g_sff_drv); check_p(g_sff_drv->set_eth_power_on_status); sscanf(buf, "%d", &value); eth_index = obj->index; if (value < 0 || value > 1) { SFF_ERR("invalid value: %d, can't set eth%u power on status.\n", value, eth_index); return -EINVAL; } ret = g_sff_drv->set_eth_power_on_status(eth_index, value); if (ret < 0) { SFF_ERR("set eth%u power on status %d failed, ret: %d\n", eth_index, value, ret); return -EIO; } SFF_DBG("set eth%u power on status %d success\n", eth_index, value); return count; } static ssize_t eth_tx_fault_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_tx_fault_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_tx_fault_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u tx fault status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_tx_disable_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_tx_disable_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_tx_disable_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u tx disable status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_tx_disable_store(struct switch_obj *obj, struct switch_attribute *attr, const char* buf, size_t count) { unsigned int eth_index; int ret, value; check_p(g_sff_drv); check_p(g_sff_drv->set_eth_tx_disable_status); sscanf(buf, "%d", &value); eth_index = obj->index; if (value < 0 || value > 1) { SFF_ERR("invalid value: %d, can't set eth%u tx disable status.\n", value, eth_index); return -EINVAL; } ret = g_sff_drv->set_eth_tx_disable_status(eth_index, value); if (ret < 0) { SFF_ERR("set eth%u tx disable status %d failed, ret: %d\n", eth_index, value, ret); return -EIO; } SFF_DBG("set eth%u tx disable status %d success\n", eth_index, value); return count; } static ssize_t eth_present_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_present_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_present_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u present status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_rx_los_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_rx_los_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_rx_los_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u rx los status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_reset_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_reset_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_reset_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u reset status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_reset_store(struct switch_obj *obj, struct switch_attribute *attr, const char* buf, size_t count) { unsigned int eth_index; int ret, value; check_p(g_sff_drv); check_p(g_sff_drv->set_eth_reset_status); sscanf(buf, "%d", &value); eth_index = obj->index; if (value < 0 || value > 1) { SFF_ERR("invalid value: %d, can't set eth%u reset status.\n", value, eth_index); return -EINVAL; } ret = g_sff_drv->set_eth_reset_status(eth_index, value); if (ret < 0) { SFF_ERR("set eth%u reset status %d failed, ret: %d\n", eth_index, value, ret); return -EIO; } SFF_DBG("set eth%u reset status %d success\n", eth_index, value); return count; } static ssize_t eth_low_power_mode_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_low_power_mode_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_low_power_mode_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u low power mode status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_interrupt_show(struct switch_obj *obj, struct switch_attribute *attr, char *buf) { unsigned int eth_index; int ret; check_p(g_sff_drv); check_p(g_sff_drv->get_eth_interrupt_status); eth_index = obj->index; SFF_DBG("eth index: %u\n", eth_index); ret = g_sff_drv->get_eth_interrupt_status(eth_index, buf, PAGE_SIZE); if (ret < 0) { SFF_ERR("get eth%u interrupt status failed, ret: %d\n", eth_index, ret); return (ssize_t)snprintf(buf, PAGE_SIZE, "%s\n", SYSFS_DEV_ERROR); } return ret; } static ssize_t eth_eeprom_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t count) { struct switch_obj *eth_obj; ssize_t rd_len; unsigned int eth_index; check_p(g_sff_drv); check_p(g_sff_drv->read_eth_eeprom_data); eth_obj = to_switch_obj(kobj); eth_index = eth_obj->index; memset(buf, 0, count); rd_len = g_sff_drv->read_eth_eeprom_data(eth_index, buf, offset, count); if (rd_len < 0) { SFF_ERR("read eth%u eeprom data error, offset: 0x%llx, read len: %lu, ret: %ld.\n", eth_index, offset, count, rd_len); return -EIO; } SFF_DBG("read eth%u eeprom data success, offset:0x%llx, read len:%lu, really read len:%ld.\n", eth_index, offset, count, rd_len); return rd_len; } static ssize_t eth_eeprom_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t count) { struct switch_obj *eth_obj; ssize_t wr_len; unsigned int eth_index; check_p(g_sff_drv); check_p(g_sff_drv->write_eth_eeprom_data); eth_obj = to_switch_obj(kobj); eth_index = eth_obj->index; wr_len = g_sff_drv->write_eth_eeprom_data(eth_index, buf, offset, count); if (wr_len < 0) { SFF_ERR("write eth%u eeprom data error, offset: 0x%llx, read len: %lu, ret: %ld.\n", eth_index, offset, count, wr_len); return -EIO; } SFF_DBG("write eth%u eeprom data success, offset:0x%llx, write len:%lu, really write len:%ld.\n", eth_index, offset, count, wr_len); return wr_len; } /************************************eth* signal attrs*******************************************/ static struct switch_attribute eth_power_on_attr = __ATTR(power_on, S_IRUGO | S_IWUSR, eth_power_on_show, eth_power_on_store); static struct switch_attribute eth_tx_fault_attr = __ATTR(tx_fault, S_IRUGO, eth_tx_fault_show, NULL); static struct switch_attribute eth_tx_disable_attr = __ATTR(tx_disable, S_IRUGO | S_IWUSR, eth_tx_disable_show, eth_tx_disable_store); static struct switch_attribute eth_present_attr = __ATTR(present, S_IRUGO, eth_present_show, NULL); static struct switch_attribute eth_rx_los_attr = __ATTR(rx_los, S_IRUGO, eth_rx_los_show, NULL); static struct switch_attribute eth_reset_attr = __ATTR(reset, S_IRUGO | S_IWUSR, eth_reset_show, eth_reset_store); static struct switch_attribute eth_low_power_mode_attr = __ATTR(low_power_mode, S_IRUGO, eth_low_power_mode_show, NULL); static struct switch_attribute eth_interrupt_attr = __ATTR(interrupt, S_IRUGO, eth_interrupt_show, NULL); static struct attribute *sff_signal_attrs[] = { ð_power_on_attr.attr, ð_tx_fault_attr.attr, ð_tx_disable_attr.attr, ð_present_attr.attr, ð_rx_los_attr.attr, ð_reset_attr.attr, ð_low_power_mode_attr.attr, ð_interrupt_attr.attr, NULL, }; static struct attribute_group sff_signal_attr_group = { .attrs = sff_signal_attrs, }; /*******************************transceiver dir and attrs*******************************************/ static struct switch_attribute transceiver_power_on_attr = __ATTR(power_on, S_IRUGO | S_IWUSR, transceiver_power_on_show, transceiver_power_on_store); static struct attribute *transceiver_dir_attrs[] = { &transceiver_power_on_attr.attr, NULL, }; static struct attribute_group sff_transceiver_attr_group = { .attrs = transceiver_dir_attrs, }; /* create eth* eeprom attributes */ static int sff_sub_single_create_eeprom_attrs(unsigned int index) { int ret, eeprom_size; struct sff_obj_s *curr_sff; check_p(g_sff_drv->get_eth_eeprom_size); eeprom_size = g_sff_drv->get_eth_eeprom_size(index); if (eeprom_size <= 0) { SFF_INFO("eth%u, eeprom_size: %d, don't need to creat eeprom attr.\n", index, eeprom_size); return 0; } curr_sff = &g_sff.sff[index - 1]; sysfs_bin_attr_init(&curr_sff->bin); curr_sff->bin.attr.name = "eeprom"; curr_sff->bin.attr.mode = 0644; curr_sff->bin.read = eth_eeprom_read; curr_sff->bin.write = eth_eeprom_write; curr_sff->bin.size = eeprom_size; ret = sysfs_create_bin_file(&curr_sff->sff_obj->kobj, &curr_sff->bin); if (ret) { SFF_ERR("eth%u, create eeprom bin error, ret: %d. \n", index, ret); return -EBADRQC; } SFF_DBG("eth%u, create bin file success, eeprom size:%d.\n", index, eeprom_size); curr_sff->sff_creat_bin_flag = 1; return 0; } static int sff_sub_single_create_kobj(struct kobject *parent, unsigned int index) { struct sff_obj_s *curr_sff; char sff_dir_name[DIR_NAME_MAX_LEN]; curr_sff = &g_sff.sff[index - 1]; memset(sff_dir_name, 0, sizeof(sff_dir_name)); snprintf(sff_dir_name, sizeof(sff_dir_name), "eth%d", index); curr_sff->sff_obj = switch_kobject_create(sff_dir_name, parent); if (!curr_sff->sff_obj) { SFF_ERR("create eth%d object error! \n", index); return -EBADRQC; } curr_sff->sff_obj->index = index; if (sysfs_create_group(&curr_sff->sff_obj->kobj, &sff_signal_attr_group) != 0) { switch_kobject_delete(&curr_sff->sff_obj); return -EBADRQC; } SFF_DBG("create eth%d dir and attrs success\n", index); return 0; } /* remove eth directory and attributes */ static void sff_sub_single_remove_kobj_and_attrs(unsigned int index) { struct sff_obj_s *curr_sff; curr_sff = &g_sff.sff[index - 1]; if (curr_sff->sff_obj) { if (curr_sff->sff_creat_bin_flag) { sysfs_remove_bin_file(&curr_sff->sff_obj->kobj, &curr_sff->bin); curr_sff->sff_creat_bin_flag = 0; } sysfs_remove_group(&curr_sff->sff_obj->kobj, &sff_signal_attr_group); switch_kobject_delete(&curr_sff->sff_obj); } return; } static int sff_sub_single_create_kobj_and_attrs(struct kobject *parent, unsigned int index) { int ret; ret = sff_sub_single_create_kobj(parent, index); if (ret < 0) { SFF_ERR("create eth%d dir error.\n", index); return ret; } sff_sub_single_create_eeprom_attrs(index); return 0; } static int sff_sub_create_kobj_and_attrs(struct kobject *parent, int sff_num) { unsigned int sff_index, i; g_sff.sff = kzalloc(sizeof(struct sff_obj_s) * sff_num, GFP_KERNEL); if (!g_sff.sff) { SFF_ERR("kzalloc g_sff.sff error, sff number = %d.\n", sff_num); return -ENOMEM; } for (sff_index = 1; sff_index <= sff_num; sff_index++) { if (sff_sub_single_create_kobj_and_attrs(parent, sff_index) != 0 ) { goto error; } } return 0; error: for (i = sff_index; i > 0; i--) { sff_sub_single_remove_kobj_and_attrs(i); } kfree(g_sff.sff); g_sff.sff = NULL; return -EBADRQC; } /* create eth directory and attributes */ static int sff_sub_create(void) { int ret; ret = sff_sub_create_kobj_and_attrs(&g_sff_obj->kobj, g_sff.sff_number); return ret; } /* delete eth directory and attributes */ static void sff_sub_remove(void) { unsigned int sff_index; if (g_sff.sff) { for (sff_index = g_sff.sff_number; sff_index > 0; sff_index--) { sff_sub_single_remove_kobj_and_attrs(sff_index); } kfree(g_sff.sff); g_sff.sff = NULL; } g_sff.sff_number = 0; return; } /* create transceiver directory and attributes */ static int sff_transceiver_create(void) { g_sff_obj = switch_kobject_create("transceiver", NULL); if (!g_sff_obj) { SFF_ERR("switch_kobject_create transceiver error!\n"); return -ENOMEM; } g_sff_obj->index = 0; if (sysfs_create_group(&g_sff_obj->kobj, &sff_transceiver_attr_group) != 0) { switch_kobject_delete(&g_sff_obj); SFF_ERR("create transceiver dir attrs error!\n"); return -EBADRQC; } return 0; } /* delete transceiver directory and attributes */ static void sff_transceiver_remove(void) { if (g_sff_obj) { sysfs_remove_group(&g_sff_obj->kobj, &sff_transceiver_attr_group); switch_kobject_delete(&g_sff_obj); } return; } int s3ip_sysfs_sff_drivers_register(struct s3ip_sysfs_transceiver_drivers_s *drv) { int ret, sff_num; SFF_INFO("s3ip_sysfs_sff_drivers_register...\n"); if (g_sff_drv) { SFF_ERR("g_sff_drv is not NULL, can't register\n"); return -EPERM; } check_p(drv); check_p(drv->get_eth_number); g_sff_drv = drv; sff_num = g_sff_drv->get_eth_number(); if (sff_num <= 0) { SFF_ERR("eth number: %d, don't need to create transceiver dirs and attrs.\n", sff_num); g_sff_drv = NULL; return -EINVAL; } memset(&g_sff, 0, sizeof(struct sff_s)); g_sff.sff_number = sff_num; ret = sff_transceiver_create(); if (ret < 0) { SFF_ERR("create transceiver root dir and attrs failed, ret: %d\n", ret); g_sff_drv = NULL; return ret; } ret = sff_sub_create(); if (ret < 0) { SFF_ERR("create transceiver sub dir and attrs failed, ret: %d\n", ret); sff_transceiver_remove(); g_sff_drv = NULL; return ret; } SFF_INFO("s3ip_sysfs_sff_drivers_register success\n"); return ret; } void s3ip_sysfs_sff_drivers_unregister(void) { if (g_sff_drv) { sff_sub_remove(); sff_transceiver_remove(); g_sff_drv = NULL; SFF_DBG("s3ip_sysfs_sff_drivers_unregister success.\n"); } return; } EXPORT_SYMBOL(s3ip_sysfs_sff_drivers_register); EXPORT_SYMBOL(s3ip_sysfs_sff_drivers_unregister); module_param(g_sff_loglevel, int, 0644); MODULE_PARM_DESC(g_sff_loglevel, "the log level(info=0x1, err=0x2, dbg=0x4).\n");