1
0
mirror of https://review.coreboot.org/flashrom.git synced 2025-04-26 22:52:34 +02:00
flashrom/tests/spidriver.c
Simon Arlott 81c21880a3 spidriver: Add support for the Excamera Labs SPIDriver programmer
This is a SPI hardware interface with a display (https://spidriver.com/),
connected as an FT230X USB serial device at a fixed baud rate of 460800.

Firmware: https://github.com/jamesbowman/spidriver
Protocol: https://github.com/jamesbowman/spidriver/blob/master/protocol.md

Most of the implementation is copied from the Bus Pirate programmer.

Tested with a SPIDriver v2 by reading FM25Q128A flash memory on Linux.

Change-Id: I07b23c1146d4ad3606b54a1e8dc8030cf4ebf57b
Signed-off-by: Simon Arlott <flashrom@octiron.net>
Reviewed-on: https://review.coreboot.org/c/flashrom/+/86411
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Peter Marheine <pmarheine@chromium.org>
Reviewed-by: Anastasia Klimchuk <aklm@chromium.org>
2025-03-26 21:56:00 +00:00

220 lines
5.0 KiB
C

/*
* This file is part of the flashrom project.
*
* Copyright 2025 Simon Arlott
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "lifecycle.h"
#if CONFIG_SPIDRIVER == 1
#define SPIDRIVER_TEST_DEBUG 0
struct spidriver_state {
// most recent command
unsigned char state;
unsigned char input[256]; // for read() responses
size_t in_len; // available data to read
size_t in_pos; // remaining SPI read count
unsigned char output[256]; // incoming SPI writes
size_t out_pos; // SPI write position in buffer
size_t out_len; // remaining SPI write count
// chip select
bool cs;
size_t cs_count;
// probe detected
bool probe;
size_t cs_probe;
};
static int spidriver_read(void *state, int fd, void *buf, size_t sz)
{
struct spidriver_state *ts = state;
assert_int_equal(fd, MOCK_FD);
if (SPIDRIVER_TEST_DEBUG)
printf("read: %zu\n", sz);
sz = min(sz, ts->in_len);
if (sz > 0) {
memcpy(buf, ts->input, sz);
memmove(ts->input, &ts->input[sz], sizeof(ts->input) - sz);
ts->in_len = 0;
return sz;
} else {
return -1;
}
}
static int spidriver_write(void *state, int fd, const void *buf, size_t sz)
{
struct spidriver_state *ts = state;
assert_int_equal(fd, MOCK_FD);
if (SPIDRIVER_TEST_DEBUG)
printf("write: %zu\n", sz);
for (size_t i = 0; i < sz; i++) {
unsigned char c = ((const char *)buf)[i];
bool first = ts->state == 0;
if (first)
ts->state = c;
if (SPIDRIVER_TEST_DEBUG)
printf("c=%02X first=%d state=%02X\n", c, first, ts->state);
switch (ts->state) {
case '?':
assert_int_equal(ts->in_len, 0);
snprintf((char *)ts->input, sizeof(ts->input),
"[spidriver2 AAAAAAAA 000000002 5.190 000"
" 21.9 1 1 1 ffff 0 ]");
ts->in_len = 80;
ts->state = 0;
break;
case 0:
break;
case 'm':
case 'a':
case 'b':
if (!first)
ts->state = 0;
break;
case 's':
if (SPIDRIVER_TEST_DEBUG)
printf("select\n");
ts->cs = true;
ts->cs_count++;
ts->state = 0;
break;
case 'u':
if (SPIDRIVER_TEST_DEBUG)
printf("unselect\n");
ts->cs = false;
ts->state = 0;
break;
case 'e':
if (!first) {
if (SPIDRIVER_TEST_DEBUG)
printf("echo %02X\n", c);
assert_int_equal(ts->in_len, 0);
snprintf((char *)ts->input, sizeof(ts->input), "%c", c);
ts->in_len = 1;
ts->state = 0;
}
break;
case 0x80 ... 0xbf:
if (first) {
ts->in_pos = c - 0x80 + 1;
if (SPIDRIVER_TEST_DEBUG)
printf("SPI read begin %zu\n", ts->in_pos);
if (ts->probe) {
if (SPIDRIVER_TEST_DEBUG)
printf("probe response\n");
assert_int_equal(ts->in_pos, 3);
assert_true(ts->cs);
// Must not have lowered CS after write
assert_int_equal(ts->cs_count, ts->cs_probe);
assert_int_equal(ts->in_len, 0);
ts->input[0] = 0xEF; /* WINBOND_NEX_ID */
ts->input[1] = 0x40; /* WINBOND_NEX_W25Q128_V left byte */
ts->input[2] = 0x18; /* WINBOND_NEX_W25Q128_V right byte */
} else {
assert_int_equal(ts->in_len, 0);
memset(ts->input, 0, ts->in_pos);
}
continue;
} else if (ts->in_pos > 0) {
assert_int_equal(c, 0);
ts->in_pos--;
ts->in_len++;
}
if (ts->in_pos == 0) {
if (SPIDRIVER_TEST_DEBUG)
printf("SPI read finished\n");
ts->probe = false;
ts->state = 0;
}
break;
case 0xc0 ... 0xff:
if (first) {
assert_int_equal(ts->out_len, 0);
ts->out_len = c - 0xc0 + 1;
ts->out_pos = 0;
if (SPIDRIVER_TEST_DEBUG)
printf("SPI write begin %zu\n", ts->out_len);
continue;
} else if (ts->out_len > 0) {
ts->output[ts->out_pos++] = c;
ts->out_len--;
}
if (ts->out_len == 0) {
if (SPIDRIVER_TEST_DEBUG)
printf("SPI write finished\n");
assert_true(ts->cs);
if (ts->out_pos == 1 && ts->output[0] == JEDEC_RDID) {
if (SPIDRIVER_TEST_DEBUG)
printf("probe detected\n");
ts->probe = true;
ts->cs_probe = ts->cs_count;
}
ts->state = 0;
}
break;
default:
fail_msg("Unsupported command 0x%02X", ts->state);
break;
}
}
return sz;
}
void spidriver_probe_lifecycle_test_success(void **state)
{
struct spidriver_state ts = {};
struct io_mock_fallback_open_state spidriver_fallback_open_state = {
.noc = 0,
.paths = { "/dev/null", NULL },
.flags = { O_RDWR | O_NOCTTY | O_NDELAY },
};
const struct io_mock spidriver_io = {
.state = &ts,
.iom_read = spidriver_read,
.iom_write = spidriver_write,
.fallback_open_state = &spidriver_fallback_open_state,
};
run_probe_lifecycle(state, &spidriver_io, &programmer_spidriver, "dev=/dev/null", "W25Q128.V");
}
#else
SKIP_TEST(spidriver_probe_lifecycle_test_success)
#endif /* CONFIG_SPIDRIVER */