498 lines
16 KiB
C
Executable File
498 lines
16 KiB
C
Executable File
/*
|
|
* 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/iio/iio.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/buffer_impl.h>
|
|
#include <linux/iio/events.h>
|
|
#include <linux/iio/kfifo_buf.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/types.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "ssp.h"
|
|
#include "ssp_iio.h"
|
|
#include "ssp_cmd_define.h"
|
|
#include "ssp_data.h"
|
|
#include "ssp_iio.h"
|
|
#include "ssp_scontext.h"
|
|
|
|
#define IIO_CHANNEL -1
|
|
#define IIO_SCAN_INDEX 3
|
|
#define IIO_SIGN 's'
|
|
#define IIO_SHIFT 0
|
|
|
|
#define META_EVENT 0
|
|
#define META_TIMESTAMP 0
|
|
|
|
#define PROX_AVG_READ_NUM 80
|
|
enum
|
|
{
|
|
PROX_RAW_NUM = 0,
|
|
PROX_RAW_MIN,
|
|
PROX_RAW_SUM,
|
|
PROX_RAW_MAX,
|
|
PROX_RAW_DATA_SIZE,
|
|
};
|
|
|
|
#define SCONTEXT_DATA_LEN 56
|
|
#define SCONTEXT_HEADER_LEN 8
|
|
|
|
#define RESET_REASON_KERNEL_RESET 0x01
|
|
#define RESET_REASON_MCU_CRASHED 0x02
|
|
#define RESET_REASON_SYSFS_REQUEST 0x03
|
|
#define RESET_REASON_HUB_REQUEST 0x04
|
|
|
|
static int ssp_preenable(struct iio_dev *indio_dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ssp_predisable(struct iio_dev *indio_dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct iio_buffer_setup_ops ssp_iio_ring_setup_ops = {
|
|
.preenable = &ssp_preenable,
|
|
.predisable = &ssp_predisable,
|
|
};
|
|
|
|
static int ssp_iio_configure_ring(struct iio_dev *indio_dev)
|
|
{
|
|
struct iio_buffer *ring;
|
|
|
|
ring = iio_kfifo_allocate();
|
|
if (!ring) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ring->scan_timestamp = true;
|
|
ring->bytes_per_datum = 8;
|
|
indio_dev->buffer = ring;
|
|
indio_dev->setup_ops = &ssp_iio_ring_setup_ops;
|
|
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ssp_iio_push_buffers(struct iio_dev *indio_dev, u64 timestamp,
|
|
char *data, int data_len)
|
|
{
|
|
char buf[data_len + sizeof(timestamp)];
|
|
|
|
if (!indio_dev || !data) {
|
|
return;
|
|
}
|
|
|
|
memcpy(buf, data, data_len);
|
|
memcpy(buf + data_len, ×tamp, sizeof(timestamp));
|
|
mutex_lock(&indio_dev->mlock);
|
|
iio_push_to_buffers(indio_dev, buf);
|
|
mutex_unlock(&indio_dev->mlock);
|
|
}
|
|
|
|
#ifdef CONFIG_SENSORS_SSP_PROXIMITY
|
|
static void report_prox_raw_data(struct ssp_data *data, int type,
|
|
struct sensor_value *proxrawdata)
|
|
{
|
|
if (data->prox_raw_avg[PROX_RAW_NUM]++ >= PROX_AVG_READ_NUM) {
|
|
data->prox_raw_avg[PROX_RAW_SUM] /= PROX_AVG_READ_NUM;
|
|
data->buf[type].prox_raw[1] = (u16)data->prox_raw_avg[1];
|
|
data->buf[type].prox_raw[2] = (u16)data->prox_raw_avg[2];
|
|
data->buf[type].prox_raw[3] = (u16)data->prox_raw_avg[3];
|
|
|
|
data->prox_raw_avg[PROX_RAW_NUM] = 0;
|
|
data->prox_raw_avg[PROX_RAW_MIN] = 0;
|
|
data->prox_raw_avg[PROX_RAW_SUM] = 0;
|
|
data->prox_raw_avg[PROX_RAW_MAX] = 0;
|
|
} else {
|
|
data->prox_raw_avg[PROX_RAW_SUM] += proxrawdata->prox_raw[0];
|
|
|
|
if (data->prox_raw_avg[PROX_RAW_NUM] == 1) {
|
|
data->prox_raw_avg[PROX_RAW_MIN] = proxrawdata->prox_raw[0];
|
|
} else if (proxrawdata->prox_raw[0] < data->prox_raw_avg[PROX_RAW_MIN]) {
|
|
data->prox_raw_avg[PROX_RAW_MIN] = proxrawdata->prox_raw[0];
|
|
}
|
|
|
|
if (proxrawdata->prox_raw[0] > data->prox_raw_avg[PROX_RAW_MAX]) {
|
|
data->prox_raw_avg[PROX_RAW_MAX] = proxrawdata->prox_raw[0];
|
|
}
|
|
}
|
|
|
|
data->buf[type].prox_raw[0] = proxrawdata->prox_raw[0];
|
|
}
|
|
|
|
static void report_prox_cal_data(struct ssp_data *data, int type,
|
|
struct sensor_value *p_cal_data)
|
|
{
|
|
data->prox_thresh[0] = p_cal_data->prox_cal[0];
|
|
data->prox_thresh[1] = p_cal_data->prox_cal[1];
|
|
ssp_info("prox thresh %u %u", data->prox_thresh[0], data->prox_thresh[1]);
|
|
|
|
proximity_calibration_off(data);
|
|
}
|
|
#endif
|
|
|
|
void report_sensor_data(struct ssp_data *data, int type,
|
|
struct sensor_value *event)
|
|
{
|
|
if (type == SENSOR_TYPE_PROXIMITY) {
|
|
ssp_info("Proximity Sensor Detect : %u, raw : %u",
|
|
event->prox, event->prox_ex);
|
|
#ifdef CONFIG_SENSORS_SSP_PROXIMITY
|
|
} else if (type == SENSOR_TYPE_PROXIMITY_RAW) {
|
|
report_prox_raw_data(data, type, event);
|
|
return;
|
|
} else if (type == SENSOR_TYPE_PROXIMITY_CALIBRATION) {
|
|
report_prox_cal_data(data, type, event);
|
|
return;
|
|
#endif
|
|
} else if (type == SENSOR_TYPE_LIGHT) {
|
|
#ifdef CONFIG_SENSORS_SSP_LIGHT
|
|
if (data->light_log_cnt < 3) {
|
|
ssp_info("Light Sensor : lux=%u brightness=%u r=%d g=%d b=%d c=%d atime=%d again=%d",
|
|
data->buf[SENSOR_TYPE_LIGHT].lux,
|
|
data->buf[SENSOR_TYPE_LIGHT].brightness,
|
|
data->buf[SENSOR_TYPE_LIGHT].r, data->buf[SENSOR_TYPE_LIGHT].g,
|
|
data->buf[SENSOR_TYPE_LIGHT].b,
|
|
data->buf[SENSOR_TYPE_LIGHT].w, data->buf[SENSOR_TYPE_LIGHT].a_time,
|
|
data->buf[SENSOR_TYPE_LIGHT].a_gain);
|
|
data->light_log_cnt++;
|
|
}
|
|
} else if (type == SENSOR_TYPE_LIGHT_CCT) {
|
|
if (data->light_cct_log_cnt < 3) {
|
|
ssp_info("Light cct Sensor : lux=%u r=%d g=%d b=%d c=%d atime=%d again=%d",
|
|
data->buf[SENSOR_TYPE_LIGHT_CCT].lux,
|
|
data->buf[SENSOR_TYPE_LIGHT_CCT].r, data->buf[SENSOR_TYPE_LIGHT_CCT].g,
|
|
data->buf[SENSOR_TYPE_LIGHT_CCT].b,
|
|
data->buf[SENSOR_TYPE_LIGHT_CCT].w, data->buf[SENSOR_TYPE_LIGHT_CCT].a_time,
|
|
data->buf[SENSOR_TYPE_LIGHT_CCT].a_gain);
|
|
data->light_cct_log_cnt++;
|
|
}
|
|
} else if (type == SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS) {
|
|
|
|
#if defined(CONFIG_SENSORS_SSP_CAMERALIGHT_FOR_TAB)
|
|
//ssp_infof("[%d]Light AB Sensor : lux %d ", data->low_lux_mode, data->buf[type].ab_lux);
|
|
|
|
if(data->low_lux_mode > 0 && data->buf[type].ab_lux > data->camera_lux_hysteresis[0]) {
|
|
// this means that environment is bright
|
|
data->low_lux_mode = 0;
|
|
data->report_ab_lux = data->buf[type].ab_lux;
|
|
ssp_infof("Light AB Sensor : low_lux_mode clear (%d)", data->buf[type].ab_lux);
|
|
if(data->camera_lux_en) {
|
|
ssp_infof("Light AB Sensor : report cam disable");
|
|
data->camera_lux_en = false;
|
|
report_camera_lux_data(data, CAMERA_LUX_DISABLE);
|
|
}
|
|
} else if(data->low_lux_mode == 0 && (data->buf[type].ab_lux <= data->camera_lux_hysteresis[0])) {
|
|
if(data->light_ab_log_cnt == 0) {
|
|
ssp_infof("Light AB Sensor : report first lux form light sensor (%d)", data->buf[type].ab_lux);
|
|
report_camera_lux_data(data, data->buf[type].ab_lux);
|
|
}
|
|
|
|
ssp_infof("Light AB Sensor : report cam enable");
|
|
data->camera_lux_en = true;
|
|
data->low_lux_mode = 1;
|
|
data->report_ab_lux = data->buf[type].ab_lux;
|
|
data->camera_lux = -1;
|
|
data->buf[type].ab_lux = CAMERA_LUX_ENABLE;
|
|
} else if(data->camera_lux_en) {
|
|
data->report_ab_lux = data->buf[type].ab_lux;
|
|
if(data->camera_lux < 0) {
|
|
ssp_infof("Light AB Sensor : there is no camera lux(%d), low_lux_mode %d", data->buf[type].ab_lux, data->low_lux_mode);
|
|
} else if(data->low_lux_mode == 1) {
|
|
ssp_infof("Light AB Sensor : high cam lux mode(%d)", data->buf[type].ab_lux);
|
|
}
|
|
return;
|
|
}
|
|
#else
|
|
if(!data->camera_lux_en &&
|
|
(data->buf[type].ab_lux <= data->camera_lux_hysteresis[0]) &&
|
|
((int)data->buf[type].ab_brightness > data->camera_br_hysteresis[0]))
|
|
{
|
|
|
|
if(data->light_ab_log_cnt == 0)
|
|
{
|
|
ssp_infof("Light AB Sensor : report first lux form light sensor");
|
|
report_camera_lux_data(data, data->buf[type].ab_lux);
|
|
}
|
|
|
|
ssp_infof("Light AB Sensor : report cam enable");
|
|
data->camera_lux_en = true;
|
|
data->buf[type].ab_lux = CAMERA_LUX_ENABLE;
|
|
}
|
|
else if(data->camera_lux_en &&
|
|
((data->buf[type].ab_lux >= data->camera_lux_hysteresis[1]) ||
|
|
((int)data->buf[type].ab_brightness <= data->camera_br_hysteresis[1])))
|
|
{
|
|
ssp_infof("Light AB Sensor : report cam disable");
|
|
data->camera_lux_en = false;
|
|
data->buf[type].ab_lux = CAMERA_LUX_DISABLE;
|
|
}
|
|
else if(data->camera_lux_en)
|
|
{
|
|
//ssp_infof("Light AB Sensor : report skip");
|
|
return;
|
|
}
|
|
#endif //CONFIG_SENSORS_SSP_CAMERALIGHT_FOR_TAB
|
|
if (data->light_ab_log_cnt < 3) {
|
|
ssp_info("Light AB Sensor : lux=%u brightness=%u camera_lux_en=%d / %d %d %d %d",
|
|
data->buf[SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS].ab_lux,
|
|
data->buf[SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS].ab_brightness,
|
|
data->camera_lux_en,
|
|
data->camera_lux_hysteresis[0], data->camera_lux_hysteresis[1],
|
|
data->camera_br_hysteresis[0], data->camera_br_hysteresis[1]);
|
|
data->light_ab_log_cnt++;
|
|
}
|
|
#endif //CONFIG_SENSORS_SSP_LIGHT
|
|
|
|
} else if (type == SENSOR_TYPE_STEP_COUNTER) {
|
|
data->buf[type].step_total += event->step_diff;
|
|
}
|
|
|
|
if (!(atomic64_read(&data->sensor_en_state) & (1ULL << type)))
|
|
{
|
|
ssp_errf("sensor is not enabled(%d)", type);
|
|
return;
|
|
}
|
|
|
|
ssp_iio_push_buffers(data->indio_devs[type], event->timestamp,
|
|
(char *)&data->buf[type], data->info[type].report_data_len);
|
|
|
|
/* wake-up sensor */
|
|
if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION
|
|
|| type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_PICK_UP_GESTURE
|
|
|| type == SENSOR_TYPE_WAKE_UP_MOTION) {
|
|
wake_lock_timeout(&data->ssp_wake_lock, 0.3 * HZ);
|
|
}
|
|
}
|
|
|
|
void report_camera_lux_data(struct ssp_data *data, int lux)
|
|
{
|
|
int type = SENSOR_TYPE_LIGHT_AUTOBRIGHTNESS;
|
|
|
|
ssp_infof("%d", lux);
|
|
|
|
data->buf[type].ab_lux = lux;
|
|
ssp_iio_push_buffers(data->indio_devs[type], get_current_timestamp(),
|
|
(char *)&data->buf[type], data->info[type].report_data_len);
|
|
|
|
}
|
|
|
|
void report_meta_data(struct ssp_data *data, struct sensor_value *s)
|
|
{
|
|
char *meta_event = kzalloc(data->info[s->meta_data.sensor].report_data_len, GFP_KERNEL);
|
|
|
|
ssp_infof("what: %d, sensor: %d", s->meta_data.what, s->meta_data.sensor);
|
|
|
|
memset(meta_event, META_EVENT,
|
|
data->info[s->meta_data.sensor].report_data_len);
|
|
ssp_iio_push_buffers(data->indio_devs[s->meta_data.sensor],
|
|
META_TIMESTAMP, meta_event,
|
|
data->info[s->meta_data.sensor].report_data_len);
|
|
kfree(meta_event);
|
|
}
|
|
|
|
void report_scontext_data(struct ssp_data *data, char *data_buf, u32 length)
|
|
{
|
|
char buf[SCONTEXT_HEADER_LEN + SCONTEXT_DATA_LEN] = {0, };
|
|
u16 start, end;
|
|
u64 timestamp;
|
|
|
|
ssp_scontext_log(__func__, data_buf, length);
|
|
|
|
start = 0;
|
|
memcpy(buf, &length, sizeof(u32));
|
|
timestamp = get_current_timestamp();
|
|
|
|
while (start < length) {
|
|
if (start + SCONTEXT_DATA_LEN < length) {
|
|
end = start + SCONTEXT_DATA_LEN - 1;
|
|
} else {
|
|
memset(buf + SCONTEXT_HEADER_LEN, 0, SCONTEXT_DATA_LEN);
|
|
end = length - 1;
|
|
}
|
|
|
|
memcpy(buf + sizeof(length), &start, sizeof(u16));
|
|
memcpy(buf + sizeof(length) + sizeof(start), &end, sizeof(u16));
|
|
memcpy(buf + SCONTEXT_HEADER_LEN, data_buf + start, end - start + 1);
|
|
|
|
/*
|
|
ssp_infof("[%d, %d, %d] 0x%x 0x%x 0x%x 0x%x// 0x%x 0x%x// 0x%x 0x%x",
|
|
length, start, end,
|
|
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
|
|
|
|
|
|
ssp_infof("0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
|
|
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
|
|
|
|
|
|
ssp_infof("0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x, //0x%llx",
|
|
buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], timestamp);
|
|
*/
|
|
ssp_iio_push_buffers(data->indio_devs[SENSOR_TYPE_SCONTEXT], timestamp,
|
|
buf, data->info[SENSOR_TYPE_SCONTEXT].report_data_len);
|
|
|
|
start = end + 1;
|
|
}
|
|
}
|
|
|
|
void report_scontext_notice_data(struct ssp_data *data, char notice)
|
|
{
|
|
char notice_buf[4] = {0x02, 0x01, 0x00, 0x00};
|
|
int len = 3;
|
|
|
|
notice_buf[2] = notice;
|
|
if (notice == SCONTEXT_AP_STATUS_RESET) {
|
|
len = 4;
|
|
if (data->reset_type == RESET_TYPE_KERNEL_SYSFS) {
|
|
notice_buf[3] = RESET_REASON_SYSFS_REQUEST;
|
|
} else if(data->reset_type == RESET_TYPE_KERNEL_NO_EVENT) {
|
|
notice_buf[3] = RESET_REASON_KERNEL_RESET;
|
|
} else if(data->reset_type == RESET_TYPE_KERNEL_COM_FAIL) {
|
|
notice_buf[3] = RESET_REASON_KERNEL_RESET;
|
|
} else if(data->reset_type == RESET_TYPE_HUB_CRASHED) {
|
|
notice_buf[3] = RESET_REASON_MCU_CRASHED;
|
|
} else if(data->reset_type == RESET_TYPE_HUB_NO_EVENT) {
|
|
notice_buf[3] = RESET_REASON_HUB_REQUEST;
|
|
}
|
|
}
|
|
|
|
report_scontext_data(data, notice_buf, len);
|
|
|
|
if (notice == SCONTEXT_AP_STATUS_WAKEUP) {
|
|
ssp_infof("wake up");
|
|
} else if (notice == SCONTEXT_AP_STATUS_SLEEP) {
|
|
ssp_infof("sleep");
|
|
} else if (notice == SCONTEXT_AP_STATUS_RESET) {
|
|
ssp_infof("reset");
|
|
} else {
|
|
ssp_errf("invalid notice(0x%x)", notice);
|
|
}
|
|
}
|
|
|
|
void report_sensorhub_data(struct ssp_data *data, char* buf)
|
|
{
|
|
ssp_infof();
|
|
ssp_iio_push_buffers(data->indio_devs[SENSOR_TYPE_SENSORHUB], get_current_timestamp(),
|
|
buf, data->info[SENSOR_TYPE_SENSORHUB].report_data_len);
|
|
}
|
|
|
|
static void *init_indio_device(struct device *dev, struct ssp_data *data,
|
|
const struct iio_info *info,
|
|
const struct iio_chan_spec *channels,
|
|
const char *device_name)
|
|
{
|
|
struct iio_dev *indio_dev;
|
|
int ret = 0;
|
|
|
|
indio_dev = iio_device_alloc(0);
|
|
if (!indio_dev) {
|
|
goto err_alloc;
|
|
}
|
|
|
|
indio_dev->name = device_name;
|
|
indio_dev->dev.parent = dev;
|
|
indio_dev->info = info;
|
|
indio_dev->channels = channels;
|
|
indio_dev->num_channels = 1;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
|
|
|
ret = ssp_iio_configure_ring(indio_dev);
|
|
if (ret) {
|
|
goto err_config_ring;
|
|
}
|
|
|
|
ret = iio_device_register(indio_dev);
|
|
if (ret) {
|
|
goto err_register_device;
|
|
}
|
|
|
|
return indio_dev;
|
|
|
|
err_register_device:
|
|
ssp_err("fail to register %s device", device_name);
|
|
iio_kfifo_free(indio_dev->buffer);
|
|
err_config_ring:
|
|
ssp_err("failed to configure %s buffer\n", indio_dev->name);
|
|
iio_device_unregister(indio_dev);
|
|
err_alloc:
|
|
ssp_err("fail to allocate memory for iio %s device", device_name);
|
|
return NULL;
|
|
}
|
|
|
|
static const struct iio_info indio_info = {
|
|
.driver_module = THIS_MODULE,
|
|
};
|
|
|
|
int initialize_indio_dev(struct device *dev, struct ssp_data *data)
|
|
{
|
|
int timestamp_len = 0;
|
|
int type;
|
|
int realbits_size = 0;
|
|
int repeat_size = 0;
|
|
|
|
for (type = 0; type < SENSOR_TYPE_MAX; type++) {
|
|
if (!data->info[type].enable || (data->info[type].report_data_len == 0)) {
|
|
continue;
|
|
}
|
|
|
|
timestamp_len = sizeof(data->buf[type].timestamp);
|
|
|
|
realbits_size = (data->info[type].report_data_len+timestamp_len) * BITS_PER_BYTE;
|
|
repeat_size = 1;
|
|
|
|
while ((realbits_size / repeat_size > 255) && (realbits_size % repeat_size == 0))
|
|
repeat_size++;
|
|
|
|
realbits_size /= repeat_size;
|
|
|
|
data->indio_channels[type].type = IIO_TIMESTAMP;
|
|
data->indio_channels[type].channel = IIO_CHANNEL;
|
|
data->indio_channels[type].scan_index = IIO_SCAN_INDEX;
|
|
data->indio_channels[type].scan_type.sign = IIO_SIGN;
|
|
data->indio_channels[type].scan_type.realbits = realbits_size;
|
|
data->indio_channels[type].scan_type.storagebits = realbits_size;
|
|
data->indio_channels[type].scan_type.shift = IIO_SHIFT;
|
|
data->indio_channels[type].scan_type.repeat = repeat_size;
|
|
|
|
data->indio_devs[type]
|
|
= (struct iio_dev *)init_indio_device(dev, data,
|
|
&indio_info, &data->indio_channels[type],
|
|
data->info[type].name);
|
|
|
|
if (!data->indio_devs[type]) {
|
|
ssp_err("fail to init %s iio dev", data->info[type].name);
|
|
remove_indio_dev(data);
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
void remove_indio_dev(struct ssp_data *data)
|
|
{
|
|
int type;
|
|
|
|
for (type = SENSOR_TYPE_MAX - 1; type >= 0; type--) {
|
|
if (data->indio_devs[type]) {
|
|
iio_device_unregister(data->indio_devs[type]);
|
|
}
|
|
}
|
|
}
|
|
|