diff --git a/share/man/man4/asmc.4 b/share/man/man4/asmc.4 --- a/share/man/man4/asmc.4 +++ b/share/man/man4/asmc.4 @@ -23,7 +23,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd April 2, 2019 +.Dd January 8, 2026 .Dt ASMC 4 .Os .Sh NAME @@ -110,6 +110,44 @@ respectively. .Pp All values are in RPM. +.Sh RAW SMC KEY ACCESS +The +.Va dev.asmc.%d.raw +sysctl tree provides direct access to arbitrary SMC keys for debugging +and hardware exploration. +This interface enables reading and writing any SMC key by name, +allowing discovery of undocumented sensors and hardware controls. +.Pp +To use the raw SMC interface: +.Bl -enum -offset indent +.It +Set the desired 4-character SMC key name: +.Bd -literal -offset indent +sysctl dev.asmc.0.raw.key=AUPO +.Ed +.It +Read the key value as a hexadecimal string: +.Bd -literal -offset indent +sysctl dev.asmc.0.raw.value +.Ed +.It +Write a new value as a hexadecimal string: +.Bd -literal -offset indent +sysctl dev.asmc.0.raw.value=01 +.Ed +.El +.Pp +The key type and length are automatically detected when setting a key name. +The +.Va dev.asmc.%d.raw.len +and +.Va dev.asmc.%d.raw.type +sysctls provide the detected length and 4-character type code. +.Pp +.Sy Warning : +Writing incorrect values to SMC keys can cause system instability or +hardware damage. +This interface is intended for advanced users and developers only. .Sh SUDDEN MOTION SENSOR The Sudden Motion Sensor (SMS for short) is a device that detects laptop movement and notifies the operating system via an interrupt. diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c --- a/sys/dev/asmc/asmc.c +++ b/sys/dev/asmc/asmc.c @@ -109,6 +109,13 @@ static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS); static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS); +/* Raw key access */ +static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *); +static int asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS); + struct asmc_model { const char *smc_model; /* smbios.system.product env var. */ const char *smc_desc; /* driver description */ @@ -716,6 +723,41 @@ "Keyboard backlight brightness control"); } + /* + * Raw SMC key access for debugging. + */ + sc->sc_raw_tree = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "raw", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Raw SMC key access"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "key", + CTLTYPE_STRING | CTLFLAG_RW, + dev, 0, asmc_raw_key_sysctl, "A", + "SMC key name (4 chars)"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "value", + CTLTYPE_STRING | CTLFLAG_RW, + dev, 0, asmc_raw_value_sysctl, "A", + "SMC key value (hex string)"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "len", + CTLTYPE_U8 | CTLFLAG_RW, + dev, 0, asmc_raw_len_sysctl, "CU", + "SMC key value length"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "type", + CTLTYPE_STRING | CTLFLAG_RD, + dev, 0, asmc_raw_type_sysctl, "A", + "SMC key type (4 chars)"); + if (model->smc_sms_x == NULL) goto nosms; @@ -1151,6 +1193,162 @@ } #endif +/* + * Get key info (length and type) from SMC using command 0x13. + * Returns 0 on success, -1 on failure. + * If len is non-NULL, stores the key's value length. + * If type is non-NULL, stores the 4-char type string (must be at least 5 bytes). + */ +static int +asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t info[6]; + int i, error; + + mtx_lock_spin(&sc->sc_mtx); + + if (asmc_command(dev, 0x13)) { + mtx_unlock_spin(&sc->sc_mtx); + return (-1); + } + + for (i = 0; i < 4; i++) { + ASMC_DATAPORT_WRITE(sc, key[i]); + if (asmc_wait(dev, 0x04)) { + mtx_unlock_spin(&sc->sc_mtx); + return (-1); + } + } + + ASMC_DATAPORT_WRITE(sc, 6); + + for (i = 0; i < 6; i++) { + if (asmc_wait(dev, 0x05)) { + mtx_unlock_spin(&sc->sc_mtx); + return (-1); + } + info[i] = ASMC_DATAPORT_READ(sc); + } + + error = 0; + mtx_unlock_spin(&sc->sc_mtx); + + if (error == 0) { + if (len != NULL) + *len = info[0]; + if (type != NULL) { + for (i = 0; i < 4; i++) + type[i] = info[i + 1]; + type[4] = '\0'; + } + } + return (error); +} + +/* + * Raw SMC key access sysctls - enables reading/writing any SMC key by name + * Usage: + * sysctl dev.asmc.0.raw.key=AUPO # Set key, auto-detects length + * sysctl dev.asmc.0.raw.value # Read current value (hex) + * sysctl dev.asmc.0.raw.value=01 # Write new value (hex) + */ +static int +asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + char newkey[5]; + uint8_t keylen; + int error; + + strlcpy(newkey, sc->sc_rawkey, sizeof(newkey)); + error = sysctl_handle_string(oidp, newkey, sizeof(newkey), req); + if (error || req->newptr == NULL) + return (error); + + if (strlen(newkey) != 4) + return (EINVAL); + + /* Get key info to auto-detect length and type */ + if (asmc_key_getinfo(dev, newkey, &keylen, sc->sc_rawtype) != 0) + return (ENOENT); + + if (keylen > ASMC_MAXVAL) + keylen = ASMC_MAXVAL; + + strlcpy(sc->sc_rawkey, newkey, sizeof(sc->sc_rawkey)); + sc->sc_rawlen = keylen; + memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval)); + + /* Read the key value */ + asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen); + + return (0); +} + +static int +asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + char hexbuf[ASMC_MAXVAL * 2 + 1]; + int error, i; + + /* Refresh from SMC */ + if (sc->sc_rawkey[0] != '\0') { + asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, + sc->sc_rawlen > 0 ? sc->sc_rawlen : ASMC_MAXVAL); + } + + /* Format as hex string */ + for (i = 0; i < sc->sc_rawlen && i < ASMC_MAXVAL; i++) + snprintf(hexbuf + i * 2, 3, "%02x", sc->sc_rawval[i]); + hexbuf[i * 2] = '\0'; + + error = sysctl_handle_string(oidp, hexbuf, sizeof(hexbuf), req); + if (error || req->newptr == NULL) + return (error); + + /* Parse hex and write */ + if (sc->sc_rawkey[0] == '\0') + return (EINVAL); + + memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval)); + for (i = 0; i < sc->sc_rawlen && hexbuf[i * 2] && + hexbuf[i * 2 + 1]; i++) { + unsigned int val; + char tmp[3] = { hexbuf[i * 2], hexbuf[i * 2 + 1], 0 }; + if (sscanf(tmp, "%02x", &val) == 1) + sc->sc_rawval[i] = (uint8_t)val; + } + + if (asmc_key_write(dev, sc->sc_rawkey, sc->sc_rawval, + sc->sc_rawlen) != 0) + return (EIO); + + return (0); +} + +static int +asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + + return (sysctl_handle_8(oidp, &sc->sc_rawlen, 0, req)); +} + +static int +asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + + return (sysctl_handle_string(oidp, sc->sc_rawtype, + sizeof(sc->sc_rawtype), req)); +} + static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) { diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h --- a/sys/dev/asmc/asmcvar.h +++ b/sys/dev/asmc/asmcvar.h @@ -28,6 +28,7 @@ */ #define ASMC_MAXFANS 6 +#define ASMC_MAXVAL 32 /* Maximum SMC value size */ struct asmc_softc { device_t sc_dev; @@ -51,6 +52,12 @@ struct taskqueue *sc_sms_tq; struct task sc_sms_task; uint8_t sc_sms_intr_works; + /* Raw key access */ + struct sysctl_oid *sc_raw_tree; + char sc_rawkey[5]; /* 4-char key + NUL */ + uint8_t sc_rawval[ASMC_MAXVAL]; + uint8_t sc_rawlen; + char sc_rawtype[5]; /* 4-char type + NUL */ }; /*