Skip to content

Refactor!: Migrate to device-driver 2.1 - #29

Merged
tullom merged 18 commits into
OpenDevicePartnership:mainfrom
tullom:ddv2
Oct 7, 2026
Merged

tullom merged 18 commits into
OpenDevicePartnership:mainfrom
tullom:ddv2

Conversation

@tullom

@tullom tullom commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Adopt DDSL, regenerate bindings, and update CI and audits. Upgrade to defmt 1 and bump the crate to 0.3.0.

BREAKING CHANGE: Update the register API and require Rust 1.94.

Assisted-by: GitHub Copilot:gpt-6-astra

Uprev to v0.3.0, will cargo publish after merge.

@tullom tullom self-assigned this Sep 21, 2026
Copilot AI lite review requested due to automatic review settings September 21, 2026 21:03
@tullom
tullom requested a review from a team as a code owner September 21, 2026 21:03
@tullom tullom added the enhancement New feature or request label Sep 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved CI generation mismatches, a stale manifest, and register-definition naming issues block approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Migrates the driver to device-driver 2.1 and DDSL, regenerates bindings, updates dependencies and CI, and raises the MSRV to Rust 1.94.

Changes:

  • Replaces YAML register definitions with device.ddsl.
  • Updates APIs, defmt, crate metadata, audits, and lockfiles.
  • Refreshes generation checks, testing workflows, documentation, and line-ending rules.
File Summary
supply-chain/​imports.lock Refreshes dependency audit records.
src/​lib.rs Adapts the driver to the v2 register API.
README.md Documents the Rust 1.94 requirement.
device.ddsl Adds DDSL register definitions; contains naming and documentation issues to correct.
Cargo.toml Updates crate metadata, dependencies, features, and MSRV; leaves the old YAML manifest tracked.
Cargo.lock Locks the upgraded dependency graph.
build.rs Watches the DDSL source but leaves the obsolete YAML manifest unresolved.
AGENTS.md Updates repository guidance for DDSL and device-driver v2.
.github/​workflows/​device-driver.yml Verifies generated bindings, but needs pinned toolchain/version and matching defmt options.
.github/​workflows/​check.yml Updates feature-matrix and MSRV checks.
.gitattributes Enforces line endings for generated and DDSL sources.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/device-driver.yml
Copilot AI review requested due to automatic review settings September 21, 2026 21:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The device-driver workflow must pass the defmt feature flag and use a deterministic exact CLI version.

Review effort: Lite
Findings: 1 Medium severity

Open (1)

Copilot AI review requested due to automatic review settings September 23, 2026 18:23
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Cargo Vet Audit Passed

cargo vet has passed in this PR. No new unvetted dependencies were found.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Resolve the defmt feature-gate mismatch and provide an audit covering device-driver 2.1.1.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity · 1 Low severity

Open (2)
Resolved since last review (1)

Comment thread Cargo.toml
Comment thread AGENTS.md Outdated
Copilot AI review requested due to automatic review settings September 23, 2026 18:29
@tullom
tullom enabled auto-merge (squash) September 23, 2026 18:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The DDSL register grouping can cause incorrect multi-byte I2C accesses, and documentation references remain stale.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Low severity

Open (2)
Resolved since last review (2)

Comment thread device.ddsl
Comment thread AGENTS.md Outdated
Copilot AI review requested due to automatic review settings September 23, 2026 18:34
Table 7-38 (section 7.6.27) types the IIN_DPM field R and the register
figure shows R-C8h. Without an access annotation the register inherited
default-access: RW from the device block, so the driver generated
write_async and modify_async for a register the silicon never accepts
writes to. A caller reading IIN_DPM as "the input current limit" would
write it and silently achieve nothing.

Annotate the register and its field RO, matching how ADC_VBAT and the
other read-only registers in this manifest are already declared.

Assisted-by: OpenCode:claude-opus-5
Tables 7-43 and 7-44 (sections 7.6.32 and 7.6.33) type both ID fields R
and show R-40h and R-9h in the register figures. Both registers were
inheriting default-access: RW, so the driver generated write accessors
for identification registers that cannot be written.

Annotate both registers and their fields RO, and fold the documented
reset values into the doc comments.

Assisted-by: OpenCode:claude-opus-5
Table 7-57 (section 7.6.46) types STAT_IDCHG2 and STAT_PTM as R, and
Table 7-58 (section 7.6.47) types STAT_VBUS_VAP as R. All three were
left writable, so the generated API offered setters that the silicon
ignores and that suggested a latched status could be cleared by
writing it. Section 7.6.46 says the latch clears on read instead.

