Files
onyx/docs/spikes/A-smc.md
Scarriffle 386eb6ae70 Ladelimit ist eine Anzeige, Akku-Kachel sagt etwas, Menüleiste bekommt Kürzel
Der Regler war eine Pseudobedienung. Der SMC lehnt Schreibzugriffe auf CHLT
ab — der Helfer hat es mit korrekter Größe versucht und „geschrieben (false),
zurückgelesen 85" gemeldet. Das Sicherheitsnetz hat also die Wahrheit gesagt;
die Oberfläche hat sie nur nicht gezeigt.

Weiter zu raten hieße, im Lade-Subsystem Keys durchzuprobieren, und das ist
die eine Operation in diesem Projekt, die Hardware dauerhaft beschädigen
kann. Also: das Limit wird gelesen und angezeigt, mit einem Weg in die
Systemeinstellungen, wo es tatsächlich geht. Der Schreibpfad ist aus dem
privilegierten Helfer entfernt — ein Root-Dienst trägt nur, was benutzt wird.

Die Akku-Kachel zeigte „40 %" und einen Verlauf über die letzten Sekunden.
Der Ladestand ändert sich in Minuten, nicht in Sekunden; der Graph war eine
waagerechte Linie. Jetzt: verbleibende Zeit bzw. Zeit bis voll, Leistung in
Watt mit Richtung, Ladelimit, Zustand, Zyklen — und der Energiesparmodus.
Der erklärt, warum die Maschine langsamer wirkt, und ohne diesen Hinweis
sucht man den Grund woanders.

