// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021 Samsung Electronics Co., Ltd. * http://www.samsung.com * * DP bigdata * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #define EDID_BUF_SIZE 512 #define ERR_DATA_BUF_SIZE 1024 #define COL_NAME_SIZE 20 enum DP_ITEM_TYPE { INT = 1, HEX = 2, STR = 4, CHR = 8, ERR = 16, }; enum DP_STATUS { STATUS_NO_CONNECTION, STATUS_CONNECTION, STATUS_ERROR_OCCURRED, }; struct bd_item_info { char name[COL_NAME_SIZE]; char type; void *data; int str_max_len; }; struct bd_error_data { int limit; int count; }; static char err_data_buf[ERR_DATA_BUF_SIZE]; static struct bd_item_info item_to_column[BD_ITEM_MAX]; static enum DP_STATUS dp_status; static void secdp_bigdata_save_data(void); static void secdp_bigdata_init_item(enum DP_BD_ITEM_LIST item, char *col_name, enum DP_ITEM_TYPE type, ...); static void secdp_bigdata_init_error(enum DP_BD_ITEM_LIST item, char *col_name, int err_limit); static void secdp_bigdata_save_item_int(enum DP_BD_ITEM_LIST item, int val); static void secdp_bigdata_save_item_hex(enum DP_BD_ITEM_LIST item, int val); static void secdp_bigdata_save_item_char(enum DP_BD_ITEM_LIST item, char val); static void secdp_bigdata_save_item_str(enum DP_BD_ITEM_LIST item, char *val); ssize_t _secdp_bigdata_show(struct class *class, struct class_attribute *attr, char *buf) { if (dp_status == STATUS_NO_CONNECTION) return 0; return scnprintf(buf, ERR_DATA_BUF_SIZE, "%s", err_data_buf); } ssize_t _secdp_bigdata_store(struct class *dev, struct class_attribute *attr, const char *buf, size_t size) { if ((buf[0] | 0x20) == 'c') dp_status = STATUS_NO_CONNECTION; return size; } void secdp_bigdata_init(struct class *dp_class) { secdp_bigdata_init_item(BD_LINK_CONFIGURE, "LINK_CFG", CHR); secdp_bigdata_init_item(BD_ADAPTER_HWID, "ADT_HWID", HEX); secdp_bigdata_init_item(BD_ADAPTER_FWVER, "ADT_FWVER", HEX); secdp_bigdata_init_item(BD_ADAPTER_TYPE, "ADT_TYPE", STR, 20); secdp_bigdata_init_item(BD_MAX_LANE_COUNT, "MLANE_CNT", INT); secdp_bigdata_init_item(BD_MAX_LINK_RATE, "MLINK_RATE", INT); secdp_bigdata_init_item(BD_CUR_LANE_COUNT, "CLANE_CNT", INT); secdp_bigdata_init_item(BD_CUR_LINK_RATE, "CLINK_RATE", INT); secdp_bigdata_init_item(BD_HDCP_VER, "HDCP_VER", STR, 10); secdp_bigdata_init_item(BD_ORIENTATION, "ORIENTATION", STR, 10); secdp_bigdata_init_item(BD_RESOLUTION, "RESOLUTION", STR, 20); secdp_bigdata_init_item(BD_EDID, "EDID", STR, EDID_BUF_SIZE); secdp_bigdata_init_item(BD_ADT_VID, "ADT_VID", HEX); secdp_bigdata_init_item(BD_ADT_PID, "ADT_PID", HEX); secdp_bigdata_init_item(BD_DP_MODE, "DP_MODE", STR, 10); secdp_bigdata_init_item(BD_SINK_NAME, "SINK_NAME", STR, 14); secdp_bigdata_init_item(BD_AUD_CH, "AUD_CH", INT); secdp_bigdata_init_item(BD_AUD_FREQ, "AUD_FREQ", INT); secdp_bigdata_init_item(BD_AUD_BIT, "AUD_BIT", INT); secdp_bigdata_init_error(ERR_AUX, "ERR_AUX", 3); secdp_bigdata_init_error(ERR_EDID, "ERR_EDID", 1); secdp_bigdata_init_error(ERR_HDCP_AUTH, "ERR_HDCP", 5); secdp_bigdata_init_error(ERR_LINK_TRAIN, "ERR_LT_TRAIN", 1); secdp_bigdata_init_error(ERR_INF_IRQHPD, "ERR_INF_IRQHPD", 10); } static void secdp_bigdata_init_item_str(enum DP_BD_ITEM_LIST item, char *val, int max_len) { kfree(item_to_column[item].data); item_to_column[item].data = kzalloc(max_len + 1, GFP_KERNEL); if (!item_to_column[item].data) return; item_to_column[item].str_max_len = max_len; strlcpy((char *)item_to_column[item].data, val, max_len + 1); } static void secdp_bigdata_init_item(enum DP_BD_ITEM_LIST item, char *col_name, enum DP_ITEM_TYPE type, ...) { va_list vl; va_start(vl, type); strlcpy(item_to_column[item].name, col_name, COL_NAME_SIZE); item_to_column[item].type = type; switch (type) { case INT: case HEX: secdp_bigdata_save_item_int(item, -1); break; case STR: secdp_bigdata_init_item_str(item, "X", (int)va_arg(vl, int)); break; case CHR: secdp_bigdata_save_item_char(item, 'X'); break; default: break; } va_end(vl); } static void secdp_bigdata_init_error(enum DP_BD_ITEM_LIST item, char *col_name, int err_limit) { struct bd_error_data *err = kzalloc(sizeof(struct bd_error_data), GFP_KERNEL); if (err) err->limit = err_limit; strlcpy(item_to_column[item].name, col_name, COL_NAME_SIZE); item_to_column[item].type = ERR; item_to_column[item].data = err; } static void secdp_bigdata_save_item_int(enum DP_BD_ITEM_LIST item, int val) { if (!item_to_column[item].data) { item_to_column[item].data = kzalloc(sizeof(int), GFP_KERNEL); if (!item_to_column[item].data) return; } *((int *)item_to_column[item].data) = val; } static void secdp_bigdata_save_item_hex(enum DP_BD_ITEM_LIST item, int val) { secdp_bigdata_save_item_int(item, val); } static void secdp_bigdata_save_item_char(enum DP_BD_ITEM_LIST item, char val) { if (!item_to_column[item].data) { item_to_column[item].data = kzalloc(sizeof(char), GFP_KERNEL); if (!item_to_column[item].data) return; } *((char *)item_to_column[item].data) = val; } static void secdp_bigdata_save_item_str(enum DP_BD_ITEM_LIST item, char *val) { if (!item_to_column[item].data || !val) return; if (item == BD_EDID && val[0] != 'X') { int ret = 0; int i; int ext_blk_cnt = val[0x7e] ? 1 : 0; int edid_size = 128 * (ext_blk_cnt + 1); for (i = 0; i < edid_size; i++) { ret += scnprintf(((char *)item_to_column[item].data) + ret, EDID_BUF_SIZE + 1 - ret, "%02x", val[i]); } } else { strlcpy((char *)item_to_column[item].data, val, item_to_column[item].str_max_len + 1); } } void secdp_bigdata_save_item(enum DP_BD_ITEM_LIST item, ...) { va_list vl; if (item >= BD_ITEM_MAX || item < 0) return; va_start(vl, item); switch (item_to_column[item].type) { case INT: secdp_bigdata_save_item_hex(item, (int)va_arg(vl, int)); break; case HEX: secdp_bigdata_save_item_int(item, (int)va_arg(vl, int)); break; case STR: secdp_bigdata_save_item_str(item, (char *)va_arg(vl, char *)); break; case CHR: secdp_bigdata_save_item_char(item, (char)va_arg(vl, int)); break; default: break; } va_end(vl); } void secdp_bigdata_inc_error_cnt(enum DP_BD_ITEM_LIST err) { if (err >= BD_ITEM_MAX || err < 0) return; if (item_to_column[err].data && item_to_column[err].type == ERR) ((struct bd_error_data *)item_to_column[err].data)->count++; } void secdp_bigdata_clr_error_cnt(enum DP_BD_ITEM_LIST err) { if (err >= BD_ITEM_MAX || err < 0) return; if (item_to_column[err].data && item_to_column[err].type == ERR) ((struct bd_error_data *)item_to_column[err].data)->count = 0; } static void secdp_bigdata_save_data(void) { int i; int ret = 0; for (i = 0; i < BD_ITEM_MAX; i++) { switch (item_to_column[i].type) { case INT: ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret, "\"%s\":\"%d\",", item_to_column[i].name, (item_to_column[i].data != NULL) ? *((int *)item_to_column[i].data) : -1); break; case HEX: ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret, "\"%s\":\"0x%x\",", item_to_column[i].name, (item_to_column[i].data != NULL) ? *((int *)item_to_column[i].data) : -1); break; case STR: ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret, "\"%s\":\"%s\",", item_to_column[i].name, (item_to_column[i].data != NULL) ? (char *)item_to_column[i].data : "X"); break; case CHR: ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret, "\"%s\":\"%c\",", item_to_column[i].name, (item_to_column[i].data != NULL) ? *((char *)item_to_column[i].data) : 'X'); break; case ERR: ret += scnprintf(err_data_buf + ret, ERR_DATA_BUF_SIZE - ret, "\"%s\":\"%d\",", item_to_column[i].name, (item_to_column[i].data != NULL) ? ((struct bd_error_data *)item_to_column[i].data)->count : 0); break; default: break; } } if (ret > 0) err_data_buf[ret - 1] = '\n'; } static int secdp_bigdata_check_err(void) { int i; struct bd_error_data *e_data; for (i = 0; i < BD_ITEM_MAX; i++) { if (item_to_column[i].type == ERR) { e_data = item_to_column[i].data; if (e_data != NULL && e_data->count >= e_data->limit) return 1; } } return 0; } void secdp_bigdata_connection(void) { int i; if (dp_status != STATUS_ERROR_OCCURRED) dp_status = STATUS_CONNECTION; for (i = 0; i < BD_ITEM_MAX; i++) { switch (item_to_column[i].type) { case INT: case HEX: secdp_bigdata_save_item_int(i, -1); break; case STR: secdp_bigdata_save_item_str(i, "X"); break; case CHR: secdp_bigdata_save_item_char(i, 'X'); break; case ERR: secdp_bigdata_clr_error_cnt(i); break; default: break; } } } void secdp_bigdata_disconnection(void) { if (secdp_bigdata_check_err()) { dp_status = STATUS_ERROR_OCCURRED; secdp_bigdata_save_data(); } if (dp_status != STATUS_ERROR_OCCURRED) secdp_bigdata_save_data(); }