lineage_kernel_xcoverpro/drivers/sensorhub/ssp_sysfs.c

1008 lines
27 KiB
C
Raw Normal View History

2023-06-18 22:53:49 +00:00
/*
* 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();
}