cros_ec_dev.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * This file is part of the flashrom project.
  3. *
  4. * Copyright (C) 2012 Google Inc.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. *
  13. * Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. *
  17. * Neither the name of Google or the names of contributors or
  18. * licensors may be used to endorse or promote products derived from this
  19. * software without specific prior written permission.
  20. *
  21. * This software is provided "AS IS," without a warranty of any kind.
  22. * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
  23. * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
  24. * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
  25. * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE
  26. * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
  27. * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
  28. * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
  29. * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
  30. * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
  31. * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
  32. * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  33. */
  34. #include <assert.h>
  35. #include <errno.h>
  36. #include <fcntl.h>
  37. #include <inttypes.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <unistd.h>
  42. #include <sys/ioctl.h>
  43. #include <sys/stat.h>
  44. #include <sys/types.h>
  45. #include "cros_ec_dev.h"
  46. #include "file.h"
  47. #include "flash.h"
  48. #include "cros_ec_commands.h"
  49. #include "cros_ec.h"
  50. #include "programmer.h"
  51. #define CROS_EC_DEV_PREFIX "/dev/cros_"
  52. #define CROS_EC_COMMAND_RETRIES 50
  53. int cros_ec_fd; /* File descriptor for kernel device */
  54. /* ec device interface v1 (used with Chrome OS v3.18 and earlier) */
  55. /**
  56. * Wait for a command to complete, then return the response
  57. *
  58. * This is called when we get an EAGAIN response from the EC. We need to
  59. * send EC_CMD_GET_COMMS_STATUS commands until the EC indicates it is
  60. * finished the command that we originally sent.
  61. *
  62. * returns 0 if command is successful, <0 to indicate timeout or error
  63. */
  64. static int command_wait_for_response(void)
  65. {
  66. struct ec_response_get_comms_status status;
  67. struct cros_ec_command cmd;
  68. int ret;
  69. int i;
  70. cmd.version = 0;
  71. cmd.command = EC_CMD_GET_COMMS_STATUS;
  72. cmd.outdata = NULL;
  73. cmd.outsize = 0;
  74. cmd.indata = (uint8_t *)&status;
  75. cmd.insize = sizeof(status);
  76. /* FIXME: magic delay until we fix the underlying problem (probably in
  77. the kernel driver) */
  78. usleep(10 * 1000);
  79. for (i = 1; i <= CROS_EC_COMMAND_RETRIES; i++) {
  80. ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD, &cmd, sizeof(cmd));
  81. if (ret < 0) {
  82. msg_perr("%s(): CrOS EC command failed: %d, errno=%d\n",
  83. __func__, ret, errno);
  84. ret = -EC_RES_ERROR;
  85. break;
  86. }
  87. if (cmd.result) {
  88. msg_perr("%s(): CrOS EC command failed: result=%d\n",
  89. __func__, cmd.result);
  90. ret = -cmd.result;
  91. break;
  92. }
  93. if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) {
  94. ret = -EC_RES_SUCCESS;
  95. break;
  96. }
  97. usleep(1000);
  98. }
  99. return ret;
  100. }
  101. /*
  102. * __cros_ec_command_dev - Issue command to CROS_EC device
  103. *
  104. * @command: command code
  105. * @outdata: data to send to EC
  106. * @outsize: number of bytes in outbound payload
  107. * @indata: (unallocated) buffer to store data received from EC
  108. * @insize: number of bytes in inbound payload
  109. *
  110. * This uses the kernel Chrome OS EC driver to communicate with the EC.
  111. *
  112. * The outdata and indata buffers contain payload data (if any); command
  113. * and response codes as well as checksum data are handled transparently by
  114. * this function.
  115. *
  116. * Returns >=0 for success, or negative if other error.
  117. */
  118. static int __cros_ec_command_dev(int command, int version,
  119. const void *outdata, int outsize,
  120. void *indata, int insize)
  121. {
  122. struct cros_ec_command cmd;
  123. int ret;
  124. cmd.version = version;
  125. cmd.command = command;
  126. cmd.outdata = outdata;
  127. cmd.outsize = outsize;
  128. cmd.indata = indata;
  129. cmd.insize = insize;
  130. ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD, &cmd, sizeof(cmd));
  131. if (ret < 0 && errno == EAGAIN) {
  132. ret = command_wait_for_response();
  133. cmd.result = 0;
  134. }
  135. if (ret < 0) {
  136. msg_perr("%s(): Command 0x%02x failed: %d, errno=%d\n",
  137. __func__, command, ret, errno);
  138. return -EC_RES_ERROR;
  139. }
  140. if (cmd.result) {
  141. msg_pdbg("%s(): Command 0x%02x returned result: %d\n",
  142. __func__, command, cmd.result);
  143. return -cmd.result;
  144. }
  145. return ret;
  146. }
  147. /*
  148. * ec device interface v2
  149. * (used with upstream kernel as well as with Chrome OS v4.4 and later)
  150. */
  151. static int command_wait_for_response_v2(void)
  152. {
  153. uint8_t s_cmd_buf[sizeof(struct cros_ec_command_v2) +
  154. sizeof(struct ec_response_get_comms_status)];
  155. struct ec_response_get_comms_status *status;
  156. struct cros_ec_command_v2 *s_cmd;
  157. int ret;
  158. int i;
  159. s_cmd = (struct cros_ec_command_v2 *)s_cmd_buf;
  160. status = (struct ec_response_get_comms_status *)s_cmd->data;
  161. s_cmd->version = 0;
  162. s_cmd->command = EC_CMD_GET_COMMS_STATUS;
  163. s_cmd->outsize = 0;
  164. s_cmd->insize = sizeof(*status);
  165. /*
  166. * FIXME: magic delay until we fix the underlying problem (probably in
  167. * the kernel driver)
  168. */
  169. usleep(10 * 1000);
  170. for (i = 1; i <= CROS_EC_COMMAND_RETRIES; i++) {
  171. ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD_V2, s_cmd_buf,
  172. sizeof(s_cmd_buf));
  173. if (ret) {
  174. msg_perr("%s(): CrOS EC command failed: %d, errno=%d\n",
  175. __func__, ret, errno);
  176. ret = -EC_RES_ERROR;
  177. break;
  178. }
  179. if (s_cmd->result) {
  180. msg_perr("%s(): CrOS EC command failed: result=%d\n",
  181. __func__, s_cmd->result);
  182. ret = -s_cmd->result;
  183. break;
  184. }
  185. if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) {
  186. ret = -EC_RES_SUCCESS;
  187. break;
  188. }
  189. usleep(1000);
  190. }
  191. return ret;
  192. }
  193. static int __cros_ec_command_dev_v2(int command, int version,
  194. const void *outdata, int outsize,
  195. void *indata, int insize)
  196. {
  197. struct cros_ec_command_v2 *s_cmd;
  198. int size = sizeof(struct cros_ec_command_v2) + max(outsize, insize);
  199. int ret;
  200. assert(outsize == 0 || outdata != NULL);
  201. assert(insize == 0 || indata != NULL);
  202. s_cmd = malloc(size);
  203. if (s_cmd == NULL)
  204. return -EC_RES_ERROR;
  205. s_cmd->command = command;
  206. s_cmd->version = version;
  207. s_cmd->result = 0xff;
  208. s_cmd->outsize = outsize;
  209. s_cmd->insize = insize;
  210. memcpy(s_cmd->data, outdata, outsize);
  211. ret = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD_V2, s_cmd, size);
  212. if (ret < 0 && errno == -EAGAIN) {
  213. ret = command_wait_for_response_v2();
  214. s_cmd->result = 0;
  215. }
  216. if (ret < 0) {
  217. msg_perr("%s(): Command 0x%02x failed: %d, errno=%d\n",
  218. __func__, command, ret, errno);
  219. free(s_cmd);
  220. return -EC_RES_ERROR;
  221. }
  222. if (s_cmd->result) {
  223. msg_pdbg("%s(): Command 0x%02x returned result: %d\n",
  224. __func__, command, s_cmd->result);
  225. free(s_cmd);
  226. return -s_cmd->result;
  227. }
  228. memcpy(indata, s_cmd->data, min(ret, insize));
  229. free(s_cmd);
  230. return min(ret, insize);
  231. }
  232. /*
  233. * Attempt to communicate with kernel using old ioctl format.
  234. * If it returns ENOTTY, assume that this kernel uses the new format.
  235. */
  236. static int ec_dev_is_v2()
  237. {
  238. struct ec_params_hello h_req = {
  239. .in_data = 0xa0b0c0d0
  240. };
  241. struct ec_response_hello h_resp;
  242. struct cros_ec_command s_cmd = { };
  243. int r;
  244. s_cmd.command = EC_CMD_HELLO;
  245. s_cmd.result = 0xff;
  246. s_cmd.outsize = sizeof(h_req);
  247. s_cmd.outdata = (uint8_t *)&h_req;
  248. s_cmd.insize = sizeof(h_resp);
  249. s_cmd.indata = (uint8_t *)&h_resp;
  250. r = ioctl(cros_ec_fd, CROS_EC_DEV_IOCXCMD, &s_cmd, sizeof(s_cmd));
  251. if (r < 0 && errno == ENOTTY)
  252. return 1;
  253. return 0;
  254. }
  255. static int (*__cros_ec_command_dev_fn)(int command, int version,
  256. const void *outdata, int outsize, void *indata, int insize);
  257. /*
  258. * cros_ec_command_dev - Issue command to CROS_EC device with retry
  259. *
  260. * @command: command code
  261. * @outdata: data to send to EC
  262. * @outsize: number of bytes in outbound payload
  263. * @indata: (unallocated) buffer to store data received from EC
  264. * @insize: number of bytes in inbound payload
  265. *
  266. * This uses the kernel Chrome OS EC driver to communicate with the EC.
  267. *
  268. * The outdata and indata buffers contain payload data (if any); command
  269. * and response codes as well as checksum data are handled transparently by
  270. * this function.
  271. *
  272. * Returns >=0 for success, or negative if other error.
  273. */
  274. static int cros_ec_command_dev(int command, int version,
  275. const void *outdata, int outsize,
  276. void *indata, int insize)
  277. {
  278. int ret = EC_RES_ERROR;
  279. int try;
  280. for (try = 0; try < CROS_EC_DEV_RETRY; try++) {
  281. ret = __cros_ec_command_dev_fn(command, version, outdata,
  282. outsize, indata, insize);
  283. if (ret >= 0)
  284. return ret;
  285. }
  286. return ret;
  287. }
  288. static struct cros_ec_priv cros_ec_dev_priv = {
  289. .detected = 0,
  290. .ec_command = cros_ec_command_dev,
  291. .dev = "ec",
  292. };
  293. static struct opaque_programmer opaque_programmer_cros_ec_dev = {
  294. .max_data_read = 128,
  295. .max_data_write = 128,
  296. .probe = cros_ec_probe_size,
  297. .read = cros_ec_read,
  298. .write = cros_ec_write,
  299. .erase = cros_ec_block_erase,
  300. };
  301. static int cros_ec_dev_shutdown(void *data)
  302. {
  303. close(cros_ec_fd);
  304. return 0;
  305. }
  306. int cros_ec_probe_dev(void)
  307. {
  308. char dev_path[32];
  309. if (alias && alias->type != ALIAS_EC)
  310. return 1;
  311. if (cros_ec_parse_param(&cros_ec_dev_priv))
  312. return 1;
  313. snprintf(dev_path, sizeof(dev_path), "%s%s",
  314. CROS_EC_DEV_PREFIX, cros_ec_dev_priv.dev);
  315. msg_pdbg("%s: probing for CROS_EC at %s\n", __func__, dev_path);
  316. cros_ec_fd = open(dev_path, O_RDWR);
  317. if (cros_ec_fd < 0)
  318. return cros_ec_fd;
  319. if (ec_dev_is_v2())
  320. __cros_ec_command_dev_fn = __cros_ec_command_dev_v2;
  321. else
  322. __cros_ec_command_dev_fn = __cros_ec_command_dev;
  323. if (cros_ec_test(&cros_ec_dev_priv))
  324. return 1;
  325. cros_ec_set_max_size(&cros_ec_dev_priv, &opaque_programmer_cros_ec_dev);
  326. msg_pdbg("CROS_EC detected at %s\n", dev_path);
  327. register_opaque_programmer(&opaque_programmer_cros_ec_dev);
  328. register_shutdown(cros_ec_dev_shutdown, NULL);
  329. cros_ec_dev_priv.detected = 1;
  330. cros_ec_priv = &cros_ec_dev_priv;
  331. return 0;
  332. }