Annotate the three fields RO and say so in their doc comments. The
neighbouring STAT_PKPWR_RELAX and STAT_PKPWR_OVLD bits stay writable
because Table 7-48 really does type those R/W.

Assisted-by: OpenCode:claude-opus-5
Table 7-49 (section 7.6.38) names bit 7 of REG0x34 BATFET_ENZ: "Turn
off BATFET under battery only low power mode." The manifest carried
the name PKPWR_TOVLD_DEG here, copy-pasted from the unrelated field in
CHARGE_OPTION_2. The bit position and the doc comment were right, only
the identifier was wrong, so charge_option_3().set_pkpwr_tovld_deg(true)
turned off the BATFET and grepping the driver for BATFET_ENZ found
nothing.

Assisted-by: OpenCode:claude-opus-5
Table 7-30 (section 7.6.19) spells the upper four MODE_STAT encodings
"NA/Normal Compensation/Fsw-600kHz" and so on; the phase topology is
explicitly not applicable. Table 7-1 only makes 000b-011b reachable
through MODE pin programming, and all four of those are quasi dual
phase.

The manifest named 100b-111b SinglePhase*, asserting a topology the
datasheet does not. Rename them Na* so the generated enum stops
claiming more than the source does, and record the caveat in the
field's doc comment.

Assisted-by: OpenCode:claude-opus-5
VRECHG, VSYS_TH1, IDCHG_TH1, VSYS_TH2 and VBUS_VAP_TH all carry a step
and an offset that the manifest recorded nowhere, so a field value of
zero does not mean zero for any of them. IDCHG_TH1 is the worst: it is
a discharge-current PROCHOT threshold with a 1500mA offset and a 500mA
step, so reading it as raw milliamps puts the trip point 1.5 A off.

device-driver cannot express scaling, so the doc comment is the only
place this can live, and it is where anyone about to hand-roll the
conversion will look.

Sources: Table 7-28 (7.6.17) VRECHG, step 50mV offset 50mV; Table 7-51
(7.6.40) VSYS_TH1, step 100mV offset 5000mV; Table 7-54 (7.6.43)
IDCHG_TH1, step 500mA offset 1500mA; Table 7-59 (7.6.48) VSYS_TH2,
step 100mV offset 5000mV; Table 7-60 (7.6.49) VBUS_VAP_TH, step 100mV
offset 3200mV.

Assisted-by: OpenCode:claude-opus-5
Table 7-39 (section 7.6.28) and Table 7-42 (section 7.6.31) both give
"Bit Step: 2mV" and "Range: 0mV-65534mV" for these two channels. The
ADC_VBAT, ADC_PSYS and ADC_CMPIN_TR channels sitting next to them in
the manifest are 1mV/bit, so the natural assumption is wrong by a
factor of two and nothing in the driver said otherwise.

Record the step and range, and point out the difference from the
neighbouring channels.

Assisted-by: OpenCode:claude-opus-5
These three fields are enumerated in the datasheet but were left as
bare uints, which is out of step with the roughly forty other enums in
this manifest and leaves the caller to look the encodings up by hand.

Table 7-52 (section 7.6.41) gives ILIM2_VTH thirty-two levels from
110% to 450%, with 00000b and 11111b both marked out of range. Table
7-57 (section 7.6.46) gives IDCHG_TH2 eight levels from 125% to 400%
of IDCHG_TH1. Table 7-58 (section 7.6.47) gives VSYS_UVP eight levels
from 2.4V to 8.0V.

Bit positions and reset values are unchanged; only the encodings are
now expressed in the type.

Assisted-by: OpenCode:claude-opus-5
Table 7-35 (section 7.6.24) calls this bit the VINDPM/VOTG Status.

Assisted-by: OpenCode:claude-opus-5
The YAML-to-DDSL conversion left a working note wedged between
CMPIN_TR_SELECT's doc comment and the field it documents, splitting
the two.

The decision it records is worth keeping: device.yaml had these three
one-bit selections as base: int, a one-bit signed field sign-extends
so the 1 variant loads back as -1, and the v1 codegen then performed
try_into().unwrap_unchecked() on it, which is undefined behaviour.

Hoist the note above the doc comment and name the other two fields it
covers.

Assisted-by: OpenCode:claude-opus-5
Table 7-15 (section 7.6.4) notes that writing 0 to CHARGE_VOLTAGE()
leaves the register unchanged and forces CHARGE_CURRENT() to zero to
disable charge, and that non-zero values outside the clamp are set to
the clamp. Neither behaviour was documented, so charging_voltage(0)
looked like a request for 0 V and returned the still-programmed
voltage with no explanation.

