Skip to content
Merged
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
9 changes: 6 additions & 3 deletions cli/src/mpftp/espusb.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@
#: trailing ``; calc`` or a second path component cannot ride along, and the
#: ``\\`` after the product id
#: rejects ``&MI_00`` children -- restarting the parent brings its interfaces
#: with it. The task's PowerShell side enforces this same rule independently;
#: this copy is here so a bad id is refused before it is ever written down.
INSTANCE_ID_RE = re.compile(r"^USB\\VID_(303A|1A86)&PID_[0-9A-Fa-f]{4}\\[0-9A-Za-z&_.\-]+$")
#: with it. The serial may contain colons because a chip in ROM download mode
#: has no USB serial number, so Windows uses its MAC address
#: (``USB\VID_303A&PID_0009\48:27:E2:E9:B0:10``). The task's PowerShell side
#: enforces this same rule independently; this copy is here so a bad id is
#: refused before it is ever written down.
INSTANCE_ID_RE = re.compile(r"^USB\\VID_(303A|1A86)&PID_[0-9A-Fa-f]{4}\\[0-9A-Za-z&_.:\-]+$")

MAX_REQUEST_CHARS = 200
_TASK_WAIT_SECS = 45.0
Expand Down
27 changes: 27 additions & 0 deletions cli/tests/test_esp_usb_restart.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
ABSENT = r"USB\VID_303A&PID_4003\DEADBEEFDEADBEEF"
CH343 = r"USB\VID_1A86&PID_55D3\5ABA052144"
CH343_ABSENT = r"USB\VID_1A86&PID_55D3\DEADBEEF00"
ROM_MODE = r"USB\VID_303A&PID_0009\48:27:E2:E9:B0:10"
ROM_MODE_ABSENT = r"USB\VID_303A&PID_0009\DE:AD:BE:EF:00:01"
ROM_MODE_MI_CHILD = r"USB\VID_303A&PID_0009&MI_00\48:27:E2:E9:B0:10"
CH343_MI_CHILD = r"USB\VID_1A86&PID_55D2&MI_00\6&1B2C3D4&0&0000"
FTDI = r"USB\VID_0403&PID_6001\A50285BI"
CP210X = r"USB\VID_10C4&PID_EA60\0001"
Expand Down Expand Up @@ -71,6 +74,19 @@ def test_a_ch343_mi_child_is_refused(self):
with self.assertRaises(ValueError):
espusb.validate_instance_id(CH343_MI_CHILD)

def test_a_rom_download_mode_id_with_colons_is_accepted(self):
# A chip in ROM download mode has no USB serial; Windows uses its MAC.
self.assertEqual(espusb.validate_instance_id(ROM_MODE), ROM_MODE)

def test_a_colon_cannot_smuggle_an_mi_child_or_a_wildcard(self):
for bad in (ROM_MODE_MI_CHILD, ROM_MODE + "*", ROM_MODE + "?",
r"USB\VID_303A&PID_0009\48:27:*", r"USB\VID_303A&PID_0009\48:27:E2:E9:B0:1?",
r"USB\VID_303A&PID_0009\*:*", ROM_MODE + "; calc", ROM_MODE + " calc",
ROM_MODE + '"', ROM_MODE + "'", ROM_MODE + "\\x", r"USB\VID_303A&PID_0009\:\x",
ROM_MODE + "\n" + ROM_MODE):
with self.assertRaises(ValueError, msg=repr(bad)):
espusb.validate_instance_id(bad)

def test_a_ch343_with_a_command_or_wildcard_is_refused(self):
for bad in (CH343 + "; calc", CH343 + '" ; calc ; "', r"USB\VID_1A86&PID_55D3\*",
r"USB\VID_1A86*", CH343 + "\n" + CH343):
Expand Down Expand Up @@ -273,6 +289,17 @@ def test_a_ch343_id_passes_the_allow_list(self):
def test_a_ch343_mi_child_is_refused(self):
self.assertEqual(self._run(CH343_MI_CHILD + "\n"), 3)

def test_a_rom_download_mode_id_with_colons_passes_the_allow_list(self):
# Past the allow-list (not 3), stopped at the presence check (4).
self.assertEqual(self._run(ROM_MODE_ABSENT + "\n"), 4)

