diff --git a/CHANGELOG.md b/CHANGELOG.md index f4825fb..de2c766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ The version lives in `_VER` in `src/main.c`. Binaries are published only as the [`latest`](https://github.com/OpenIPC/microsnander/releases/latest) release, rebuilt from every push to `main`; this file is the change history. +## 1.22.0 — 2026-11-04 + +- SigmaStar ISP (`-m sigmastar`): the SPI flash of a SigmaStar (MStar) board through the ISP + bridge of its SoC over I2C, without desoldering, to bring back a board with a broken + loader. The I2C master is that of the CH341A or the CH347 (CH347T in mode 1, CH347F), + chosen with `-P` or by itself; the SoC's UART goes to it (TX to SDA, RX to SCL, GND). + `-S` is the I2C clock in this mode (20K, 100K, 400K, 750K); `-q` lists the I2C addresses + that answer, alone or before the operation (`-m sigmastar -i -q`). Every operation and + SPI NOR and SPI NAND work as with a chip in the socket; not in the web mode or the app. + The CH341A and the CH347 drivers get I2C transactions. + ## 1.21.2 — 2026-11-04 - Web mode and Android app: a Macronix SPI NOR shows its one status register with its own diff --git a/README.md b/README.md index 905c95d..958025a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ sudo ./microsnander -w firmware.bin -v - [Examples](#examples) - [Web mode](#web-mode) - [Android app](#android-app) +- [SigmaStar ISP](#sigmastar-isp) - [Supported chips](#supported-chips) - [Chip voltage](#chip-voltage) - [Chips with uncertain data](#chips-with-uncertain-data) @@ -157,7 +158,12 @@ microsnander [options] (the first; -a for another) and put its data back -v verify after write on chip -P programmer: ch341a, ch347, ezp, auto (default: the one connected) - -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M + -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M; + with -m sigmastar the I2C clock: 20K, 100K (default), 400K, 750K + -m sigmastar the flash of a SigmaStar board through the ISP of its SoC: the SoC's UART + (TX to SDA, RX to SCL, GND) on the I2C of the CH341A or the CH347 + -q list the addresses that answer on the I2C of the programmer (alone, or + before the operation: -m sigmastar -i -q) -W [addr:]port web mode: a page with Detect, Read, Erase, Write, Registers -K web mode: ask for this password (any user name) -o web mode: where Read saves the dumps (default: the current one) @@ -281,8 +287,8 @@ How it works: can be replugged, or used by a normal microsnander run. - One operation runs at a time; a request while one runs gets `409`. The page polls the state twice a second while an operation runs. -- `-W` takes no operation of its own (`-i`, `-r`, `-w`, `-e`, `-u`, `-t`) and no `-v`, `-S`, `-d`, - `-I` or `-k`: the page does the operations, writes whole files from address 0 with ECC on +- `-W` takes no operation of its own (`-i`, `-r`, `-w`, `-e`, `-u`, `-t`) and no `-q`, `-m`, `-v`, + `-S`, `-d`, `-I` or `-k`: the page does the operations, writes whole files from address 0 with ECC on and always verifies. `-K` and `-o` go with `-W` only. - The service keeps running when a client disconnects; a slow client does not hold the others (up to 16 connections at a time). @@ -343,6 +349,32 @@ with the remote). Build: `cd android && gradle assembleDebug` with the Android SDK and NDK r27c. +## SigmaStar ISP + +`-m sigmastar` reaches the SPI flash of a SigmaStar (MStar) board on its SoC, without +desoldering: through the ISP bridge of the SoC, over I2C. It brings back a board with a broken +bootloader ([OpenIPC wiki: SigmaStar unbrick](https://github.com/OpenIPC/wiki/blob/master/en/sigmastar-unbrick.md)). + +- Wires: the UART of the SoC to the I2C of the programmer, TX to SDA, RX to SCL, and GND. The + board is powered on its own; when the bridge does not answer, a power cycle of the board helps. +- The I2C master is that of the CH341A or the CH347 (CH347T in mode 1, CH347F), chosen as always + with `-P` or by itself; the EZP programmers have no I2C. +- `-S` is the I2C clock in this mode: `20K`, `100K` (default), `400K`, `750K`. +- `-q` lists the I2C addresses that answer, to check the wiring (the bridge is at `0x49`, the + debug port of the SoC at `0x59`): alone, or before the operation, as `-m sigmastar -i -q`. +- Everything else works as with a chip in the socket: `-i`, `-r`, `-w`, `-e`, `-v`, `-a`, `-l`, + SPI NOR and SPI NAND. Not in the web mode or the Android app. + +```sh +microsnander -m sigmastar -i -q # 0x49 and 0x59 must answer, then the flash +microsnander -m sigmastar -r backup.bin # back it up first +microsnander -m sigmastar -a 0 -l 0x40000 -e # the loader part, then +microsnander -m sigmastar -w u-boot.bin -v # a working loader +``` + +It is slow: the data goes over I2C, about 20-25 bytes a transaction. A loader of a few +hundred KB is minutes; a whole 16 MB chip is much longer. + ## Supported chips `microsnander -L` prints the list: 291 SPI NOR chips (Winbond, Eon, Fudan diff --git a/README.ru.md b/README.ru.md index b034f08..bd425e7 100644 --- a/README.ru.md +++ b/README.ru.md @@ -48,6 +48,7 @@ sudo ./microsnander -w firmware.bin -v - [Примеры](#примеры) - [Веб-режим](#веб-режим) - [Приложение для Android](#приложение-для-android) +- [SigmaStar ISP](#sigmastar-isp) - [Поддерживаемые микросхемы](#поддерживаемые-микросхемы) - [Напряжение микросхем](#напряжение-микросхем) - [Микросхемы с неточными данными](#микросхемы-с-неточными-данными) @@ -158,7 +159,12 @@ microsnander [options] (the first; -a for another) and put its data back -v verify after write on chip -P programmer: ch341a, ch347, ezp, auto (default: the one connected) - -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M + -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M; + with -m sigmastar the I2C clock: 20K, 100K (default), 400K, 750K + -m sigmastar the flash of a SigmaStar board through the ISP of its SoC: the SoC's UART + (TX to SDA, RX to SCL, GND) on the I2C of the CH341A or the CH347 + -q list the addresses that answer on the I2C of the programmer (alone, or + before the operation: -m sigmastar -i -q) -W [addr:]port web mode: a page with Detect, Read, Erase, Write, Registers -K web mode: ask for this password (any user name) -o web mode: where Read saves the dumps (default: the current one) @@ -285,8 +291,8 @@ microsnander -P ch347 -W 8080 -o /srv/dumps можно переподключить или работать с ним обычным запуском microsnander. - Операции идут по одной; запрос во время операции получает `409`. Пока операция идёт, страница спрашивает состояние два раза в секунду. -- С `-W` не задаются свои операции (`-i`, `-r`, `-w`, `-e`, `-u`, `-t`) и ключи `-v`, `-S`, `-d`, `-I`, - `-k`: операции делает страница, пишет целые файлы с адреса 0 с включённым ECC и всегда +- С `-W` не задаются свои операции (`-i`, `-r`, `-w`, `-e`, `-u`, `-t`) и ключи `-q`, `-m`, `-v`, `-S`, + `-d`, `-I`, `-k`: операции делает страница, пишет целые файлы с адреса 0 с включённым ECC и всегда проверяет запись. `-K` и `-o` — только вместе с `-W`. - Служба продолжает работать, когда клиент отключается; медленный клиент не держит остальных (до 16 соединений одновременно). @@ -348,6 +354,32 @@ Android TV (Android 5.0 и новее, без root; на приставке он Сборка: `cd android && gradle assembleDebug` с Android SDK и NDK r27c. +## SigmaStar ISP + +`-m sigmastar` работает с флешкой платы SigmaStar (MStar) прямо на плате, без выпаивания: через +ISP-мост SoC по I2C. Так восстанавливают плату с испорченным загрузчиком +([OpenIPC wiki: SigmaStar unbrick](https://github.com/OpenIPC/wiki/blob/master/en/sigmastar-unbrick.md)). + +- Подключение: UART SoC к I2C программатора, TX к SDA, RX к SCL, и GND. Плата питается сама; + если мост не отвечает, помогает перезапуск питания платы. +- I2C — от CH341A или CH347 (CH347T в режиме 1, CH347F), программатор выбирается как обычно: + `-P` или сам; у программаторов EZP I2C нет. +- `-S` в этом режиме — частота I2C: `20K`, `100K` (по умолчанию), `400K`, `750K`. +- `-q` выводит адреса I2C, которые отвечают, — для проверки подключения (мост — `0x49`, порт + отладки SoC — `0x59`): отдельно или перед операцией, например `-m sigmastar -i -q`. +- Остальное — как с микросхемой в панельке: `-i`, `-r`, `-w`, `-e`, `-v`, `-a`, `-l`, SPI NOR и + SPI NAND. В веб-режиме и в приложении для Android этого режима нет. + +```sh +microsnander -m sigmastar -i -q # должны ответить 0x49 и 0x59, затем флешка +microsnander -m sigmastar -r backup.bin # сначала копия +microsnander -m sigmastar -a 0 -l 0x40000 -e # область загрузчика, затем +microsnander -m sigmastar -w u-boot.bin -v # рабочий загрузчик +``` + +Режим медленный: данные идут по I2C, примерно по 20–25 байт за транзакцию. Загрузчик в несколько +сотен КБ — минуты; вся микросхема на 16 МБ — намного дольше. + ## Поддерживаемые микросхемы `microsnander -L` выводит список: 291 микросхему SPI NOR (Winbond, Eon, Fudan diff --git a/README.zh-CN.md b/README.zh-CN.md index 51a8c19..69793e1 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -46,6 +46,7 @@ sudo ./microsnander -w firmware.bin -v - [示例](#示例) - [Web 模式](#web-模式) - [Android 应用](#android-应用) +- [SigmaStar ISP](#sigmastar-isp) - [支持的芯片](#支持的芯片) - [芯片电压](#芯片电压) - [数据不确定的芯片](#数据不确定的芯片) @@ -144,7 +145,12 @@ microsnander [options] (the first; -a for another) and put its data back -v verify after write on chip -P programmer: ch341a, ch347, ezp, auto (default: the one connected) - -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M + -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M; + with -m sigmastar the I2C clock: 20K, 100K (default), 400K, 750K + -m sigmastar the flash of a SigmaStar board through the ISP of its SoC: the SoC's UART + (TX to SDA, RX to SCL, GND) on the I2C of the CH341A or the CH347 + -q list the addresses that answer on the I2C of the programmer (alone, or + before the operation: -m sigmastar -i -q) -W [addr:]port web mode: a page with Detect, Read, Erase, Write, Registers -K web mode: ask for this password (any user name) -o web mode: where Read saves the dumps (default: the current one) @@ -252,7 +258,7 @@ microsnander -P ch347 -W 8080 -o /srv/dumps - 只使用一个编程器,选择方式与不带 `-W` 时相同;连接了多个时请用 `-P`。 - 每个操作开始时打开编程器、结束后关闭,因此两次操作之间可以重新插拔,或用普通方式运行 microsnander。 - 每次只运行一个操作;操作进行中的请求会得到 `409`。操作进行时页面每秒查询两次状态。 -- `-W` 不接受自身的操作(`-i`、`-r`、`-w`、`-e`、`-u`、`-t`),也不接受 `-v`、`-S`、`-d`、`-I` 或 `-k`: +- `-W` 不接受自身的操作(`-i`、`-r`、`-w`、`-e`、`-u`、`-t`),也不接受 `-q`、`-m`、`-v`、`-S`、`-d`、`-I` 或 `-k`: 操作由页面完成,在开启 ECC 的情况下从地址 0 写入完整文件,并始终校验。`-K` 和 `-o` 只能与 `-W` 一起使用。 - 客户端断开时服务继续运行;慢速客户端不会阻塞其他客户端(最多同时 16 个连接)。 @@ -308,6 +314,28 @@ root;在电视盒子上会出现在主屏幕,可用遥控器操作)。 构建:在安装了 Android SDK 和 NDK r27c 的环境中运行 `cd android && gradle assembleDebug`。 +## SigmaStar ISP + +`-m sigmastar` 无需拆焊,直接在板上通过 SoC 的 ISP 桥(I2C)访问 SigmaStar(MStar)板的 SPI 闪存, +用于修复引导程序损坏的板子([OpenIPC wiki:SigmaStar unbrick](https://github.com/OpenIPC/wiki/blob/master/en/sigmastar-unbrick.md))。 + +- 接线:SoC 的 UART 接编程器的 I2C,TX 接 SDA,RX 接 SCL,并连接 GND。板子自行供电;桥无响应时, + 给板子重新上电通常有效。 +- I2C 主机为 CH341A 或 CH347(CH347T 模式 1、CH347F),照常用 `-P` 选择或自动选择;EZP 编程器没有 I2C。 +- 此模式下 `-S` 为 I2C 时钟:`20K`、`100K`(默认)、`400K`、`750K`。 +- `-q` 列出有响应的 I2C 地址,用于检查接线(桥为 `0x49`,SoC 调试端口为 `0x59`):可单独使用,也可在操作之前,如 `-m sigmastar -i -q`。 +- 其余与插座中的芯片相同:`-i`、`-r`、`-w`、`-e`、`-v`、`-a`、`-l`,SPI NOR 和 SPI NAND。Web 模式和 + Android 应用不支持此模式。 + +```sh +microsnander -m sigmastar -i -q # 0x49 和 0x59 必须有响应,然后是闪存 +microsnander -m sigmastar -r backup.bin # 先备份 +microsnander -m sigmastar -a 0 -l 0x40000 -e # 引导程序区域,然后 +microsnander -m sigmastar -w u-boot.bin -v # 写入可用的引导程序 +``` + +此模式较慢:数据通过 I2C 传输,每次事务约 20–25 字节。几百 KB 的引导程序需要几分钟;整片 16 MB 芯片要久得多。 + ## 支持的芯片 `microsnander -L` 会列出支持的芯片:291 款 SPI NOR 芯片(Winbond、Eon、Fudan diff --git a/src/Makefile b/src/Makefile index 3d39322..dc0afd5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -26,7 +26,7 @@ CFLAGS ?= -Os ALL_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -D_FILE_OFFSET_BITS=64 $(USB_CFLAGS) LDLIBS = $(LIBUSB_LIBS) -pthread -OBJS = flashcmd_api.o spi_controller.o spi_nand_flash.o spi_nor_flash.o programmer.o ch341a_spi.o ch347_spi.o ezp.o usb.o $(USB_OBJS) voltage.o selftest.o ops.o web.o timer.o main.o +OBJS = flashcmd_api.o spi_controller.o spi_nand_flash.o spi_nor_flash.o programmer.o ch341a_spi.o ch347_spi.o ezp.o usb.o $(USB_OBJS) voltage.o sigmastar.o selftest.o ops.o web.o timer.o main.o all: microsnander diff --git a/src/ch341a_spi.c b/src/ch341a_spi.c index 3fa2656..5cecc0f 100644 --- a/src/ch341a_spi.c +++ b/src/ch341a_spi.c @@ -301,6 +301,60 @@ int config_stream(unsigned int speed) return ret; } +/* ---- I2C, for the SigmaStar ISP ---- + One stream command packet a transaction: START, the address byte, the data + or the reads, STOP. STM_OUT with a length sends bytes without answers; + STM_OUT without one sends one byte and answers its ACK (bit 7 set: NACK); + STM_IN with a length reads bytes with ACK, STM_IN alone one byte with NACK, + the last of a read. */ + +int ch341a_i2c_speed(unsigned int speed) +{ + return config_stream(speed) < 0 ? -1 : 0; /* the SPI does not run in this mode */ +} + +int ch341a_i2c_write(unsigned char addr, const unsigned char *data, unsigned int len) +{ + uint8_t buf[CH341_PACKET_LENGTH]; + unsigned int n = 0; + + if (len > CH341A_I2C_MAX) + return -1; + buf[n++] = CH341A_CMD_I2C_STREAM; + buf[n++] = CH341A_CMD_I2C_STM_START; + buf[n++] = CH341A_CMD_I2C_STM_OUT | (len + 1); + buf[n++] = addr << 1; + memcpy(buf + n, data, len); + n += len; + buf[n++] = CH341A_CMD_I2C_STM_STOP; + buf[n++] = CH341A_CMD_I2C_STM_END; + return usb_transfer(__func__, n, 0, buf, NULL) < 0 ? -1 : 0; +} + +int ch341a_i2c_read(unsigned char addr, unsigned char *data, unsigned int len) +{ + uint8_t buf[CH341_PACKET_LENGTH], in[CH341_PACKET_LENGTH]; + unsigned int n = 0; + + if (len > CH341A_I2C_MAX) + return -1; + buf[n++] = CH341A_CMD_I2C_STREAM; + buf[n++] = CH341A_CMD_I2C_STM_START; + buf[n++] = CH341A_CMD_I2C_STM_OUT; /* the address, with its ACK */ + buf[n++] = addr << 1 | 1; + if (len > 1) + buf[n++] = CH341A_CMD_I2C_STM_IN | (len - 1); + if (len) + buf[n++] = CH341A_CMD_I2C_STM_IN; + buf[n++] = CH341A_CMD_I2C_STM_STOP; + buf[n++] = CH341A_CMD_I2C_STM_END; + if (usb_transfer(__func__, n, len + 1, buf, in) < 0 || (in[0] & 0x80)) + return -1; + if (len) + memcpy(data, in + 1, len); + return 0; +} + /* ch341 requires LSB first, swap the bit order before send and after receive */ static uint8_t swap_byte(uint8_t x) { diff --git a/src/ch341a_spi.h b/src/ch341a_spi.h index 401924f..d5c6601 100644 --- a/src/ch341a_spi.h +++ b/src/ch341a_spi.h @@ -32,5 +32,12 @@ int enable_pins(bool enable); int ch341a_cs(bool low); /* sent with the next stream */ int config_stream(unsigned int speed); +/* the I2C master of the CH341A (SDA, SCL), for the SigmaStar ISP; a USB + packet of 32 bytes holds one transaction of up to CH341A_I2C_MAX bytes */ +#define CH341A_I2C_MAX 24 +int ch341a_i2c_speed(unsigned int speed); +int ch341a_i2c_write(unsigned char addr, const unsigned char *buf, unsigned int len); +int ch341a_i2c_read(unsigned char addr, unsigned char *buf, unsigned int len); + #endif /* __CH341_SPI_H__ */ /* End of [ch341a_spi.h] package */ diff --git a/src/ch347_spi.c b/src/ch347_spi.c index 1f185df..752cd2f 100644 --- a/src/ch347_spi.c +++ b/src/ch347_spi.c @@ -313,3 +313,86 @@ int ch347_spi_send_command(unsigned int writecnt, unsigned int readcnt, const un return -1; return 0; } + +/* ---- I2C, for the SigmaStar ISP ---- + The stream commands of the CH341 (0xAA ...), on the same interface as the + SPI. Every byte sent with STM_OUT answers 0x01 for ACK; a read answers its + address ACK and then the data. The CH347F first switches its pins to I2C. */ + +#define CH347_I2C_STREAM 0xAA +#define CH347_I2C_STM_SET 0x60 +#define CH347_I2C_STM_START 0x74 +#define CH347_I2C_STM_STOP 0x75 +#define CH347_I2C_STM_OUT 0x80 +#define CH347_I2C_STM_IN 0xC0 +#define CH347_I2C_STM_END 0x00 + +int ch347_i2c_speed(unsigned int speed) +{ + unsigned char pkt[16] = { CH347_I2C_STREAM, CH347_I2C_STM_SET | (speed & 3), CH347_I2C_STM_END }; + + if (!ch347) + return -1; + if (ch347_iface == CH347F_IFACE) { + static const unsigned char to_i2c[11] = { 0xe2, 0x08, 0x00, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00 }; + unsigned char cmd[sizeof(to_i2c)], ans[4]; + int n = 0; + + memcpy(cmd, to_i2c, sizeof(cmd)); + if (bulk(CH347_WRITE_EP, cmd, sizeof(cmd), NULL) || bulk(CH347_READ_EP, ans, sizeof(ans), &n) || n != 4) { + printf("CH347F: the pins do not switch to I2C.\n"); + return -1; + } + } + return bulk(CH347_WRITE_EP, pkt, 3, NULL) ? -1 : 0; +} + +int ch347_i2c_write(unsigned char addr, const unsigned char *data, unsigned int len) +{ + unsigned char pkt[CH347_I2C_MAX + 8], ans[CH347_PACKET_SIZE]; + unsigned int n = 0; + int got = 0; + + if (!ch347 || len > CH347_I2C_MAX) + return -1; + pkt[n++] = CH347_I2C_STREAM; + pkt[n++] = CH347_I2C_STM_START; + pkt[n++] = CH347_I2C_STM_OUT | (len + 1); + pkt[n++] = addr << 1; + memcpy(pkt + n, data, len); + n += len; + pkt[n++] = CH347_I2C_STM_STOP; + pkt[n++] = CH347_I2C_STM_END; + if (bulk(CH347_WRITE_EP, pkt, n, NULL) || bulk(CH347_READ_EP, ans, sizeof(ans), &got) || got != (int)len + 1) + return -1; + for (int i = 0; i < got; i++) + if (ans[i] != 0x01) /* NACK */ + return -1; + return 0; +} + +int ch347_i2c_read(unsigned char addr, unsigned char *data, unsigned int len) +{ + unsigned char pkt[16], ans[CH347_PACKET_SIZE]; + unsigned int n = 0; + int got = 0; + + if (!ch347 || len > CH347_I2C_MAX) + return -1; + pkt[n++] = CH347_I2C_STREAM; + pkt[n++] = CH347_I2C_STM_START; + pkt[n++] = CH347_I2C_STM_OUT | 1; + pkt[n++] = addr << 1 | 1; + if (len > 1) + pkt[n++] = CH347_I2C_STM_IN | (len - 1); + if (len) + pkt[n++] = CH347_I2C_STM_IN; + pkt[n++] = CH347_I2C_STM_STOP; + pkt[n++] = CH347_I2C_STM_END; + if (bulk(CH347_WRITE_EP, pkt, n, NULL) || bulk(CH347_READ_EP, ans, sizeof(ans), &got) || + got != (int)len + 1 || ans[0] != 0x01) + return -1; + if (len) + memcpy(data, ans + 1, len); + return 0; +} diff --git a/src/ch347_spi.h b/src/ch347_spi.h index 25c3305..42d0441 100644 --- a/src/ch347_spi.h +++ b/src/ch347_spi.h @@ -16,6 +16,13 @@ int ch347_spi_shutdown(void); int ch347_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); int ch347_cs(bool low); int ch347_set_speed(const char *speed); + +/* the I2C master of the CH347 (CH347T in mode 1, CH347F), for the SigmaStar + ISP: one transaction of up to CH347_I2C_MAX bytes a packet */ +#define CH347_I2C_MAX 60 +int ch347_i2c_speed(unsigned int speed); +int ch347_i2c_write(unsigned char addr, const unsigned char *buf, unsigned int len); +int ch347_i2c_read(unsigned char addr, unsigned char *buf, unsigned int len); const char *ch347_speed_list(void); #endif /* __CH347_SPI_H__ */ diff --git a/src/main.c b/src/main.c index 51310eb..f47cd46 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,7 @@ #include "flashcmd_api.h" #include "programmer.h" #include "ch347_spi.h" +#include "sigmastar.h" #include "selftest.h" #include "web.h" #include "spi_nand_flash.h" @@ -36,7 +37,7 @@ struct flash_cmd prog; extern unsigned int bsize; -#define _VER "1.21.2" +#define _VER "1.22.0" const char *ms_version = _VER; @@ -82,7 +83,12 @@ void usage(int code) " (the first; -a for another) and put its data back\n"\ " -v verify after write on chip\n"\ " -P programmer: ch341a, ch347, ezp, auto (default: the one connected)\n"\ - " -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M\n"\ + " -S SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M;\n"\ + " with -m sigmastar the I2C clock: 20K, 100K (default), 400K, 750K\n"\ + " -m sigmastar the flash of a SigmaStar board through the ISP of its SoC: the SoC's UART\n"\ + " (TX to SDA, RX to SCL, GND) on the I2C of the CH341A or the CH347\n"\ + " -q list the addresses that answer on the I2C of the programmer (alone, or\n"\ + " before the operation: -m sigmastar -i -q)\n"\ " -W [addr:]port web mode: a page with Detect, Read, Erase, Write, Registers\n"\ " -K web mode: ask for this password (any user name)\n"\ " -o web mode: where Read saves the dumps (default: the current one)\n"; @@ -93,6 +99,8 @@ void usage(int code) int main(int argc, char* argv[]) { int c, vr = 0, ret = 0, rc = 1, speed_set = 0; + const char *speed = NULL; + int scan = 0; char *fname = NULL, op = 0, *web = NULL, *web_key = NULL, *web_dir = NULL; unsigned char *buf; int long long len = 0, addr = 0, flen = 0, wlen = 0; @@ -101,7 +109,7 @@ int main(int argc, char* argv[]) title(); - while ((c = getopt(argc, argv, "diIhveutkLl:a:w:r:P:S:W:K:o:")) != -1) + while ((c = getopt(argc, argv, "diIhveutkqLl:a:w:r:P:S:W:K:o:m:")) != -1) { switch(c) { @@ -117,10 +125,12 @@ int main(int argc, char* argv[]) exit(1); } break; - case 'S': - speed_set = 1; - if (ch347_set_speed(optarg) < 0) { - printf("Unknown SPI speed %s; use one of: %s.\n\n", optarg, ch347_speed_list()); + case 'S': /* SPI or I2C: known after -m */ + speed = optarg; + break; + case 'm': + if (sigmastar_mode(optarg) < 0) { + printf("Unknown mode %s; the one there is: sigmastar.\n\n", optarg); exit(1); } break; @@ -148,6 +158,9 @@ int main(int argc, char* argv[]) case 'o': web_dir = optarg; break; + case 'q': /* before the operation, or alone */ + scan = 1; + break; case 'u': /* -u and -uu, nothing more and with no other operation */ if (++unlock > 2 || (op && op != 'u')) @@ -181,12 +194,23 @@ int main(int argc, char* argv[]) } } + if (speed) { + speed_set = 1; + if (sigmastar_on() ? sigmastar_set_speed(speed) < 0 : ch347_set_speed(speed) < 0) { + if (sigmastar_on()) + printf("Unknown I2C speed %s; use one of: %s.\n\n", speed, sigmastar_speed_list()); + else + printf("Unknown SPI speed %s; use one of: %s.\n\n", speed, ch347_speed_list()); + exit(1); + } + } + if (web) { /* the web mode: the page does the operations, one programmer, auto */ /* the page writes whole files from 0 with ECC on: no raw NAND (-d), no ECC ignore (-I), no BAD page skipping (-k) */ - if (op || vr || speed_set || !ECC_fcheck || ECC_ignore || Skip_BAD_page) { - printf("-W runs the web mode: give no -i, -r, -w, -e, -u, -t, -v, -S, -d, -I or -k with it.\n\n"); + if (op || scan || vr || speed_set || !ECC_fcheck || ECC_ignore || Skip_BAD_page || sigmastar_on()) { + printf("-W runs the web mode: give no -i, -r, -w, -e, -u, -t, -q, -v, -S, -m, -d, -I or -k with it.\n\n"); return 1; } return web_serve(web, web_key, web_dir, addr, len); @@ -196,6 +220,8 @@ int main(int argc, char* argv[]) return 1; } + if (op == 0 && scan) + op = 'q'; /* -q alone: only the bus */ if (op == 0) usage(1); if (op == 'x' || (ECC_ignore && !ECC_fcheck) || (ECC_ignore && Skip_BAD_page) || (op == 'w' && ECC_ignore) || @@ -204,13 +230,28 @@ int main(int argc, char* argv[]) return 1; } - ret = programmer_init(speed_set); + /* in the SigmaStar mode -S is the I2C clock, not the SPI one */ + ret = programmer_init(speed_set && !sigmastar_on()); if (ret < 0) { if (ret == -1) printf("Programmer device not found!\n\n"); return 1; } + if (scan && i2c_scan(programmer)) + goto out; + if (op == 'q') { + rc = 0; + goto out; + } + if (sigmastar_on()) { + const struct programmer *isp = sigmastar_attach(programmer); + + if (!isp) + goto out; + programmer = isp; + } + if((flen = flash_cmd_init(&prog)) <= 0) goto out; diff --git a/src/programmer.c b/src/programmer.c index 15bad57..9306d81 100644 --- a/src/programmer.c +++ b/src/programmer.c @@ -18,8 +18,10 @@ static const struct programmer programmers[] = { - { "ch341a", "CH341A", 0x1a86, { 0x5512 }, ch341a_spi_init, ch341a_spi_shutdown, ch341a_spi_send_command, ch341a_cs, false }, - { "ch347", "CH347", 0x1a86, { 0x55db, 0x55de }, ch347_spi_init, ch347_spi_shutdown, ch347_spi_send_command, ch347_cs, true }, + { "ch341a", "CH341A", 0x1a86, { 0x5512 }, ch341a_spi_init, ch341a_spi_shutdown, ch341a_spi_send_command, ch341a_cs, false, + NULL, ch341a_i2c_speed, ch341a_i2c_write, ch341a_i2c_read, CH341A_I2C_MAX }, + { "ch347", "CH347", 0x1a86, { 0x55db, 0x55de }, ch347_spi_init, ch347_spi_shutdown, ch347_spi_send_command, ch347_cs, true, + NULL, ch347_i2c_speed, ch347_i2c_write, ch347_i2c_read, CH347_I2C_MAX }, { "ezp", "EZP", 0x1fc8, { 0x310b, 0x310c, 0x310d }, ezp_init, ezp_shutdown, NULL, NULL, false, ezp_flash_init }, }; #define NPROG (sizeof(programmers) / sizeof(programmers[0])) diff --git a/src/programmer.h b/src/programmer.h index 9384e88..f2588bc 100644 --- a/src/programmer.h +++ b/src/programmer.h @@ -25,6 +25,15 @@ struct programmer { /* a programmer that runs the SPI in its own firmware: sets up the flash operations itself (no send/cs); returns the chip size or -1 */ long (*flash_init)(struct flash_cmd *cmd); + /* its I2C master, for the SigmaStar ISP (-m sigmastar) and -q; NULL: none. + speed: 0 20 kHz, 1 100 kHz, 2 400 kHz, 3 750 kHz. A write or a read is + one I2C transaction (START, address, data, STOP) of up to i2c_max + bytes; a read of 0 bytes only tells whether the address answers. + 0, or -1 (also on NACK). */ + int (*i2c_speed)(unsigned int speed); + int (*i2c_write)(unsigned char addr, const unsigned char *buf, unsigned int len); + int (*i2c_read)(unsigned char addr, unsigned char *buf, unsigned int len); + unsigned int i2c_max; }; extern const struct programmer *programmer; /* the one in use */ diff --git a/src/sigmastar.c b/src/sigmastar.c new file mode 100644 index 0000000..b84736b --- /dev/null +++ b/src/sigmastar.c @@ -0,0 +1,166 @@ +/* + * sigmastar.c — the SigmaStar (MStar) ISP: the SPI flash of a board on its + * SoC, reached through the SoC's ISP bridge over I2C, without desoldering + * (-m sigmastar). The I2C master is the programmer's (CH341A, CH347): the + * UART of the SoC goes to it, TX to SDA, RX to SCL, and GND. + * + * The bridge answers at 0x49: 0x10 and bytes — they go out on the SPI bus; + * 0x11 — the next I2C read clocks bytes in from it; 0x12 — the end of the + * SPI command (CS high). Before that the SoC's debug port, 0x59, is told to + * let the pins of its UART be the bridge, and "MSTAR" to 0x49 starts it. + * The flash drivers do not know: the programmer's send and cs go here. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include "programmer.h" +#include "sigmastar.h" + +#define ISP_ADDR 0x49 +#define ISP_DEBUG 0x59 +#define ISP_WRITE 0x10 +#define ISP_READ 0x11 +#define ISP_END 0x12 + +static bool on; /* -m sigmastar */ +static int speed = 1; /* 100 kHz */ +static const struct programmer *bus; /* the programmer whose I2C it is */ +static struct programmer isp; /* bus with send and cs of the bridge */ + +static const struct { const char *name; int code; } speeds[] = { + { "20K", 0 }, { "100K", 1 }, { "400K", 2 }, { "750K", 3 }, +}; + +int sigmastar_mode(const char *name) +{ + if (strcasecmp(name, "sigmastar")) + return -1; + on = true; + return 0; +} + +bool sigmastar_on(void) { return on; } + +const char *sigmastar_speed_list(void) { return "20K, 100K (default), 400K, 750K"; } + +int sigmastar_set_speed(const char *name) +{ + for (unsigned i = 0; i < sizeof(speeds) / sizeof(speeds[0]); i++) + if (!strcasecmp(name, speeds[i].name)) { + speed = speeds[i].code; + return 0; + } + return -1; +} + +/* the SPI command: bytes out, then bytes in, in parts that fit the I2C of + the programmer; CS stays low until isp_cs(false) */ +static int isp_send(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) +{ + unsigned char buf[64]; + unsigned int part = bus->i2c_max - 1; /* the command byte first */ + + for (unsigned int done = 0; done < writecnt; ) { + unsigned int n = writecnt - done < part ? writecnt - done : part; + + buf[0] = ISP_WRITE; + memcpy(buf + 1, writearr + done, n); + if (bus->i2c_write(ISP_ADDR, buf, n + 1)) + return -1; + done += n; + } + for (unsigned int done = 0; done < readcnt; ) { + unsigned int n = readcnt - done < bus->i2c_max ? readcnt - done : bus->i2c_max; + + buf[0] = ISP_READ; + if (bus->i2c_write(ISP_ADDR, buf, 1) || bus->i2c_read(ISP_ADDR, readarr + done, n)) + return -1; + done += n; + } + return 0; +} + +static int isp_cs(bool low) +{ + unsigned char end = ISP_END; + + return low ? 0 : bus->i2c_write(ISP_ADDR, &end, 1); +} + +/* the debug port lets the UART pins of the SoC go to the ISP bridge */ +static int debug_port_to_isp(void) +{ + static const unsigned char serdb[] = { 'S', 'E', 'R', 'D', 'B' }; + static const unsigned char init[] = { 0x81, 0x83, 0x84, 0x53, 0x7F, 0x35, 0x71 }; + static const unsigned char uart[] = { 0x10, 0x00, 0x00, 0x0E, 0x12, 0x00, 0x00 }; + static const unsigned char leave[] = { 0x34, 0x45 }; + unsigned char reg; + + if (bus->i2c_write(ISP_DEBUG, serdb, sizeof(serdb))) + return -1; + for (unsigned i = 0; i < sizeof(init); i++) + if (bus->i2c_write(ISP_DEBUG, init + i, 1)) + return -1; + if (bus->i2c_write(ISP_DEBUG, uart, sizeof(uart)) || bus->i2c_write(ISP_DEBUG, uart, 6) || + bus->i2c_read(ISP_DEBUG, ®, 1) || reg) + return -1; + for (unsigned i = 0; i < sizeof(leave); i++) + if (bus->i2c_write(ISP_DEBUG, leave + i, 1)) + return -1; + return 0; +} + +int i2c_scan(const struct programmer *p) +{ + if (!p->i2c_read) { + printf("The %s has no I2C: -q works with the CH341A and the CH347.\n", p->name); + return -1; + } + if (p->i2c_speed(speed)) + return -1; + printf("I2C devices on the %s:\n 0 1 2 3 4 5 6 7 8 9 a b c d e f", p->name); + for (int a = 0; a < 0x80; a++) { + if (a % 16 == 0) + printf("\n%02x: ", a); + if (p->i2c_read(a, NULL, 0)) + printf("-- "); + else + printf("%02x ", a); + } + printf("\n\n"); + return 0; +} + +const struct programmer *sigmastar_attach(const struct programmer *p) +{ + static const unsigned char start[] = { 'M', 'S', 'T', 'A', 'R' }; + + if (!p->i2c_write) { + printf("The %s has no I2C: -m sigmastar works with the CH341A and the CH347.\n", p->name); + return NULL; + } + bus = p; + if (p->i2c_speed(speed)) + return NULL; + if (p->i2c_read(ISP_ADDR, NULL, 0)) { + printf("SigmaStar ISP: nothing answers at I2C 0x%02x. Check the wires (TX to SDA, RX to SCL, GND)\n" + "and that the board is powered; a power cycle of the board can help.\n", ISP_ADDR); + return NULL; + } + if (p->i2c_read(ISP_DEBUG, NULL, 0) && debug_port_to_isp()) + printf("SigmaStar ISP: the debug port 0x%02x did not take the UART pins; going on.\n", ISP_DEBUG); + if (p->i2c_write(ISP_ADDR, start, sizeof(start)) && isp_cs(false)) + return NULL; + printf("SigmaStar ISP through the I2C of the %s at 0x%02x.\n", p->name, ISP_ADDR); + isp = *p; + isp.send = isp_send; + isp.cs = isp_cs; + isp.clock = false; + isp.flash_init = NULL; + return &isp; +} diff --git a/src/sigmastar.h b/src/sigmastar.h new file mode 100644 index 0000000..525bfbe --- /dev/null +++ b/src/sigmastar.h @@ -0,0 +1,28 @@ +/* + * sigmastar.h — the SigmaStar ISP (-m sigmastar) and the I2C bus scan (-q). + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __MS_SIGMASTAR_H__ +#define __MS_SIGMASTAR_H__ + +#include + +struct programmer; + +int sigmastar_mode(const char *name); /* -m; -1: unknown mode */ +bool sigmastar_on(void); +int sigmastar_set_speed(const char *name); /* -S in this mode: the I2C clock */ +const char *sigmastar_speed_list(void); + +/* the open programmer p, with its send and cs going to the ISP bridge; + NULL (the message printed) when there is no bridge or no I2C */ +const struct programmer *sigmastar_attach(const struct programmer *p); + +/* -q: which I2C addresses answer on the bus of the open programmer p */ +int i2c_scan(const struct programmer *p); + +#endif /* __MS_SIGMASTAR_H__ */ diff --git a/tests/ispsim.c b/tests/ispsim.c new file mode 100644 index 0000000..e8ffa83 --- /dev/null +++ b/tests/ispsim.c @@ -0,0 +1,107 @@ +/* tests/ispsim.c — src/sigmastar.c against a fake I2C master with a SigmaStar + ISP bridge on it: 0x10 + bytes go out on the SPI bus, 0x11 makes the next + read clock bytes in, 0x12 ends the command (CS high); behind it a SPI NOR + that answers READ ID (9F) and READ (03) from a pattern. The debug port 0x59 + does not answer a probe, so the attach goes through its unlock sequence. */ +#include +#include +#include "../src/sigmastar.c" + +static int fail; +static void check(int ok, const char *what) { printf("%s %s\n", ok ? "ok " : "FAIL", what); if (!ok) fail = 1; } + +static unsigned char pattern(unsigned int i) { return (unsigned char)(i * 13 + 7); } + +static unsigned char cmd[600]; /* the SPI bytes of the command under CS */ +static unsigned int ncmd, clocked; /* bytes clocked in since the command */ +static int reading, ends, max_part, writes59, mstar, bad; +static unsigned char log59[64]; +static unsigned int nlog59; +static int answer59 = 1; /* the register read at 0x59 after the UART setup */ + +static int f_speed(unsigned int s) { return s > 3 ? -1 : 0; } + +static int f_write(unsigned char a, const unsigned char *b, unsigned int n) +{ + if ((int)n > max_part) max_part = n; + if (n > 24) bad = 1; /* past the I2C limit given below */ + if (a == 0x59) { + writes59++; + for (unsigned int i = 0; i < n && nlog59 < sizeof(log59); i++) log59[nlog59++] = b[i]; + if (n == 6) answer59 = 0; /* the second UART write: the register is clear */ + return 0; + } + if (a != 0x49) + return -1; + if (n == 5 && !memcmp(b, "MSTAR", 5)) { mstar++; return 0; } + switch (b[0]) { + case 0x10: if (reading) bad = 2; memcpy(cmd + ncmd, b + 1, n - 1); ncmd += n - 1; return 0; + case 0x11: if (n != 1) bad = 3; reading = 1; return 0; + case 0x12: if (n != 1) bad = 4; ends++; ncmd = clocked = 0; reading = 0; return 0; + default: bad = 5; return -1; + } +} + +static int f_read(unsigned char a, unsigned char *b, unsigned int n) +{ + if (a == 0x59) { + if (!n) return -1; /* the probe: no answer */ + b[0] = answer59; + return 0; + } + if (a != 0x49) return -1; + if (!n) return 0; + if (!reading) { bad = 6; return -1; } + if (n > 24) bad = 1; + for (unsigned int i = 0; i < n; i++, clocked++) + if (ncmd == 1 && cmd[0] == 0x9f) + b[i] = clocked < 3 ? (unsigned char[]){ 0xef, 0x40, 0x18 }[clocked] : 0; + else if (ncmd == 4 && cmd[0] == 0x03) + b[i] = pattern(((cmd[1] << 16) | (cmd[2] << 8) | cmd[3]) + clocked); + else + b[i] = 0xff; + return 0; +} + +static const struct programmer fake = { + .key = "ch341a", .name = "CH341A", .send = (void *)1, .cs = (void *)1, + .i2c_speed = f_speed, .i2c_write = f_write, .i2c_read = f_read, .i2c_max = 24, +}; +static const struct programmer noi2c = { .key = "ezp", .name = "EZP" }; + +int main(void) +{ + const struct programmer *p; + unsigned char id[3], buf[200], wr[300]; + + check(sigmastar_mode("mstar") < 0 && sigmastar_mode("SigmaStar") == 0 && sigmastar_on(), "-m sigmastar only"); + check(sigmastar_set_speed("400k") == 0 && sigmastar_set_speed("15M") < 0, "-S takes the I2C clocks"); + check(!sigmastar_attach(&noi2c), "a programmer without I2C is refused"); + + p = sigmastar_attach(&fake); + check(p && p->send != fake.send && p->cs != fake.cs && !p->clock && mstar == 1, "attach: send and cs go to the bridge, MSTAR sent once"); + check(nlog59 >= 5 && !memcmp(log59, "SERDB", 5) && writes59 == 12, " the silent debug port 0x59 got its unlock sequence"); + + /* READ ID: one byte out, three in, then CS high */ + check(!p->cs(true) && !p->send(1, 3, (const unsigned char[]){ 0x9f }, id) && !p->cs(false) && + id[0] == 0xef && id[1] == 0x40 && id[2] == 0x18 && ends == 1, "READ ID through the bridge: ef 40 18, then 0x12"); + + /* READ of 200 bytes at 0x1234: the read goes in parts of the I2C limit, each after a 0x11 */ + int same = 1; + p->cs(true); + check(!p->send(4, sizeof(buf), (const unsigned char[]){ 0x03, 0x00, 0x12, 0x34 }, buf) && !p->cs(false), "READ of 200 bytes"); + for (unsigned int i = 0; i < sizeof(buf); i++) same &= buf[i] == pattern(0x1234 + i); + check(same && max_part <= 24 && !bad, " the data comes back in order, no I2C transaction over 24 bytes"); + + /* a page program: 4 bytes of command and 256 of data out in parts of 23, all under one CS */ + for (unsigned int i = 0; i < sizeof(wr); i++) wr[i] = (unsigned char)i; + int before = ends; + ncmd = 0; + p->cs(true); + int ok = !p->send(260, 0, wr, NULL); + check(ok && ncmd == 260 && !memcmp(cmd, wr, 260) && ends == before, "a write of 260 bytes goes out whole, before the CS goes high"); + p->cs(false); + check(ends == before + 1 && !bad, " then one 0x12"); + + return fail; +} diff --git a/tests/run.sh b/tests/run.sh index 9ae4071..a28b36e 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -19,7 +19,7 @@ set -u T=$(cd "$(dirname "$0")" && pwd) W=$(mktemp -d) trap 'rm -rf "$W"' EXIT -${CC:-cc} -std=gnu99 -Wall -Werror -I"$T/../src" -o "$W/ms" "$T/../src/main.c" "$T/../src/selftest.c" "$T/stub.c" || exit 1 +${CC:-cc} -std=gnu99 -Wall -Werror -I"$T/../src" -o "$W/ms" "$T/../src/main.c" "$T/../src/sigmastar.c" "$T/../src/selftest.c" "$T/stub.c" || exit 1 ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -DSPI_NAND_OIP_TIMEOUT_MS=50 -o "$W/nandsim" "$T/nandsim.c" "$T/../src/timer.c" || exit 1 ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" ${LIBUSB_CFLAGS:-} -o "$W/ch341sim" "$T/ch341sim.c" || exit 1 ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -o "$W/norsim" "$T/norsim.c" "$T/../src/timer.c" || exit 1 @@ -27,6 +27,7 @@ ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" ${LIBUSB_CFLAGS:-} -o "$W/c ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" ${LIBUSB_CFLAGS:-} -o "$W/ezpsim" "$T/ezpsim.c" "$T/../src/timer.c" || exit 1 ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" -o "$W/spisim" "$T/spisim.c" || exit 1 ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" -o "$W/usbfssim" "$T/usbfssim.c" || exit 1 +${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" -o "$W/ispsim" "$T/ispsim.c" || exit 1 make -s -C "$T/../src" web_page.h || exit 1 ${CC:-cc} -std=gnu99 -Wall -Werror -O1 -I"$T/../src" -o "$W/websim" "$T/websim.c" -pthread || exit 1 cd "$W"; B=./ms; fail=0 @@ -61,6 +62,19 @@ t 1 "-S unknown speed" $B -S 100M -i t 0 "-P ch347 -S 15M" $B -P ch347 -S 15M -i t 1 "-S with the CH341A is an error" $B -P ch341a -S 15M -i t 1 "-S with auto (CH341A) is an error" $B -S 15M -i +t 1 "-m unknown mode" $B -m mstar -i +t 0 "-m sigmastar -i (CH341A, auto)" $B -m sigmastar -i +t 0 "-P ch347 -m sigmastar -i" $B -P ch347 -m sigmastar -i +t 1 "-P ezp -m sigmastar: no I2C" $B -P ezp -m sigmastar -i +t 1 "-m sigmastar, nothing at 0x49" MS_I2C=none $B -m sigmastar -i +t 0 "-m sigmastar -S 400K (CH341A)" $B -m sigmastar -S 400K -i +t 0 "-S before -m is the I2C clock" $B -S 750K -m sigmastar -i +t 1 "-m sigmastar -S 15M is an error" $B -m sigmastar -S 15M -i +t 1 "-W with -m is an error" $B -W 8080 -m sigmastar +t 1 "-P ezp -q: no I2C" $B -P ezp -q +$B -m sigmastar -i -q | grep -q '^40: .* 49 ' && $B -m sigmastar -i -q | grep -q 'Status\|SigmaStar ISP' && echo "ok -q before -i: the bus, then the operation" || { echo "FAIL -q with -i"; fail=1; } +$B -q | grep -q '^40: .* 49 ' && $B -q | grep -q '^50: .* 59 ' && $B -q | grep -q '^00: -- ' && echo "ok -q lists the I2C addresses that answer" || { echo "FAIL -q"; fail=1; } +$B -h | grep -q '^ -m sigmastar' && $B -h | grep -q '^ -q ' && echo "ok -h lists -m and -q" || { echo "FAIL -h without -m/-q"; fail=1; } MS_PROGRAMMER=1 $B -P ch347 -i | grep -q '^programmer: ch347' && MS_PROGRAMMER=1 $B -i | grep -q '^programmer: ch341a' && echo "ok -P chooses the programmer, auto by default" || { echo "FAIL programmer choice"; fail=1; } $B -h | grep -q '^ -P ' && $B -h | grep -q '^ -S ' && echo "ok -h lists -P and -S" || { echo "FAIL -h without -P/-S"; fail=1; } $B -h | grep -q '^ -k ' && echo "ok -h lists -k" || { echo "FAIL -h without -k"; fail=1; } @@ -145,6 +159,8 @@ grep -a '^ok\|^FAIL' ezpsim.log grep -a '^ok\|^FAIL' spisim.log ./usbfssim > usbfssim.log 2>&1 || fail=1 grep -a '^ok\|^FAIL' usbfssim.log +./ispsim > ispsim.log 2>&1 || fail=1 +grep -a '^ok\|^FAIL' ispsim.log ./websim > websim.log 2>&1 || fail=1 grep -a '^ok\|^FAIL' websim.log [ "$fail" = 0 ] && echo "all tests passed" diff --git a/tests/stub.c b/tests/stub.c index 54be939..59292c5 100644 --- a/tests/stub.c +++ b/tests/stub.c @@ -16,7 +16,14 @@ static int u(void) { return strcmp(env("MS_FAIL"),"unprotect") ? 0 : -3; } static int q(void) { printf("clear QE\n"); return strcmp(env("MS_FAIL"),"qe") ? 0 : -2; } #include #include "programmer.h" -static const struct programmer stubprogs[] = { { .key = "ch341a", .name = "CH341A", .send = (void *)1 }, { .key = "ch347", .name = "CH347", .send = (void *)1, .clock = true }, { .key = "ezp", .name = "EZP" } }; +/* the I2C of the CH341A and the CH347: a SigmaStar ISP bridge at 0x49 and 0x59, or (MS_I2C=none) nothing */ +static int i2c_sp(unsigned int sp) { (void)sp; return 0; } +static int i2c_w(unsigned char a, const unsigned char *b, unsigned int n) { (void)b; (void)n; return strcmp(env("MS_I2C"), "none") && (a == 0x49 || a == 0x59) ? 0 : -1; } +static int i2c_r(unsigned char a, unsigned char *b, unsigned int n) { if (n) memset(b, 0, n); return i2c_w(a, NULL, 0); } +static const struct programmer stubprogs[] = { + { .key = "ch341a", .name = "CH341A", .send = (void *)1, .i2c_speed = i2c_sp, .i2c_write = i2c_w, .i2c_read = i2c_r, .i2c_max = 24 }, + { .key = "ch347", .name = "CH347", .send = (void *)1, .clock = true, .i2c_speed = i2c_sp, .i2c_write = i2c_w, .i2c_read = i2c_r, .i2c_max = 60 }, + { .key = "ezp", .name = "EZP" } }; static const struct programmer *chosen = &stubprogs[0]; /* auto: the stub has a CH341A */ const struct programmer *programmer; int programmer_select(const char *n) {