| /* |
| * 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 <linux/miscdevice.h> |
| #include <linux/math64.h> |
| #include <linux/uaccess.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| |
| #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 <linux/rtc.h> |
| |
| 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(); |
| |
| } |