asmc(4)
Source: sys/dev/asmc/asmc.c, sys/dev/asmc/asmcvar.h,
sys/dev/asmc/asmcmmio.c, sys/dev/asmc/asmcmmio.h,
sys/modules/asmc/.
What it is
Section titled “What it is”The SMC is the microcontroller in every Intel Mac that owns fans,
temperature sensors, the sudden motion sensor, the keyboard backlight, the
sleep LED, and the power sequencing. The host talks to it through a keyed
store: every value lives under a four-character key (TC0P, F0Ac,
MSLD), and the driver speaks a small command protocol to read and write
those keys.
Two backends, picked at probe time:
| Backend | Transport | Machines |
|---|---|---|
| PIO | I/O ports at base+0x00 (data), base+0x04 (command/status) | Every pre-T2 Intel Mac |
| MMIO | Memory window, command fired at 0x7F, status at 0x4005 | T2 Macs, where the PIO range is claimable but dead |
The probe matches ACPI APP0001 and names the device from
smbios.system.product, so devmatch autoloads it on anything Apple.
No model table
Section titled “No model table”There is no per-model capability list. The old approach shipped a static table mapping every SMBIOS product string to its keys, which meant every new Mac was a driver patch. This driver asks the SMC instead:
The SMC’s own key directory is sorted, so asmc_detect_sensors binary
searches to the start of each prefix region and walks forward with
get-key-by-index. Every sysctl leaf exists only if its key answered a
getinfo, so the tree shapes itself per machine. Static tables remain only
for decoration: key descriptions (“TC0P” is “CPU Proximity”), fixed-point
divisors per type (sp78 = 256, sp4b = 2048, …), and the
shutdown-cause code names.
PIO handshake
Section titled “PIO handshake”Status is the low nibble of the command port. Three values matter:
0x0c command accepted, 0x04 SMC awaits the next byte, 0x05 SMC has
a byte ready.
The asymmetry: a read waits for 0x05 before each byte it takes, a write
waits for 0x04 before each byte it gives. The length byte gets no wait
of its own, the first data-phase wait covers it. Commands are 0x10
read, 0x11 write, 0x12 get-key-by-index, 0x13 getinfo (6-byte
reply: length, 4-byte type, attributes).
The whole transaction runs under a spin mutex, polling with DELAY().
That is the price of sharing the lock with the SMS interrupt filter.
MMIO handshake, the T2 path
Section titled “MMIO handshake, the T2 path”One shot per call, no outer retry loop. The result code comes back in the same register the command was written to.
T2 also changes the data: fan keys report type flt , 4-byte IEEE 754
little-endian, converted by kernel soft-float since there is no FPU
context. Pre-T2 fans are fpe2 fixed point. Manual fan mode moves from the
global FS! bitmask to a per-fan F%dMd key.
The shutdown write
Section titled “The shutdown write”The one feature that is not a sensor. On a T2 Mac, bridgeOS watches the
host. If the host goes down without saying goodbye, the watchdog calls it
a crash and powers the machine back on. So a clean shutdown -p reboots
instead of halting.
The handler registers at SHUTDOWN_PRI_FIRST on shutdown_pre_sync, so
the SMC hears about the shutdown before the disks start syncing.
Sudden motion sensor
Section titled “Sudden motion sensor”Init writes the threshold and interval keys, then hammers MOCN until it
reads back 0xe0 0xf8, up to 1000 tries. Success arms the interrupt
path:
The taskqueue runs at PI_REALTIME for the original reason SMS exists:
park the disk heads before the laptop lands.
Everything else
Section titled “Everything else”- Keyboard backlight:
light.controlsysctl plus a backlight(9) registration, sobacklight(8)works. The ambient light sensor keyALV0comes in a 6-byte and a 10-byte format; attach detects which and picks the handler. - Sleep LED:
silsysctl. On isMSLS=1thenMSLD=1, off isMSLD=0xffalone. - Auto power-on:
AUPO, boot when AC returns after power loss. Not wake-on-LAN. - Battery charge limit:
BCLM, 0-100, T2 only. - System state: shutdown and sleep cause codes decoded through a table (5 is a good shutdown, -61 is a forced power-button hold), thermal status bitmap, board codename, security chip generation.
sysctl tree
Section titled “sysctl tree”dev.asmc.0.├── fan.N.{id, speed, safespeed, minspeed, maxspeed, targetspeed, manual}├── temp.<KEY> one per detected sp78 key├── voltage.<KEY> current.<KEY> power.<KEY> ambient.<KEY>├── light.{left, right, control}├── sms.{x, y, z}├── auto_poweron sil battery_charge_limit└── system.{shutdown_cause, sleep_cause, thermal_status, time_of_day, power_state, board_id, chip_gen}Every leaf is conditional on its key existing. Two Macs rarely show the same tree, and that is the point.
Reading it on hardware
Section titled “Reading it on hardware”kldload asmcsysctl dev.asmc.0.temp # every sp78 the SMC admits tosysctl dev.asmc.0.fan.0.speedsysctl dev.asmc.0.system.shutdown_causesysctl dev.asmc.0.sil=1 # sleep LED on, at full brightness