| |
| #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; |
| } |
| |