In der Menüleiste standen fünf nackte Prozentzahlen nebeneinander. Man sah
Zahlen und wusste nicht, welche wozu gehört. Zwei neue Darstellungen: Kürzel
und Wert („CPU 34 %") sowie Symbol und Wert. Beim Akku stehen sie zuoberst in
der Auswahl.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 12:37:04 +02:00

123 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Spike A — SMC-Keys auf Mac17,9 (M5 Pro)
Gemessen am 10.08.2026, macOS 26.6.1 (25G76), **ohne root**, ausschließlich lesend.
Werkzeug: `Spikes/smc-dump.swift`. 3486 Keys insgesamt.
## Ergebnis in einem Satz
**Lüftersteuerung: bestätigt und umsetzbar.** **Ladelimit: Key noch nicht identifiziert**
das Standardverfahren aus AlDente/batt greift auf dieser Maschine nicht, weil die dort benutzten
Keys schlicht nicht existieren.
## Wichtige Querbefunde
**SMC-Integer sind little-endian.** Meine erste BE-Dekodierung ergab Unsinn (Akkuspannung
31023 mV). LE ergibt konsistente Werte, zweifach gegengeprüft:
| Key | Roh | LE-Wert | Gegenprobe |
|---|---|---|---|
| `B0AV` | `AA 2F` | 12202 mV | plausible Zellspannung |
| `CH0V` | `AA 2F 00 00` | 12202 mV | identisch zu `B0AV` |
| `B0AC` | `85 FD` | 635 mA | entlädt; passt zu `PMVR` = 0 W (kein Netzteil) |
| `PPBR` | — | 12,13 W | ≈ 12,2 V × 0,64 A ✓ |
`flt`-Keys sind ebenfalls little-endian IEEE-754. Für die Implementierung heißt das:
**alle Mehrbyte-SMC-Werte little-endian lesen**, entgegen dem verbreiteten Beispielcode, der
big-endian annimmt (der stammt aus der Intel-Ära).
## Lüfter — vollständig nutzbar
Zwei Lüfter vorhanden (`FNum` = 2).
| Key | Typ | Wert | Bedeutung |
|---|---|---|---|
| `F0md` / `F1md` | ui8 | 0 | **Modus: 0 = auto, 1 = manuell** |
| `F0Tg` / `F1Tg` | flt | 2317 / 2502 | Ziel-Drehzahl |
| `F0Ac` / `F1Ac` | flt | 2321 / 2499 | Ist-Drehzahl |
| `F0Mn` / `F1Mn` | flt | 2317 | Minimum |
| `F0Mx` / `F1Mx` | flt | 7826 | Maximum |
| `F0St` / `F1St` | ui8 | 5 | Status |
| `FEna` | hex | 07 | Freigabe-Bitmaske |
**Korrektur zur Planannahme:** der Modus-Key heißt `F0md` **klein geschrieben**, nicht `F0Md`.
Mit der Großschreibung aus der Intel-Dokumentation findet man ihn nicht — genau deshalb lief
dieser Spike vor dem Implementierungscode.
`FS! ` (die alte Force-Bitmaske) existiert **nicht**. Der Weg ist also:
`F0md = 1``F0Tg = Wunschdrehzahl`, zurück auf Automatik mit `F0md = 0`.
Der Sicherheitsrahmen aus dem Plan lässt sich damit sauber umsetzen: `F0Mn`/`F0Mx` liefern
die harten Grenzen direkt aus der Firmware, und `F0md = 0` ist ein einzelner, immer
verfügbarer Rückfallbefehl.
## Ladelimit — noch offen
**Nicht vorhanden:** `CHWA`, `CH0B`, `CH0C`, `CH0I`, `CH0J`, `CH0K`, `BCLM`, `bfE0`, `bfF0`.
Damit scheiden sowohl das AlDente-Legacy-Verfahren (`CH0B`/`CH0C`) als auch das
80-%-Flag (`CHWA`) als auch das vollständige Firmware-Backend (`bfD0`/`bfE0`/`bfF0`) aus —
von letzterem existiert nur `bfD0` (hex, 2 Byte, `00 00`).
Vorhanden ist stattdessen ein umfangreicher `CH*`-Satz. Plausible Kandidaten:
| Key | Typ | Wert (LE) | Vermutung |
|---|---|---|---|
| `CHTE` | ui32 | 0 | Ladeziel — 0 könnte „kein Limit" heißen |
| `CHTL` | ui32 | 3600 | untere Schwelle? |
| `CHTU` | ui32 | 4100 | obere Schwelle? (mV je Zelle) |
| `CHBV` | ui32 | 4125 | Ladeschlussspannung mV |
| `CHIL` / `CHIO` | flag | false | Inhibit-Flags |
| `CHSE` / `CHST` / `CHRT` | ui8 | 0 | Status/Steuerung |
**Das bleibt Vermutung, und Vermutungen werden hier nicht geschrieben.** Ein falscher Wert im
Lade-Subsystem ist die eine Operation in diesem Projekt, die Hardware dauerhaft beschädigen kann.
### Nachtrag 11.08.2026: Key gefunden, aber nicht beschreibbar
Die Differenzmessung hat ihn geliefert. Der Key heißt **`CHLT`**, Typ `hex_`, drei Bytes:
| macOS-Einstellung | `CHLT` |
|---|---|
| 80 % | `50 01 00` |
| 85 % | `55 01 00` |
| 95 % | `5F 01 00` |
Erstes Byte ist die Prozentzahl, zweites offenbar „Limit aktiv", drittes unbenutzt.
Drei Messpunkte, weil einer Zufall sein kann: `BACC` passte beim ersten Vergleich ebenfalls —
ein Zähler, in dem gerade `50` stand. `ChargerConfiguration` aus `ioreg` sah mit niedrigem
Byte `0x50` ebenfalls wie eine Quelle aus, blieb aber unverändert, als `CHLT` längst auf 95
stand.
**Schreiben scheitert.** Der Helfer hat es mit korrekter Größe (3 Byte) versucht; der
SMC-Userclient gibt einen Fehler zurück, der Wert bleibt stehen:
```
Ladelimit: 95 % gewünscht, 95 % geschrieben (false), zurückgelesen 85 %
```
macOS setzt das Limit also auf einem anderen Weg. Welchem, ist offen — und weiter zu raten
hieße, im Lade-Subsystem Keys durchzuprobieren. Das ist die eine Operation in diesem Projekt,
die Hardware dauerhaft beschädigen kann, also unterbleibt es.
**Folge für Onyx:** das Ladelimit ist eine **Anzeige**. Der Wert wird gelesen und im
Akku-Widget sowie im Akku-Popover gezeigt, mit einem Weg in die Systemeinstellungen. Der
Schreibpfad ist aus dem privilegierten Helfer wieder entfernt — ein Root-Dienst trägt nur,
was benutzt wird.
### Damals geplanter Schritt: Differenzmessung statt Raten
macOS hat selbst eine Ladebegrenzung (Systemeinstellungen → Batterie → Batteriezustand →
„Optimiertes Laden" bzw. das 80-%-Limit). Wird sie umgeschaltet, muss sich der zuständige
SMC-Key ändern. `Spikes/smc-diff.sh` nimmt zwei vollständige Dumps auf und zeigt die Differenz —
damit ist der Key **ohne einen einzigen Schreibzugriff** identifizierbar.
Bis dieser Test gelaufen ist, bleibt die Akkukarte reine Anzeige.
## Auswirkungen auf den Plan
1. Phase 6 wird geteilt: **Lüftersteuerung kann gebaut werden**, das Ladelimit wartet auf die
Differenzmessung.
2. Alle SMC-Werte werden little-endian gelesen — betrifft `MetricsProvider` und `OnyxHelper`.
3. `F0Mn`/`F0Mx` werden zur Laufzeit aus der Firmware gelesen statt fest verdrahtet.
4. Lüfterdrehzahlen sind `flt`, nicht `fpe2`.