diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/comm-lpc.c | 114 |
1 files changed, 108 insertions, 6 deletions
diff --git a/util/comm-lpc.c b/util/comm-lpc.c index 3548f4eb29..c6ddee93c5 100644 --- a/util/comm-lpc.c +++ b/util/comm-lpc.c @@ -126,6 +126,99 @@ static int ec_command_lpc(int command, int version, return args.data_size; } +static int ec_command_lpc_3(int command, int version, + const void *outdata, int outsize, + void *indata, int insize) +{ + struct ec_host_request rq; + struct ec_host_response rs; + const uint8_t *d; + uint8_t *dout; + int csum = 0; + int i; + + /* Fail if output size is too big */ + if (outsize + sizeof(rq) > EC_HOST_PACKET_SIZE) + return -EC_RES_REQUEST_TRUNCATED; + + /* Fill in request packet */ + /* TODO: this should be common to all protocols */ + rq.struct_version = EC_HOST_REQUEST_VERSION; + rq.checksum = 0; + rq.command = command; + rq.command_version = version; + rq.reserved = 0; + rq.data_len = outsize; + + /* Copy data and start checksum */ + for (i = 0, d = (const uint8_t *)outdata; i < outsize; i++, d++) { + outb(*d, EC_LPC_ADDR_HOST_PACKET + sizeof(rq) + i); + csum += *d; + } + + /* Finish checksum */ + for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++) + csum += *d; + + /* Write checksum field so the entire packet sums to 0 */ + rq.checksum = (uint8_t)(-csum); + + /* Copy header */ + for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++) + outb(*d, EC_LPC_ADDR_HOST_PACKET + i); + + /* Start the command */ + outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); + + if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) { + fprintf(stderr, "Timeout waiting for EC response\n"); + return -EC_RES_ERROR; + } + + /* Check result */ + i = inb(EC_LPC_ADDR_HOST_DATA); + if (i) { + fprintf(stderr, "EC returned error result code %d\n", i); + return -i; + } + + /* Read back response header and start checksum */ + csum = 0; + for (i = 0, dout = (uint8_t *)&rs; i < sizeof(rs); i++, dout++) { + *dout = inb(EC_LPC_ADDR_HOST_PACKET + i); + csum += *dout; + } + + if (rs.struct_version != EC_HOST_RESPONSE_VERSION) { + fprintf(stderr, "EC response version mismatch\n"); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs.reserved) { + fprintf(stderr, "EC response reserved != 0\n"); + return -EC_RES_INVALID_RESPONSE; + } + + if (rs.data_len > insize) { + fprintf(stderr, "EC returned too much data\n"); + return -EC_RES_RESPONSE_TOO_BIG; + } + + /* Read back data and update checksum */ + for (i = 0, dout = (uint8_t *)indata; i < rs.data_len; i++, dout++) { + *dout = inb(EC_LPC_ADDR_HOST_PACKET + sizeof(rs) + i); + csum += *dout; + } + + /* Verify checksum */ + if ((uint8_t)csum) { + fprintf(stderr, "EC response has invalid checksum\n"); + return -EC_RES_INVALID_CHECKSUM; + } + + /* Return actual amount of data received */ + return rs.data_len; +} static int ec_readmem_lpc(int offset, int bytes, void *dest) { @@ -194,15 +287,24 @@ int comm_init_lpc(void) * in args when it responds. */ if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E' || - inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C' || - !(inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS) & - EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { - fprintf(stderr, "EC doesn't support command args.\n"); + inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C') { + fprintf(stderr, "Missing Chromium EC memory map.\n"); + return -5; + } + + /* Check which command version we'll use */ + i = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS); + + if (i & EC_HOST_CMD_FLAG_VERSION_3) { + ec_command = ec_command_lpc_3; + } else if (i & EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED) { + ec_command = ec_command_lpc; + } else { + fprintf(stderr, "EC doesn't support protocols we need.\n"); return -5; } - /* Okay, this works */ - ec_command = ec_command_lpc; + /* Either one supports reading mapped memory directly */ ec_readmem = ec_readmem_lpc; return 0; } |