
Firmware RP2040 que conecta un microdrive SDIO Toshiba MK4001MTD de 0.85" como un dispositivo de almacenamiento masivo USB, implementando la pila completa del protocolo SDIO-ATA desde cero con lecturas/escrituras aceleradas por PIO y recuperación de sectores defectuosos.
Firmware RP2040 Pico que puentea un microdrive SDIO Toshiba MK4001MTD de 0.85" como dispositivo de almacenamiento masivo USB.

El MK4001MTD es un microdrive de 4 GB originalmente usado en el Nokia N91 y otros dispositivos, como reproductores MP3 o unidades USB, cuando el almacenamiento flash aún era bastante caro.
Quizás hayas visto presentaciones que afirman que esta unidad usa el protocolo MMC, pero eso es incorrecto. He estado investigando esto por un tiempo: intenté construir un lector de tarjetas MMCplus de 8 bits y probé diferentes lectores SD/MMC sin éxito. Como último recurso, compré un Nokia N91 para capturar trazas lógicas y confirmar qué protocolo usa realmente.
Aquí la foto cuando intentaba usarlo con mi placa lectora 8bit-MMCPlus, y resulta que no es MMC :(

Así que terminé comprando el N91 para recolectar trazas:

A diferencia de los microdrives ATA/CF estándar, utiliza una interfaz SDIO con comandos ATA tunelizados a través de CMD52/CMD53. Ningún controlador existente soporta este protocolo, por lo que este firmware implementa la pila completa desde cero.
Esto me sorprendió, porque existe un estándar SDIO-to-ATA llamado CE-ATA. Pero si observas la línea de tiempo de lanzamiento, CE-ATA llegó después que esta unidad. Como resultado, esta unidad depende completamente de comandos SDIO, y CE-ATA no está disponible. CE-ATA tiene dos nuevos comandos CMD60/CMD61 y utiliza CMD12/39, pero se puede ver en las trazas que no usa ninguno de ellos.
El segundo punto de hardware es que otra desinformación flotando —afirmando que es una tarjeta MMCPlus de 8 bits— no solo es falsa, sino que el pinout tampoco sigue el estándar MMC. Puedes encontrar el manual de servicio del Nokia N91 con algo de documentación sobre el pinout: aunque la numeración de pines sigue el estándar MMCPlus, la asignación de pines no. Este es un detalle importante si lo estás cableando tú mismo: usa el mismo conector MMC, pero la asignación de pines es diferente, más en la sección de Hardware.
Finalmente, ten en cuenta que esto es co-desarrollado con Claude/OpenClaw. Yo recolecté las trazas lógicas manualmente y configuré una estación de prueba de bucle cerrado para que OpenClaw iterara en el desarrollo —analizando las trazas e implementando funcionalidades. La documentación será principalmente escrita por Claude; también agregaré mis notas inline. También he leído y verificado la documentación yo mismo, y debería ser confiable y fácil de seguir.
Para obtener información sobre el análisis de la traza del N91, está en /docs/N91_TRACE_ANALYSIS.md; también he puesto allí el manual de servicio del N91 junto con las trazas lógicas sin procesar.
Ver más en la publicación del blog aquí: https://www.willwhang.dev/Reading-MK4001MTD/
Verlo en acción aquí: https://youtu.be/GC4xil3_Bbc
Almacenamiento masivo USB completamente funcional con lecturas/escrituras aceleradas por PIO y gestión de energía en reposo.
| Métrica | Valor |
|---|---|
| Velocidad de lectura | ~985 kB/s (limitado por USB full-speed) |
| Velocidad de escritura | ~920 kB/s (limitado por USB full-speed, caché de escritura anunciada) |
| Velocidad bruta lado SDIO | ~2.35 MB/s lectura / ~2.15 MB/s escritura (limitado por la unidad) |
| Capacidad | 3.75 GB (7,862,400 sectores) |
| Sistema de archivos | FAT32 verificado (mount/unmount/fsck limpio) |
| Integridad de datos | Lectura+reescritura verificada; CRC16 por bloque en las 4 líneas DAT |
| Standby en reposo | 5 s inactivo o suspensión USB → STANDBY IMMEDIATE + corte de energía |
USB Host ←→ USB MSC (TinyUSB) ←→ Capa ATA ←→ Capa SDIO (PIO) ←→ MK4001MTD
El firmware tiene cuatro capas:
USB MSC (msc_device.c) — TinyUSB Mass Storage Class. Traduce SCSI READ(10)/WRITE(10) a operaciones de sectores ATA. Buffer EP de 32 KB, agrupando hasta 64 sectores por transferencia USB. La E/S de la unidad se superpone con USB en ambas direcciones, como un puente ATA-USB real con un disco de caché: un pre-buscador de lectura secuencial obtiene el siguiente bloque mientras el anterior se transmite al host, y las escrituras se ponen en cola y se vacían mientras USB recibe la siguiente pieza. El dispositivo anuncia su caché de escritura (Página de modo Caching, WCE=1 — los hosts reportan "Write cache: enabled" y emiten SYNCHRONIZE CACHE en fsync/desmontaje/suspensión, que el firmware respeta). Un vaciado en segundo plano fallido se refleja como MEDIUM ERROR en la próxima WRITE o SYNCHRONIZE CACHE; las escrituras a sectores malos conocidos siguen una ruta síncrona estricta.
ATA-over-SDIO (ata_sdio.c) — Implementa comandos ATA (IDENTIFY, READ SECTORS, WRITE SECTORS) escribiendo en registros ATA mapeados en el espacio de direcciones de la función SDIO 1 a través de CMD52, y transfiriendo datos de sectores a través de CMD53. Lógica de reintento de 3 niveles a nivel de CMD, datos y ATA.
PIO SDIO (sdio_pio.c, sdio.pio) — SDIO acelerado por hardware usando el periférico PIO del RP2040 (bus de 4 bits a 10 MHz, 4 ciclos PIO por bit con sincronizadores de entrada omitidos). Tres programas PIO comparten una sola máquina de estados mediante intercambio dinámico de programas:
Pin/Power (sdio_hw.c) — Inicialización de GPIO y control de energía del HDD. Toda la comunicación SDIO usa PIO.
Notas humanas: Curiosamente, Claude fue muy reacio a implementar SDIO en PIO, y se desperdiciaron muchos ciclos de desarrollo yendo y viniendo entre PIO y bit-banging.
El MK4001MTD se presenta como una tarjeta SDIO con una función de E/S. La inicialización estándar de tarjetas SDIO (CMD5/CMD3/CMD7) configura el bus, luego los registros ATA se acceden mediante comandos SDIO:
Acceso a registros (CMD52): Cada registro ATA está mapeado a una dirección de función 1:
| Dirección | Registro | Uso |
|---|---|---|
| 0x00 | DATA | Objetivo CMD53 para datos de sector |
| 0x01 | ERR/FEAT | Error (lectura) / Feature (escritura) |
| 0x02 | SECCOUNT | Conteo de sectores |
| 0x03 | LBA_LO | LBA bits 0-7 |
| 0x04 | LBA_MID | LBA bits 8-15 |
| 0x05 | LBA_HI | LBA bits 16-23 |
| 0x06 | DEV/HEAD | Dispositivo/Cabeza + LBA bits 24-27 |
| 0x07 | CMD/STATUS | Comando (escritura) / Estado (lectura) |
Transferencia de datos (CMD53): Los datos de sectores se transfieren emitiendo CMD53 en modo bloque apuntando al registro DATA (dirección 0x00). Para lecturas de múltiples sectores, un solo CMD53 con block_count=N transfiere N × 512 bytes en una sola transacción SDIO multi-bloque.