123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /*
- * This file is part of the flashrom project.
- *
- * Copyright (C) 2012 Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of Google or the names of contributors or
- * licensors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind.
- * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
- * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE
- * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
- * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
- * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
- * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
- * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
- * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
- * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
- */
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <inttypes.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include "cros_ec_dev.h"
- #include "file.h"
- #include "flash.h"
- #include "cros_ec_commands.h"
- #include "cros_ec.h"
- #include "programmer.h"
- #define CROS_EC_DEV_PREFIX "/dev/cros_"
- #define CROS_EC_COMMAND_RETRIES 50
- int cros_ec_fd; /* File descriptor for kernel device */
- /* ec device interface v1 (used with Chrome OS v3.18 and earlier) */
- /**
- * Wait for a command to complete, then return the response
- *
- * This is called when we get an EAGAIN response from the EC. We need to
- * send EC_CMD_GET_COMMS_STATUS commands until the EC indicates it is
- * finished the command that we originally sent.
- *
- * returns 0 if command is successful, <0 to indicate timeout or error
- */
- static int command_wait_for_response(void)
- {
- struct ec_response_get_comms_status status;
- struct cros_ec_command cmd;
- int ret;
- int i;
- cmd.version = 0;
- cmd.command = EC_CMD_GET_COMMS_STATUS;
- cmd.outdata = NULL;
- cmd.outsize = 0;
- cmd.indata = (uint8_t *)&status;
- cmd.insize = sizeof(status);
- /* FIXME: magic delay until we fix the underlying problem (probably in
- the kernel driver) */
- usleep(10 * 1000);
- for (i = 1; i <= CROS_EC_COMMAND_RETRIES; i++) {
- ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD, &cmd, sizeof(cmd));
- if (ret < 0) {
- msg_perr("%s(): CrOS EC command failed: %d, errno=%d\n",
- __func__, ret, errno);
- ret = -EC_RES_ERROR;
- break;
- }
- if (cmd.result) {
- msg_perr("%s(): CrOS EC command failed: result=%d\n",
- __func__, cmd.result);
- ret = -cmd.result;
- break;
- }
- if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) {
- ret = -EC_RES_SUCCESS;
- break;
- }
- usleep(1000);
- }
- return ret;
- }
- /*
- * __cros_ec_command_dev - Issue command to CROS_EC device
- *
- * @command: command code
- * @outdata: data to send to EC
- * @outsize: number of bytes in outbound payload
- * @indata: (unallocated) buffer to store data received from EC
- * @insize: number of bytes in inbound payload
- *
- * This uses the kernel Chrome OS EC driver to communicate with the EC.
- *
- * The outdata and indata buffers contain payload data (if any); command
- * and response codes as well as checksum data are handled transparently by
- * this function.
- *
- * Returns >=0 for success, or negative if other error.
- */
- static int __cros_ec_command_dev(int command, int version,
- const void *outdata, int outsize,
- void *indata, int insize)
- {
- struct cros_ec_command cmd;
- int ret;
- cmd.version = version;
- cmd.command = command;
- cmd.outdata = outdata;
- cmd.outsize = outsize;
- cmd.indata = indata;
- cmd.insize = insize;
- ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD, &cmd, sizeof(cmd));
- if (ret < 0 && errno == EAGAIN) {
- ret = command_wait_for_response();
- cmd.result = 0;
- }
- if (ret < 0) {
- msg_perr("%s(): Command 0x%02x failed: %d, errno=%d\n",
- __func__, command, ret, errno);
- return -EC_RES_ERROR;
- }
- if (cmd.result) {
- msg_pdbg("%s(): Command 0x%02x returned result: %d\n",
- __func__, command, cmd.result);
- return -cmd.result;
- }
- return ret;
- }
- /*
- * ec device interface v2
- * (used with upstream kernel as well as with Chrome OS v4.4 and later)
- */
- static int command_wait_for_response_v2(void)
- {
- uint8_t s_cmd_buf[sizeof(struct cros_ec_command_v2) +
- sizeof(struct ec_response_get_comms_status)];
- struct ec_response_get_comms_status *status;
- struct cros_ec_command_v2 *s_cmd;
- int ret;
- int i;
- s_cmd = (struct cros_ec_command_v2 *)s_cmd_buf;
- status = (struct ec_response_get_comms_status *)s_cmd->data;
- s_cmd->version = 0;
- s_cmd->command = EC_CMD_GET_COMMS_STATUS;
- s_cmd->outsize = 0;
- s_cmd->insize = sizeof(*status);
- /*
- * FIXME: magic delay until we fix the underlying problem (probably in
- * the kernel driver)
- */
- usleep(10 * 1000);
- for (i = 1; i <= CROS_EC_COMMAND_RETRIES; i++) {
- ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD_V2, s_cmd_buf,
- sizeof(s_cmd_buf));
- if (ret) {
- msg_perr("%s(): CrOS EC command failed: %d, errno=%d\n",
- __func__, ret, errno);
- ret = -EC_RES_ERROR;
- break;
- }
- if (s_cmd->result) {
- msg_perr("%s(): CrOS EC command failed: result=%d\n",
- __func__, s_cmd->result);
- ret = -s_cmd->result;
- break;
- }
- if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) {
- ret = -EC_RES_SUCCESS;
- break;
- }
- usleep(1000);
- }
- return ret;
- }
- static int __cros_ec_command_dev_v2(int command, int version,
- const void *outdata, int outsize,
- void *indata, int insize)
- {
- struct cros_ec_command_v2 *s_cmd;
- int size = sizeof(struct cros_ec_command_v2) + max(outsize, insize);
- int ret;
- assert(outsize == 0 || outdata != NULL);
- assert(insize == 0 || indata != NULL);
- s_cmd = malloc(size);
- if (s_cmd == NULL)
- return -EC_RES_ERROR;
- s_cmd->command = command;
- s_cmd->version = version;
- s_cmd->result = 0xff;
- s_cmd->outsize = outsize;
- s_cmd->insize = insize;
- memcpy(s_cmd->data, outdata, outsize);
- ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD_V2, s_cmd, size);
- if (ret < 0 && errno == -EAGAIN) {
- ret = command_wait_for_response_v2();
- s_cmd->result = 0;
- }
- if (ret < 0) {
- msg_perr("%s(): Command 0x%02x failed: %d, errno=%d\n",
- __func__, command, ret, errno);
- free(s_cmd);
- return -EC_RES_ERROR;
- }
- if (s_cmd->result) {
- msg_pdbg("%s(): Command 0x%02x returned result: %d\n",
- __func__, command, s_cmd->result);
- free(s_cmd);
- return -s_cmd->result;
- }
- memcpy(indata, s_cmd->data, min(ret, insize));
- free(s_cmd);
- return min(ret, insize);
- }
- /*
- * Attempt to communicate with kernel using old ioctl format.
- * If it returns ENOTTY, assume that this kernel uses the new format.
- */
- static int ec_dev_is_v2()
- {
- struct ec_params_hello h_req = {
- .in_data = 0xa0b0c0d0
- };
- struct ec_response_hello h_resp;
- struct cros_ec_command s_cmd = { };
- int r;
- s_cmd.command = EC_CMD_HELLO;
- s_cmd.result = 0xff;
- s_cmd.outsize = sizeof(h_req);
- s_cmd.outdata = (uint8_t *)&h_req;
- s_cmd.insize = sizeof(h_resp);
- s_cmd.indata = (uint8_t *)&h_resp;
- r = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD, &s_cmd, sizeof(s_cmd));
- if (r < 0 && errno == ENOTTY)
- return 1;
- return 0;
- }
- static int (*__cros_ec_command_dev_fn)(int command, int version,
- const void *outdata, int outsize, void *indata, int insize);
- /*
- * cros_ec_command_dev - Issue command to CROS_EC device with retry
- *
- * @command: command code
- * @outdata: data to send to EC
- * @outsize: number of bytes in outbound payload
- * @indata: (unallocated) buffer to store data received from EC
- * @insize: number of bytes in inbound payload
- *
- * This uses the kernel Chrome OS EC driver to communicate with the EC.
- *
- * The outdata and indata buffers contain payload data (if any); command
- * and response codes as well as checksum data are handled transparently by
- * this function.
- *
- * Returns >=0 for success, or negative if other error.
- */
- static int cros_ec_command_dev(int command, int version,
- const void *outdata, int outsize,
- void *indata, int insize)
- {
- int ret = EC_RES_ERROR;
- int try;
- for (try = 0; try < CROS_EC_DEV_RETRY; try++) {
- ret = __cros_ec_command_dev_fn(command, version, outdata,
- outsize, indata, insize);
- if (ret >= 0)
- return ret;
- }
- return ret;
- }
- static struct cros_ec_priv cros_ec_dev_priv = {
- .detected = 0,
- .ec_command = cros_ec_command_dev,
- .dev = "ec",
- };
- static struct opaque_programmer opaque_programmer_cros_ec_dev = {
- .max_data_read = 128,
- .max_data_write = 128,
- .probe = cros_ec_probe_size,
- .read = cros_ec_read,
- .write = cros_ec_write,
- .erase = cros_ec_block_erase,
- };
- static int cros_ec_dev_shutdown(void *data)
- {
- close(cros_ec_fd);
- return 0;
- }
- int cros_ec_probe_dev(void)
- {
- char dev_path[32];
- if (alias && alias->type != ALIAS_EC)
- return 1;
- if (cros_ec_parse_param(&cros_ec_dev_priv))
- return 1;
- snprintf(dev_path, sizeof(dev_path), "%s%s",
- CROS_EC_DEV_PREFIX, cros_ec_dev_priv.dev);
- msg_pdbg("%s: probing for CROS_EC at %s\n", __func__, dev_path);
- cros_ec_fd = open(dev_path, O_RDWR);
- if (cros_ec_fd < 0)
- return cros_ec_fd;
- if (ec_dev_is_v2())
- __cros_ec_command_dev_fn = __cros_ec_command_dev_v2;
- else
- __cros_ec_command_dev_fn = __cros_ec_command_dev;
- if (cros_ec_test(&cros_ec_dev_priv))
- return 1;
- cros_ec_set_max_size(&cros_ec_dev_priv, &opaque_programmer_cros_ec_dev);
- msg_pdbg("CROS_EC detected at %s\n", dev_path);
- register_opaque_programmer(&opaque_programmer_cros_ec_dev);
- register_shutdown(cros_ec_dev_shutdown, NULL);
- cros_ec_dev_priv.detected = 1;
- cros_ec_priv = &cros_ec_dev_priv;
- return 0;
- }
|