Skip to content

Commit 2aa4805

Browse files
committed
dfu: fix dfu-util --device args
It seems that only the first arg after the first "," in the dfu-util --device arg is used to match attached devices. Since SPIKE Prime and MINDSTORMS RI have different product IDs, we need to get the right ID to make it work for both hubs. We can do this by scraping the ID from the dfu-util --list command. Tested with dfu-util 0.9 on Ubuntu 20.04. Issue: pybricks/support#420
1 parent bb4d137 commit 2aa4805

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Fixed
1010
- Fixed crash in CRC32 checksum.
11+
- Fixed flashing with `dfu-util` not always working ([support#420]).
1112

1213
## [1.0.0-alpha.12]
1314

@@ -105,6 +106,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
105106
- Typo in `pip` arguments `README.md`.
106107

107108

109+
<!-- let's try to keep these sorted alphabetically -->
110+
[support#420]: https://github.com/pybricks/support/issues/420
111+
108112
[Unreleased]: https://github.com/pybricks/pybricksdev/compare/v1.0.0-alpha.12..HEAD
109113
[1.0.0-alpha.12]: https://github.com/pybricks/pybricksdev/compare/v1.0.0-alpha.11...v1.0.0-alpha.12
110114
[1.0.0-alpha.11]: https://github.com/pybricks/pybricksdev/compare/v1.0.0-alpha.10...v1.0.0-alpha.11

pybricksdev/_dfu_create.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def build(file, targets, device=DEFAULT_DEVICE):
104104
)
105105
data += tdata
106106
data = struct.pack("<5sBIB", b"DfuSe", 1, len(data) + 11, len(targets)) + data
107-
v, d = map(lambda x: int(x, 0) & 0xFFFF, device.split(":", 1))
107+
v, d = map(lambda x: int(x, 16) & 0xFFFF, device.split(":", 1))
108108
data += struct.pack("<4H3sB", 0, d, v, 0x011A, b"UFD", 16)
109109
crc = compute_crc(data)
110110
data += struct.pack("<I", crc)

pybricksdev/dfu.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from contextlib import nullcontext
1010
from importlib.resources import path
11-
from subprocess import DEVNULL, call, check_call
11+
from subprocess import DEVNULL, PIPE, call, check_call, run
1212
from tempfile import TemporaryDirectory
1313
from typing import BinaryIO, ContextManager
1414

@@ -24,8 +24,9 @@
2424
MINDSTORMS_INVENTOR_PID = 0x0011
2525

2626

27-
SPIKE_PRIME_DEVICE = f"0x{LEGO_VID:04x}:0x{SPIKE_PRIME_PID:04x}"
28-
MINDSTORMS_INVENTOR_DEVICE = f"0x{LEGO_VID:04x}:0x{MINDSTORMS_INVENTOR_PID:04x}"
27+
SPIKE_PRIME_DEVICE = f"{LEGO_VID:04x}:{SPIKE_PRIME_PID:04x}"
28+
MINDSTORMS_INVENTOR_DEVICE = f"{LEGO_VID:04x}:{MINDSTORMS_INVENTOR_PID:04x}"
29+
ALL_DEVICES = [SPIKE_PRIME_DEVICE, MINDSTORMS_INVENTOR_DEVICE]
2930

3031

3132
def _get_dfu_util() -> ContextManager[os.PathLike]:
@@ -54,6 +55,28 @@ def _get_dfu_util() -> ContextManager[os.PathLike]:
5455
return nullcontext(dfu_util)
5556

5657

58+
def _get_vid_pid(dfu_util: os.PathLike) -> str:
59+
"""
60+
Gets the VID and PID of a connected LEGO DFU device.
61+
62+
Returns: The first matching LEGO DFU device from ``dfu-util --list``
63+
64+
Raises: RuntimeError: No matching hubs found.
65+
"""
66+
proc = run([dfu_util, "--list"], stdout=PIPE, check=True)
67+
68+
for line in proc.stdout.splitlines():
69+
if not line.startswith(b"Found DFU:"):
70+
continue
71+
72+
dev_id = line[line.index(b"[") + 1 : line.index(b"]")].decode()
73+
74+
if dev_id in ALL_DEVICES:
75+
return dev_id
76+
77+
raise RuntimeError("No LEGO DFU USB device found")
78+
79+
5780
def backup_dfu(file: BinaryIO) -> None:
5881
"""Backs up device data via DFU.
5982
@@ -79,7 +102,7 @@ def backup_dfu(file: BinaryIO) -> None:
79102
[
80103
dfu_util,
81104
"--device",
82-
f",{SPIKE_PRIME_DEVICE},{MINDSTORMS_INVENTOR_DEVICE}",
105+
f",{_get_vid_pid(dfu_util)}",
83106
"--alt",
84107
"0",
85108
"--dfuse-address",
@@ -119,7 +142,7 @@ def restore_dfu(file: BinaryIO) -> None:
119142
[
120143
dfu_util,
121144
"--device",
122-
f",{SPIKE_PRIME_DEVICE},{MINDSTORMS_INVENTOR_DEVICE}",
145+
f",{_get_vid_pid(dfu_util)}",
123146
"--alt",
124147
"0",
125148
"--dfuse-address",
@@ -197,16 +220,16 @@ def flash_dfu(firmware_bin: bytes, metadata: dict) -> None:
197220

198221
with _get_dfu_util() as dfu_util:
199222

200-
# Exact device product ID doesn't matter here since we are using
201-
# the --device command line option below.
202-
_dfu_create.build(outfile, [[target]], SPIKE_PRIME_DEVICE)
223+
dev_id = _get_vid_pid(dfu_util)
224+
225+
_dfu_create.build(outfile, [[target]], dev_id)
203226

204227
exit(
205228
call(
206229
[
207230
dfu_util,
208231
"--device",
209-
f",{SPIKE_PRIME_DEVICE},{MINDSTORMS_INVENTOR_DEVICE}",
232+
f",{dev_id}",
210233
"--alt",
211234
"0",
212235
"--download",

0 commit comments

Comments
 (0)