Record both behaviours on the DDSL field and on the Charger impl, and
add a test pinning the read-back a caller actually gets for a zero
write.

Assisted-by: OpenCode:claude-opus-5
Several doc comments in device.ddsl ran to 300-plus columns on a
single line, which made the register definitions hard to read in a
diff and inconsistent with the 120-column limit the Rust sources are
held to. DDSL joins consecutive /// lines into one doc comment, so
wrapping costs nothing.

Rewrap every /// line so no comment exceeds 80 columns, and regenerate
src/device.rs. Unwrapping the result reproduces the previous file byte
for byte; no documented behaviour changed. The handful of remaining
long lines are field declarations whose enum names cannot be broken.

Assisted-by: OpenCode:claude-opus-5
Copilot AI lite review requested due to automatic review settings October 7, 2026 16:27
@felipebalbi
felipebalbi self-requested a review October 7, 2026 16:28
felipebalbi
felipebalbi previously approved these changes Oct 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Resolve the unpinned generator version and crate-version mismatch before approval.

1 open finding
4 resolved since last review

🧠 Review effort: Lite

Comment thread src/lib.rs
The doc comment said "with 5mΩ sense resistor, in 8mA/bit steps" as if
the step were fixed. Table 7-14 (section 7.6.3) says the step is 8mA
over 0mA-16320mA (0h-7F8h) only at 5mΩ, and notes that when 2mΩ is
chosen at RSNS_RSR=1b the LSB is 20mA and the value is clamped at 5DCh,
or 30A. RSNS_RSR lives in CHARGE_OPTION_1 and is documented in Table
7-46 (section 7.6.35).

The driver already picks the right scaling factor from RSNS_RSR, so
this is the manifest catching up with src/lib.rs rather than a
behaviour change. Also record the 128mA floor on non-zero settings,
the clamping behaviour, and the seven conditions under which the
charger resets this field to 0A, all from the same table.

Assisted-by: OpenCode:claude-opus-5
Copilot AI lite review requested due to automatic review settings October 7, 2026 16:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Three moderate unresolved findings require correction before approval.

0 open findings

1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Clamp sub-5000 mV requests before integer scaling

src/​lib.rs:166

The new documentation promises that every non-zero request below 5000 mV is clamped, but requests of 1–3 mV are integer-divided by 4 at line 170 and become the special zero write. The device therefore leaves the old voltage unchanged and disables charging instead of clamping to 5000 mV. Clamp before scaling (while preserving the explicit 0 case), or document/reject this range accurately.

🧠 Review effort: Lite

@felipebalbi

Copy link
Copy Markdown
Contributor

Unrelated to this PR, but it surfaced during review: 0.1.0 and 0.1.1 should be yanked.

Both write raw milliamps and millivolts straight into scaled register fields:

  • CHARGE_CURRENT is 8 mA/LSB (§7.6.3, Table 7-14) → every charge current is 8× too high. Ask for 1000 mA, get 8000 mA.
  • CHARGE_VOLTAGE is 4 mV/LSB in a 13-bit field (§7.6.4, Table 7-15) → values truncate. A 3S pack asking for 12600 mV gets regulated at 17632 mV. That's a legal value for the chip, so the clamp doesn't save you — it's just wrong for the cell count.

So: an 8× overcurrent and a ~5 V overvoltage into a 2–5 cell Li-ion pack, both in the damaging direction. Only EN_BATOVP stands between the second one and cell damage.

Fixed in 0.2.0 by #30 and #31.

Yanking looks cheap — zero reverse dependencies on crates.io, and 0.1.1 traffic dropped to ~zero the day 0.2.0 shipped. A yank doesn't break existing Cargo.lock builds, it only stops new resolution onto the affected versions.

cargo yank --vers 0.1.0 bq25773
cargo yank --vers 0.1.1 bq25773

@tullom
tullom merged commit 1d33068 into OpenDevicePartnership:main Oct 7, 2026
12 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 7, 2026
tullom pushed a commit that referenced this pull request Oct 7, 2026
## 🤖 New release

* `bq25773`: 0.2.1 -> 0.3.0

<details><summary><i><b>Changelog</b></i></summary><p>

<blockquote>

##
[0.3.0](v0.2.1...v0.3.0)
- 2026-10-07

### Other

- [**breaking**] Migrate to device-driver 2.1
([#29](#29))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cargo vet enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants