blob: 78276d40d057be29452f09fff68a997e0d9b2b5a [file] [log] [blame]
#include <linux/kernel.h>
#include <linux/slab.h>
#include "ssp.h"
#include "ssp_comm.h"
#include "ssp_data.h"
#include "ssp_cmd_define.h"
#include "ssp_debug.h"
#define SSP_CMD_SIZE 64
#define SSP_MSG_HEADER_SIZE 13
void handle_packet(struct ssp_data *data, char *packet, int packet_size)
{
u16 msg_length = 0;
u8 msg_cmd = 0, msg_subcmd = 0, msg_type = 0;
char *buffer;
if(packet_size < SSP_MSG_HEADER_SIZE) {
ssp_infof("nanohub packet size is small/(%s)", packet);
return;
}
msg_cmd = packet[0];
msg_type = packet[1];
msg_subcmd = packet[2];
msg_length = MAKE_WORD(packet[4], packet[3]);
/*
ssp_infof("cmd %d, type %d, sub_cmd %d, length %d(0x%x, 0x%x)",
msg_cmd, msg_type, msg_subcmd, msg_length, packet[3], packet[4]);
*/
if (msg_length == 0) {
ssp_errf("lengh is zero %d %d %d", msg_cmd, msg_type, msg_subcmd);
return;
}
if (msg_cmd <= CMD_GETVALUE) {
bool found = false;
struct ssp_msg *msg, *n;
mutex_lock(&data->pending_mutex);
if (!list_empty(&data->pending_list)) {
list_for_each_entry_safe(msg, n,
&data->pending_list, list) {
if ((msg->cmd == msg_cmd) && (msg->type == msg_type) &&
(msg->subcmd == msg_subcmd)) {
list_del(&msg->list);
found = true;
break;
}
}
if (!found) {
ssp_errf("%d %d %d - Not match error", msg_cmd, msg_type, msg_subcmd);
goto exit;
}
if (msg_cmd == CMD_GETVALUE) {
msg->length = msg_length;
if (msg->length != 0) {
if (msg->buffer != NULL) {
kfree(msg->buffer);
}
msg->buffer = kzalloc(msg->length, GFP_KERNEL);
memcpy(msg->buffer, packet + SSP_MSG_HEADER_SIZE, msg->length);
} else {
msg->res = 0;
}
}
if (msg->done != NULL && !completion_done(msg->done)) {
complete(msg->done);
}
} else {
ssp_errf("List empty error(%d %d %d)", msg_cmd, msg_type, msg_subcmd);
}
exit:
mutex_unlock(&data->pending_mutex);
} else if (msg_cmd == CMD_REPORT) {
buffer = kzalloc(msg_length, GFP_KERNEL);
memcpy(buffer, &packet[SSP_MSG_HEADER_SIZE], msg_length);
parse_dataframe(data, buffer, msg_length);
kfree(buffer);
} else {
ssp_infof("msg_cmd does not define. cmd is %d", msg_cmd);
}
return;
}
static char ssp_cmd_data[SSP_CMD_SIZE];
static int do_transfer(struct ssp_data *data, struct ssp_msg *msg, int timeout)
{
int status = 0;
int ret = 0;
bool is_ssp_shutdown;
mutex_lock(&data->comm_mutex);
if (!is_sensorhub_working(data)) {
ssp_errf("sensorhub is not working");
mutex_unlock(&data->comm_mutex);
return -EIO;
}
msg->timestamp = get_current_timestamp();
memcpy(ssp_cmd_data, msg, SSP_MSG_HEADER_SIZE);
if (msg->length > 0) {
memcpy(&ssp_cmd_data[SSP_MSG_HEADER_SIZE], msg->buffer, msg->length);
} else if (msg->length > (SSP_CMD_SIZE - SSP_MSG_HEADER_SIZE)) {
ssp_errf("command size over !");
mutex_unlock(&data->comm_mutex);
return -EINVAL;
}
if (msg->done != NULL) {
mutex_lock(&data->pending_mutex);
list_add_tail(&msg->list, &data->pending_list);
mutex_unlock(&data->pending_mutex);
}
status = sensorhub_comms_write(data, ssp_cmd_data, SSP_CMD_SIZE, timeout);
if (status < 0 && msg->done != NULL) {
ssp_errf("comm write fail!!");
mutex_lock(&data->pending_mutex);
list_del(&msg->list);
mutex_unlock(&data->pending_mutex);
goto exit;
}
exit:
mutex_unlock(&data->comm_mutex);
if (status < 0) {
is_ssp_shutdown = !is_sensorhub_working(data);
data->cnt_com_fail += (is_ssp_shutdown)? 0 : 1;
ssp_errf("cnt_com_fail %d , ssp_down %d ", data->cnt_com_fail, is_ssp_shutdown);
return status;
}
if ((status >= 0) && (msg->done != NULL) && (timeout > 0)) {
ret = wait_for_completion_timeout(msg->done,
msecs_to_jiffies(timeout));
if (msg->clean_pending_list_flag) {
ssp_errf("clean_pending_list_flag %d", msg->clean_pending_list_flag);
msg->clean_pending_list_flag = 0;
return -EINVAL;
}
/* when timeout happen */
if (!ret) {
msg->done = NULL;
list_del(&msg->list);
is_ssp_shutdown = !is_sensorhub_working(data);
data->cnt_timeout += (is_ssp_shutdown)? 0 : 1;
if (msg->done != NULL) {
list_del(&msg->list);
}
ssp_errf("cnt_timeout %d, ssp_down %d !!",
data->cnt_timeout, is_ssp_shutdown);
return -EINVAL;
}
}
return status;
}
static void clean_msg(struct ssp_msg *msg)
{
if (msg->buffer != NULL) {
kfree(msg->buffer);
}
kfree(msg);
}
void clean_pending_list(struct ssp_data *data)
{
struct ssp_msg *msg, *n;
ssp_infof("");
mutex_lock(&data->pending_mutex);
list_for_each_entry_safe(msg, n, &data->pending_list, list) {
list_del(&msg->list);
if (msg->done != NULL && !completion_done(msg->done)) {
msg->clean_pending_list_flag = 1;
complete(msg->done);
}
}
mutex_unlock(&data->pending_mutex);
}
int ssp_send_command(struct ssp_data *data, u8 cmd, u8 type, u8 subcmd,
int timeout, char *send_buf, int send_buf_len, char **receive_buf,
int *receive_buf_len)
{
int status = 0;
struct ssp_msg *msg;
DECLARE_COMPLETION_ONSTACK(done);
if ((type < SENSOR_TYPE_MAX) && !(data->sensor_probe_state& (1ULL << type))) {
ssp_infof("Skip this function!, sensor is not connected(0x%llx)", data->sensor_probe_state);
return -ENODEV;
}
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
msg->cmd = cmd;
msg->type = type;
msg->subcmd = subcmd;
msg->length = send_buf_len;
if (timeout > 0) {
if (send_buf != NULL && send_buf_len != 0) {
msg->buffer = kzalloc(send_buf_len, GFP_KERNEL);
memcpy(msg->buffer, send_buf, send_buf_len);
} else {
msg->length = 0;
}
msg->done = &done;
} else {
if (send_buf != NULL && send_buf_len != 0) {
msg->buffer = kzalloc(send_buf_len, GFP_KERNEL);
memcpy(msg->buffer, send_buf, send_buf_len);
} else {
msg->length = 0;
}
msg->done = NULL;
}
ssp_infof("cmd %d type %d subcmd %d send_buf_len %d timeout %d", cmd, type,
subcmd, send_buf_len, timeout);
if (do_transfer(data, msg, timeout) < 0) {
ssp_errf("do_transfer error");
status = ERROR;
}
//mutex_lock(&data->cmd_mutex);
if (((msg->cmd == CMD_GETVALUE) && (receive_buf != NULL) &&
((receive_buf_len != NULL) && (msg->length != 0))) &&
(status != ERROR)) {
if (timeout > 0) {
*receive_buf = kzalloc(msg->length, GFP_KERNEL);
*receive_buf_len = msg->length;
memcpy(*receive_buf, msg->buffer, msg->length);
} else {
ssp_errf("CMD_GETVALUE zero timeout");
//mutex_unlock(&data->cmd_mutex);
return -EINVAL;
}
}
clean_msg(msg);
//mutex_unlock(&data->cmd_mutex);
if(status < 0) {
reset_mcu(data, RESET_TYPE_KERNEL_COM_FAIL);
ssp_errf("status=%d", status);
}
return status;
}
static void get_tm(struct rtc_time *tm)
{
struct timespec ts;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, tm);
}
int enable_sensor(struct ssp_data *data, unsigned int type, u8* buf, int buf_len)
{
int ret = 0;
if (type != SENSOR_TYPE_SCONTEXT)
ret = ssp_send_command(data, CMD_ADD, type, 0, 0, buf, buf_len, NULL, NULL);
if (ret < 0) {
ssp_errf("commnd error %d", ret);
} else {
data->en_info[type].enabled = true;
data->en_info[type].regi_time.timestamp = get_current_timestamp();
get_tm(&(data->en_info[type].regi_time.tm));
}
return ret;
}
int disable_sensor(struct ssp_data *data, unsigned int type, u8 *buf, int buf_len)
{
int ret = 0;
if (type != SENSOR_TYPE_SCONTEXT)
ret = ssp_send_command(data, CMD_REMOVE, type, 0, 0, buf, buf_len, NULL, NULL);
if (ret < 0) {
ssp_errf("commnd error %d", ret);
} else {
data->en_info[type].enabled = false;
data->en_info[type].unregi_time.timestamp = get_current_timestamp();
get_tm(&(data->en_info[type].unregi_time.tm));
}
return ret;
}
static int convert_ap_status(int command)
{
int ret = -1;
switch (command) {
case SCONTEXT_AP_STATUS_SHUTDOWN:
ret = AP_SHUTDOWN;
break;
case SCONTEXT_AP_STATUS_WAKEUP:
ret = LCD_ON;
break;
case SCONTEXT_AP_STATUS_SLEEP:
ret = LCD_OFF;
break;
case SCONTEXT_AP_STATUS_RESUME:
ret = AP_RESUME;
break;
case SCONTEXT_AP_STATUS_SUSPEND:
ret = AP_SUSPEND;
break;
#if 0
case SCONTEXT_AP_STATUS_RESET:
ret = AP_SHUTDOWN;
break;
#endif
case SCONTEXT_AP_STATUS_POW_CONNECTED:
ret = POW_CONNECTED;
break;
case SCONTEXT_AP_STATUS_POW_DISCONNECTED:
ret = POW_DISCONNECTED;
break;
case SCONTEXT_AP_STATUS_CALL_IDLE:
ret = CALL_IDLE;
break;
case SCONTEXT_AP_STATUS_CALL_ACTIVE:
ret = CALL_ACTIVE;
break;
}
return ret;
}
int ssp_send_status(struct ssp_data *data, char command)
{
int ret = 0;
ret = ssp_send_command(data, CMD_SETVALUE, TYPE_MCU, convert_ap_status(command),
0, NULL, 0, NULL, NULL);
if (ret != SUCCESS) {
ssp_errf("command 0x%x failed %d", command, ret);
return ERROR;
}
ssp_infof("command 0x%x", command);
return SUCCESS;
}