| /* Copyright 2012 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* Host command module for Chrome EC */ |
| |
| #include "builtin/assert.h" |
| #include "common.h" |
| #include "console.h" |
| #include "ec_commands.h" |
| #include "host_command.h" |
| #include "link_defs.h" |
| #include "lpc.h" |
| #include "power.h" |
| #include "printf.h" |
| #include "shared_mem.h" |
| #include "system.h" |
| #include "system_safe_mode.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| |
| /* Console output macros */ |
| #define CPUTS(outstr) cputs(CC_HOSTCMD, outstr) |
| #define CPRINTF(format, args...) cprintf(CC_HOSTCMD, format, ##args) |
| #define CPRINTS(format, args...) cprints(CC_HOSTCMD, format, ##args) |
| |
| #if !defined(CONFIG_HOSTCMD_X86) || defined(CONFIG_ZTEST) |
| /* |
| * Simulated memory map. Must be word-aligned, because some of the elements |
| * in the memory map are words. |
| */ |
| static uint8_t host_memmap[EC_MEMMAP_SIZE] __aligned(4); |
| #endif |
| |
| uint8_t *host_get_memmap(int offset) |
| { |
| #if defined(CONFIG_HOSTCMD_X86) && !defined(CONFIG_ZTEST) |
| return lpc_get_memmap_range() + offset; |
| #else |
| return host_memmap + offset; |
| #endif |
| } |
| |
| void host_command_init(void) |
| { |
| /* Initialize memory map ID area */ |
| host_get_memmap(EC_MEMMAP_ID)[0] = 'E'; |
| host_get_memmap(EC_MEMMAP_ID)[1] = 'C'; |
| *host_get_memmap(EC_MEMMAP_ID_VERSION) = 1; |
| *host_get_memmap(EC_MEMMAP_EVENTS_VERSION) = 1; |
| |
| #ifdef CONFIG_HOSTCMD_EVENTS |
| host_set_single_event(EC_HOST_EVENT_INTERFACE_READY); |
| HOST_EVENT_CPRINTS("hostcmd init", host_get_events()); |
| #endif |
| } |
| |
| int host_request_expected_size(const struct ec_host_request *r) |
| { |
| /* Check host request version */ |
| if (r->struct_version != EC_HOST_REQUEST_VERSION) |
| return 0; |
| |
| /* Reserved byte should be 0 */ |
| if (r->reserved) |
| return 0; |
| |
| return sizeof(*r) + r->data_len; |
| } |
| /*****************************************************************************/ |
| /* Host commands */ |
| |
| /* TODO(crosbug.com/p/11223): Remove this once the kernel no longer cares */ |
| static enum ec_status |
| host_command_proto_version(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_proto_version *r = args->response; |
| |
| r->version = EC_PROTO_VERSION; |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_PROTO_VERSION, host_command_proto_version, |
| EC_VER_MASK(0)); |
| |
| static enum ec_status host_command_hello(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_hello *p = args->params; |
| struct ec_response_hello *r = args->response; |
| uint32_t d = p->in_data; |
| |
| r->out_data = d + 0x01020304; |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_HELLO, host_command_hello, EC_VER_MASK(0)); |
| |
| #ifndef CONFIG_HOSTCMD_X86 |
| /* |
| * Host command to read memory map is not needed on LPC, because LPC can |
| * directly map the data to the host's memory space. |
| */ |
| static enum ec_status |
| host_command_read_memmap(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_read_memmap *p = args->params; |
| |
| /* Copy params out of data before we overwrite it with output */ |
| uint8_t offset = p->offset; |
| uint8_t size = p->size; |
| |
| if (size > EC_MEMMAP_SIZE || offset > EC_MEMMAP_SIZE || |
| offset + size > EC_MEMMAP_SIZE || size > args->response_max) |
| return EC_RES_INVALID_PARAM; |
| |
| /* Make sure switch data is initialized */ |
| if (offset == EC_MEMMAP_SWITCHES && |
| *host_get_memmap(EC_MEMMAP_SWITCHES_VERSION) == 0) |
| return EC_RES_UNAVAILABLE; |
| |
| memcpy(args->response, host_get_memmap(offset), size); |
| args->response_size = size; |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_READ_MEMMAP, host_command_read_memmap, |
| EC_VER_MASK(0)); |
| #endif |
| |
| /* CONFIG_EC_HOST_CMD enables the upstream Host Command support */ |
| #ifndef CONFIG_EC_HOST_CMD |
| static enum ec_status |
| host_command_get_cmd_versions(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_get_cmd_versions *p = args->params; |
| const struct ec_params_get_cmd_versions_v1 *p_v1 = args->params; |
| struct ec_response_get_cmd_versions *r = args->response; |
| |
| const struct host_command *cmd = (args->version == 1) ? |
| find_host_command(p_v1->cmd) : |
| find_host_command(p->cmd); |
| |
| if (!cmd) |
| return EC_RES_INVALID_PARAM; |
| |
| r->version_mask = cmd->version_mask; |
| |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_CMD_VERSIONS, host_command_get_cmd_versions, |
| EC_VER_MASK(0) | EC_VER_MASK(1)); |
| #endif /* CONFIG_EC_HOST_CMD */ |
| |
| /* Returns what we tell it to. */ |
| static enum ec_status |
| host_command_test_protocol(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_test_protocol *p = args->params; |
| struct ec_response_test_protocol *r = args->response; |
| int copy_len = MIN(p->ret_len, sizeof(r->buf)); /* p,r bufs same size */ |
| |
| memset(r->buf, 0, sizeof(r->buf)); |
| memcpy(r->buf, p->buf, copy_len); |
| args->response_size = copy_len; |
| |
| return p->ec_result; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_TEST_PROTOCOL, host_command_test_protocol, |
| EC_VER_MASK(0)); |
| |
| /* Returns supported features. */ |
| static enum ec_status |
| host_command_get_features(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_get_features *r = args->response; |
| args->response_size = sizeof(*r); |
| |
| memset(r, 0, sizeof(*r)); |
| r->flags[0] = get_feature_flags0(); |
| r->flags[1] = get_feature_flags1(); |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_FEATURES, host_command_get_features, |
| EC_VER_MASK(0)); |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| #ifdef CONFIG_HOST_COMMAND_STATUS |
| /* Returns current command status (busy or not) */ |
| static enum ec_status |
| host_command_get_comms_status(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_get_comms_status *r = args->response; |
| bool command_ended; |
| |
| #ifndef CONFIG_EC_HOST_CMD |
| command_ended = host_command_in_process_ended(); |
| #else |
| command_ended = ec_host_cmd_send_in_progress_ended(); |
| #endif |
| |
| r->flags = command_ended ? 0 : EC_COMMS_STATUS_PROCESSING; |
| args->response_size = sizeof(*r); |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_COMMS_STATUS, host_command_get_comms_status, |
| EC_VER_MASK(0)); |
| |
| /* Resend the last saved response */ |
| static enum ec_status |
| host_command_resend_response(struct host_cmd_handler_args *args) |
| { |
| uint16_t result; |
| |
| #ifndef CONFIG_EC_HOST_CMD |
| result = host_command_get_saved_result(); |
| #else |
| result = ec_host_cmd_send_in_progress_status(); |
| #endif |
| |
| /* Handle resending response */ |
| args->response_size = 0; |
| |
| #ifndef CONFIG_EC_HOST_CMD |
| args->result = result; |
| return EC_RES_SUCCESS; |
| #else |
| return result; |
| #endif |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_RESEND_RESPONSE, host_command_resend_response, |
| EC_VER_MASK(0)); |
| #endif /* CONFIG_HOST_COMMAND_STATUS */ |
| |
| #if defined(CONFIG_AP_PWRSEQ_S0IX_COUNTER) || \ |
| defined(CONFIG_POWERSEQ_S0IX_COUNTER) |
| static enum ec_status |
| host_command_get_s0ix_cnt(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_s0ix_cnt *p = args->params; |
| struct ec_response_s0ix_cnt *r = args->response; |
| |
| if (p->flags & EC_S0IX_COUNTER_RESET) { |
| atomic_clear(&s0ix_counter); |
| } |
| |
| r->s0ix_counter = atomic_get(&s0ix_counter); |
| |
| args->response_size = sizeof(*r); |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_GET_S0IX_COUNTER, host_command_get_s0ix_cnt, |
| EC_VER_MASK(0)); |
| #endif |
| |
| static enum ec_status |
| host_command_ap_fw_state(struct host_cmd_handler_args *args) |
| { |
| const struct ec_params_ap_fw_state *p = args->params; |
| |
| ccprintf("AP_FW %x\n", p->state); |
| args->response_size = 0; |
| |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_AP_FW_STATE, host_command_ap_fw_state, |
| EC_VER_MASK(0)); |