/* * Copyright (C) 2015, 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 #include #include #include "ssp_sysfs.h" #include "sensors_core.h" #include "ssp_cmd_define.h" #include "ssp_type_define.h" #include "ssp_comm.h" #include "ssp_data.h" #include "ssp_debug.h" #include "ssp_platform.h" #include "ssp_sensor_dump.h" #include "./factory/ssp_sensor.h" #include "./factory/ssp_factory.h" #include "ssp_dump.h" #include "ssp_iio.h" #include "ssp_scontext.h" #include int enable_legacy_sensor(struct ssp_data *data, unsigned int type) { int ret = 0; u8 buf[8] = {0,}; s32 max_report_latency = 0; s32 sampling_period = 0; if (type == SENSOR_TYPE_PROXIMITY) { #ifdef CONFIG_SENSORS_SSP_PROXIMITY set_proximity_threshold(data); #ifdef CONFIG_SENSORS_SSP_PROXIMITY_MODIFY_SETTINGS set_proximity_setting_mode(data); #endif #endif #ifdef CONFIG_SENSORS_SSP_LIGHT } else if (type == SENSOR_TYPE_LIGHT) data->light_log_cnt = 0; else if (type == SENSOR_TYPE_LIGHT_CCT) data->light_cct_log_cnt = 0; else if (type == SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS) { data->light_ab_log_cnt = 0; #if defined(CONFIG_SENSORS_SSP_CAMERALIGHT_FOR_TAB) data->low_lux_mode = 0; #endif #endif } sampling_period = data->delay[type].sampling_period; max_report_latency = data->delay[type].max_report_latency; memcpy(&buf[0], &sampling_period, 4); memcpy(&buf[4], &max_report_latency, 4); ssp_infof("ADD %s , type %d sampling %d report %d ", data->info[type].name, type, sampling_period, max_report_latency); data->latest_timestamp[type] = get_current_timestamp(); ret = enable_sensor(data, type, buf, sizeof(buf)); return ret; } int disable_legacy_sensor(struct ssp_data *data, unsigned int type) { int ret = 0; u8 buf[4] = {0, }; int sampling_period = data->delay[type].sampling_period; #ifdef CONFIG_SENSORS_SSP_LIGHT if (type == SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS && data->camera_lux_en) { data->camera_lux_en = false; report_camera_lux_data(data, CAMERA_LUX_DISABLE); } #endif memcpy(&buf[0], &sampling_period, 4); ssp_infof("REMOVE %s, type %d", data->info[type].name, type); ret = disable_sensor(data, type, buf, sizeof(buf)); if (ret >= 0) set_delay_legacy_sensor(data, type, DEFAULT_POLLING_DELAY, 0); return ret; } int set_delay_legacy_sensor(struct ssp_data *data, unsigned int type, int sampling_period, int max_report_latency) { int ret = 0; u8 buf[8] = {0,}; int delay_ms, timeout_ms = 0; delay_ms = data->delay[type].sampling_period; timeout_ms = data->delay[type].max_report_latency; data->delay[type].sampling_period = sampling_period; data->delay[type].max_report_latency = max_report_latency; if (data->en_info[type].enabled && (delay_ms != data->delay[type].sampling_period || timeout_ms != data->delay[type].max_report_latency)) { ssp_infof("CHANGE RATE %s, type %d(%d, %d)", data->info[type].name, type, sampling_period, max_report_latency); memcpy(&buf[0], &sampling_period, 4); memcpy(&buf[4], &max_report_latency, 4); ret = ssp_send_command(data, CMD_CHANGERATE, type, 0, 0, buf, sizeof(buf), NULL, NULL); } return ret; } static ssize_t show_sensors_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%llu\n", (uint64_t)atomic64_read(&data->sensor_en_state)); } static ssize_t set_sensors_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { uint64_t new_state = 0, type = 0; bool new_enable = 0, old_enable = 0; struct ssp_data *data = dev_get_drvdata(dev); int ret = 0, temp = 0; mutex_lock(&data->enable_mutex); if (kstrtoint(buf, 10, &temp) < 0) { mutex_unlock(&data->enable_mutex); return -EINVAL; } type = temp / 10; new_enable = temp % 10; if (type >= LEGACY_SENSOR_MAX || (temp % 10) > 1) { ssp_errf("type = (%d) or new_enable = (%d) is wrong.", type, (temp % 10)); mutex_unlock(&data->enable_mutex); return -EINVAL; } old_enable = (atomic64_read(&data->sensor_en_state) & (1ULL << type)) ? 1 : 0; if (new_enable != old_enable) { new_state = atomic64_read(&data->sensor_en_state); if (new_enable) ret = enable_legacy_sensor(data, (unsigned int) type); else ret = disable_legacy_sensor(data, (unsigned int) type); if (data->en_info[type].enabled) new_state |= (1ULL << type); else new_state = new_state & (~(1ULL << type)); atomic64_set(&data->sensor_en_state, new_state); } else ssp_infof("type = %d is already enable/disabled = %d", type, new_enable); mutex_unlock(&data->enable_mutex); return (ret == 0) ? size : ret; } ssize_t mcu_update_kernel_bin_show(struct device *dev, struct device_attribute *attr, char *buf) { bool is_success = true; int ret = 0; struct ssp_data *data = dev_get_drvdata(dev); ssp_infof("mcu binany update!"); ret = sensorhub_firmware_download(data); if (!ret) is_success = false; return sprintf(buf, "%s\n", (is_success ? "OK" : "NG")); } ssize_t mcu_update_kernel_crashed_bin_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "OK\n"); } ssize_t mcu_reset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); bool is_success = false; int ret = 0; int prev_reset_cnt; prev_reset_cnt = data->cnt_reset; reset_mcu(data, RESET_TYPE_KERNEL_SYSFS); ret = ssp_wait_event_timeout(&data->reset_lock, 2000); ssp_infof(""); if (ret == SUCCESS && is_sensorhub_working(data) && prev_reset_cnt != data->cnt_reset) is_success = true; return sprintf(buf, "%s\n", (is_success ? "OK" : "NG")); } int flush(struct ssp_data *data, u8 sensor_type) { int ret = 0; if (sensor_type == SENSOR_TYPE_SCONTEXT) return SUCCESS; ret = ssp_send_command(data, CMD_GETVALUE, sensor_type, SENSOR_FLUSH, 0, NULL, 0, NULL, NULL); if (ret != SUCCESS) { ssp_errf("fail %d", ret); return ERROR; } return SUCCESS; } static ssize_t set_flush(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int64_t dTemp; u8 sensor_type = 0; struct ssp_data *data = dev_get_drvdata(dev); if (kstrtoll(buf, 10, &dTemp) < 0) return -EINVAL; sensor_type = (u8)dTemp; if (!(atomic64_read(&data->sensor_en_state) & (1ULL << sensor_type))) { ssp_infof("ssp sensor is not enabled(%d)", sensor_type); return -EINVAL; } if (flush(data, sensor_type) < 0) { ssp_errf("ssp returns error for flush(%x)", sensor_type); return -EINVAL; } return size; } static ssize_t show_debug_enable(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", data->debug_enable); } static ssize_t set_debug_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct ssp_data *data = dev_get_drvdata(dev); int64_t debug_enable; if (kstrtoll(buf, 10, &debug_enable) < 0) return -EINVAL; if (debug_enable != 1 && debug_enable != 0) return -EINVAL; data->debug_enable = (bool)debug_enable; return size; } static ssize_t show_sensor_axis(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d: %d\n%d: %d\n%d: %d\n", #ifdef CONFIG_SENSORS_SSP_ACCELOMETER SENSOR_TYPE_ACCELEROMETER, data->accel_position, #else SENSOR_TYPE_ACCELEROMETER, -1, #endif #ifdef CONFIG_SENSORS_SSP_GYROSCOPE SENSOR_TYPE_GYROSCOPE, data->gyro_position, #else SENSOR_TYPE_GYROSCOPE, -1, #endif #ifdef CONFIG_SENSOR_SSP_MAGNETIC SENSOR_TYPE_GEOMAGNETIC_FIELD, data->mag_position #else SENSOR_TYPE_GEOMAGNETIC_FIELD, -1 #endif ); } static ssize_t set_sensor_axis(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct ssp_data *data = dev_get_drvdata(dev); int sensor = 0; int position = 0; int ret = 0; sscanf(buf, "%9d,%9d", &sensor, &position); if (position < 0 || position > 7) return -EINVAL; if (sensor == SENSOR_TYPE_ACCELEROMETER) { #ifdef CONFIG_SENSORS_SSP_ACCELOMETER data->accel_position = position; #else ssp_errf("type %d is not suppoerted", sensor); return -EINVAL; #endif } else if (sensor == SENSOR_TYPE_GYROSCOPE) { #ifdef CONFIG_SENSORS_SSP_GYROSCOPE data->gyro_position = position; #else ssp_errf("type %d is not suppoerted", sensor); return -EINVAL; #endif } else if (sensor == SENSOR_TYPE_GEOMAGNETIC_FIELD) { #ifdef CONFIG_SENSOR_SSP_MAGNETIC data->mag_position = position; #else ssp_errf("type %d is not suppoerted", sensor); return -EINVAL; #endif } else return -EINVAL; ret = set_sensor_position(data); if (ret < 0) { ssp_errf("set_sensor_position failed"); return -EIO; } return size; } static ssize_t show_sensor_state(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); return sprintf(buf, "%s\n", data->sensor_state); } static ssize_t show_reset_info(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); ssize_t ret = 0; if (data->reset_type == RESET_TYPE_KERNEL_NO_EVENT) ret = sprintf(buf, "Kernel No Event\n"); else if (data->reset_type == RESET_TYPE_KERNEL_COM_FAIL) ret = sprintf(buf, "Com Fail\n"); else if (data->reset_type == RESET_TYPE_HUB_CRASHED) ret = sprintf(buf, "HUB Reset\n"); else if (data->reset_type == RESET_TYPE_HUB_NO_EVENT) ret = sprintf(buf, "Hub Req No Event\n"); data->reset_type = RESET_TYPE_MAX; return ret; } #define TIMEINFO_SIZE 50 #define SUPPORT_SENSORLIST = {SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, \ SENSOR_TYPE_GEOMAGNETIC_FIELD, SENSOR_TYPE_PRESSURE, \ SENSOR_TYPE_PROXIMITY, SENSOR_TYPE_LIGHT}; static ssize_t sensor_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); int types[] = SENSOR_DUMP_SENSOR_LIST; char str_no_sensor_dump[] = "there is no sensor dump"; int i = 0, ret; char *sensor_dump; char temp[sensor_dump_length(DUMPREGISTER_MAX_SIZE) + LENGTH_SENSOR_TYPE_MAX + 2] = {0,}; char time_temp[TIMEINFO_SIZE] = ""; char *time_info; int cnt = 0; sensor_dump = (char *)kzalloc((sensor_dump_length(DUMPREGISTER_MAX_SIZE) + LENGTH_SENSOR_TYPE_MAX + 3) * (sizeof(types) / sizeof(types[0])), GFP_KERNEL); for (i = 0; i < sizeof(types) / sizeof(types[0]); i++) { if (data->sensor_dump[types[i]] != NULL) { snprintf(temp, (int)strlen(data->sensor_dump[types[i]]) + LENGTH_SENSOR_TYPE_MAX + 3, "%3d\n%s\n\n", types[i], data->sensor_dump[types[i]]); /* %3d -> 3 : LENGTH_SENSOR_TYPE_MAX */ strcpy(&sensor_dump[(int)strlen(sensor_dump)], temp); } } for (i = 0; i < SS_SENSOR_TYPE_MAX; i++) { if (data->en_info[i].regi_time.timestamp != 0) cnt ++; } time_info = (char *)kzalloc(TIMEINFO_SIZE * 3 * cnt, GFP_KERNEL); for (i = 0; i < SS_SENSOR_TYPE_MAX; i++) { if (data->en_info[i].regi_time.timestamp != 0) { struct rtc_time regi_tm = data->en_info[i].regi_time.tm; struct rtc_time unregi_tm = data->en_info[i].unregi_time.tm; char name[SENSOR_NAME_MAX_LEN] = ""; if (i < SENSOR_TYPE_MAX) memcpy(name, data->info[i].name, SENSOR_NAME_MAX_LEN); else get_ss_sensor_name(data, i, name, SENSOR_NAME_MAX_LEN); memset(time_temp, 0, sizeof(time_temp)); snprintf(time_temp, TIMEINFO_SIZE, "%3d %s\n", i, name); strcpy(&time_info[(int)strlen(time_info)], time_temp); if (data->en_info[i].enabled) { if (data->en_info[i].unregi_time.timestamp != 0) { snprintf(time_temp, TIMEINFO_SIZE, "- %04d%02d%02d %02d:%02d:%02d UTC(%llu)\n", unregi_tm.tm_year + 1900, unregi_tm.tm_mon + 1, unregi_tm.tm_mday, unregi_tm.tm_hour, unregi_tm.tm_min, unregi_tm.tm_sec, data->en_info[i].unregi_time.timestamp); strcpy(&time_info[(int)strlen(time_info)], time_temp); } snprintf(time_temp, TIMEINFO_SIZE, "+ %04d%02d%02d %02d:%02d:%02d UTC(%llu)\n", regi_tm.tm_year + 1900, regi_tm.tm_mon + 1, regi_tm.tm_mday, regi_tm.tm_hour, regi_tm.tm_min, regi_tm.tm_sec, data->en_info[i].regi_time.timestamp); strcpy(&time_info[(int)strlen(time_info)], time_temp); } else { snprintf(time_temp, TIMEINFO_SIZE, "+ %04d%02d%02d %02d:%02d:%02d UTC(%llu)\n", regi_tm.tm_year + 1900, regi_tm.tm_mon + 1, regi_tm.tm_mday, regi_tm.tm_hour, regi_tm.tm_min, regi_tm.tm_sec, data->en_info[i].regi_time.timestamp); strcpy(&time_info[(int)strlen(time_info)], time_temp); if (data->en_info[i].unregi_time.timestamp != 0) { snprintf(time_temp, TIMEINFO_SIZE, "- %04d%02d%02d %02d:%02d:%02d UTC(%llu)\n", unregi_tm.tm_year + 1900, unregi_tm.tm_mon + 1, unregi_tm.tm_mday, unregi_tm.tm_hour, unregi_tm.tm_min, unregi_tm.tm_sec, data->en_info[i].unregi_time.timestamp); strcpy(&time_info[(int)strlen(time_info)], time_temp); } } } } if ((int)strlen(sensor_dump) == 0) ret = snprintf(buf, PAGE_SIZE, "%s%s\n", str_no_sensor_dump, time_info); else ret = snprintf(buf, PAGE_SIZE, "%s%s\n", sensor_dump, time_info); kfree(sensor_dump); kfree(time_info); return ret; } static ssize_t sensor_dump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct ssp_data *data = dev_get_drvdata(dev); int sensor_type, ret; char name[LENGTH_SENSOR_NAME_MAX + 1] = {0,}; sscanf(buf, "%30s", name); /* 30 : LENGTH_SENSOR_NAME_MAX */ if ((strcmp(name, "all")) == 0) { save_ram_dump(data); ret = send_all_sensor_dump_command(data); } else { if (strcmp(name, "accelerometer") == 0) sensor_type = SENSOR_TYPE_ACCELEROMETER; else if (strcmp(name, "gyroscope") == 0) sensor_type = SENSOR_TYPE_GYROSCOPE; else if (strcmp(name, "magnetic") == 0) sensor_type = SENSOR_TYPE_GEOMAGNETIC_FIELD; else if (strcmp(name, "pressure") == 0) sensor_type = SENSOR_TYPE_PRESSURE; else if (strcmp(name, "proximity") == 0) sensor_type = SENSOR_TYPE_PROXIMITY; else if (strcmp(name, "light") == 0) sensor_type = SENSOR_TYPE_LIGHT; else { ssp_errf("is not supported : %s", buf); sensor_type = -1; return -EINVAL; } ret = send_sensor_dump_command(data, sensor_type); } return (ret == SUCCESS) ? size : ret; } static ssize_t ssp_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); save_ram_dump(data); return 0; } #ifdef CONFIG_SSP_ENG_DEBUG int htou8(char input) { int ret = 0; if ('0' <= input && input <= '9') return ret = input - '0'; else if ('a' <= input && input <= 'f') return ret = input - 'a' + 10; else if ('A' <= input && input <= 'F') return ret = input - 'A' + 10; else return 0; } static ssize_t set_make_command(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct ssp_data *data = dev_get_drvdata(dev); int ret = 0; u8 cmd = 0, type = 0, subcmd = 0; char *send_buf = NULL; int send_buf_len = 0; char *input_str, *tmp, *dup_str = NULL; int index = 0, i = 0; ssp_infof("%s", buf); if (strlen(buf) == 0) return size; input_str = kzalloc(strlen(buf) + 1, GFP_KERNEL); memcpy(input_str, buf, strlen(buf)); dup_str = kstrdup(input_str, GFP_KERNEL); while (((tmp = strsep(&dup_str, " ")) != NULL)) { switch (index) { case 0 : if (kstrtou8(tmp, 10, &cmd) < 0) { ssp_errf("invalid cmd(%d)", cmd); goto exit; } break; case 1 : if (kstrtou8(tmp, 10, &type) < 0) { ssp_errf("invalid type(%d)", type); goto exit; } break; case 2 : if (kstrtou8(tmp, 10, &subcmd) < 0) { ssp_errf("invalid subcmd(%d)", subcmd); goto exit; } break; case 3 : if ((strlen(tmp) - 1) % 2 != 0) { ssp_errf("not match buf len(%d) != %d", (int)strlen(tmp), send_buf_len); goto exit; } send_buf_len = (strlen(tmp) - 1) / 2; send_buf = kzalloc(send_buf_len, GFP_KERNEL); for (i = 0; i < send_buf_len; i++) send_buf[i] = (u8)((htou8(tmp[2 * i]) << 4) | htou8(tmp[2 * i + 1])); break; default: goto exit; break; } index++; } if (index < 2) { ssp_errf("need more input"); goto exit; } ret = ssp_send_command(data, cmd, type, subcmd, 0, send_buf, send_buf_len, NULL, NULL); if (ret < 0) { ssp_errf("ssp_send_command failed"); return -EIO; } exit: if (send_buf != NULL) kfree(send_buf); kfree(dup_str); kfree(input_str); return size; } static ssize_t register_rw_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); if (data->register_value[1] == 'r') { return sprintf(buf, "sensor(%d) %c regi(0x%x) val(0x%x) ret(%d)\n", data->register_value[0], data->register_value[1], data->register_value[2], data->register_value[3], data->register_value[4]); } else { if (data->register_value[4] == true) { return sprintf(buf, "sensor(%d) %c regi(0x%x) val(0x%x) SUCCESS\n", data->register_value[0], data->register_value[1], data->register_value[2], data->register_value[3]); } else { return sprintf(buf, "sensor(%d) %c regi(0x%x) val(0x%x) FAIL\n", data->register_value[0], data->register_value[1], data->register_value[2], data->register_value[3]); } } } static ssize_t register_rw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct ssp_data *data = dev_get_drvdata(dev); int index = 0, ret = 0; u8 sensor_type, send_val[2]; char rw_cmd; char input_str[20] = {0,}; char *dup_str = NULL; char *tmp; memcpy(input_str, buf, strlen(buf)); dup_str = kstrdup(input_str, GFP_KERNEL); while (((tmp = strsep(&dup_str, " ")) != NULL)) { switch (index) { case 0 : if (kstrtou8(tmp, 10, &sensor_type) < 0 || (sensor_type >= SENSOR_TYPE_MAX)) { ssp_errf("invalid type(%d)", sensor_type); goto exit; } break; case 1 : if (tmp[0] == 'r' || tmp[0] == 'w') rw_cmd = tmp[0]; else { ssp_errf("invalid cmd(%c)", tmp[0]); goto exit; } break; case 2 : case 3 : if ((strlen(tmp) == 4) && tmp[0] != '0' && tmp[1] != 'x') { ssp_errf("invalid value(0xOO) %s", tmp); goto exit; } send_val[index - 2] = (u8)((htou8(tmp[2]) << 4) | htou8(tmp[3])); break; default: goto exit; break; } index++; } data->register_value[0] = sensor_type; data->register_value[1] = rw_cmd; data->register_value[2] = send_val[0]; if (rw_cmd == 'r') { char *rec_buf = NULL; int rec_buf_len; ret = ssp_send_command(data, CMD_GETVALUE, sensor_type, SENSOR_REGISTER_RW, 1000, send_val, 1, &rec_buf, &rec_buf_len); data->register_value[4] = true; if (ret != SUCCESS) { data->register_value[4] = false; ssp_errf("ssp_send_command fail %d", ret); if (rec_buf != NULL) kfree(rec_buf); goto exit; } if (rec_buf == NULL) { ssp_errf("buffer is null"); ret = -EINVAL; goto exit; } data->register_value[3] = rec_buf[0]; kfree(rec_buf); } else { /* rw_cmd == w */ ret = ssp_send_command(data, CMD_SETVALUE, sensor_type, SENSOR_REGISTER_RW, 0, send_val, 2, NULL, NULL); data->register_value[3] = send_val[1]; data->register_value[4] = true; if (ret != SUCCESS) { data->register_value[4] = false; ssp_errf("ssp_send_command fail %d", ret); goto exit; } } exit: kfree(dup_str); return size; } static DEVICE_ATTR(make_command, S_IWUSR | S_IWGRP, NULL, set_make_command); static DEVICE_ATTR(register_rw, S_IRUGO | S_IWUSR | S_IWGRP, register_rw_show, register_rw_store); #endif /* CONFIG_SSP_REGISTER_RW */ #ifdef CONFIG_SENSORS_SSP_LIGHT static ssize_t set_hall_ic_status(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret = 0; u8 hall_ic = 0; struct ssp_data *data = dev_get_drvdata(dev); if (kstrtou8(buf, 10, &hall_ic) < 0) return -EINVAL; ssp_infof("%d", hall_ic); if (!(data->sensor_probe_state & (1ULL << SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS))) { ssp_infof("light autobrightness sensor is not connected(0x%llx)\n", data->sensor_probe_state); return size; } ret = ssp_send_command(data, CMD_SETVALUE, SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS, HALL_IC_STATUS, 0, (char *)&hall_ic, sizeof(hall_ic), NULL, NULL); if (ret != SUCCESS) { ssp_errf("CMD fail %d\n", ret); return size; } return size; } static DEVICE_ATTR(hall_ic, S_IWUSR | S_IWGRP, NULL, set_hall_ic_status); #endif static ssize_t show_sensor_spec(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); int cnt = 0, i; for (i = 0; i < SENSOR_TYPE_MAX; i++) { if (data->sensor_probe_state & (1ULL << i)) cnt++; } ssp_infof("probed cnt %d spec %d", cnt, data->sensor_spec_size/sizeof(struct sensor_spec_t)); if (cnt == 0) return 0; memcpy(buf, data->sensor_spec, data->sensor_spec_size); return data->sensor_spec_size; } static ssize_t scontext_list_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ssp_data *data = dev_get_drvdata(dev); memcpy(buf, &data->ss_sensor_probe_state, sizeof(data->ss_sensor_probe_state)); return sizeof(data->ss_sensor_probe_state); } /* ssp_sensor sysfs */ static DEVICE_ATTR(mcu_rev, S_IRUGO, mcu_revision_show, NULL); static DEVICE_ATTR(mcu_name, S_IRUGO, mcu_model_name_show, NULL); static DEVICE_ATTR(mcu_update, S_IRUGO, mcu_update_kernel_bin_show, NULL); static DEVICE_ATTR(mcu_update2, S_IRUGO, mcu_update_kernel_crashed_bin_show, NULL); static DEVICE_ATTR(mcu_reset, S_IRUGO, mcu_reset_show, NULL); static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, show_sensors_enable, set_sensors_enable); static DEVICE_ATTR(ssp_flush, S_IWUSR | S_IWGRP, NULL, set_flush); static DEVICE_ATTR(debug_enable, S_IRUGO | S_IWUSR | S_IWGRP, show_debug_enable, set_debug_enable); static DEVICE_ATTR(sensor_axis, S_IRUGO | S_IWUSR | S_IWGRP, show_sensor_axis, set_sensor_axis); static DEVICE_ATTR(sensor_state, S_IRUGO, show_sensor_state, NULL); static DEVICE_ATTR(sensor_dump, S_IRUGO | S_IWUSR | S_IWGRP, sensor_dump_show, sensor_dump_store); static DEVICE_ATTR(reset_info, S_IRUGO, show_reset_info, NULL); static DEVICE_ATTR(ssp_dump, S_IRUGO, ssp_dump_show, NULL); static DEVICE_ATTR(mcu_test, S_IRUGO | S_IWUSR | S_IWGRP, mcu_factorytest_show, mcu_factorytest_store); static DEVICE_ATTR(mcu_sleep_test, S_IRUGO | S_IWUSR | S_IWGRP, mcu_sleep_factorytest_show, mcu_sleep_factorytest_store); static DEVICE_ATTR(sensor_spec, S_IRUGO, show_sensor_spec, NULL); static DEVICE_ATTR(scontext_list, 0444, scontext_list_show, NULL); static struct device_attribute *mcu_attrs[] = { &dev_attr_mcu_rev, &dev_attr_mcu_name, &dev_attr_mcu_reset, &dev_attr_mcu_update, &dev_attr_mcu_update2, &dev_attr_enable, &dev_attr_ssp_flush, &dev_attr_debug_enable, &dev_attr_sensor_axis, &dev_attr_sensor_state, &dev_attr_sensor_dump, #ifdef CONFIG_SSP_ENG_DEBUG &dev_attr_make_command, &dev_attr_register_rw, #endif &dev_attr_reset_info, &dev_attr_ssp_dump, &dev_attr_mcu_test, &dev_attr_mcu_sleep_test, #ifdef CONFIG_SENSORS_SSP_LIGHT &dev_attr_hall_ic, #endif &dev_attr_sensor_spec, &dev_attr_scontext_list, NULL, }; static void initialize_mcu_factorytest(struct ssp_data *data) { sensors_device_register(&data->mcu_device, data, mcu_attrs, "ssp_sensor"); } static void remove_mcu_factorytest(struct ssp_data *data) { sensors_unregister(data->mcu_device, mcu_attrs); } /* batch io */ #define BATCH_IOCTL_MAGIC 0xFC struct batch_config { int64_t timeout; int64_t delay; int flag; }; static long ssp_batch_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ssp_data *data = container_of(file->private_data, struct ssp_data, batch_io_device); struct batch_config batch; void __user *argp = (void __user *)arg; int retries = 2; int ret = ERROR; int sensor_type; int delay_ms, timeout_ms = 0; u8 buf[8]; sensor_type = (cmd & 0xFF); ssp_infof("cmd = 0x%x, sensor_type = %d", cmd, sensor_type); if ((cmd >> 8 & 0xFF) != BATCH_IOCTL_MAGIC) { ssp_err("Invalid BATCH CMD %x", cmd); return -EINVAL; } if (sensor_type >= SENSOR_TYPE_MAX) { ssp_err("Invalid sensor_type %d", sensor_type); return -EINVAL; } if (sensor_type == SENSOR_TYPE_SCONTEXT) return 0; while (retries--) { ret = copy_from_user(&batch, argp, sizeof(batch)); if (likely(!ret)) break; } if (unlikely(ret)) { ssp_err("batch ioctl err(%d)", ret); return -EINVAL; } delay_ms = div_s64(batch.delay, NSEC_PER_MSEC); timeout_ms = div_s64(batch.timeout, NSEC_PER_MSEC); memcpy(&buf[0], &delay_ms, 4); memcpy(&buf[4], &timeout_ms, 4); ret = set_delay_legacy_sensor(data, sensor_type, delay_ms, timeout_ms); ssp_info("batch %d: delay %lld, timeout %lld, ret %d", sensor_type, batch.delay, batch.timeout, ret); if (ret < 0) return -EINVAL; else return 0; } static struct file_operations ssp_batch_fops = { .owner = THIS_MODULE, .open = nonseekable_open, .unlocked_ioctl = ssp_batch_ioctl, }; int initialize_sysfs(struct ssp_data *data) { data->batch_io_device.minor = MISC_DYNAMIC_MINOR; data->batch_io_device.name = "batch_io"; data->batch_io_device.fops = &ssp_batch_fops; if (misc_register(&data->batch_io_device)) goto err_batch_io_dev; initialize_mcu_factorytest(data); #ifdef CONFIG_SENSORS_SSP_ACCELOMETER initialize_accel_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_GYROSCOPE initialize_gyro_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_PROXIMITY initialize_prox_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_LIGHT initialize_light_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_BAROMETER initialize_barometer_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_MAGNETIC initialize_magnetic_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_MOBEAM initialize_mobeam(data); #endif return SUCCESS; err_batch_io_dev: ssp_err("error init sysfs"); return ERROR; } void remove_sysfs(struct ssp_data *data) { ssp_batch_fops.unlocked_ioctl = NULL; misc_deregister(&data->batch_io_device); remove_mcu_factorytest(data); #ifdef CONFIG_SENSORS_SSP_ACCELOMETER remove_accel_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_GYROSCOPE remove_gyro_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_PROXIMITY remove_prox_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_LIGHT remove_light_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_BAROMETER remove_barometer_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_MAGNETIC remove_magnetic_factorytest(data); #endif #ifdef CONFIG_SENSORS_SSP_MOBEAM remove_mobeam(data); #endif destroy_sensor_class(); }