/* * Copyright (C) 2020, Samsung Electronics Co. Ltd. All Rights Reserved. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #if IS_ENABLED(CONFIG_SEC_DEBUG) #include #endif #include #include "../comm/shub_comm.h" #include "../sensormanager/shub_sensor.h" #include "../sensormanager/shub_sensor_manager.h" #include "../sensormanager/shub_sensor_type.h" #include "../sensorhub/shub_device.h" #include "../sensorhub/shub_firmware.h" #include "../utility/shub_utility.h" #include "../utility/shub_file_manager.h" #include "shub_debug.h" static struct timer_list debug_timer; static struct workqueue_struct *debug_wq; static struct work_struct work_debug; #ifdef CONFIG_SHUB_DEBUG static struct workqueue_struct *print_log_wq; static struct work_struct work_print_log; static struct list_head print_log_list; #endif static int check_noevent_reset_cnt; static char open_cal_result[9]; #define SHUB_DEBUG_TIMER_SEC (5 * HZ) /* * check_sensor_event * - return true : there is no accel or light sensor event over 5sec when sensor is registered */ static void check_no_event(void) { u64 timestamp = get_current_timestamp(); int type, ret; struct shub_sensor *sensor; struct sensor_event *event; bool check_reset = false; char buffer[9] = {0,}; if (check_noevent_reset_cnt >= 0 && check_noevent_reset_cnt == get_reset_count()) check_reset = true; check_noevent_reset_cnt = -1; for (type = 0 ; type < SENSOR_TYPE_LEGACY_MAX ; type++) { sensor = get_sensor(type); if (!sensor) continue; event = get_sensor_event(type); if (sensor->report_mode_continuous && sensor->enabled && sensor->max_report_latency == 0 && MAX(sensor->enable_timestamp, sensor->change_timestamp) + 5000000000ULL < timestamp && event->received_timestamp + 5000000000ULL < timestamp) { shub_infof("sensor(%d) %lld(%lld), cur = %lld en = %lld change = %lld", type, event->received_timestamp, event->timestamp, timestamp, sensor->enable_timestamp, sensor->change_timestamp); if (sensor->enable_timestamp < sensor->disable_timestamp) { shub_infof("enable_timestamp invalid: enable_timestamp(%lld) disable_timestamp(%lld)", sensor->enable_timestamp, sensor->disable_timestamp); break; } if (check_reset) { shub_errf("no event, no sensorhub reset"); reset_mcu(RESET_TYPE_KERNEL_NO_EVENT); break; } check_noevent_reset_cnt = get_reset_count(); buffer[0] = type; memcpy(&buffer[1], &(sensor->sampling_period), sizeof(sensor->sampling_period)); ret = shub_send_command(CMD_SETVALUE, TYPE_HUB, NO_EVENT_CHECK, buffer, sizeof(buffer)); if (ret < 0) shub_errf("type %d comm failed ret %d", type, ret); else break; } } } static void debug_work_func(struct work_struct *work) { int type; uint64_t en_state = 0; struct shub_data_t *data = get_shub_data(); en_state = get_sensors_legacy_enable_state(); for (type = 0; type < SENSOR_TYPE_MAX; type++) { if (get_sensor_enabled(type)) print_sensor_debug(type); } shub_infof("FW(%d):%u, Sensor state: 0x%llx, En: 0x%llx, Reset cnt: %d[%d : C %u(%u, %u), N %u, %u, %u]" ", Cal result : [M:%c, P:%c]", get_firmware_type(), get_firmware_rev(), get_sensors_legacy_probe_state(), en_state, data->cnt_reset, data->cnt_shub_reset[RESET_TYPE_MAX], data->cnt_shub_reset[RESET_TYPE_KERNEL_COM_FAIL], get_cnt_comm_fail(), get_cnt_timeout(), data->cnt_shub_reset[RESET_TYPE_KERNEL_NO_EVENT], data->cnt_shub_reset[RESET_TYPE_HUB_NO_EVENT], data->cnt_shub_reset[RESET_TYPE_HUB_REQ_TASK_FAILURE], open_cal_result[SENSOR_TYPE_GEOMAGNETIC_FIELD], open_cal_result[SENSOR_TYPE_PRESSURE]); if (is_shub_working()) check_no_event(); #if defined(CONFIG_SHUB_MTK) && IS_ENABLED(CONFIG_SEC_DEBUG) if (data->hub_crash_timestamp && data->hub_crash_timestamp + 100000000000ULL < get_current_timestamp() ) { shub_infof("hub crash timestamp %llu", data->hub_crash_timestamp); /* only work for debug level is mid */ #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 0, 0) if (!is_debug_level_low()) { shub_infof("panic!"); panic("sensorhub crash error\n"); #else if (SEC_DEBUG_LEVEL(kernel)) { shub_infof("panic!"); panic("sensorhub crash error\n"); #endif } else { shub_infof("debug level is low"); } data->hub_crash_timestamp = 0; } #endif } static void debug_timer_func(struct timer_list *timer) { queue_work(debug_wq, &work_debug); mod_timer(&debug_timer, round_jiffies_up(jiffies + SHUB_DEBUG_TIMER_SEC)); } #ifdef CONFIG_SHUB_DEBUG static void print_log_work_func(struct work_struct *work) { struct list_head *curr; struct print_log_t *log; if (!list_empty(&print_log_list)) { curr = print_log_list.next; log = list_entry(curr, struct print_log_t, list); shub_infof("[%lld] %s", log->timestamp, log->buf); list_del(curr); kfree(log->buf); kfree(log); } else { shub_errf("print_log_list is empty"); } } #endif void set_open_cal_result(int type, int result) { if (result >= 0) open_cal_result[type] = 'P'; else open_cal_result[type] = 'F'; } int init_shub_debug(void) { int type; timer_setup(&debug_timer, debug_timer_func, 0); debug_wq = create_singlethread_workqueue("shub_debug_wq"); if (!debug_wq) return -ENOMEM; INIT_WORK(&work_debug, debug_work_func); #ifdef CONFIG_SHUB_DEBUG print_log_wq = create_singlethread_workqueue("shub_print_log_wq"); if (!print_log_wq) return -ENOMEM; INIT_WORK((struct work_struct *)&work_print_log, print_log_work_func); INIT_LIST_HEAD(&print_log_list); #endif for (type = 0 ; type <= SENSOR_TYPE_PROXIMITY ; type++) open_cal_result[type] = 'N'; return 0; } void exit_shub_debug(void) { del_timer(&debug_timer); cancel_work_sync(&work_debug); destroy_workqueue(debug_wq); } void enable_debug_timer(void) { mod_timer(&debug_timer, round_jiffies_up(jiffies + SHUB_DEBUG_TIMER_SEC)); } void disable_debug_timer(void) { del_timer_sync(&debug_timer); cancel_work_sync(&work_debug); } int print_mcu_debug(char *dataframe, int *index, int frame_len) { u16 length = 0; int cur = *index; memcpy(&length, dataframe + *index, 1); *index += 1; if (length > frame_len - *index || length <= 0) { shub_infof("[M] invalid debug length(%u/%d/%d)", length, frame_len, cur); return -1; } shub_info("[M] %s", &dataframe[*index]); *index += length; return 0; } #ifdef CONFIG_SHUB_DEBUG void print_log_debug(char *buf, int buf_len, u64 timestamp) { struct print_log_t *print_log; print_log = kzalloc(sizeof(struct print_log_t), GFP_KERNEL); print_log->buf = kzalloc(buf_len, GFP_KERNEL); memcpy(print_log->buf, buf, buf_len); print_log->timestamp = timestamp; list_add_tail(&print_log->list, &print_log_list); queue_work(print_log_wq, (struct work_struct *)&work_print_log); } #endif #define SHUB_LOG_MAX_BYTE 200 #define LOG_UNIT_SIZE 3 void print_dataframe(char *dataframe, int frame_len) { char raw_data[SHUB_LOG_MAX_BYTE * LOG_UNIT_SIZE + 1]; int i = 0, cur = 0, size; int len = sizeof(raw_data); while ((frame_len - cur) > 0) { int pr_size = ((frame_len - cur) > SHUB_LOG_MAX_BYTE) ? SHUB_LOG_MAX_BYTE : (frame_len - cur); memset(raw_data, 0, sizeof(raw_data)); for (i = 0, size = 0; i < pr_size; i++) size += snprintf(raw_data + size, len - size, "%02x ", *(dataframe + cur++)); shub_info("(%d/%d) %s", cur, frame_len, raw_data); } } int print_system_info(char *dataframe, int *index, int frame_len) { struct sensor_debug_info { uint8_t uid; uint8_t total_count; uint8_t ext_client; int32_t ext_sampling; int32_t ext_report; int32_t fastest_sampling; }; struct base_timestamp { uint64_t kernel_base; uint64_t hub_base; }; struct utc_time { int8_t nHour; int8_t nMinute; int8_t nSecond; int16_t nMilliSecond; }; struct system_debug_info { int32_t version; int32_t rate; int8_t ap_state; struct utc_time time; struct base_timestamp timestamp; }; //===================================================// struct sensor_debug_info *info = 0; struct system_debug_info *s_info = 0; int i; int count, system_info_len; if (*index + 1 > frame_len) { shub_errf("parsing error"); return -EINVAL; } count = *dataframe; ++dataframe; *index += 1; system_info_len = (sizeof(struct sensor_debug_info) * count + sizeof(struct system_debug_info)); if (*index + system_info_len > frame_len) { shub_errf("parsing error %d", count); return -EINVAL; } *index += system_info_len; shub_info("==system info ==="); for (i = 0; i < count; ++i) { info = (struct sensor_debug_info *)dataframe; shub_info("id(%d), total(%d), external(%d), e_sampling(%d), e_report(%d), fastest(%d)", info->uid, info->total_count, info->ext_client, info->ext_sampling, info->ext_report, info->fastest_sampling); dataframe += sizeof(struct sensor_debug_info); } s_info = (struct system_debug_info *)dataframe; shub_info("version(%d), rate(%d), ap_state(%s), time(%d:%d:%d.%d), base_ts_k(%lld), base_ts_hub(%lld)", s_info->version, s_info->rate, s_info->ap_state == 0 ? "run" : "suspend", s_info->time.nHour, s_info->time.nMinute, s_info->time.nSecond, s_info->time.nMilliSecond, s_info->timestamp.kernel_base, s_info->timestamp.hub_base); return 0; } int log_dump_size; void init_log_dump(void) { shub_infof(""); log_dump_size = 0; shub_file_remove("/data/vendor/sensorhub/hub_log_dump.txt"); } int save_log_dump(char *dataframe, int *index, int frame_len) { u16 length = 0; int ret; shub_infof("%d", log_dump_size); memcpy(&length, dataframe + *index, 1); *index += 1; shub_info("[D] %s", &dataframe[*index]); ret = shub_file_write("/data/vendor/sensorhub/hub_log_dump.txt", dataframe + *index, length, log_dump_size); if (ret > 0) { shub_infof("ret %d length %d", ret, length); log_dump_size += length; } else { shub_errf("save file failed"); } *index += length; return 0; }