Index | Thread | Search

From:
Marcus Glocker <marcus@nazgul.ch>
Subject:
samsabi(4): Keyboard backlight support for the SGB4E
To:
tech@openbsd.org
Date:
Mon, 25 May 2026 21:10:36 +0200

Download raw body.

Thread
This diff adds initial keyboard backlight support for the Samsung Galaxy
Book4 Edge.  From the man page:

"The samsabi driver provides support for the Samsung Advanced BIOS
Interface (SABI), a vendor command interface of the embedded controller
found in Samsung laptops based on the Qualcomm Snapdragon X1E80100, such
as the Galaxy Book4 Edge.  The controller, identified as "SAM060B" in
ACPI, is an ENE KB9058 micro controller running Samsung firmware.  It
exposes the SABI command protocol on the I2C bus at a separate address
from the "Mbox" channel used by sambat(4) for battery status."

Further devices can be controlled through this interface in the future,
for example fan control.

Ok?


Index: sys/dev/i2c/samsabi.c
===================================================================
RCS file: sys/dev/i2c/samsabi.c
diff -N sys/dev/i2c/samsabi.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/i2c/samsabi.c	25 May 2026 19:03:21 -0000
@@ -0,0 +1,286 @@
+/*	$OpenBSD$ */
+
+/*
+ * Copyright (c) 2026 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * A driver to control the Samsung Advanced BIOS Interface (SABI), a Samsung
+ * proprietary firmware/EC command interface on address 0x62.
+ *
+ * For now the driver supports:
+ * 
+ *  - Keyboard backlight control
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/timeout.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/wscons/wsconsio.h>
+
+/* Debugging */
+/* #define SAMSABI_DEBUG */
+#ifdef SAMSABI_DEBUG
+#define DPRINTF(x)	printf x
+#else
+#define DPRINTF(x)
+#endif
+
+/* Keyboard control */
+#define SAMSABI_KBD_CMD_SET		0x10
+#define SAMSABI_KBD_CMD_GET		0x11
+#define SAMSABI_KBD_MIN_BACKLIGHT	0
+#define SAMSABI_KBD_MAX_BACKLIGHT	3
+
+struct samsabi_softc {
+	struct device	sc_dev;
+	i2c_tag_t	sc_tag;
+	int		sc_addr;
+
+	struct rwlock	sc_lock;
+	unsigned int	sc_backlight;
+	struct timeout	sc_to_keepalive;
+};
+
+int		samsabi_match(struct device *, void *, void *);
+void		samsabi_attach(struct device *, struct device *, void *);
+int		samsabi_activate(struct device *, int);
+
+int		samsabi_cmd(struct samsabi_softc *, const uint8_t *, size_t,
+		    uint8_t *, size_t);
+int		samsabi_kbd_get(struct samsabi_softc *, uint8_t *);
+int		samsabi_kbd_set(struct samsabi_softc *, uint8_t);
+int		samsabi_kbd_probe(struct samsabi_softc *);
+void		samsabi_kbd_keepalive(void *);
+int		samsabi_get_backlight(struct wskbd_backlight *);
+int		samsabi_set_backlight(struct wskbd_backlight *);
+extern int	(*wskbd_get_backlight)(struct wskbd_backlight *);
+extern int	(*wskbd_set_backlight)(struct wskbd_backlight *);
+
+const struct cfattach samsabi_ca = {
+	sizeof(struct samsabi_softc), samsabi_match, samsabi_attach, NULL,
+	samsabi_activate
+};
+
+struct cfdriver samsabi_cd = {
+	NULL, "samsabi", DV_DULL
+};
+
+int
+samsabi_match(struct device *parent, void *match, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+
+	if (strcmp(ia->ia_name, "samsung,galaxybook-sabi") == 0)
+		return 1;
+
+	return 0;
+}
+
+void
+samsabi_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct samsabi_softc *sc = (struct samsabi_softc *)self;
+	struct i2c_attach_args *ia = aux;
+	int error;
+
+	rw_init(&sc->sc_lock, "samsabi");
+
+	sc->sc_tag = ia->ia_tag;
+	sc->sc_addr = ia->ia_addr;
+
+	error = samsabi_kbd_probe(sc);
+	if (error) {
+		printf(": keyboard backlight probe error=%d\n", error);
+		return;
+	}
+
+	wskbd_get_backlight = samsabi_get_backlight;
+	wskbd_set_backlight = samsabi_set_backlight;
+
+	timeout_set_proc(&sc->sc_to_keepalive, samsabi_kbd_keepalive, sc);
+
+	printf(": %s\n", ia->ia_name);
+}
+
+int
+samsabi_activate(struct device *self, int act)
+{
+	struct samsabi_softc *sc = (struct samsabi_softc *)self;
+
+	switch (act) {
+	case DVACT_QUIESCE:
+		timeout_del(&sc->sc_to_keepalive);
+		samsabi_kbd_set(sc, 0);
+		break;
+	case DVACT_WAKEUP:
+		samsabi_kbd_set(sc, sc->sc_backlight);
+		if (sc->sc_backlight != 0)
+			timeout_add_sec(&sc->sc_to_keepalive, 1);
+		break;
+	}
+
+	return 0;
+}
+
+int
+samsabi_cmd(struct samsabi_softc *sc, const uint8_t *cmd, size_t cmdlen,
+    uint8_t *rsp, size_t rsplen)
+{
+	int error;
+
+	rw_enter_write(&sc->sc_lock);
+	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
+
+	if (rsplen > 0) {
+		/* write command and read response */
+		error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
+		    sc->sc_addr, cmd, cmdlen, rsp, rsplen, I2C_F_POLL);
+	} else {
+		/* write command */
+		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+		    sc->sc_addr, cmd, cmdlen, NULL, 0, I2C_F_POLL);
+	}
+
+	iic_release_bus(sc->sc_tag, I2C_F_POLL);
+	rw_exit_write(&sc->sc_lock);
+
+	return error;
+}
+
+int
+samsabi_kbd_get(struct samsabi_softc *sc, uint8_t *level)
+{
+	uint8_t cmd = SAMSABI_KBD_CMD_GET;
+	/*
+	 * GET command response:
+	 *     rsp[3] = Backlight brightness level (0-3)
+	 */
+	uint8_t rsp[8];
+	int error;
+
+	memset(rsp, 0, sizeof(rsp));
+
+	error = samsabi_cmd(sc, &cmd, 1, rsp, sizeof(rsp));
+	if (error)
+		return error;
+
+	DPRINTF(("%s: rsp=%02x %02x %02x %02x %02x %02x %02x %02x\n",
+	    __func__, rsp[0], rsp[1], rsp[2], rsp[3], rsp[4], rsp[5], rsp[6],
+	    rsp[7]));
+
+	*level = rsp[3];
+
+	return 0;
+}
+
+int
+samsabi_kbd_set(struct samsabi_softc *sc, uint8_t level)
+{
+	/*
+	 * SET command:
+	 *     cmd[0] = 0x10
+	 *     cmd[2] = Backlight brightness level:
+	 *         0 = off, 1 = low, 2 = mid, 3 = high
+	 */
+	uint8_t cmd[3] = { SAMSABI_KBD_CMD_SET, 0, level };
+	int error;
+
+	error = samsabi_cmd(sc, cmd, sizeof(cmd), NULL, 0);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+int
+samsabi_kbd_probe(struct samsabi_softc *sc)
+{
+	int error;
+	uint8_t level;
+
+	/* Check if we can get the level value */
+	error = samsabi_kbd_get(sc, &level);
+	if (error)
+		return error;
+
+	/* Make the keyboard light up once */
+	error = samsabi_kbd_set(sc, SAMSABI_KBD_MAX_BACKLIGHT);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+void
+samsabi_kbd_keepalive(void *arg)
+{
+	struct samsabi_softc *sc = arg;
+
+	DPRINTF(("%s: level=%d\n", __func__, sc->sc_backlight));
+
+	(void)samsabi_kbd_set(sc, sc->sc_backlight);
+
+	timeout_add_sec(&sc->sc_to_keepalive, 1);
+}
+
+int
+samsabi_get_backlight(struct wskbd_backlight *kbl)
+{
+	struct samsabi_softc *sc = samsabi_cd.cd_devs[0];
+
+	KASSERT(sc != NULL);
+
+	kbl->min = SAMSABI_KBD_MIN_BACKLIGHT;
+	kbl->max = SAMSABI_KBD_MAX_BACKLIGHT;
+	kbl->curval = sc->sc_backlight;
+
+	return 0;
+}
+
+int
+samsabi_set_backlight(struct wskbd_backlight *kbl)
+{
+	struct samsabi_softc *sc = samsabi_cd.cd_devs[0];
+	int error;
+	uint8_t level;
+
+	KASSERT(sc != NULL);
+
+	DPRINTF(("%s: curval=%d\n", __func__, kbl->curval));
+
+	/*
+	 * XXX Only full brightness can be held lit: the EC fades lower
+	 * levels faster than the keepalive timeout can refresh them, so
+	 * treat any non-zero request as full brightness for now.
+	 */
+	level = kbl->curval ? SAMSABI_KBD_MAX_BACKLIGHT : 0;
+
+	error = samsabi_kbd_set(sc, level);
+	if (error)
+		return error;
+
+	sc->sc_backlight = level;
+
+	if (sc->sc_backlight == 0)
+		timeout_del(&sc->sc_to_keepalive);
+	else
+		timeout_add_sec(&sc->sc_to_keepalive, 1);
+
+	return 0;
+}
Index: sys/dev/i2c/files.i2c
===================================================================
RCS file: /cvs/src/sys/dev/i2c/files.i2c,v
diff -u -p -u -p -r1.75 files.i2c
--- sys/dev/i2c/files.i2c	19 May 2026 04:40:45 -0000	1.75
+++ sys/dev/i2c/files.i2c	25 May 2026 19:03:21 -0000
@@ -279,6 +279,11 @@ device	sambat
 attach	sambat at i2c
 file	dev/i2c/sambat.c			sambat
 
+# Samsung Advanced BIOS Interface (SABI)
+device	samsabi
+attach	samsabi at i2c
+file	dev/i2c/samsabi.c			samsabi
+
 # Consumer Control Keyboards
 device	icc: hid, hidcc, wskbddev
 attach	icc at ihidbus
Index: sys/arch/arm64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
diff -u -p -u -p -r1.316 GENERIC
--- sys/arch/arm64/conf/GENERIC	19 May 2026 04:49:33 -0000	1.316
+++ sys/arch/arm64/conf/GENERIC	25 May 2026 19:03:21 -0000
@@ -628,6 +628,7 @@ tcpci*		at iic?			# USB Type-C controlle
 tipd*		at iic?			# TPS6598x Type-C controller
 pijuice*	at iic?			# PiJuice HAT
 sambat*		at iic?			# Samsung SAM060B battery monitor
+samsabi*	at iic?			# Samsung Advance BIOS Interface
 
 # GPIO "pin bus" drivers
 gpioiic*	at gpio?		# I2C bus bit-banging
Index: share/man/man4/samsabi.4
===================================================================
RCS file: share/man/man4/samsabi.4
diff -N share/man/man4/samsabi.4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ share/man/man4/samsabi.4	25 May 2026 19:03:21 -0000
@@ -0,0 +1,72 @@
+.\"	$OpenBSD$
+.\"
+.\" Copyright (c) 2026 Marcus Glocker <mglocker@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 25 2026 $
+.Dt SAMSABI 4
+.Os
+.Sh NAME
+.Nm samsabi
+.Nd Samsung laptop keyboard backlight
+.Sh SYNOPSIS
+.Cd "samsabi* at iic?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the Samsung Advanced BIOS Interface
+.Pq SABI ,
+a vendor command interface of the embedded controller found in Samsung
+laptops based on the Qualcomm Snapdragon X1E80100, such as the Galaxy
+Book4 Edge.
+The controller, identified as
+.Dq SAM060B
+in ACPI, is an ENE KB9058 micro controller running Samsung firmware.
+It exposes the SABI command protocol on the I2C bus at a separate
+address from the
+.Dq Mbox
+channel used by
+.Xr sambat 4
+for battery status.
+.Pp
+The
+.Nm
+driver currently uses this interface to control the keyboard backlight.
+The backlight level can be retrieved and modified with the
+.Xr wsconsctl 8
+variable
+.Va keyboard.backlight .
+.Sh SEE ALSO
+.Xr iic 4 ,
+.Xr intro 4 ,
+.Xr sambat 4 ,
+.Xr wskbd 4 ,
+.Xr wsconsctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 8.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Marcus Glocker Aq Mt mglocker@openbsd.org .
+.Sh BUGS
+The embedded controller does not hold intermediate keyboard backlight
+levels: it fades the backlight off shortly after a level is set, and
+only full brightness can be refreshed often enough to be kept lit.
+As a result the driver treats any non-zero brightness request as full
+brightness.