| /* |
| * 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 "ssp_scontext.h" |
| #include "ssp_cmd_define.h" |
| #include "ssp_comm.h" |
| #include "ssp_sysfs.h" |
| #include <linux/kernel.h> |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include <linux/uaccess.h> |
| |
| void ssp_scontext_log(const char *func_name, |
| const char *data, int length) |
| { |
| char buf[6]; |
| char *log_str; |
| int log_size; |
| int i; |
| |
| if (likely(length <= BIG_DATA_SIZE)) { |
| log_size = length; |
| } else { |
| log_size = PRINT_TRUNCATE * 2 + 1; |
| } |
| |
| log_size = sizeof(buf) * log_size + 1; |
| log_str = kzalloc(log_size, GFP_ATOMIC); |
| if (unlikely(!log_str)) { |
| ssp_errf("allocate memory for data log err"); |
| return; |
| } |
| |
| for (i = 0; i < length; i++) { |
| if (length < BIG_DATA_SIZE || |
| i < PRINT_TRUNCATE || i >= length - PRINT_TRUNCATE) { |
| snprintf(buf, sizeof(buf), "0x%x", (unsigned char)data[i]); |
| strlcat(log_str, buf, log_size); |
| |
| if (i < length - 1) { |
| strlcat(log_str, ", ", log_size); |
| } |
| } |
| if (length > BIG_DATA_SIZE && i == PRINT_TRUNCATE) { |
| strlcat(log_str, "..., ", log_size); |
| } |
| } |
| |
| ssp_info("%s(%d): %s", func_name, length, log_str); |
| kfree(log_str); |
| } |
| |
| static int ssp_scontext_send_cmd(struct ssp_data *data, |
| const char *buf, int count) |
| { |
| int ret = 0; |
| |
| if (buf[2] < SCONTEXT_AP_STATUS_WAKEUP || |
| buf[2] > SCONTEXT_AP_STATUS_CALL_ACTIVE) { |
| ssp_errf("INST_LIB_NOTI err(%d)", buf[2]); |
| return -EINVAL; |
| } |
| |
| ret = ssp_send_status(data, buf[2]); |
| |
| if (buf[2] == SCONTEXT_AP_STATUS_WAKEUP || |
| buf[2] == SCONTEXT_AP_STATUS_SLEEP) { |
| data->last_ap_status = buf[2]; |
| } |
| |
| if (buf[2] == SCONTEXT_AP_STATUS_SUSPEND || |
| buf[2] == SCONTEXT_AP_STATUS_RESUME) { |
| data->last_resume_status = buf[2]; |
| } |
| |
| return ret; |
| } |
| |
| |
| #define SCONTEXT_VALUE_CURRENTSYSTEMTIME 0x0E |
| #define SCONTEXT_VALUE_PEDOMETER_USERHEIGHT 0x12 |
| #define SCONTEXT_VALUE_PEDOMETER_USERWEIGHT 0x13 |
| #define SCONTEXT_VALUE_PEDOMETER_USERGENDER 0x14 |
| #define SCONTEXT_VALUE_PEDOMETER_INFOUPDATETIME 0x15 |
| |
| int convert_scontext_putvalue_subcmd(int subcmd) |
| { |
| int ret = -1; |
| switch (subcmd) { |
| case SCONTEXT_VALUE_CURRENTSYSTEMTIME : |
| ret = CURRENT_SYSTEM_TIME; |
| break; |
| case SCONTEXT_VALUE_PEDOMETER_USERHEIGHT : |
| ret = PEDOMETER_USERHEIGHT; |
| break; |
| case SCONTEXT_VALUE_PEDOMETER_USERWEIGHT: |
| ret = PEDOMETER_USERWEIGHT; |
| break; |
| case SCONTEXT_VALUE_PEDOMETER_USERGENDER: |
| ret = PEDOMETER_USERGENDER; |
| break; |
| case SCONTEXT_VALUE_PEDOMETER_INFOUPDATETIME: |
| ret = PEDOMETER_INFOUPDATETIME; |
| break; |
| default: |
| ret = subcmd; |
| } |
| |
| return ret; |
| } |
| |
| int convert_scontext_getvalue_subcmd(int subcmd) |
| { |
| int ret = -1; |
| switch (subcmd) { |
| case SCONTEXT_VALUE_CURRENTSTATUS : |
| ret = LIBRARY_CURRENTSTATUS; |
| break; |
| case SCONTEXT_VALUE_CURRENTSTATUS_BATCH : |
| ret = LIBRARY_CURRENTSTATUS_BATCH; |
| break; |
| case SCONTEXT_VALUE_VERSIONINFO: |
| ret = LIBRARY_VERSIONINFO; |
| break; |
| default: |
| ret = subcmd; |
| } |
| |
| return ret; |
| } |
| |
| void get_ss_sensor_name(struct ssp_data *data, int type, char *buf, int buf_size) |
| { |
| memset(buf, 0, buf_size); |
| switch (type) { |
| case SS_SENSOR_TYPE_PEDOMETER: |
| strncpy(buf, "pedometer", buf_size); |
| break; |
| case SS_SENSOR_TYPE_STEP_COUNT_ALERT: |
| strncpy(buf, "step count alert", buf_size); |
| break; |
| case SS_SENSOR_TYPE_AUTO_ROTATION: |
| strncpy(buf, "auto rotation", buf_size); |
| break; |
| case SS_SENSOR_TYPE_SLOCATION: |
| strncpy(buf, "slocation", buf_size); |
| break; |
| case SS_SENSOR_TYPE_MOVEMENT: |
| strncpy(buf, "smart alert", buf_size); |
| break; |
| case SS_SENSOR_TYPE_ACTIVITY_TRACKER: |
| strncpy(buf, "activity tracker", buf_size); |
| break; |
| case SS_SENSOR_TYPE_DPCM: |
| strncpy(buf, "dpcm", buf_size); |
| break; |
| case SS_SENSOR_TYPE_SENSOR_STATUS_CHECK: |
| strncpy(buf, "sensor status check", buf_size); |
| break; |
| case SS_SENSOR_TYPE_ACTIVITY_CALIBRATION: |
| strncpy(buf, "activity calibration", buf_size); |
| break; |
| case SS_SENSOR_TYPE_DEVICE_POSITION: |
| strncpy(buf, "device position", buf_size); |
| break; |
| case SS_SENSOR_TYPE_CHANGE_LOCATION_TRIGGER: |
| strncpy(buf, "change location trigger", buf_size); |
| break; |
| } |
| |
| return; |
| } |
| |
| |
| static int ssp_scontext_send_instruction(struct ssp_data *data, |
| const char *buf, int count) |
| { |
| char command, type, sub_cmd = 0; |
| char *buffer = (char *)(buf + 2); |
| int length = count - 2; |
| char name[SENSOR_NAME_MAX_LEN] = ""; |
| |
| if (buf[0] == SCONTEXT_INST_LIBRARY_REMOVE) { |
| command = CMD_REMOVE; |
| type = buf[1] + SS_SENSOR_TYPE_BASE; |
| if(type < SS_SENSOR_TYPE_MAX) |
| { |
| get_ss_sensor_name(data, type, name, sizeof(name)); |
| ssp_infof("REMOVE LIB %s, type %d", name, type); |
| |
| return disable_sensor(data, type, buffer, length); |
| } |
| else |
| return -EINVAL; |
| } else if (buf[0] == SCONTEXT_INST_LIBRARY_ADD) { |
| command = CMD_ADD; |
| type = buf[1] + SS_SENSOR_TYPE_BASE; |
| if(type < SS_SENSOR_TYPE_MAX) |
| { |
| get_ss_sensor_name(data, type, name, sizeof(name)); |
| ssp_infof("ADD LIB, type %d", type); |
| |
| return enable_sensor(data, type, buffer, length); |
| } |
| else |
| return ERROR; |
| } else if (buf[0] == SCONTEXT_INST_LIB_SET_DATA) { |
| command = CMD_SETVALUE; |
| if (buf[1] != SCONTEXT_VALUE_LIBRARY_DATA) { |
| type = TYPE_MCU; |
| sub_cmd = convert_scontext_putvalue_subcmd(buf[1]); |
| } else { |
| type = buf[2] + SS_SENSOR_TYPE_BASE; |
| sub_cmd = LIBRARY_DATA; |
| length = count - 3; |
| if (length > 0) { |
| buffer = (char *)(buf + 3); |
| } else { |
| buffer = NULL; |
| } |
| } |
| } else if (buf[0] == SCONTEXT_INST_LIB_GET_DATA) { |
| command = CMD_GETVALUE; |
| type = buf[1] + SS_SENSOR_TYPE_BASE; |
| sub_cmd = convert_scontext_getvalue_subcmd(buf[2]); |
| length = count - 3; |
| if (length > 0) { |
| buffer = (char *)(buf + 3); |
| } else { |
| buffer = NULL; |
| } |
| } else { |
| ssp_errf("0x%x is not supported", buf[0]); |
| return ERROR; |
| } |
| |
| return ssp_send_command(data, command, type, sub_cmd, 0, |
| buffer, length, NULL, NULL); |
| } |
| |
| static ssize_t ssp_scontext_write(struct file *file, const char __user *buf, |
| size_t count, loff_t *pos) |
| { |
| struct ssp_data *data = container_of(file->private_data, struct ssp_data, scontext_device); |
| int ret = 0; |
| char *buffer; |
| |
| if (!is_sensorhub_working(data)) { |
| ssp_errf("stop sending library data(is not working)"); |
| return -EIO; |
| } |
| |
| if (unlikely(count < 2)) { |
| ssp_errf("library data length err(%d)", (int)count); |
| return -EINVAL; |
| } |
| |
| buffer = kzalloc(count * sizeof(char), GFP_KERNEL); |
| |
| ret = copy_from_user(buffer, buf, count); |
| if (unlikely(ret)) { |
| ssp_errf("memcpy for kernel buffer err"); |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| ssp_scontext_log(__func__, buffer, count); |
| |
| if (buffer[0] == SCONTEXT_INST_LIB_NOTI) { |
| ret = ssp_scontext_send_cmd(data, buffer, count); |
| } else { |
| ret = ssp_scontext_send_instruction(data, buffer, count); |
| } |
| |
| if (unlikely(ret < 0)) { |
| ssp_errf("send library data err(%d)", ret); |
| if (ret == ERROR) { |
| ret = -EIO; |
| } |
| |
| else if (ret == FAIL) { |
| ret = -EAGAIN; |
| } |
| |
| goto exit; |
| } |
| |
| ret = count; |
| |
| exit: |
| kfree(buffer); |
| return ret; |
| } |
| |
| static struct file_operations ssp_scontext_fops = { |
| .owner = THIS_MODULE, |
| .open = nonseekable_open, |
| .write = ssp_scontext_write, |
| }; |
| |
| int ssp_scontext_initialize(struct ssp_data *data) |
| { |
| int ret; |
| ssp_dbgf("----------"); |
| |
| /* register scontext misc device */ |
| data->scontext_device.minor = MISC_DYNAMIC_MINOR; |
| data->scontext_device.name = "ssp_sensorhub"; |
| data->scontext_device.fops = &ssp_scontext_fops; |
| |
| ret = misc_register(&data->scontext_device); |
| if (ret < 0) { |
| ssp_errf("register scontext misc device err(%d)", ret); |
| } |
| |
| return ret; |
| } |
| |
| void ssp_scontext_remove(struct ssp_data *data) |
| { |
| ssp_scontext_fops.write = NULL; |
| misc_deregister(&data->scontext_device); |
| } |
| |
| MODULE_DESCRIPTION("Seamless Sensor Platform(SSP) sensorhub driver"); |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_LICENSE("GPL"); |