/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ /***************************** Maple platform ******************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SHOW_ATTR_WARNING ("N/A") #define SHOW_ATTR_NOTPRINT ("Not Available") #define SHOW_ATTR_NOTSUPPORT ("Not Support") #define INV_HWMID_MAX (10) #define INV_HWMID_INIT (-1) /*access userspace data to kernel space*/ #define ACC_R (0) #define ACC_W (1) #define TINY_BUF_SIZE (8) #define MAX_PATH_SIZE (64) #define MIN_ACC_SIZE (32) #define MAX_ACC_SIZE (256) /* * LED definitions */ #define STATUS_LED_MODE_AUTO 0 #define STATUS_LED_MODE_DIAG 1 #define STATUS_LED_MODE_MANU 2 #define STATUS_LED_GRN0 10 // 0 - 000: off #define STATUS_LED_GRN1 11 // 1 - 001: 0.5hz #define STATUS_LED_GRN2 12 // 2 - 010: 1 hz #define STATUS_LED_GRN3 13 // 3 - 011: 2 hz #define STATUS_LED_GRN7 17 // 7 - 111: on #define STATUS_LED_RED0 20 // 0 - 000: off #define STATUS_LED_RED1 21 // 1 - 001: 0.5hz #define STATUS_LED_RED2 22 // 2 - 010: 1 hz #define STATUS_LED_RED3 23 // 3 - 011: 2 hz #define STATUS_LED_RED7 27 // 7 - 111: on #define STATUS_LED_INVALID 0 // Invalid ssize_t status_led_change(const char *path1, const char *tmp1, const char *path2, const char *tmp2); ssize_t status_led_grn(const char *freq); ssize_t status_led_red(const char *freq); ssize_t status_led_diag_mode_enable(void); ssize_t status_led_diag_mode_disable(void); int status_led_check_color(void); int status_led_check_diag_mode(void); int status_led_psu_control(void); ssize_t status_fan_led(const char *freq); ssize_t status_psu_led(const char *freq); #if 1 /* For timestamps in SYSFS_LOG */ #define SYSFS_LOG printk #else //#define SYSFS_LOG(fmt, args...) printk(KERN_WARNING "[SYSFS] %s/%d: " fmt, __func__, __LINE__, ##args) #define SYSFS_LOG(fmt, args...) printk(KERN_WARNING "[p_thread] " fmt, ##args) #endif #define INV_PTHREAD_KERNEL_MODULE (1) /* inventec_class *********************************/ static struct kobject *status_kobj; static struct kset *status_kset; static struct mutex rw_lock; static int hwm_psoc = INV_HWMID_INIT; static int hwm_cpld = INV_HWMID_INIT; int get_hwm_psoc(void) { return hwm_psoc; } int get_hwm_cpld(void) { return hwm_cpld; } static ssize_t access_user_space(const char *name, int mode, char *buf, size_t len, loff_t offset) { struct file *fp; mm_segment_t fs; loff_t pos = offset; char *mark = NULL; ssize_t vfs_ret = 0; if (mode == ACC_R) { fp = filp_open(name, O_RDONLY, S_IRUGO); if (IS_ERR(fp)) return -ENODEV; fs = get_fs(); set_fs(KERNEL_DS); vfs_ret = vfs_read(fp, buf, len, &pos); mark = strpbrk(buf, "\n"); if (mark) *mark = '\0'; filp_close(fp, NULL); set_fs(fs); } else if (mode == ACC_W) { fp = filp_open(name, O_WRONLY, S_IWUSR | S_IRUGO); if (IS_ERR(fp)) return -ENODEV; fs = get_fs(); set_fs(KERNEL_DS); vfs_ret = vfs_write(fp, buf, len, &pos); filp_close(fp, NULL); set_fs(fs); } return vfs_ret; } int inventec_strtol(const char *sbufp, char **endp, unsigned int base) { char *endptr; int value = simple_strtol(sbufp, &endptr, base); if (value == 0 && sbufp == endptr) { *endp = NULL; return value; } *endp = (char*)1; return value; } int inventec_singlechar_to_int(const char c) { if ((c >= '0') && (c <= '9')) { return (c - '0'); } else if ((c >= 'a') && (c <= 'f')) { return (c - 'a' + 10); } else if ((c >= 'A') && (c <= 'F')) { return (c - 'A' + 10); } return -1; } int inventec_store_input(char *inputp, int count) { int i = 0; while(inputp[i] != '\n' && inputp[i] != '\0' && i < count) { i++; } inputp[i] = '\0'; return strlen(inputp); } #if 0 /* * Time stamps for kernel log on yocto */ #include void SYSFS_LOG(char *fmt, ...) { char buf[80], ts[32]; va_list args; int hlen; inventec_tmstmp(&ts[0]); hlen = sprintf(buf, "[SYSFS] %s ", ts); // Do not edit this line va_start(args, fmt); vsprintf(&buf[hlen-1], fmt, args); va_end(args); printk(KERN_WARNING "[p_thread] %s\n", buf); } #endif ssize_t inventec_show_attr(char *buf_p, const char *invdevp) { int inv_len = MAX_ACC_SIZE; /* INV driver return max length */ char tmp_buf[MAX_ACC_SIZE]; char *str_negative = "-", *mark = NULL; /* [Step2] Get data by uaccess */ memset(tmp_buf, 0, sizeof(tmp_buf)); mutex_lock(&rw_lock); if (access_user_space(invdevp, ACC_R, tmp_buf, inv_len, 0) < 0) { /* u_access fail */ mutex_unlock(&rw_lock); return sprintf(buf_p, "%s\n", SHOW_ATTR_WARNING); } mutex_unlock(&rw_lock); /* [Step3] Check return value * - Ex: When transceiver not plugged * => SWPS return error code "-202" * => Pic8 need return "NA" (assume) */ if (strcspn(tmp_buf, str_negative) == 0) { /* error case: "-202" */ return sprintf(buf_p, "%s\n", SHOW_ATTR_WARNING); } /* OK case:*/ mark = strpbrk(tmp_buf, "\n"); if (mark) { *mark = '\0'; } return sprintf(buf_p, "%s\n", tmp_buf); } ssize_t inventec_store_attr(const char *buf_p, size_t count, const char *invdevp) { ssize_t ret = 0; /* [Step2] Get data by uaccess */ mutex_lock(&rw_lock); if ((ret = access_user_space(invdevp, ACC_W, (char*)buf_p, count, 0)) < 0) { /* u_access fail */ mutex_unlock(&rw_lock); return -EINVAL; } mutex_unlock(&rw_lock); /* OK case:*/ return ret; } int sysfs_detect_hwmon_index(void) { char hwmon_buf[MAX_ACC_SIZE]; char hwmon_path[MAX_PATH_SIZE]; char hwmon_dev_path[MAX_PATH_SIZE]; int hwid = 0; for (hwid = 0; hwid < INV_HWMID_MAX && (hwm_psoc == INV_HWMID_INIT || hwm_cpld == INV_HWMID_INIT); hwid++) { memset(hwmon_buf, 0, sizeof(hwmon_buf)); sprintf(hwmon_path, "/sys/class/hwmon/hwmon%d/name", hwid); inventec_show_attr(hwmon_buf, hwmon_path); if (strncmp(hwmon_buf, "inv_psoc", 8) == 0) { hwm_psoc = hwid; } else if (strncmp(hwmon_buf, "inv_bmc", 7) == 0) { hwm_psoc = hwid; } sprintf(hwmon_dev_path, "/sys/class/hwmon/hwmon%d/device/name", hwid); inventec_show_attr(hwmon_buf, hwmon_dev_path); if (strncmp(hwmon_buf, "inv_cpld", 8) == 0) { hwm_cpld = hwid; } } if (hwid >= INV_HWMID_MAX) { printk(KERN_ERR "[p_thread] detect hwmon index failed, psoc = %d, cpld = %d\n", hwm_psoc, hwm_cpld); return -1; } printk(KERN_INFO "[p_thread] detect hwmon index success, psoc = %d, cpld = %d\n", hwm_psoc, hwm_cpld); return 0; } static int __init inventec_class_init(void) { mutex_init(&rw_lock); #ifdef INV_PTHREAD_KERNEL_MODULE if (sysfs_detect_hwmon_index() < 0) { return -1; } #endif printk(KERN_INFO "[p_thread] [%s/%d] Module initial success.\n",__func__,__LINE__); return 0; } static void __exit inventec_class_exit(void) { printk(KERN_INFO "[p_thread] [%s/%d] Remove module.\n",__func__,__LINE__); } /* fan device *************************************/ #define FAN_DEV_PATH_STATE "/sys/class/hwmon/hwmon%d/device/fan_gpi" #define FAN_DEV_PATH_FAN1_INPUT "/sys/class/hwmon/hwmon%d/device/fan1_input" #define FAN_DEV_PATH_FAN2_INPUT "/sys/class/hwmon/hwmon%d/device/fan2_input" #define FAN_DEV_PATH_FAN3_INPUT "/sys/class/hwmon/hwmon%d/device/fan3_input" #define FAN_DEV_PATH_FAN4_INPUT "/sys/class/hwmon/hwmon%d/device/fan4_input" #define FAN_DEV_PATH_FAN5_INPUT "/sys/class/hwmon/hwmon%d/device/fan5_input" #define FAN_DEV_PATH_FAN6_INPUT "/sys/class/hwmon/hwmon%d/device/fan6_input" #define FAN_DEV_PATH_FAN7_INPUT "/sys/class/hwmon/hwmon%d/device/fan7_input" #define FAN_DEV_PATH_FAN8_INPUT "/sys/class/hwmon/hwmon%d/device/fan8_input" static char fan_dev_path_state[MAX_PATH_SIZE]; static char fan_dev_path_fan1_input[MAX_PATH_SIZE]; static char fan_dev_path_fan2_input[MAX_PATH_SIZE]; static char fan_dev_path_fan3_input[MAX_PATH_SIZE]; static char fan_dev_path_fan4_input[MAX_PATH_SIZE]; static char fan_dev_path_fan5_input[MAX_PATH_SIZE]; static char fan_dev_path_fan6_input[MAX_PATH_SIZE]; static char fan_dev_path_fan7_input[MAX_PATH_SIZE]; static char fan_dev_path_fan8_input[MAX_PATH_SIZE]; void sysfs_fan_path_init(void) { sprintf(&fan_dev_path_state[0], FAN_DEV_PATH_STATE, get_hwm_psoc()); sprintf(&fan_dev_path_fan1_input[0],FAN_DEV_PATH_FAN1_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan2_input[0],FAN_DEV_PATH_FAN2_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan3_input[0],FAN_DEV_PATH_FAN3_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan4_input[0],FAN_DEV_PATH_FAN4_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan5_input[0],FAN_DEV_PATH_FAN5_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan6_input[0],FAN_DEV_PATH_FAN6_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan7_input[0],FAN_DEV_PATH_FAN7_INPUT, get_hwm_psoc()); sprintf(&fan_dev_path_fan8_input[0],FAN_DEV_PATH_FAN8_INPUT, get_hwm_psoc()); } #define FAN_STATE_NORMAL "normal" #define FAN_STATE_FAULTY "faulty" #define FAN_STATE_UNINSTALLED "uninstalled" #define FAN_STATE_UNKNOW "unknown state" #define FAN_STATE_INVALID "Invalid state value" #define FAN_STATE_READ_ERROR "state read error" #define FAN_LOG_UNINSTALLED "removed" #define FAN_LOG_NORMAL "inserted" //#define FAN_STATE_BIT_NORMAL 0 #define FAN_STATE_BIT_FAULTY 0 #define FAN_STATE_BIT_UNINSTALLED 1 #define FAN_STATE_BIT_UNKNOW 2 #define FAN_STATE_BIT_INVALID 3 #define FAN_STATE_BIT_READ_ERROR 4 static struct fans_tbl_s { char *fan_name; char *fan_front; char *fan_rear; unsigned int fan_state; } fans_tbl[] = { {"fan1", fan_dev_path_fan1_input, fan_dev_path_fan2_input, 0}, {"fan2", fan_dev_path_fan3_input, fan_dev_path_fan4_input, 0}, {"fan3", fan_dev_path_fan5_input, fan_dev_path_fan6_input, 0}, {"fan4", fan_dev_path_fan7_input, fan_dev_path_fan8_input, 0}, }; #define FAN_TBL_TOTAL ( sizeof(fans_tbl)/ sizeof(const struct fans_tbl_s) ) #define FAN_STATE_CHECK(i,b) (fans_tbl[i].fan_state & (1< 0) { printk(KERN_ERR "[p_thread] All fans failed.\n"); printk(KERN_ERR "[p_thread] System shutdown immediately in %d seconds.\n", cd_shutdown); } cd_shutdown -= 1; } return ret; } /* End of faninfo_device */ static int __init fan_device_init(void) { #ifdef INV_PTHREAD_KERNEL_MODULE sysfs_fan_path_init(); #endif return 0; } static void __exit fan_device_exit(void) { printk(KERN_INFO "[p_thread] Remove fan module.\n"); } /* psu device *************************************/ static unsigned int psu_voltin = 0; #define PSU_VOLTIN_ACDC (70000) /* * normal/unpower/uninstall/fault are PSU states output from driver level * checkpsu/error are defined by sysfs */ #define PSU_STATE_VAL_NORMAL (0) #define PSU_STATE_VAL_UNPOWER (2) #define PSU_STATE_VAL_FAULT (4) #define PSU_STATE_VAL_UNINSTALL (7) #define PSU_STATE_VAL_CHECKPSU (8) #define PSU_STATE_VAL_ERROR (9) #define PSU_STATE_NORMAL ("0 : normal") #define PSU_STATE_UNPOWER ("2 : unnormal") #define PSU_STATE_FAULT ("4 : fault") #define PSU_STATE_UNINSTALL ("7 : not installed") #define PSU_STATE_CHECKPSU ("8 : check psu") #define PSU_STATE_ERROR ("9 : state error") #define PSU_STATE_LEN_NORMAL (strlen(PSU_STATE_NORMAL)) #define PSU_STATE_LEN_UNPOWER (strlen(PSU_STATE_UNPOWER)) #define PSU_STATE_LEN_FAULT (strlen(PSU_STATE_FAULT)) #define PSU_STATE_LEN_UNINSTALL (strlen(PSU_STATE_UNINSTALL)) #define PSU_STATE_LEN_CHECKPSU (strlen(PSU_STATE_CHECKPSU)) typedef struct { char *inv_dev_attrp; char *inv_dev_pathp; } psu_dev_t; typedef struct { const char *psu_name; int psu_major; dev_t psu_devt; struct device *psu_dev_p; psu_dev_t *psu_dev_namep; int psu_dev_total; char *psu_inv_pathp; void *psu_tracking; char *psu_currentin; char *psu_currentout; char *psu_powerin; char *psu_powerout; char *psu_voltin; char *psu_voltout; } psu_dev_group_t; #define PSU_DEV_PATH_TEMPLATE "/sys/class/hwmon/hwmon%d/device/%s" static char psu_dev_path_state[MAX_PATH_SIZE]; static char psu_dev_path_psu_voltin[MAX_PATH_SIZE]; static char psu_0_dev_path_state[MAX_PATH_SIZE]; static char psu_1_dev_path_state[MAX_PATH_SIZE]; #if 0 static char psu_dev_path_vendor[MAX_PATH_SIZE]; static char psu_dev_path_version[MAX_PATH_SIZE]; static char psu_dev_path_sn[MAX_PATH_SIZE]; static char psu_dev_path_temperature[MAX_PATH_SIZE]; static char psu_dev_path_fan_speed[MAX_PATH_SIZE]; static char psu_dev_path_fan_pwm[MAX_PATH_SIZE]; static char psu_dev_path_fan_faulty[MAX_PATH_SIZE]; static char psu_dev_path_psu_currentin[MAX_PATH_SIZE]; static char psu_dev_path_psu_currentout[MAX_PATH_SIZE]; static char psu_dev_path_psu_powerin[MAX_PATH_SIZE]; static char psu_dev_path_psu_powerout[MAX_PATH_SIZE]; static char psu_dev_path_psu_voltout[MAX_PATH_SIZE]; static char psu_dev_path_psu_pwm[MAX_PATH_SIZE]; static char psu_dev_path_psu_rpm[MAX_PATH_SIZE]; #endif void sysfs_psu_path_init(void) { sprintf(&psu_dev_path_state[0], PSU_DEV_PATH_TEMPLATE, get_hwm_cpld(), "\%s" ); sprintf(&psu_dev_path_psu_voltin[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_0_dev_path_state[0], PSU_DEV_PATH_TEMPLATE, get_hwm_cpld(), "psu0" ); sprintf(&psu_1_dev_path_state[0], PSU_DEV_PATH_TEMPLATE, get_hwm_cpld(), "psu1" ); #if 0 sprintf(&psu_dev_path_vendor[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_version[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_sn[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_temperature[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_fan_speed[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_fan_pwm[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_fan_faulty[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_currentin[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_currentout[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_powerin[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_powerout[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_voltout[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_pwm[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); sprintf(&psu_dev_path_psu_rpm[0], PSU_DEV_PATH_TEMPLATE, get_hwm_psoc(), "\%s" ); #endif } static psu_dev_t psu_dev_name[] = { { "state", psu_dev_path_state }, // Using cpld { "psu_voltin", psu_dev_path_psu_voltin }, #if 0 { "vendor", psu_dev_path_vendor }, { "version", psu_dev_path_version }, { "sn", psu_dev_path_sn }, { "temperature", psu_dev_path_temperature }, { "fan_speed", psu_dev_path_fan_speed }, { "fan_pwm", psu_dev_path_fan_pwm }, { "fan_faulty", psu_dev_path_fan_faulty }, { "psu_currentin", psu_dev_path_psu_currentin }, { "psu_currentout", psu_dev_path_psu_currentout }, { "psu_powerin", psu_dev_path_psu_powerin }, { "psu_powerout", psu_dev_path_psu_powerout }, { "psu_voltout", psu_dev_path_psu_voltout }, { "psu_pwm", psu_dev_path_psu_pwm }, { "psu_rpm", psu_dev_path_psu_rpm }, #endif }; #define PSU_DEV_NAME_TOTAL ( sizeof(psu_dev_name) / sizeof(const psu_dev_t) ) static psu_dev_group_t psu_dev_group[] = { { .psu_name = "psu1", .psu_dev_namep = &psu_dev_name[0], .psu_dev_total = sizeof(psu_dev_name) / sizeof(const psu_dev_t), }, { .psu_name = "psu2", .psu_dev_namep = &psu_dev_name[0], .psu_dev_total = sizeof(psu_dev_name) / sizeof(const psu_dev_t), }, }; #define PSU_DEV_GROUP_TOTAL ( sizeof(psu_dev_group)/ sizeof(const psu_dev_group_t) ) static char psu_state[4][MIN_ACC_SIZE]; static struct psu_wire_tbl_s { char *psu_attr; char *psu_name; char *psu_wire; char *psu_state; } psu_wire_tbl[] = { { "state", "psu1", "psu0", psu_state[0] }, // Using cpld { "state", "psu2", "psu1", psu_state[1] }, { "psu_voltin", "psu1", "psoc_psu1_vin", psu_state[2] }, { "psu_voltin", "psu2", "psoc_psu2_vin", psu_state[3] }, }; #define PSU_WIRE_TBL_TOTAL ( sizeof(psu_wire_tbl)/ sizeof(const struct psu_wire_tbl_s) ) static char * psu_attr_get_wirep(const char *psu_attrp, const char *psu_namep, char **psu_statepp) { int i; for (i = 0; i < PSU_WIRE_TBL_TOTAL; i++) { if (strncmp(psu_wire_tbl[i].psu_attr, psu_attrp, strlen(psu_attrp)) == 0 && strncmp(psu_wire_tbl[i].psu_name, psu_namep, strlen(psu_namep)) == 0) { if (psu_statepp) { *psu_statepp = psu_wire_tbl[i].psu_state; } return psu_wire_tbl[i].psu_wire; } } return NULL; } int psu_check_state_normal(char *statep) { if (strstr(statep, "normal")) { return 1; } return 0; } #define PSU_ATTR_VOLTIN ("psu_voltin") #define PSU_ATTR_VOLTIN_LEN (10) /* Get PSU voltin for determon AC(110v) or DC(48v) */ void psu_get_voltin(void) { char acc_path[MAX_PATH_SIZE], volt[MIN_ACC_SIZE]; psu_dev_t *devnamep; unsigned int voltin; char *invwirep; int i, j; for (i = 0; i < PSU_DEV_GROUP_TOTAL; i++) { //psu_dev_group[i].psu_name; devnamep = psu_dev_group[i].psu_dev_namep; for (j = 0; j < psu_dev_group[i].psu_dev_total; j++, devnamep++) { if (strncmp(devnamep->inv_dev_attrp, PSU_ATTR_VOLTIN, PSU_ATTR_VOLTIN_LEN) == 0) { invwirep = psu_attr_get_wirep(PSU_ATTR_VOLTIN, psu_dev_group[i].psu_name, NULL); if (invwirep == NULL) { printk(KERN_DEBUG "[p_thread] Invalid psuname: %s\n", psu_dev_group[i].psu_name); continue; } sprintf(acc_path, devnamep->inv_dev_pathp, invwirep); //printk(KERN_DEBUG "[p_thread] RYU: %s/%d: acc_path = %s\n",__func__,__LINE__,acc_path); if (inventec_show_attr(volt, acc_path) <= 0) { printk(KERN_DEBUG "[p_thread] Read %s failed\n", acc_path); continue; } else { voltin = simple_strtol(&volt[0], NULL, 10); printk(KERN_DEBUG "[p_thread] Read %s %s = %u\n",acc_path,volt,voltin); if (voltin > psu_voltin) { psu_voltin = voltin; } } } } } SYSFS_LOG("[p_thread] PSU voltin = %u\n", psu_voltin); } #define PSU_ATTR_STATE ("state") #define PSU_ATTR_STATE_LEN (5) /* psus_control() by inv_thread */ int psus_control(int log_only) { char acc_path[MAX_PATH_SIZE], state[MIN_ACC_SIZE]; psu_dev_t *devnamep = NULL; char *invwirep = NULL; char *psu_statep = NULL; int i, j, flag = 0; for (i = 0; i < PSU_DEV_GROUP_TOTAL; i++) { devnamep = psu_dev_group[i].psu_dev_namep; for (j = 0; j < psu_dev_group[i].psu_dev_total; j++, devnamep++) { if (strncmp(devnamep->inv_dev_attrp, PSU_ATTR_STATE, PSU_ATTR_STATE_LEN) == 0) { invwirep = psu_attr_get_wirep(PSU_ATTR_STATE, psu_dev_group[i].psu_name, &psu_statep); if (invwirep == NULL) { printk(KERN_DEBUG "[p_thread] Invalid psuname: %s\n", psu_dev_group[i].psu_name); continue; } sprintf(acc_path, devnamep->inv_dev_pathp, invwirep); //printk(KERN_INFO "[p_thread] RYU: %s/%d: acc_path = %s\n",__func__,__LINE__,acc_path); if (inventec_show_attr(state, acc_path) <= 0) { printk(KERN_DEBUG "[p_thread] Read %s failed\n", acc_path); if (strncmp(psu_statep, PSU_STATE_ERROR, strlen(PSU_STATE_ERROR)) != 0) { strcpy(psu_statep, PSU_STATE_ERROR); SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,PSU_STATE_ERROR); } flag = 1; } else if (strstr(state, "normal")) { //printk(KERN_INFO "[p_thread] %s: %s\n", psu_dev_group[i].psu_name, state); if (strncmp(psu_statep, state, strlen(state)) != 0) { strcpy(psu_statep, state); SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,state); } } else if (psu_voltin > PSU_VOLTIN_ACDC) { /* AC PSUS */ //printk(KERN_INFO "[p_thread] RYU: %s: %s\n", psu_dev_group[i].psu_name, state); if (strncmp(psu_statep, state, strlen(state)) != 0) { strcpy(psu_statep, state); SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,state); } flag = 1; } else { /* DC PSUS */ if (strncmp(psu_statep, PSU_STATE_CHECKPSU, PSU_STATE_LEN_CHECKPSU) != 0) { strcpy(psu_statep, PSU_STATE_CHECKPSU); SYSFS_LOG("[p_thread] %s: %s\n",psu_dev_group[i].psu_name,PSU_STATE_CHECKPSU); } flag = 1; } } } } if (log_only) { return 0; } //SYSFS_LOG("[p_thread] RYU: %s: flag = %d\n",psu_wire_tbl[i].psu_name,flag); if (flag == 1) { status_psu_led("0x20"); return 1; } return 0; } int status_led_psu_control(void) { int retVal = 0; //no issue char buf[MAX_ACC_SIZE]; int normal_psu_cnt = 0; //normal int fault_psu_cnt = 0; //read fan status one by one //read psu 0 status if (inventec_show_attr(&buf[0], psu_0_dev_path_state) > 0) { if(buf[0] == '0') //normal { normal_psu_cnt = normal_psu_cnt + 1; } else if (buf[0] == '4') // psu fault { fault_psu_cnt = fault_psu_cnt + 1; } //Check 2nd psu if(inventec_show_attr(&buf[0], psu_1_dev_path_state) > 0) { if(buf[0] == '0') //normal { normal_psu_cnt = normal_psu_cnt + 1; } else if (buf[0] == '4') // psu fault { fault_psu_cnt = fault_psu_cnt + 1; } } } if (normal_psu_cnt == 0) { status_psu_led("0"); retVal = 1; } else { if( fault_psu_cnt > 0) { status_psu_led("0x01"); //Red lit retVal = 1; } else if (normal_psu_cnt == 1) { status_psu_led("0x20"); // Green blink retVal = 1; } else if (normal_psu_cnt == 2) { status_psu_led("0x10"); // Green Lit retVal = 0; } } return retVal; } /* End of psuinfo_device */ static int __init psu_device_init(void) { #ifdef INV_PTHREAD_KERNEL_MODULE sysfs_psu_path_init(); #endif return 0; } static void __exit psu_device_exit(void) { printk(KERN_INFO "[p_thread] Remove psu module.\n"); } /* led device *************************************/ #define STATUS_LED_GRN_PATH "/sys/class/hwmon/hwmon%d/device/grn_led" #define STATUS_LED_RED_PATH "/sys/class/hwmon/hwmon%d/device/red_led" #define STATUS_LED_FAN_PATH "/sys/class/hwmon/hwmon%d/device/fan_led" #define STATUS_LED_PSU_PATH "/sys/class/hwmon/hwmon%d/device/psu_led" #define FAN_LED_GRN1_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn1" #define FAN_LED_GRN2_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn2" #define FAN_LED_GRN3_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn3" #define FAN_LED_GRN4_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_grn4" #define FAN_LED_RED1_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red1" #define FAN_LED_RED2_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red2" #define FAN_LED_RED3_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red3" #define FAN_LED_RED4_PATH "/sys/class/hwmon/hwmon%d/device/fan_led_red4" #define HWMON_DEVICE_DIAG_PATH "/sys/class/hwmon/hwmon%d/device/diag" #define HWMON_DEVICE_CTRL_PATH "/sys/class/hwmon/hwmon%d/device/ctl" static char status_led_grn_path[MAX_PATH_SIZE]; static char status_led_red_path[MAX_PATH_SIZE]; static char status_led_fan_path[MAX_PATH_SIZE]; static char status_led_psu_path[MAX_PATH_SIZE]; static char fan_led_grn1_path[MAX_PATH_SIZE]; static char fan_led_grn2_path[MAX_PATH_SIZE]; static char fan_led_grn3_path[MAX_PATH_SIZE]; static char fan_led_grn4_path[MAX_PATH_SIZE]; static char fan_led_red1_path[MAX_PATH_SIZE]; static char fan_led_red2_path[MAX_PATH_SIZE]; static char fan_led_red3_path[MAX_PATH_SIZE]; static char fan_led_red4_path[MAX_PATH_SIZE]; static char hwmon_device_diag_path[MAX_PATH_SIZE]; static char hwmon_device_ctrl_path[MAX_PATH_SIZE]; void sysfs_led_path_init(void) { sprintf(&status_led_grn_path[0], STATUS_LED_GRN_PATH, get_hwm_cpld()); sprintf(&status_led_red_path[0], STATUS_LED_RED_PATH, get_hwm_cpld()); sprintf(&status_led_fan_path[0], STATUS_LED_FAN_PATH, get_hwm_cpld()); sprintf(&status_led_psu_path[0], STATUS_LED_PSU_PATH, get_hwm_cpld()); sprintf(&fan_led_grn1_path[0], FAN_LED_GRN1_PATH, get_hwm_psoc()); sprintf(&fan_led_grn2_path[0], FAN_LED_GRN2_PATH, get_hwm_psoc()); sprintf(&fan_led_grn3_path[0], FAN_LED_GRN3_PATH, get_hwm_psoc()); sprintf(&fan_led_grn4_path[0], FAN_LED_GRN4_PATH, get_hwm_psoc()); sprintf(&fan_led_red1_path[0], FAN_LED_RED1_PATH, get_hwm_psoc()); sprintf(&fan_led_red2_path[0], FAN_LED_RED2_PATH, get_hwm_psoc()); sprintf(&fan_led_red3_path[0], FAN_LED_RED3_PATH, get_hwm_psoc()); sprintf(&fan_led_red4_path[0], FAN_LED_RED4_PATH, get_hwm_psoc()); sprintf(&hwmon_device_diag_path[0], HWMON_DEVICE_DIAG_PATH, get_hwm_psoc()); sprintf(&hwmon_device_ctrl_path[0], HWMON_DEVICE_CTRL_PATH, get_hwm_cpld()); } /* return 0/off 1/green 2/red */ int status_led_check_color(void) { char tmpbuf[MIN_ACC_SIZE]; int ret = STATUS_LED_INVALID; if (inventec_show_attr(&tmpbuf[0], status_led_grn_path) > 0) { if (tmpbuf[0] == '0') { ret = STATUS_LED_GRN0; } if (tmpbuf[0] == '1') { ret = STATUS_LED_GRN1; } if (tmpbuf[0] == '2') { ret = STATUS_LED_GRN2; } if (tmpbuf[0] == '3') { ret = STATUS_LED_GRN3; } if (tmpbuf[0] == '7') { ret = STATUS_LED_GRN7; } return ret; } if (inventec_show_attr(&tmpbuf[0], status_led_red_path) > 0) { if (tmpbuf[0] == '0') { ret = STATUS_LED_RED0; } if (tmpbuf[0] == '1') { ret = STATUS_LED_RED1; } if (tmpbuf[0] == '2') { ret = STATUS_LED_RED2; } if (tmpbuf[0] == '3') { ret = STATUS_LED_RED3; } if (tmpbuf[0] == '7') { ret = STATUS_LED_RED7; } return ret; } return ret; } /* * Store attr Section */ static DEFINE_MUTEX(diag_mutex); ssize_t status_led_diag_mode_enable(void) { char tmp[MIN_ACC_SIZE]; ssize_t ret; ret = inventec_show_attr(&tmp[0], hwmon_device_diag_path); if (ret <= 0) { return ret; } if (tmp[0] == '0') { mutex_lock(&diag_mutex); ret = inventec_store_attr("1", 1, hwmon_device_diag_path); if (ret < 0) { mutex_unlock(&diag_mutex); return ret; } ret = inventec_store_attr("1", 1, hwmon_device_ctrl_path); if (ret < 0) { mutex_unlock(&diag_mutex); return ret; } mutex_unlock(&diag_mutex); } return ret; } ssize_t status_led_diag_mode_disable(void) { char tmp[MIN_ACC_SIZE]; ssize_t ret; ret = inventec_show_attr(&tmp[0], hwmon_device_diag_path); if (ret <= 0) { return ret; } if (tmp[0] == '1') { mutex_lock(&diag_mutex); ret = inventec_store_attr("0", 1, hwmon_device_diag_path); if (ret < 0) { mutex_unlock(&diag_mutex); return 1; } ret = inventec_store_attr("1", 1, hwmon_device_ctrl_path); if (ret < 0) { mutex_unlock(&diag_mutex); return 1; } mutex_unlock(&diag_mutex); } return 1; } ssize_t status_led_change(const char *path1, const char *tmp1, const char *path2, const char *tmp2) { ssize_t ret; ret = inventec_store_attr(tmp1, strlen(tmp1), path1); if (ret < 0) { return ret; } ret = inventec_store_attr(tmp2, strlen(tmp2), path2); if (ret < 0) { return ret; } if ((ret = status_led_diag_mode_enable()) <= 0) { return ret; } ssleep(1); if ((ret = status_led_diag_mode_disable()) <= 0) { return ret; } return ret; } ssize_t status_led_red(const char *freq) { ssize_t ret; ret = inventec_store_attr("0", 1, &status_led_grn_path[0]); if (ret < 0) { return ret; } ret = inventec_store_attr(freq, strlen(freq), &status_led_red_path[0]); if (ret < 0) { return ret; } if ((ret = status_led_diag_mode_enable()) <= 0) { return ret; } ssleep(1); if ((ret = status_led_diag_mode_disable()) <= 0) { return ret; } return ret; } ssize_t status_led_grn(const char *freq) { ssize_t ret; ret = inventec_store_attr("0", 1, &status_led_red_path[0]); if (ret < 0) { return ret; } ret = inventec_store_attr(freq, strlen(freq), &status_led_grn_path[0]); if (ret < 0) { return ret; } if ((ret = status_led_diag_mode_enable()) <= 0) { return ret; } ssleep(1); if ((ret = status_led_diag_mode_disable()) <= 0) { return ret; } return ret; } static int status_led_diag_mode = STATUS_LED_MODE_AUTO; int status_led_check_diag_mode(void) { return status_led_diag_mode; } //status led: FAN & PSU ssize_t status_fan_led(const char *freq) { ssize_t ret; ret = inventec_store_attr(freq, strlen(freq), &status_led_fan_path[0]); if (ret < 0) { return ret; } if ((ret = status_led_diag_mode_enable()) <= 0) { return ret; } ssleep(1); if ((ret = status_led_diag_mode_disable()) <= 0) { return ret; } return ret; } ssize_t status_psu_led(const char *freq) { ssize_t ret; ret = inventec_store_attr(freq, strlen(freq), &status_led_psu_path[0]); if (ret < 0) { return ret; } if ((ret = status_led_diag_mode_enable()) <= 0) { return ret; } ssleep(1); if ((ret = status_led_diag_mode_disable()) <= 0) { return ret; } return ret; } /* End of ledinfo_device */ static int __init led_device_init(void) { #ifdef INV_PTHREAD_KERNEL_MODULE sysfs_led_path_init(); #endif return 0; } static void __exit led_device_exit(void) { printk(KERN_INFO "[p_thread] Remove led module.\n"); } /* sensor device **********************************/ #define SENSOR_DEV_PATH_SWITCH_TEMP "/sys/class/hwmon/hwmon%d/device/switch_tmp" static char sensor_dev_path_switch_temp[MAX_PATH_SIZE]; void sysfs_sensor_path_init(void) { sprintf(&sensor_dev_path_switch_temp[0], SENSOR_DEV_PATH_SWITCH_TEMP, get_hwm_psoc()); } void switch_temp_update(void) { char buf[MIN_ACC_SIZE]; ssize_t count = inventec_show_attr(&buf[0], "proc/switch/temp"); if (count > 0) { //printk(KERN_ERR "[p_thread] [STEMP] Switch temperature is out of range: %d\n", stemp); inventec_store_attr(&buf[0], count, sensor_dev_path_switch_temp); } } /**************************************************/ /* From system_device */ static int inv_pthread_control = 1; int thread_control(void) { return inv_pthread_control; } void thread_control_set(int val) { inv_pthread_control = val; } /* End system_device */ #define THREAD_SLEEP_MINS (3) #define THREAD_DELAY_MINS (THREAD_SLEEP_MINS + THREAD_SLEEP_MINS + 1) extern void psu_get_voltin(void); static struct task_struct *thread_st; static int thread_data; #ifdef SWITCH_HEALTH_LED_CHANGE_VIA_GPIO void led_set_gpio_to_change_status_led(void) { ssize_t ret = inventec_store_attr("253", 2, "/sys/class/gpio/export"); if (ret < 0) { SYSFS_LOG("[p_thread] Write 253 to /sys/class/gpio/export failed\n"); return; } printk("[p_thread] Write 253 to /sys/class/gpio/export\n"); ret = inventec_store_attr("out", 3, "/sys/class/gpio/gpio253/direction"); if (ret < 0) { SYSFS_LOG("[p_thread] Write low to /sys/class/gpio/gpio253/direction failed\n"); return; } //pull high and then low ret = inventec_store_attr("1", 1, "sys/class/gpio/gpio253/value"); if (ret < 0) { SYSFS_LOG("[p_thread] Write 1 to sys/class/gpio/gpio253/value failed\n"); } //pull low ret = inventec_store_attr("0", 1, "sys/class/gpio/gpio253/value"); if (ret < 0) { SYSFS_LOG("[p_thread] Write 0 to sys/class/gpio/gpio253/value failed\n"); } SYSFS_LOG("[p_thread] Set gpio to support status led change successfully\n"); } #endif // Function executed by kernel thread static int thread_fn(void *unused) { bool fan_issue = false; bool psu_issue = false; bool psu_number_issue = false; /* Delay for guarantee HW ready */ ssleep(THREAD_DELAY_MINS); #ifndef INV_PTHREAD_KERNEL_MODULE sysfs_led_path_init(); sysfs_fan_path_init(); sysfs_psu_path_init(); #endif //sysfs_sensor_path_init(); /* Default status init */ status_led_grn("7"); psu_get_voltin(); #ifdef SWITCH_HEALTH_LED_CHANGE_VIA_GPIO led_set_gpio_to_change_status_led(); #endif while (1) { ssleep(THREAD_SLEEP_MINS); if (thread_control() == 0) { printk(KERN_INFO "[p_thread] %s/%d: Thread Stop by inv_pthread control\n",__func__,__LINE__); break; } if (status_led_check_diag_mode() == STATUS_LED_MODE_MANU) { /* status led in change color/freq mode, higher priority. Ignore fans sttaus */ continue; } //switch_temp_update(); if (fans_control() > 0) //has at least one failed fan { psus_control(0); //now we have separate led for FAN and PSU fan_issue = true; } if (psus_control(1) > 0) { psu_issue = true; } if (status_led_psu_control() > 0) { psu_number_issue = true; } if(fan_issue || psu_issue || psu_number_issue) { continue; } if (status_led_check_color() != STATUS_LED_GRN7) { /* status led red, change it to green */ status_led_grn("7"); } } #ifndef INV_PTHREAD_KERNEL_MODULE err_inv_pthread_fn_1: #endif do_exit(0); printk(KERN_INFO "[p_thread] %s/%d: Thread Stopped\n",__func__,__LINE__); return 0; } static ssize_t s_show(struct kobject *kobj, struct attribute *attr, char *buf) { int fan_absence; size_t count; fan_absence = fans_control(); count += sprintf(&buf[count], "%d\n", fan_absence); return count; } static ssize_t s_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { return count; } static struct attribute status_att = { .name = "fan_absence", .mode = 0777, }; static const struct sysfs_ops status_ops = { .show = s_show, .store = s_store, }; static struct kobj_type status_ktype = { .sysfs_ops = &status_ops, }; static int __init inv_pthread_init(void) { int retval; status_kobj = kzalloc(sizeof(*status_kobj), GFP_KERNEL); if(!status_kobj) return PTR_ERR(status_kobj); status_kset = kset_create_and_add("platform_status", NULL, kernel_kobj); if(!status_kset) return -1; status_kobj->kset = status_kset; retval = kobject_init_and_add(status_kobj, &status_ktype, NULL, "fan"); if(retval) return retval; retval = sysfs_create_file(status_kobj, &status_att); inventec_class_init(); fan_device_init(); psu_device_init(); led_device_init(); thread_control_set(1); printk(KERN_INFO "[p_thread] %s/%d: Creating Thread\n",__func__,__LINE__); //Create the kernel thread with name 'inv_pthread' thread_st = kthread_run(thread_fn, (void*)&thread_data, "inv_pthread"); if (thread_st) printk(KERN_INFO "[p_thread] inv_pthread Created successfully\n"); else printk(KERN_ERR "[p_thread] inv_pthread creation failed\n"); return retval; } static void __exit inv_pthread_exit(void) { thread_control_set(0); /* Delay for guarantee thread exit */ ssleep(THREAD_DELAY_MINS); fan_device_exit(); psu_device_exit(); led_device_exit(); inventec_class_exit(); sysfs_remove_file(status_kobj, &status_att); kset_unregister(status_kset); kobject_del(status_kobj); printk(KERN_INFO "[p_thread] inv_pthread cleaning Up\n"); } module_init(inv_pthread_init); module_exit(inv_pthread_exit); MODULE_AUTHOR("Robert "); MODULE_DESCRIPTION("Inventec Platform Management Thread"); MODULE_VERSION("version 1.0"); MODULE_LICENSE("GPL");