def test_a_colon_id_cannot_smuggle_an_mi_child_or_a_wildcard(self):
for bad in (ROM_MODE_MI_CHILD, ROM_MODE_ABSENT + "*",
ROM_MODE_ABSENT + "?", r"USB\VID_303A&PID_0009\DE:AD:*", r"USB\VID_303A&PID_0009\*:*",
ROM_MODE_ABSENT + "; calc", ROM_MODE_ABSENT + " calc", ROM_MODE_ABSENT + '"'):
with self.subTest(bad=bad):
self.assertEqual(self._run(bad + "\n"), 3)

def test_a_ch343_with_a_trailing_command_is_refused(self):
self.assertEqual(self._run(CH343 + "; calc\n"), 3)

Expand Down
6 changes: 6 additions & 0 deletions docs/agent-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,12 @@ instance id: an Espressif board on its native USB (`VID_303A`), and a WCH
CH343 USB-serial bridge (`VID_1A86`), the UART port on the P4 and many other
boards. Nothing else is accepted.

An Espressif board sitting in ROM download mode is covered too. It has no USB
serial number, so Windows names the node after the chip's MAC address, colons
included: `USB\VID_303A&PID_0009\48:27:E2:E9:B0:10`. `--list` prints it that
way and `--instance` accepts it. A machine that installed the task before this
needs the install run again; until then a colon id is refused with exit 3.

**A CH343 port that answers "Incorrect function".** When a COM port behind a
CH343 fails every open with Windows error 1, "Incorrect function", the node
usually still reads `Status OK` and `mpftp ports` still lists it. The vendor
Expand Down
11 changes: 11 additions & 0 deletions tools/windows/install-restart-esp-usb-task.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ if ($LASTEXITCODE -ne 4) {
}
Write-Output "self-check a CH343 instance id passes the allow-list (exit 4, no such device)"

# And the ROM download mode shape: no USB serial number, so Windows names the
# node after the chip's MAC address, colons included. An older installed script
# refuses it with 3.
& powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $target `
-InstanceId 'USB\VID_303A&PID_0009\DE:AD:BE:EF:00:01' -DryRun | Out-Null
if ($LASTEXITCODE -ne 4) {
Write-Error "self-check failed: a well-formed ROM download mode id (MAC with colons) for an absent device should exit 4, got $LASTEXITCODE"
exit 1
}
Write-Output "self-check a ROM download mode instance id (MAC with colons) passes the allow-list (exit 4, no such device)"

if (-not (Test-Path -LiteralPath $keep)) {
Write-Error "self-check failed: $keep is missing, so the request directory could be emptied and converted into a junction"
exit 1
Expand Down
9 changes: 6 additions & 3 deletions tools/windows/restart-esp-usb.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,12 @@ if (-not $LogPath) {
# product id is deliberate: it admits the composite *parent* and rejects its
# `&MI_00` children -- re-enumerating the parent brings the interfaces with it,
# and a child whose parent is about to vanish reports a confusing failure of
# its own. The vendor alternation is the only thing WCH added: the rest of the
# rule is unchanged, and still names one device.
$ALLOWED_INSTANCE_ID = '^USB\\VID_(303A|1A86)&PID_[0-9A-Fa-f]{4}\\[0-9A-Za-z&_.\-]+$'
# its own. The vendor alternation is the only thing WCH added, and the colon in
# the serial is the only thing ROM download mode added: a chip in that mode has no
# USB serial number, so Windows uses the chip's MAC address, with colons
# (USB\VID_303A&PID_0009\48:27:E2:E9:B0:10). Neither widens the rule beyond one
# device: no `*`, `?`, space, quote or backslash is admitted.
$ALLOWED_INSTANCE_ID = '^USB\\VID_(303A|1A86)&PID_[0-9A-Fa-f]{4}\\[0-9A-Za-z&_.:\-]+$'
# Espressif boards change identity on a restart (ROM download mode); nothing else
# here does.
$ESPRESSIF_INSTANCE_ID = '^USB\\VID_303A&'
Expand Down
Loading