/* * 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 #include #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]); } } }