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

5.4 KiB
Raw Blame History

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 = 1F0Tg = 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.