#pragma GCC diagnostic ignored "-Wsign-compare"
-typedef int (*SendCommandFunc)(int cmd, int cmd_version, const void *dout,
- int dout_len, void *dinp, int din_len);
-
-SendCommandFunc send_command_func;
-
-static struct ec_host_request *proto3_request;
-static int proto3_request_size;
-
-static struct ec_host_response *proto3_response;
-static int proto3_response_size;
-
-static int max_param_size;
-static int passthru_param_size;
-static int initialized;
-
static const uint64_t FramingTimeoutUs = 1000 * 1000;
static const uint8_t EcFramingByte = 0xec;
/*****************************************************************************/
-/*
- * Value written to legacy command port / prefix byte to indicate protocol
- * 3+ structs are being used. Usage is bus-dependent.
- */
-#define EC_COMMAND_PROTOCOL_3 0xda
-
-#define EC_HOST_REQUEST_VERSION 3
-
-/* Version 3 request from host */
-struct ec_host_request {
- /* Struct version (=3)
- *
- * EC will return EC_RES_INVALID_HEADER if it receives a header with a
- * version it doesn't know how to parse.
- */
- uint8_t struct_version;
-
- /*
- * Checksum of request and data; sum of all bytes including checksum
- * should total to 0.
- */
- uint8_t checksum;
-
- /* Command code */
- uint16_t command;
-
- /* Command version */
- uint8_t command_version;
-
- /* Unused byte in current protocol version; set to 0 */
- uint8_t reserved;
-
- /* Length of data which follows this header */
- uint16_t data_len;
-} __packed;
-
-#define EC_HOST_RESPONSE_VERSION 3
-
-/* Version 3 response from EC */
-struct ec_host_response {
- /* Struct version (=3) */
- uint8_t struct_version;
-
- /*
- * Checksum of response and data; sum of all bytes including checksum
- * should total to 0.
- */
- uint8_t checksum;
-
- /* Result code (EC_RES_*) */
- uint16_t result;
-
- /* Length of data which follows this header */
- uint16_t data_len;
-
- /* Unused bytes in current protocol version; set to 0 */
- uint16_t reserved;
-} __packed;
-
-/*****************************************************************************/
-/*
- * Notes on commands:
- *
- * Each command is an 16-bit command value. Commands which take params or
- * return response data specify structs for that data. If no struct is
- * specified, the command does not input or output data, respectively.
- * Parameter/response length is implicit in the structs. Some underlying
- * communication protocols (I2C, SPI) may add length or checksum headers, but
- * those are implementation-dependent and not defined here.
- */
-
-/*****************************************************************************/
-/* General / test commands */
-
-/*
- * Get protocol version, used to deal with non-backward compatible protocol
- * changes.
- */
-#define EC_CMD_PROTO_VERSION 0x00
-
-struct ec_response_proto_version {
- uint32_t version;
-} __packed;
-
-
-/*
- * Check EC communcations status (busy). This is needed on i2c/spi but not
- * on lpc since it has its own out-of-band busy indicator.
- *
- * lpc must read the status from the command register. Attempting this on
- * lpc will overwrite the args/parameter space and corrupt its data.
- */
-#define EC_CMD_GET_COMMS_STATUS 0x09
-
-/* Avoid using ec_status which is for return values */
-enum ec_comms_status {
- EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */
-};
-
-struct ec_response_get_comms_status {
- uint32_t flags; /* Mask of enum ec_comms_status */
-} __packed;
-
-/* Get prococol information */
-#define EC_CMD_GET_PROTOCOL_INFO 0x0b
-
-/* Flags for ec_response_get_protocol_info.flags */
-/* EC_RES_IN_PROGRESS may be returned if a command is slow */
-#define EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED (1 << 0)
-
-struct ec_response_get_protocol_info {
- /* Fields which exist if at least protocol version 3 supported */
-
- /* Bitmask of protocol versions supported (1 << n means version n)*/
- uint32_t protocol_versions;
-
- /* Maximum request packet size, in bytes */
- uint16_t max_request_packet_size;
-
- /* Maximum response packet size, in bytes */
- uint16_t max_response_packet_size;
-
- /* Flags; see EC_PROTOCOL_INFO_* */
- uint32_t flags;
-} __packed;
-
-
-/*****************************************************************************/
-/* Real-time clock */
-
-/* RTC params and response structures */
-struct ec_params_rtc {
- uint32_t time;
-} __packed;
-
-struct ec_response_rtc {
- uint32_t time;
-} __packed;
-
-/* These use ec_response_rtc */
-#define EC_CMD_RTC_GET_VALUE 0x44
-#define EC_CMD_RTC_GET_ALARM 0x45
-
-/* These all use ec_params_rtc */
-#define EC_CMD_RTC_SET_VALUE 0x46
-#define EC_CMD_RTC_SET_ALARM 0x47
-
-/*****************************************************************************/
-/* MKBP - Matrix KeyBoard Protocol */
-
-/*
- * Read key state
- *
- * Returns raw data for keyboard cols; see ec_response_mkbp_info.cols for
- * expected response size.
- */
#define EC_CMD_MKBP_STATE 0x60
-/* Simulate key press */
-#define EC_CMD_MKBP_SIMULATE_KEY 0x62
-
-struct ec_params_mkbp_simulate_key {
- uint8_t col;
- uint8_t row;
- uint8_t pressed;
-} __packed;
-
-/* Configure keyboard scanning */
-#define EC_CMD_MKBP_SET_CONFIG 0x64
-#define EC_CMD_MKBP_GET_CONFIG 0x65
-
-/* flags */
-enum mkbp_config_flags {
- EC_MKBP_FLAGS_ENABLE = 1, /* Enable keyboard scanning */
-};
-
-enum mkbp_config_valid {
- EC_MKBP_VALID_SCAN_PERIOD = 1 << 0,
- EC_MKBP_VALID_POLL_TIMEOUT = 1 << 1,
- EC_MKBP_VALID_MIN_POST_SCAN_DELAY = 1 << 3,
- EC_MKBP_VALID_OUTPUT_SETTLE = 1 << 4,
- EC_MKBP_VALID_DEBOUNCE_DOWN = 1 << 5,
- EC_MKBP_VALID_DEBOUNCE_UP = 1 << 6,
- EC_MKBP_VALID_FIFO_MAX_DEPTH = 1 << 7,
-};
-
-/* Configuration for our key scanning algorithm */
-struct ec_mkbp_config {
- uint32_t valid_mask; /* valid fields */
- uint8_t flags; /* some flags (enum mkbp_config_flags) */
- uint8_t valid_flags; /* which flags are valid */
- uint16_t scan_period_us; /* period between start of scans */
- /* revert to interrupt mode after no activity for this long */
- uint32_t poll_timeout_us;
- /*
- * minimum post-scan relax time. Once we finish a scan we check
- * the time until we are due to start the next one. If this time is
- * shorter this field, we use this instead.
- */
- uint16_t min_post_scan_delay_us;
- /* delay between setting up output and waiting for it to settle */
- uint16_t output_settle_us;
- uint16_t debounce_down_us; /* time for debounce on key down */
- uint16_t debounce_up_us; /* time for debounce on key up */
- /* maximum depth to allow for fifo (0 = no keyscan output) */
- uint8_t fifo_max_depth;
-} __packed;
-
-struct ec_params_mkbp_set_config {
- struct ec_mkbp_config config;
-} __packed;
-
-struct ec_response_mkbp_get_config {
- struct ec_mkbp_config config;
-} __packed;
-
-/* Run the key scan emulation */
-#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66
-
-enum ec_keyscan_seq_cmd {
- EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */
- EC_KEYSCAN_SEQ_CLEAR = 1, /* Clear sequence */
- EC_KEYSCAN_SEQ_ADD = 2, /* Add item to sequence */
- EC_KEYSCAN_SEQ_START = 3, /* Start running sequence */
- EC_KEYSCAN_SEQ_COLLECT = 4, /* Collect sequence summary data */
-};
-
-enum ec_collect_flags {
- /*
- * Indicates this scan was processed by the EC. Due to timing, some
- * scans may be skipped.
- */
- EC_KEYSCAN_SEQ_FLAG_DONE = 1 << 0,
-};
-
-struct ec_collect_item {
- uint8_t flags; /* some flags (enum ec_collect_flags) */
-};
-
-struct ec_params_keyscan_seq_ctrl {
- uint8_t cmd; /* Command to send (enum ec_keyscan_seq_cmd) */
- union {
- struct {
- uint8_t active; /* still active */
- uint8_t num_items; /* number of items */
- /* Current item being presented */
- uint8_t cur_item;
- } status;
- struct {
- /*
- * Absolute time for this scan, measured from the
- * start of the sequence.
- */
- uint32_t time_us;
- uint8_t scan[0]; /* keyscan data */
- } add;
- struct {
- uint8_t start_item; /* First item to return */
- uint8_t num_items; /* Number of items to return */
- } collect;
- };
-} __packed;
-
-struct ec_result_keyscan_seq_ctrl {
- union {
- struct {
- uint8_t num_items; /* Number of items */
- /* Data for each item */
- struct ec_collect_item item[0];
- } collect;
- };
-} __packed;
-
-/*
- * Get the next pending MKBP event.
- *
- * Returns EC_RES_UNAVAILABLE if there is no event pending.
- */
-#define EC_CMD_GET_NEXT_EVENT 0x67
-
-enum ec_mkbp_event {
- /* Keyboard matrix changed. The event data is the new matrix state. */
- EC_MKBP_EVENT_KEY_MATRIX = 0,
-
- /* New host event. The event data is 4 bytes of host event flags. */
- EC_MKBP_EVENT_HOST_EVENT = 1,
-
- /* New Sensor FIFO data. The event data is fifo_info structure. */
- EC_MKBP_EVENT_SENSOR_FIFO = 2,
-
- /* Number of MKBP events */
- EC_MKBP_EVENT_COUNT,
-};
-
-
-/*
- * Resend last response (not supported on LPC).
- *
- * Returns EC_RES_UNAVAILABLE if there is no response available - for example,
- * there was no previous command, or the previous command's response was too
- * big to save.
- */
-#define EC_CMD_RESEND_RESPONSE 0xdb
-
/*
* This header byte on a command indicate version 0. Any header byte less
* than this means that we are talking to an old EC which doesn't support
CROS_EC_SPI_OUT_HDR_SIZE = 3
};
-static int send_packet(const void *dout, uint32_t dout_len,
- void *din, uint32_t din_len)
-{
- while (grub_get_time_us() - last_transfer < 200)
- ;
-
- if (spi_start())
- return -1;
-
- // Allow EC to ramp up clock after being awaken.
- // See chrome-os-partner:32223 for more details.
- grub_microsleep (100);
-
- if (spi_send (dout, dout_len)) {
- stop_bus();
- return -1;
- }
-
- // Wait until the EC is ready.
- if (wait_for_frame()) {
- stop_bus();
- return -1;
- }
-
- if (spi_read(din, din_len)) {
- stop_bus();
- return -1;
- }
-
- stop_bus();
- return 0;
-}
-
static grub_uint8_t busbuf[256];
#define MSG_BYTES sizeof (busbuf)
/* Timeout waiting for a flash erase command to complete */
static const int CROS_EC_CMD_TIMEOUT_MS = 5000;
-static void cros_ec_dump_data(const char *name, int cmd, const void *data, int len)
-{
- (void) name;
- (void) cmd;
- (void) data;
- (void) len;
-#ifdef DEBUG
- const uint8_t *bytes = data;
- int i;
-
- printf("%s: ", name);
- if (cmd != -1)
- printf("cmd=%#x: ", cmd);
- for (i = 0; i < len; i++)
- printf("%02x ", bytes[i]);
- printf("\n");
-#endif
-}
-
-/**
- * Create a request packet for protocol version 3.
- *
- * @param rq Request structure to fill
- * @param rq_size Size of request structure, including data
- * @param cmd Command to send (EC_CMD_...)
- * @param cmd_version Version of command to send (EC_VER_...)
- * @param dout Output data (may be NULL If dout_len=0)
- * @param dout_len Size of output data in bytes
- * @return packet size in bytes, or <0 if error.
- */
-static int create_proto3_request(struct ec_host_request *rq, int rq_size,
- int cmd, int cmd_version,
- const void *dout, int dout_len)
-{
- int out_bytes = dout_len + sizeof(*rq);
-
- /* Fail if output size is too big */
- if (out_bytes > rq_size) {
- printf("%s: Cannot send %d bytes\n", __func__, dout_len);
- return -EC_RES_REQUEST_TRUNCATED;
- }
-
- /* Fill in request packet */
- rq->struct_version = EC_HOST_REQUEST_VERSION;
- rq->checksum = 0;
- rq->command = cmd;
- rq->command_version = cmd_version;
- rq->reserved = 0;
- rq->data_len = dout_len;
-
- /* Copy data after header */
- memcpy(rq + 1, dout, dout_len);
-
- /* Write checksum field so the entire packet sums to 0 */
- rq->checksum = (uint8_t)(-cros_ec_calc_checksum(rq, out_bytes));
-
- cros_ec_dump_data("out", cmd, rq, out_bytes);
-
- /* Return size of request packet */
- return out_bytes;
-}
-
-/**
- * Prepare the device to receive a protocol version 3 response.
- *
- * @param rs_size Maximum size of response in bytes
- * @param din_len Maximum amount of data in the response
- * @return maximum expected number of bytes in response, or <0 if error.
- */
-static int prepare_proto3_response_buffer(int rs_size, int din_len)
-{
- int in_bytes = din_len + sizeof(struct ec_host_response);
-
- /* Fail if input size is too big */
- if (in_bytes > rs_size) {
- printf("%s: Cannot receive %d bytes\n", __func__, din_len);
- return -EC_RES_RESPONSE_TOO_BIG;
- }
-
- /* Return expected size of response packet */
- return in_bytes;
-}
-
-/**
- * Process a protocol version 3 response packet.
- *
- * @param resp Response structure to parse
- * @param dinp Returns pointer to response data
- * @param din_len Maximum size of data in response in bytes
- * @return number of bytes of response data, or <0 if error
- */
-static int handle_proto3_response(struct ec_host_response *rs,
- uint8_t **dinp, int din_len)
-{
- int in_bytes;
- int csum;
-
- cros_ec_dump_data("in-header", -1, rs, sizeof(*rs));
-
- /* Check input data */
- if (rs->struct_version != EC_HOST_RESPONSE_VERSION) {
- printf("%s: EC response version mismatch\n", __func__);
- return -EC_RES_INVALID_RESPONSE;
- }
-
- if (rs->reserved) {
- printf("%s: EC response reserved != 0\n", __func__);
- return -EC_RES_INVALID_RESPONSE;
- }
-
- if (rs->data_len > din_len) {
- printf("%s: EC returned too much data\n", __func__);
- return -EC_RES_RESPONSE_TOO_BIG;
- }
-
- cros_ec_dump_data("in-data", -1, rs + sizeof(*rs), rs->data_len);
-
- /* Update in_bytes to actual data size */
- in_bytes = sizeof(*rs) + rs->data_len;
-
- /* Verify checksum */
- csum = cros_ec_calc_checksum(rs, in_bytes);
- if (csum) {
- printf("%s: EC response checksum invalid: 0x%02x\n", __func__,
- csum);
- return -EC_RES_INVALID_CHECKSUM;
- }
-
- /* Return error result, if any */
- if (rs->result)
- return -(int)rs->result;
-
- /* If the caller wants the response data, copy it out */
- if (dinp)
- memcpy(dinp, rs + 1, din_len);
-
- return rs->data_len;
-}
-
-static int send_command_proto3_work(int cmd, int cmd_version,
- const void *dout, int dout_len,
- void *dinp, int din_len)
-{
- int out_bytes, in_bytes;
- int rv;
-
- /* Create request packet */
- out_bytes = create_proto3_request(proto3_request, proto3_request_size,
- cmd, cmd_version, dout, dout_len);
- if (out_bytes < 0)
- return out_bytes;
-
- /* Prepare response buffer */
- in_bytes = prepare_proto3_response_buffer(proto3_response_size,
- din_len);
-
- if (in_bytes < 0)
- return in_bytes;
-
- rv = send_packet(proto3_request, out_bytes,
- proto3_response, in_bytes);
-
- if (rv < 0)
- return rv;
-
- /* Process the response */
- return handle_proto3_response(proto3_response, dinp, din_len);
-}
-
-static int send_command_proto3(int cmd, int cmd_version,
- const void *dout, int dout_len,
- void *dinp, int din_len)
-{
- int rv;
-
- rv = send_command_proto3_work(cmd, cmd_version, dout, dout_len,
- dinp, din_len);
-
- /* If the command doesn't complete, wait a while */
- if (rv == -EC_RES_IN_PROGRESS) {
- struct ec_response_get_comms_status resp;
- uint64_t start;
-
- /* Wait for command to complete */
- start = grub_get_time_ms();
- do {
- int ret;
-
- mdelay(50); /* Insert some reasonable delay */
- ret = send_command_proto3_work(EC_CMD_GET_COMMS_STATUS,
- 0, NULL, 0, &resp, sizeof(resp));
- if (ret < 0)
- return ret;
-
- if (grub_get_time_ms() - start > CROS_EC_CMD_TIMEOUT_MS) {
- printf("%s: Command %#02x timeout",
- __func__, cmd);
- return -EC_RES_TIMEOUT;
- }
- } while (resp.flags & EC_COMMS_STATUS_PROCESSING);
-
- /* OK it completed, so read the status response */
- rv = send_command_proto3_work(EC_CMD_RESEND_RESPONSE,
- 0, NULL, 0, dinp, din_len);
- }
-
- return rv;
-}
-
/**
* Send a command to the ChromeOS EC device and optionally return the reply.
*
len = send_command(cmd, cmd_version, dout,
dout_len, din, din_len);
- /* If the command doesn't complete, wait a while */
- if (len == -EC_RES_IN_PROGRESS) {
- struct ec_response_get_comms_status resp;
- uint64_t start;
-
- /* Wait for command to complete */
- start = grub_get_time_ms();
- do {
- int ret;
-
- mdelay(50); /* Insert some reasonable delay */
- ret = send_command(
- EC_CMD_GET_COMMS_STATUS,
- 0, NULL, 0, &resp, sizeof(resp));
- if (ret < 0)
- return ret;
-
- if (grub_get_time_ms() - start > CROS_EC_CMD_TIMEOUT_MS) {
- printf("%s: Command %#02x timeout",
- __func__, cmd);
- return -EC_RES_TIMEOUT;
- }
- } while (resp.flags & EC_COMMS_STATUS_PROCESSING);
-
- /* OK it completed, so read the status response */
- len = send_command(
- EC_CMD_RESEND_RESPONSE,
- 0, NULL, 0, din, din_len);
- }
-
#ifdef DEBUG
printf("%s: len=%d, din=%p\n", __func__, len, din);
#endif
const void *dout, int dout_len,
void *din, int din_len)
{
- if (!initialized && cros_ec_init())
- return -1;
-
- return send_command_func(cmd, cmd_version, dout, dout_len,
- din, din_len);
-}
-
-static int cros_ec_get_protocol_info(struct ec_response_get_protocol_info *info)
-{
- if (ec_command(EC_CMD_GET_PROTOCOL_INFO, 0, NULL, 0, info,
- sizeof(*info)) < (int)sizeof(*info))
- return -1;
-
- return 0;
+ return send_command_proto2(cmd, cmd_version, dout, dout_len,
+ din, din_len);
}
int cros_ec_scan_keyboard(struct cros_ec_keyscan *scan)
return 0;
}
-
-static int set_max_proto3_sizes(int request_size, int response_size,
- int passthru_size)
-{
- grub_free(proto3_request);
- grub_free(proto3_response);
-
- if (request_size)
- proto3_request = grub_malloc(request_size);
- else
- proto3_request = NULL;
- if (response_size)
- proto3_response = grub_malloc(response_size);
- else
- proto3_response = NULL;
-
- proto3_request_size = request_size;
- proto3_response_size = response_size;
-
- max_param_size = proto3_request_size - sizeof(struct ec_host_request);
-
- passthru_param_size = passthru_size - sizeof(struct ec_host_request);
- if (passthru_param_size > max_param_size)
- passthru_param_size = max_param_size;
-
- return 0;
-}
-
-
-int cros_ec_init(void)
-{
- if (initialized)
- return 0;
-
- initialized = 1;
-
- // Figure out what protocol version to use.
-
- send_command_func = &send_command_proto3;
- if (set_max_proto3_sizes(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE,
- DEFAULT_BUF_SIZE))
- return -1;
-
- struct ec_response_get_protocol_info info;
- if (cros_ec_get_protocol_info(&info)) {
- set_max_proto3_sizes(0, 0, 0);
- send_command_func = NULL;
- } else {
- printf("%s: CrosEC protocol v3 supported (%d, %d)\n",
- __func__,
- info.max_request_packet_size,
- info.max_response_packet_size);
-
- set_max_proto3_sizes(info.max_request_packet_size,
- info.max_response_packet_size,
- 0);
- }
-
- if (!send_command_func) {
- // Fall back to protocol version 2.
- send_command_func = &send_command_proto2;
- max_param_size = EC_PROTO2_MAX_PARAM_SIZE;
- }
-
- return 0;
-}