SD Wire Driver

jumpstarter-driver-sdwire provides functionality for using the SDWire storage multiplexer. This device multiplexes an SD card between the DUT and the exporter host.

Installation

$ pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple jumpstarter-driver-sdwire

Configuration

Example configuration:

type: "jumpstarter_driver_sdwire.driver.SDWire"
config:
  # optional serial number of the sd-wire device
  # the first one found would be used if unset
  serial: "sdw-00001"
  # optional path to the block device exposed by sd-wire
  # automatically detected if unset
  storage_device: "/dev/disk/by-diskseq/1"

Unprogrammed (factory-default) SD Wire devices

SD Wires are normally initialized with sd-mux-ctrl --init, which rewrites the FTDI FT200X EEPROM to the Samsung VID/PID (0x04E8/0x6001, product sd-wire) and configures the FT200X’s CBUS0 pin as a GPIO so the mux can be switched.

A device that still has the factory-default FTDI EEPROM (0x0403/0x6015) is also supported, but with two requirements:

  1. A serial must be configured. A bare FT200X has no reliable runtime signature that distinguishes an SD Wire from any other FT200X, so the driver will only bind one when you pin it by serial number.

  2. CBUS0 must be set to GPIO in the EEPROM (one-time fix). This is the critical prerequisite:

    The FT200X’s CBUS bitbang mode (used to switch the mux) requires CBUS0 to be configured as GPIO/IOMODE in the EEPROM. On an unprogrammed FT200X CBUS0 defaults to a fixed function (e.g. TXLED) and completely ignores bitbang commands — the green LED never lights and the mux never switches to DUT.

    The driver does not reprogram the EEPROM; it only sends the runtime bitbang command and assumes CBUS0 is already a GPIO. Perform this one-time fix first (it preserves the original VID/PID and only changes CBUS0):

    from pyftdi.eeprom import FtdiEeprom
    
    e = FtdiEeprom()
    e.open("ftdi://0x0403:0x6015/1")
    e.set_property("cbus_func_0", "GPIO")   # FT200X uses 'GPIO', not 'IOMODE'
    e.commit(dry_run=False)
    

    After flashing, unplug and replug the SD Wire USB cable. If you run the driver against an unprogrammed device that has not had this fix applied, the device is found but host()/dut() silently fail to move the mux.

macOS notes

On macOS (Darwin) the driver does extra work that is unnecessary on Linux:

  • dut() ejects the card before switching. The mux only switches when the SD bus is fully idle; while macOS holds the SMSC reader open it keeps polling the card. The driver runs diskutil eject (SCSI STOP UNIT) first. If the disk cannot be determined, dut() aborts rather than risk corrupting a mounted volume.

  • Power on the DUT immediately after dut() (< ~500 ms). The mux has a protection circuit that reverts to HOST if it sees no SD activity on the DUT side shortly after switching.

  • host() power-cycles the reader’s hub port. Because the prior eject leaves the SMSC reader stopped, host() routes the card back and then power-cycles port 1 of this SD Wire’s internal hub to force a clean re-enumeration (correlated by USB topology so other attached SD Wires are not disturbed).

  • Storage discovery uses system_profiler (pyudev is Linux-only) and may take a moment to re-enumerate after a switch, so reads/writes retry discovery up to storage_timeout.

API Reference

The SDWire driver implements the StorageMuxClient class, which is a generic storage class.

class jumpstarter_driver_opendal.client.StorageMuxClient
dut()

Connect storage to dut

host()

Connect storage to host

off()

Disconnect storage

read_local_file(filepath)

Read into a local file from the storage device

write_local_file(filepath)

Write a local file to the storage device