| /* |
| * Copyright (C) 2012, 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 "ssp.h" |
| #include <linux/math64.h> |
| |
| #define BATCH_IOCTL_MAGIC 0xFC |
| |
| struct batch_config { |
| int64_t timeout; |
| int64_t delay; |
| int flag; |
| }; |
| |
| /*************************************************************************/ |
| /* SSP data delay function */ |
| /*************************************************************************/ |
| |
| int get_msdelay(int64_t dDelayRate) |
| { |
| /* |
| * From Android 5.0, There is MaxDelay Concept. |
| * If App request lower frequency then MaxDelay, |
| * Sensor have to work with MaxDelay. |
| */ |
| |
| if (dDelayRate > 200000000) |
| dDelayRate = 200000000; |
| return div_s64(dDelayRate, 1000000); |
| } |
| |
| static void enable_sensor(struct ssp_data *data, |
| int iSensorType, int64_t dNewDelay) |
| { |
| u8 uBuf[9]; |
| unsigned int uNewEnable = 0; |
| s32 maxBatchReportLatency = 0; |
| s8 batchOptions = 0; |
| int64_t dTempDelay = data->adDelayBuf[iSensorType]; |
| s32 dMsDelay = get_msdelay(dNewDelay); |
| int ret = 0; |
| |
| data->adDelayBuf[iSensorType] = dNewDelay; |
| maxBatchReportLatency = data->batchLatencyBuf[iSensorType]; |
| batchOptions = data->batchOptBuf[iSensorType]; |
| |
| switch (data->aiCheckStatus[iSensorType]) { |
| case ADD_SENSOR_STATE: |
| ssp_dbg("[SSP]: %s - add %u, New = %lldns\n", |
| __func__, 1 << iSensorType, dNewDelay); |
| |
| memcpy(&uBuf[0], &dMsDelay, 4); |
| memcpy(&uBuf[4], &maxBatchReportLatency, 4); |
| uBuf[8] = batchOptions; |
| |
| ret = send_instruction(data, ADD_SENSOR, iSensorType, uBuf, 9); |
| pr_info("[SSP], delay %d, timeout %d, flag=%d, ret%d", |
| dMsDelay, maxBatchReportLatency, uBuf[8], ret); |
| if (ret <= 0) { |
| uNewEnable = |
| (unsigned int)atomic_read(&data->aSensorEnable) |
| & (~(unsigned int)(1 << iSensorType)); |
| atomic_set(&data->aSensorEnable, uNewEnable); |
| |
| data->aiCheckStatus[iSensorType] = NO_SENSOR_STATE; |
| break; |
| } |
| data->aiCheckStatus[iSensorType] = RUNNING_SENSOR_STATE; |
| |
| break; |
| case RUNNING_SENSOR_STATE: |
| if (get_msdelay(dTempDelay) |
| == get_msdelay(data->adDelayBuf[iSensorType])) |
| break; |
| |
| ssp_dbg("[SSP]: %s - Change %u, New = %lldns\n", |
| __func__, 1 << iSensorType, dNewDelay); |
| |
| memcpy(&uBuf[0], &dMsDelay, 4); |
| memcpy(&uBuf[4], &maxBatchReportLatency, 4); |
| uBuf[8] = batchOptions; |
| send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 9); |
| |
| break; |
| default: |
| data->aiCheckStatus[iSensorType] = ADD_SENSOR_STATE; |
| } |
| } |
| |
| static void change_sensor_delay(struct ssp_data *data, |
| int iSensorType, int64_t dNewDelay) |
| { |
| u8 uBuf[9]; |
| s32 maxBatchReportLatency = 0; |
| s8 batchOptions = 0; |
| int64_t dTempDelay = data->adDelayBuf[iSensorType]; |
| s32 dMsDelay = get_msdelay(dNewDelay); |
| |
| data->adDelayBuf[iSensorType] = dNewDelay; |
| data->batchLatencyBuf[iSensorType] = maxBatchReportLatency; |
| data->batchOptBuf[iSensorType] = batchOptions; |
| |
| switch (data->aiCheckStatus[iSensorType]) { |
| case RUNNING_SENSOR_STATE: |
| if (get_msdelay(dTempDelay) |
| == get_msdelay(data->adDelayBuf[iSensorType])) |
| break; |
| |
| ssp_dbg("[SSP]: %s - Change %u, New = %lldns\n", |
| __func__, 1 << iSensorType, dNewDelay); |
| |
| memcpy(&uBuf[0], &dMsDelay, 4); |
| memcpy(&uBuf[4], &maxBatchReportLatency, 4); |
| uBuf[8] = batchOptions; |
| send_instruction(data, CHANGE_DELAY, iSensorType, uBuf, 9); |
| |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /*************************************************************************/ |
| /* SSP data enable function */ |
| /*************************************************************************/ |
| |
| static int ssp_remove_sensor(struct ssp_data *data, |
| unsigned int uChangedSensor, unsigned int uNewEnable) |
| { |
| u8 uBuf[4]; |
| int64_t dSensorDelay = data->adDelayBuf[uChangedSensor]; |
| |
| ssp_dbg("[SSP]: %s - remove sensor = %d, current state = %d\n", |
| __func__, (1 << uChangedSensor), uNewEnable); |
| |
| data->adDelayBuf[uChangedSensor] = DEFUALT_POLLING_DELAY; |
| data->batchLatencyBuf[uChangedSensor] = 0; |
| data->batchOptBuf[uChangedSensor] = 0; |
| |
| if (uChangedSensor == ORIENTATION_SENSOR) { |
| if (!(atomic_read(&data->aSensorEnable) |
| & (1 << ACCELEROMETER_SENSOR))) { |
| uChangedSensor = ACCELEROMETER_SENSOR; |
| } else { |
| change_sensor_delay(data, ACCELEROMETER_SENSOR, |
| data->adDelayBuf[ACCELEROMETER_SENSOR]); |
| return 0; |
| } |
| } else if (uChangedSensor == ACCELEROMETER_SENSOR) { |
| if (atomic_read(&data->aSensorEnable) |
| & (1 << ORIENTATION_SENSOR)) { |
| change_sensor_delay(data, ORIENTATION_SENSOR, |
| data->adDelayBuf[ORIENTATION_SENSOR]); |
| return 0; |
| } |
| } |
| |
| if (!data->bSspShutdown) |
| if (atomic_read(&data->aSensorEnable) & (1 << uChangedSensor)) { |
| s32 dMsDelay = get_msdelay(dSensorDelay); |
| memcpy(&uBuf[0], &dMsDelay, 4); |
| |
| send_instruction(data, REMOVE_SENSOR, |
| uChangedSensor, uBuf, 4); |
| } |
| data->aiCheckStatus[uChangedSensor] = NO_SENSOR_STATE; |
| |
| return 0; |
| } |
| |
| /*************************************************************************/ |
| /* ssp Sysfs */ |
| /*************************************************************************/ |
| |
| static ssize_t show_enable_irq(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| ssp_dbg("[SSP]: %s - %d\n", __func__, !data->bSspShutdown); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", !data->bSspShutdown); |
| } |
| |
| static ssize_t set_enable_irq(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| u8 dTemp; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtou8(buf, 10, &dTemp) < 0) |
| return ERROR; |
| |
| pr_info("[SSP] %s - %d start\n", __func__, dTemp); |
| if (dTemp) { |
| reset_mcu(data); |
| enable_debug_timer(data); |
| } else if (!dTemp) { |
| disable_debug_timer(data); |
| ssp_enable(data, 0); |
| } else |
| pr_err("[SSP] %s - invalid value\n", __func__); |
| pr_info("[SSP] %s - %d end\n", __func__, dTemp); |
| return size; |
| } |
| |
| static ssize_t show_sensors_enable(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| ssp_dbg("[SSP]: %s - cur_enable = %d\n", __func__, |
| atomic_read(&data->aSensorEnable)); |
| |
| return snprintf(buf, PAGE_SIZE, "%9u\n", |
| atomic_read(&data->aSensorEnable)); |
| } |
| |
| static ssize_t set_sensors_enable(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dTemp; |
| unsigned int uNewEnable = 0, uChangedSensor = 0; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dTemp) < 0) |
| return -EINVAL; |
| |
| uNewEnable = (unsigned int)dTemp; |
| ssp_dbg("[SSP]: %s - new_enable = %u, old_enable = %u\n", __func__, |
| uNewEnable, atomic_read(&data->aSensorEnable)); |
| |
| if ((uNewEnable != atomic_read(&data->aSensorEnable)) && |
| !(data->uSensorState |
| & (uNewEnable - atomic_read(&data->aSensorEnable)))) { |
| pr_info("[SSP] %s - %u is not connected(sensorstate: 0x%x)\n", |
| __func__, |
| uNewEnable - atomic_read(&data->aSensorEnable), |
| data->uSensorState); |
| return -EINVAL; |
| } |
| |
| if (uNewEnable == atomic_read(&data->aSensorEnable)) |
| return size; |
| |
| for (uChangedSensor = 0; uChangedSensor < SENSOR_MAX; |
| uChangedSensor++) { |
| if ((atomic_read(&data->aSensorEnable) & (1 << uChangedSensor)) |
| != (uNewEnable & (1 << uChangedSensor))) { |
| |
| if (!(uNewEnable & (1 << uChangedSensor))) { |
| data->reportedData[uChangedSensor] = false; |
| ssp_remove_sensor(data, uChangedSensor, |
| uNewEnable); /* disable */ |
| } else { /* Change to ADD_SENSOR_STATE from KitKat */ |
| if (data->aiCheckStatus[uChangedSensor] |
| == INITIALIZATION_STATE) { |
| if (uChangedSensor |
| == ACCELEROMETER_SENSOR) { |
| accel_open_calibration(data); |
| set_accel_cal(data); |
| } else if (uChangedSensor |
| == GYROSCOPE_SENSOR) { |
| gyro_open_calibration(data); |
| set_gyro_cal(data); |
| } |
| } |
| data->aiCheckStatus[uChangedSensor] = |
| ADD_SENSOR_STATE; |
| enable_sensor(data, uChangedSensor, |
| data->adDelayBuf[uChangedSensor]); |
| } |
| break; |
| } |
| } |
| atomic_set(&data->aSensorEnable, uNewEnable); |
| |
| return size; |
| } |
| |
| 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 (!(atomic_read(&data->aSensorEnable) & (1 << sensor_type))) |
| return -EINVAL; |
| |
| if (flush(data, sensor_type) < 0) { |
| pr_err("[SSP] ssp returns error for flush(%x)", sensor_type); |
| return -EINVAL; |
| } |
| return size; |
| } |
| |
| static ssize_t show_acc_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%lld\n", data->adDelayBuf[ACCELEROMETER_SENSOR]); |
| } |
| |
| static ssize_t set_acc_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| if ((atomic_read(&data->aSensorEnable) & (1 << ORIENTATION_SENSOR)) && |
| (data->adDelayBuf[ORIENTATION_SENSOR] < dNewDelay)) |
| data->adDelayBuf[ACCELEROMETER_SENSOR] = dNewDelay; |
| else |
| change_sensor_delay(data, ACCELEROMETER_SENSOR, dNewDelay); |
| |
| return size; |
| } |
| |
| static ssize_t show_gyro_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%lld\n", data->adDelayBuf[GYROSCOPE_SENSOR]); |
| } |
| |
| static ssize_t set_gyro_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| change_sensor_delay(data, GYROSCOPE_SENSOR, dNewDelay); |
| return size; |
| } |
| |
| static ssize_t show_mag_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%lld\n", data->adDelayBuf[GEOMAGNETIC_SENSOR]); |
| } |
| |
| static ssize_t set_mag_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| change_sensor_delay(data, GEOMAGNETIC_SENSOR, dNewDelay); |
| |
| return size; |
| } |
| |
| static ssize_t show_uncal_mag_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%lld\n", |
| data->adDelayBuf[GEOMAGNETIC_UNCALIB_SENSOR]); |
| } |
| |
| static ssize_t set_uncal_mag_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| change_sensor_delay(data, GEOMAGNETIC_UNCALIB_SENSOR, dNewDelay); |
| |
| return size; |
| } |
| |
| static ssize_t show_rot_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%lld\n", data->adDelayBuf[ROTATION_VECTOR]); |
| } |
| |
| static ssize_t set_rot_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| change_sensor_delay(data, ROTATION_VECTOR, dNewDelay); |
| |
| return size; |
| } |
| |
| static ssize_t show_game_rot_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%lld\n", data->adDelayBuf[GAME_ROTATION_VECTOR]); |
| } |
| |
| static ssize_t set_game_rot_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| change_sensor_delay(data, GAME_ROTATION_VECTOR, dNewDelay); |
| |
| return size; |
| } |
| |
| static ssize_t show_step_det_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%lld\n", |
| data->adDelayBuf[STEP_DETECTOR]); |
| } |
| |
| static ssize_t set_step_det_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return ERROR; |
| |
| change_sensor_delay(data, STEP_DETECTOR, dNewDelay); |
| return size; |
| } |
| |
| static ssize_t show_sig_motion_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%lld\n", |
| data->adDelayBuf[SIG_MOTION_SENSOR]); |
| } |
| |
| static ssize_t set_sig_motion_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return ERROR; |
| |
| change_sensor_delay(data, SIG_MOTION_SENSOR, dNewDelay); |
| return size; |
| } |
| |
| static ssize_t show_step_cnt_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%lld\n", |
| data->adDelayBuf[STEP_COUNTER]); |
| } |
| |
| static ssize_t set_step_cnt_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return ERROR; |
| |
| change_sensor_delay(data, STEP_COUNTER, dNewDelay); |
| return size; |
| } |
| |
| static ssize_t show_uncalib_gyro_delay(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, |
| "%lld\n", data->adDelayBuf[GYRO_UNCALIB_SENSOR]); |
| } |
| |
| static ssize_t set_uncalib_gyro_delay(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDelay; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDelay) < 0) |
| return -EINVAL; |
| |
| change_sensor_delay(data, GYRO_UNCALIB_SENSOR, dNewDelay); |
| |
| return size; |
| } |
| |
| static ssize_t show_debug(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->bDebugmsg ? 1 : 0); |
| } |
| |
| static ssize_t set_debug(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| int64_t dNewDebugState; |
| struct ssp_data *data = dev_get_drvdata(dev); |
| |
| if (kstrtoll(buf, 10, &dNewDebugState) < 0) |
| return -EINVAL; |
| |
| if (dNewDebugState > 0) |
| data->bDebugmsg = true; |
| else |
| data->bDebugmsg = false; |
| |
| return size; |
| } |
| |
| static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_sensors_enable, set_sensors_enable); |
| static DEVICE_ATTR(enable_irq, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_enable_irq, set_enable_irq); |
| static DEVICE_ATTR(accel_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_acc_delay, set_acc_delay); |
| static DEVICE_ATTR(gyro_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_gyro_delay, set_gyro_delay); |
| static DEVICE_ATTR(rot_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_rot_delay, set_rot_delay); |
| static DEVICE_ATTR(game_rot_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_game_rot_delay, set_game_rot_delay); |
| static DEVICE_ATTR(step_det_poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_step_det_delay, set_step_det_delay); |
| static DEVICE_ATTR(ssp_flush, S_IWUSR | S_IWGRP, |
| NULL, set_flush); |
| |
| static struct device_attribute dev_attr_mag_poll_delay |
| = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_mag_delay, set_mag_delay); |
| static struct device_attribute dev_attr_uncal_mag_poll_delay |
| = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_uncal_mag_delay, set_uncal_mag_delay); |
| static struct device_attribute dev_attr_sig_motion_poll_delay |
| = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_sig_motion_delay, set_sig_motion_delay); |
| static struct device_attribute dev_attr_uncalib_gyro_poll_delay |
| = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_uncalib_gyro_delay, set_uncalib_gyro_delay); |
| static struct device_attribute dev_attr_step_cnt_poll_delay |
| = __ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_step_cnt_delay, set_step_cnt_delay); |
| static struct device_attribute dev_attr_debug |
| = __ATTR(debug, S_IRUGO | S_IWUSR | S_IWGRP, |
| show_debug, set_debug); |
| |
| static struct device_attribute *mcu_attrs[] = { |
| &dev_attr_enable, |
| &dev_attr_enable_irq, |
| &dev_attr_accel_poll_delay, |
| &dev_attr_gyro_poll_delay, |
| &dev_attr_rot_poll_delay, |
| &dev_attr_game_rot_poll_delay, |
| &dev_attr_step_det_poll_delay, |
| &dev_attr_ssp_flush, |
| &dev_attr_debug, |
| NULL, |
| }; |
| |
| 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 = 0; |
| int sensor_type, ms_delay; |
| int timeout_ms = 0; |
| u8 uBuf[9]; |
| |
| sensor_type = (cmd & 0xFF); |
| |
| if ((cmd >> 8 & 0xFF) != BATCH_IOCTL_MAGIC) { |
| pr_err("[SSP] Invalid BATCH CMD %x", cmd); |
| return -EINVAL; |
| } |
| |
| while (retries--) { |
| ret = copy_from_user(&batch, argp, sizeof(batch)); |
| if (likely(!ret)) |
| break; |
| } |
| if (unlikely(ret)) { |
| pr_err("[SSP] batch ioctl err(%d)", ret); |
| return -EINVAL; |
| } |
| ms_delay = get_msdelay(batch.delay); |
| timeout_ms = div_s64(batch.timeout, 1000000); |
| memcpy(&uBuf[0], &ms_delay, 4); |
| memcpy(&uBuf[4], &timeout_ms, 4); |
| uBuf[8] = batch.flag; |
| |
| if (batch.timeout) { /* add or dry */ |
| /* real batch, NOT DRY, change delay */ |
| if (!(batch.flag & SENSORS_BATCH_DRY_RUN)) { |
| ret = 1; |
| /* if sensor is not running state, |
| * enable will be called. |
| * MCU return fail when receive chage delay inst |
| * during NO_SENSOR STATE */ |
| if (data->aiCheckStatus[sensor_type] |
| == RUNNING_SENSOR_STATE) |
| ret = send_instruction_sync(data, CHANGE_DELAY, |
| sensor_type, uBuf, 9); |
| |
| if (ret > 0) { /* ret 1 is success */ |
| data->batchOptBuf[sensor_type] = (u8)batch.flag; |
| data->batchLatencyBuf[sensor_type] = timeout_ms; |
| data->adDelayBuf[sensor_type] = batch.delay; |
| } |
| } else { /* real batch, DRY RUN */ |
| ret = send_instruction_sync(data, CHANGE_DELAY, |
| sensor_type, uBuf, 9); |
| if (ret > 0) { /* ret 1 is success */ |
| data->batchOptBuf[sensor_type] = (u8)batch.flag; |
| data->batchLatencyBuf[sensor_type] = timeout_ms; |
| data->adDelayBuf[sensor_type] = batch.delay; |
| } |
| } |
| } else { |
| /* remove batch or normal change delay, |
| * remove or add will be called. |
| * no batch, NOT DRY, change delay */ |
| if (!(batch.flag & SENSORS_BATCH_DRY_RUN)) { |
| data->batchOptBuf[sensor_type] = 0; |
| data->batchLatencyBuf[sensor_type] = 0; |
| data->adDelayBuf[sensor_type] = batch.delay; |
| if (data->aiCheckStatus[sensor_type] |
| == RUNNING_SENSOR_STATE) { |
| send_instruction(data, CHANGE_DELAY, |
| sensor_type, uBuf, 9); |
| } |
| } |
| } |
| |
| pr_info("[SSP] batch %d: delay %lld, timeout %lld, flag %d, ret %d", |
| sensor_type, batch.delay, batch.timeout, batch.flag, ret); |
| if (!batch.timeout) |
| return 0; |
| if (ret <= 0) |
| return -EINVAL; |
| else |
| return 0; |
| } |
| |
| static const struct file_operations ssp_batch_fops = { |
| .owner = THIS_MODULE, |
| .open = nonseekable_open, |
| .unlocked_ioctl = ssp_batch_ioctl, |
| }; |
| |
| static void initialize_mcu_factorytest(struct ssp_data *data) |
| { |
| sensors_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); |
| } |
| |
| int initialize_sysfs(struct ssp_data *data) |
| { |
| if (device_create_file(&data->mag_input_dev->dev, |
| &dev_attr_mag_poll_delay)) |
| goto err_mag_input_dev; |
| |
| if (device_create_file(&data->uncal_mag_input_dev->dev, |
| &dev_attr_uncal_mag_poll_delay)) |
| goto err_uncal_mag_input_dev; |
| |
| if (device_create_file(&data->sig_motion_input_dev->dev, |
| &dev_attr_sig_motion_poll_delay)) |
| goto err_sig_motion_input_dev; |
| |
| if (device_create_file(&data->uncalib_gyro_input_dev->dev, |
| &dev_attr_uncalib_gyro_poll_delay)) |
| goto err_uncalib_gyro_input_dev; |
| |
| if (device_create_file(&data->step_cnt_input_dev->dev, |
| &dev_attr_step_cnt_poll_delay)) |
| goto err_step_cnt_input_dev; |
| |
| 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); |
| |
| return SUCCESS; |
| err_batch_io_dev: |
| device_remove_file(&data->step_cnt_input_dev->dev, |
| &dev_attr_step_cnt_poll_delay); |
| err_step_cnt_input_dev: |
| device_remove_file(&data->uncalib_gyro_input_dev->dev, |
| &dev_attr_uncalib_gyro_poll_delay); |
| err_uncalib_gyro_input_dev: |
| device_remove_file(&data->sig_motion_input_dev->dev, |
| &dev_attr_sig_motion_poll_delay); |
| err_sig_motion_input_dev: |
| device_remove_file(&data->uncal_mag_input_dev->dev, |
| &dev_attr_uncal_mag_poll_delay); |
| err_uncal_mag_input_dev: |
| device_remove_file(&data->mag_input_dev->dev, |
| &dev_attr_mag_poll_delay); |
| err_mag_input_dev: |
| pr_err("[SSP] error init sysfs"); |
| return ERROR; |
| } |
| |
| void remove_sysfs(struct ssp_data *data) |
| { |
| device_remove_file(&data->mag_input_dev->dev, |
| &dev_attr_mag_poll_delay); |
| device_remove_file(&data->uncal_mag_input_dev->dev, |
| &dev_attr_uncal_mag_poll_delay); |
| device_remove_file(&data->sig_motion_input_dev->dev, |
| &dev_attr_sig_motion_poll_delay); |
| device_remove_file(&data->uncalib_gyro_input_dev->dev, |
| &dev_attr_uncalib_gyro_poll_delay); |
| device_remove_file(&data->step_cnt_input_dev->dev, |
| &dev_attr_step_cnt_poll_delay); |
| misc_deregister(&data->batch_io_device); |
| |
| remove_mcu_factorytest(data); |
| |
| destroy_sensor_class(); |
| } |