git: cdad55809ef5 - main - acpi_system76: Support for acpi-controlled buttons on System76
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 07 Mar 2026 15:27:52 UTC
The branch main has been updated by pouria:
URL: https://cgit.FreeBSD.org/src/commit/?id=cdad55809ef59239c3bbdc841ed307db68bb3971
commit cdad55809ef59239c3bbdc841ed307db68bb3971
Author: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-03-06 17:15:49 +0000
Commit: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-03-07 15:27:08 +0000
acpi_system76: Support for acpi-controlled buttons on System76
Add acpi_system76 for handling acpi-controlled buttons
on System76 Laptops.
Reviewed by: imp
Differential Revision: https://reviews.freebsd.org/D55694
---
sys/conf/files | 1 +
sys/dev/acpi_support/acpi_system76.c | 359 ++++++++++++++++++++++++++++++++
sys/modules/acpi/Makefile | 1 +
sys/modules/acpi/acpi_system76/Makefile | 7 +
sys/x86/conf/NOTES | 3 +
5 files changed, 371 insertions(+)
diff --git a/sys/conf/files b/sys/conf/files
index 632fddef2cb5..ceff5c9d6c16 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -744,6 +744,7 @@ dev/acpi_support/acpi_ibm.c optional acpi_ibm acpi
dev/acpi_support/acpi_panasonic.c optional acpi_panasonic acpi
dev/acpi_support/acpi_sbl_wmi.c optional acpi_sbl_wmi acpi
dev/acpi_support/acpi_sony.c optional acpi_sony acpi
+dev/acpi_support/acpi_system76.c optional acpi_system76 acpi
dev/acpi_support/acpi_toshiba.c optional acpi_toshiba acpi
dev/acpi_support/atk0110.c optional aibs acpi
dev/acpica/Osd/OsdDebug.c optional acpi
diff --git a/sys/dev/acpi_support/acpi_system76.c b/sys/dev/acpi_support/acpi_system76.c
new file mode 100644
index 000000000000..916a9a61f471
--- /dev/null
+++ b/sys/dev/acpi_support/acpi_system76.c
@@ -0,0 +1,359 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+#include <dev/acpica/acpivar.h>
+#include <sys/sysctl.h>
+
+#define _COMPONENT ACPI_OEM
+ACPI_MODULE_NAME("system76")
+
+static char *system76_ids[] = { "17761776", NULL };
+ACPI_SERIAL_DECL(system76, "System76 ACPI management");
+
+struct acpi_ctrl {
+ int val;
+ bool exists;
+};
+
+struct acpi_system76_softc {
+ device_t dev;
+ ACPI_HANDLE handle;
+
+ struct acpi_ctrl kbb, /* S76_CTRL_KBB */
+ kbc; /* S76_CTRL_KBC */
+
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+};
+
+static int acpi_system76_probe(device_t);
+static int acpi_system76_attach(device_t);
+static int acpi_system76_detach(device_t);
+static void acpi_system76_init(struct acpi_system76_softc *);
+static struct acpi_ctrl *
+ acpi_system76_ctrl_map(struct acpi_system76_softc *, int);
+static int acpi_system76_update(struct acpi_system76_softc *, int, bool);
+static int acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS);
+static void acpi_system76_notify_handler(ACPI_HANDLE, uint32_t, void *);
+static void acpi_system76_check(struct acpi_system76_softc *);
+
+/* methods */
+#define S76_CTRL_KBB 1 /* Keyboard Brightness */
+#define S76_CTRL_KBC 2 /* Keyboard Color */
+#define S76_CTRL_MAX 3
+
+struct s76_ctrl_table {
+ char *name;
+ char *get_method;
+#define S76_CTRL_GKBB "\\_SB.S76D.GKBB"
+#define S76_CTRL_GKBC "\\_SB.S76D.GKBC"
+
+ char *set_method;
+#define S76_CTRL_SKBB "\\_SB.S76D.SKBB"
+#define S76_CTRL_SKBC "\\_SB.S76D.SKBC"
+
+ char *desc;
+};
+
+static const struct s76_ctrl_table s76_sysctl_table[] = {
+ [S76_CTRL_KBB] = {
+ .name = "keyboard_backlight",
+ .get_method = S76_CTRL_GKBB,
+ .set_method = S76_CTRL_SKBB,
+ .desc = "Keyboard Backlight",
+ },
+ [S76_CTRL_KBC] = {
+ .name = "keyboard_color",
+ .get_method = S76_CTRL_GKBC,
+ .set_method = S76_CTRL_SKBC,
+ .desc = "Keyboard Color",
+ },
+};
+
+static device_method_t acpi_system76_methods[] = {
+ DEVMETHOD(device_probe, acpi_system76_probe),
+ DEVMETHOD(device_attach, acpi_system76_attach),
+ DEVMETHOD(device_detach, acpi_system76_detach),
+
+ DEVMETHOD_END
+};
+
+/* Notify event */
+#define ACPI_NOTIFY_BACKLIGHT_CHANGED 0x80
+#define ACPI_NOTIFY_COLOR_TOGGLE 0x81
+#define ACPI_NOTIFY_COLOR_DOWN 0x82
+#define ACPI_NOTIFY_COLOR_UP 0x83
+#define ACPI_NOTIFY_COLOR_CHANGED 0x84
+
+static driver_t acpi_system76_driver = {
+ "acpi_system76",
+ acpi_system76_methods,
+ sizeof(struct acpi_system76_softc)
+};
+
+/*
+ * Returns corresponding acpi_ctrl of softc from method
+ */
+static struct acpi_ctrl *
+acpi_system76_ctrl_map(struct acpi_system76_softc *sc, int method)
+{
+
+ switch (method) {
+ case S76_CTRL_KBB:
+ return (&sc->kbb);
+ break;
+ case S76_CTRL_KBC:
+ return (&sc->kbc);
+ break;
+ default:
+ device_printf(sc->dev, "Driver received unknown method\n");
+ return (NULL);
+ }
+}
+
+static int
+acpi_system76_update(struct acpi_system76_softc *sc, int method, bool set)
+{
+ struct acpi_ctrl *ctrl;
+ ACPI_STATUS status;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(system76);
+
+ if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
+ return (EINVAL);
+
+ if (set)
+ status = acpi_SetInteger(sc->handle, s76_sysctl_table[method].set_method,
+ ctrl->val);
+ else
+ status = acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method,
+ &ctrl->val);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->dev, "Couldn't query method (%s)\n",
+ s76_sysctl_table[method].name);
+ return (status);
+ }
+
+ return (0);
+}
+
+static void
+acpi_system76_notify_update(void *arg)
+{
+ struct acpi_system76_softc *sc;
+ int method;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_system76_softc *)device_get_softc(arg);
+
+ ACPI_SERIAL_BEGIN(system76);
+ for (method = 1; method < S76_CTRL_MAX; method++)
+ acpi_system76_update(sc, method, false);
+ ACPI_SERIAL_END(system76);
+}
+
+static void
+acpi_system76_check(struct acpi_system76_softc *sc)
+{
+ struct acpi_ctrl *ctrl;
+ int method;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ ACPI_SERIAL_ASSERT(system76);
+
+ for (method = 1; method < S76_CTRL_MAX; method++) {
+ if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
+ continue;
+
+ if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
+ s76_sysctl_table[method].get_method, &ctrl->val))) {
+ ctrl->exists = false;
+ device_printf(sc->dev, "Driver can't control %s\n",
+ s76_sysctl_table[method].desc);
+ } else {
+ ctrl->exists = true;
+ acpi_system76_update(sc, method, false);
+ }
+ }
+}
+
+static void
+acpi_system76_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *ctx)
+{
+
+ ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
+
+ switch (notify) {
+ case ACPI_NOTIFY_BACKLIGHT_CHANGED:
+ case ACPI_NOTIFY_COLOR_TOGGLE:
+ case ACPI_NOTIFY_COLOR_DOWN:
+ case ACPI_NOTIFY_COLOR_UP:
+ case ACPI_NOTIFY_COLOR_CHANGED:
+ AcpiOsExecute(OSL_NOTIFY_HANDLER,
+ acpi_system76_notify_update, ctx);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+ struct acpi_ctrl *ctrl;
+ struct acpi_system76_softc *sc;
+ int val, method, error;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = (struct acpi_system76_softc *)oidp->oid_arg1;
+ method = oidp->oid_arg2;
+ if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
+ return (EINVAL);
+
+ val = ctrl->val;
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error != 0) {
+ device_printf(sc->dev, "Driver query failed\n");
+ return (error);
+ }
+ if (req->newptr == NULL)
+ return (error);
+
+ /* Input validation */
+ switch (method) {
+ case S76_CTRL_KBB:
+ if (val > UINT8_MAX || val < 0)
+ return (EINVAL);
+ break;
+ case S76_CTRL_KBC:
+ if (val >= (1 << 24) || val < 0)
+ return (EINVAL);
+ break;
+ default:
+ break;
+ }
+
+ ctrl->val = val;
+
+ ACPI_SERIAL_BEGIN(system76);
+ error = acpi_system76_update(sc, method, true);
+ ACPI_SERIAL_END(system76);
+ return (error);
+}
+
+static void
+acpi_system76_init(struct acpi_system76_softc *sc)
+{
+ struct acpi_softc *acpi_sc;
+ struct acpi_ctrl *ctrl;
+ uint32_t method;
+
+ ACPI_SERIAL_ASSERT(system76);
+
+ acpi_system76_check(sc);
+ acpi_sc = acpi_device_get_parent_softc(sc->dev);
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "s76",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "system76 control");
+
+ for (method = 1; method < S76_CTRL_MAX; method++) {
+ if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
+ continue;
+
+ if (!ctrl->exists)
+ continue;
+
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, s76_sysctl_table[method].name,
+ CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE,
+ sc, method, acpi_system76_sysctl_handler, "IU", s76_sysctl_table[method].desc);
+ }
+}
+
+static int
+acpi_system76_attach(device_t dev)
+{
+ struct acpi_system76_softc *sc;
+
+ ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->handle = acpi_get_handle(dev);
+
+ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
+ acpi_system76_notify_handler, dev);
+
+ ACPI_SERIAL_BEGIN(system76);
+ acpi_system76_init(sc);
+ ACPI_SERIAL_END(system76);
+
+ return (0);
+}
+
+static int
+acpi_system76_detach(device_t dev)
+{
+ struct acpi_system76_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sysctl_ctx_free(&sc->sysctl_ctx) != 0)
+ return (EBUSY);
+
+ return (0);
+}
+
+static int
+acpi_system76_probe(device_t dev)
+{
+ int rv;
+
+ if (acpi_disabled("system76") || device_get_unit(dev) > 1)
+ return (ENXIO);
+ rv = ACPI_ID_PROBE(device_get_parent(dev), dev, system76_ids, NULL);
+ if (rv > 0) {
+ return (rv);
+ }
+
+ return (BUS_PROBE_VENDOR);
+}
+
+DRIVER_MODULE(acpi_system76, acpi, acpi_system76_driver, 0, 0);
+MODULE_VERSION(acpi_system76, 1);
+MODULE_DEPEND(acpi_system76, acpi, 1, 1, 1);
diff --git a/sys/modules/acpi/Makefile b/sys/modules/acpi/Makefile
index 5040187e906f..265e6bd6cdcb 100644
--- a/sys/modules/acpi/Makefile
+++ b/sys/modules/acpi/Makefile
@@ -9,6 +9,7 @@ SUBDIR= \
acpi_panasonic \
acpi_sbl_wmi \
acpi_sony \
+ acpi_system76 \
acpi_toshiba \
acpi_video \
acpi_wmi \
diff --git a/sys/modules/acpi/acpi_system76/Makefile b/sys/modules/acpi/acpi_system76/Makefile
new file mode 100644
index 000000000000..86d2c91e712d
--- /dev/null
+++ b/sys/modules/acpi/acpi_system76/Makefile
@@ -0,0 +1,7 @@
+.PATH: ${SRCTOP}/sys/dev/acpi_support
+
+KMOD= acpi_system76
+CFLAGS+=-I${SRCTOP}/sys/dev/acpi_support
+SRCS= acpi_system76.c opt_acpi.h acpi_if.h device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/x86/conf/NOTES b/sys/x86/conf/NOTES
index 501d4159b129..877cbb3beb7f 100644
--- a/sys/x86/conf/NOTES
+++ b/sys/x86/conf/NOTES
@@ -178,6 +178,9 @@ device acpi_sbl_wmi
# ACPI Sony extra (LCD brightness)
device acpi_sony
+# ACPI System76 extra (Keyboard brightness, Keyboard color)
+device acpi_system76
+
# ACPI Toshiba Extras (LCD backlight/brightness, video output, etc.)
device acpi_toshiba