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>
123 lines
5.4 KiB
Markdown
123 lines
5.4 KiB
Markdown
# 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`.
|