Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -157,7 +158,12 @@ microsnander [options]
(the first; -a for another) and put its data back
-v verify after write on chip
-P <name> programmer: ch341a, ch347, ezp, auto (default: the one connected)
-S <speed> SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M
-S <speed> 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 <password> web mode: ask for this password (any user name)
-o <dir> web mode: where Read saves the dumps (default: the current one)
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -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
Expand Down
38 changes: 35 additions & 3 deletions README.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ sudo ./microsnander -w firmware.bin -v
- [Примеры](#примеры)
- [Веб-режим](#веб-режим)
- [Приложение для Android](#приложение-для-android)
- [SigmaStar ISP](#sigmastar-isp)
- [Поддерживаемые микросхемы](#поддерживаемые-микросхемы)
- [Напряжение микросхем](#напряжение-микросхем)
- [Микросхемы с неточными данными](#микросхемы-с-неточными-данными)
Expand Down Expand Up @@ -158,7 +159,12 @@ microsnander [options]
(the first; -a for another) and put its data back
-v verify after write on chip
-P <name> programmer: ch341a, ch347, ezp, auto (default: the one connected)
-S <speed> SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M
-S <speed> 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 <password> web mode: ask for this password (any user name)
-o <dir> web mode: where Read saves the dumps (default: the current one)
Expand Down Expand Up @@ -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 соединений одновременно).
Expand Down Expand Up @@ -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
Expand Down
32 changes: 30 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sudo ./microsnander -w firmware.bin -v
- [示例](#示例)
- [Web 模式](#web-模式)
- [Android 应用](#android-应用)
- [SigmaStar ISP](#sigmastar-isp)
- [支持的芯片](#支持的芯片)
- [芯片电压](#芯片电压)
- [数据不确定的芯片](#数据不确定的芯片)
Expand Down Expand Up @@ -144,7 +145,12 @@ microsnander [options]
(the first; -a for another) and put its data back
-v verify after write on chip
-P <name> programmer: ch341a, ch347, ezp, auto (default: the one connected)
-S <speed> SPI clock of the CH347: 60M, 30M (default), 15M, 7.5M, 3.75M, 1.875M
-S <speed> 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 <password> web mode: ask for this password (any user name)
-o <dir> web mode: where Read saves the dumps (default: the current one)
Expand Down Expand Up @@ -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 个连接)。

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
54 changes: 54 additions & 0 deletions src/ch341a_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
7 changes: 7 additions & 0 deletions src/ch341a_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Loading