acpi_asus_wmi(4): Improve keyboard backlight support.

- Fix maximal keyboard backlight level, Although Linux source code
  comment says that backlight level is encoded in 3 bits of data,
  actual code limits maximum level to 3.
- Add backlight(9) support for keyboard
- Turn off/on keyboard backlight on suspend/resume

Sponsored by:	Future Crew LLC
MFC after:	1 month
Reviewed by:	mav
Differential Revision:	https://reviews.freebsd.org/D48983

(cherry picked from commit f134662a1a5726d78880385002a03b8fe68805f6)
This commit is contained in:
Vladimir Kondratyev 2025-03-17 18:45:14 +03:00
parent 6616f35456
commit 9b5f1a6cd6
3 changed files with 142 additions and 6 deletions

View file

@ -23,7 +23,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 2, 2012
.Dd March 25, 2024
.Dt ACPI_ASUS_WMI 4
.Os
.Sh NAME
@ -72,11 +72,19 @@ Number of other variables under the same sysctl branch are model-specific.
Defaults for these variables can be set in
.Xr sysctl.conf 5 ,
which is parsed at boot-time.
.Sh FILES
.Bl -tag -width "/dev/backlight/acpi_asus_wmi0" -compact
.It Pa /dev/backlight/acpi_asus_wmi0
Keyboard
.Xr backlight 8
device node.
.El
.Sh SEE ALSO
.Xr acpi 4 ,
.Xr acpi_asus 4 ,
.Xr acpi_video 4 ,
.Xr sysctl.conf 5 ,
.Xr backlight 8 ,
.Xr devd 8 ,
.Xr sysctl 8
.Sh HISTORY

View file

@ -42,6 +42,9 @@
#include <dev/acpica/acpivar.h>
#include "acpi_wmi_if.h"
#include <dev/backlight/backlight.h>
#include "backlight_if.h"
#ifdef EVDEV_SUPPORT
#include <dev/evdev/input.h>
#include <dev/evdev/evdev.h>
@ -117,6 +120,8 @@ struct acpi_asus_wmi_softc {
struct sysctl_oid *sysctl_tree;
int dsts_id;
int handle_keys;
struct cdev *kbd_bkl;
uint32_t kbd_bkl_level;
#ifdef EVDEV_SUPPORT
struct evdev_dev *evdev;
#endif
@ -345,6 +350,8 @@ static void acpi_asus_wmi_identify(driver_t *driver, device_t parent);
static int acpi_asus_wmi_probe(device_t dev);
static int acpi_asus_wmi_attach(device_t dev);
static int acpi_asus_wmi_detach(device_t dev);
static int acpi_asus_wmi_suspend(device_t dev);
static int acpi_asus_wmi_resume(device_t dev);
static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);
static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,
@ -357,12 +364,26 @@ static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
static int acpi_asus_wmi_backlight_update_status(device_t dev,
struct backlight_props *props);
static int acpi_asus_wmi_backlight_get_status(device_t dev,
struct backlight_props *props);
static int acpi_asus_wmi_backlight_get_info(device_t dev,
struct backlight_info *info);
static device_method_t acpi_asus_wmi_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, acpi_asus_wmi_identify),
DEVMETHOD(device_probe, acpi_asus_wmi_probe),
DEVMETHOD(device_attach, acpi_asus_wmi_attach),
DEVMETHOD(device_detach, acpi_asus_wmi_detach),
DEVMETHOD(device_suspend, acpi_asus_wmi_suspend),
DEVMETHOD(device_resume, acpi_asus_wmi_resume),
/* Backlight interface */
DEVMETHOD(backlight_update_status, acpi_asus_wmi_backlight_update_status),
DEVMETHOD(backlight_get_status, acpi_asus_wmi_backlight_get_status),
DEVMETHOD(backlight_get_info, acpi_asus_wmi_backlight_get_info),
DEVMETHOD_END
};
@ -376,10 +397,35 @@ static driver_t acpi_asus_wmi_driver = {
DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver, 0, 0);
MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);
MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);
MODULE_DEPEND(acpi_asus_wmi, backlight, 1, 1, 1);
#ifdef EVDEV_SUPPORT
MODULE_DEPEND(acpi_asus_wmi, evdev, 1, 1, 1);
#endif
static const uint32_t acpi_asus_wmi_backlight_levels[] = { 0, 33, 66, 100 };
static inline uint32_t
devstate_to_kbd_bkl_level(UINT32 val)
{
return (acpi_asus_wmi_backlight_levels[val & 0x3]);
}
static inline UINT32
kbd_bkl_level_to_devstate(uint32_t bkl)
{
UINT32 val;
int i;
for (i = 0; i < nitems(acpi_asus_wmi_backlight_levels); i++) {
if (bkl < acpi_asus_wmi_backlight_levels[i])
break;
}
val = (i - 1) & 0x3;
if (val != 0)
val |= 0x80;
return(val);
}
static void
acpi_asus_wmi_identify(driver_t *driver, device_t parent)
{
@ -418,6 +464,7 @@ acpi_asus_wmi_attach(device_t dev)
struct acpi_asus_wmi_softc *sc;
UINT32 val;
int dev_id, i;
bool have_kbd_bkl = false;
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
@ -502,6 +549,10 @@ next:
if (val == 0)
continue;
break;
case ASUS_WMI_DEVID_KBD_BACKLIGHT:
sc->kbd_bkl_level = devstate_to_kbd_bkl_level(val);
have_kbd_bkl = true;
/* FALLTHROUGH */
default:
if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0)
continue;
@ -541,12 +592,22 @@ next:
}
if (evdev_register(sc->evdev) != 0) {
device_printf(dev, "Can not register evdev\n");
acpi_asus_wmi_detach(dev);
return (ENXIO);
}
}
#endif
if (have_kbd_bkl) {
sc->kbd_bkl = backlight_register("acpi_asus_wmi", dev);
if (sc->kbd_bkl == NULL) {
device_printf(dev, "Can not register backlight\n");
acpi_asus_wmi_detach(dev);
return (ENXIO);
}
}
return (0);
}
@ -557,6 +618,9 @@ acpi_asus_wmi_detach(device_t dev)
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
if (sc->kbd_bkl != NULL)
backlight_destroy(sc->kbd_bkl);
if (sc->notify_guid) {
ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid);
#ifdef EVDEV_SUPPORT
@ -567,6 +631,34 @@ acpi_asus_wmi_detach(device_t dev)
return (0);
}
static int
acpi_asus_wmi_suspend(device_t dev)
{
struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
if (sc->kbd_bkl != NULL) {
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
acpi_wpi_asus_set_devstate(sc,
ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, NULL);
}
return (0);
}
static int
acpi_asus_wmi_resume(device_t dev)
{
struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
if (sc->kbd_bkl != NULL) {
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT,
kbd_bkl_level_to_devstate(sc->kbd_bkl_level), NULL);
}
return (0);
}
static int
acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS)
{
@ -615,7 +707,7 @@ acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id)
val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK;
break;
case ASUS_WMI_DEVID_KBD_BACKLIGHT:
val &= 0x7;
val &= 0x3;
break;
default:
if (val & ASUS_WMI_DSTS_UNKNOWN_BIT)
@ -636,7 +728,7 @@ acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, in
switch(dev_id) {
case ASUS_WMI_DEVID_KBD_BACKLIGHT:
arg = min(0x7, arg);
arg = min(0x3, arg);
if (arg != 0)
arg |= 0x80;
break;
@ -701,9 +793,9 @@ acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
if (code == 0xc4 || code == 0xc5) {
acpi_wpi_asus_get_devstate(sc,
ASUS_WMI_DEVID_KBD_BACKLIGHT, &val);
val &= 0x7;
val &= 0x3;
if (code == 0xc4) {
if (val < 0x7)
if (val < 0x3)
val++;
} else if (val > 0)
val--;
@ -711,6 +803,7 @@ acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
val |= 0x80;
acpi_wpi_asus_set_devstate(sc,
ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL);
sc->kbd_bkl_level = devstate_to_kbd_bkl_level(val);
}
/* Touchpad control. */
if (code == 0x6b) {
@ -767,3 +860,38 @@ acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval));
}
static int
acpi_asus_wmi_backlight_update_status(device_t dev, struct backlight_props
*props)
{
struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT,
kbd_bkl_level_to_devstate(props->brightness), NULL);
sc->kbd_bkl_level = props->brightness;
return (0);
}
static int
acpi_asus_wmi_backlight_get_status(device_t dev, struct backlight_props *props)
{
struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
props->brightness = sc->kbd_bkl_level;
props->nlevels = nitems(acpi_asus_wmi_backlight_levels);
memcpy(props->levels, acpi_asus_wmi_backlight_levels,
sizeof(acpi_asus_wmi_backlight_levels));
return (0);
}
static int
acpi_asus_wmi_backlight_get_info(device_t dev, struct backlight_info *info)
{
info->type = BACKLIGHT_TYPE_KEYBOARD;
strlcpy(info->name, "ASUS Keyboard", BACKLIGHTMAXNAMELENGTH);
return (0);
}

View file

@ -4,6 +4,6 @@
KMOD= acpi_asus_wmi
CFLAGS+=-I${SRCTOP}/sys/dev/acpi_support
SRCS= acpi_asus_wmi.c opt_acpi.h acpi_if.h acpi_wmi_if.h device_if.h bus_if.h
SRCS+= opt_evdev.h
SRCS+= opt_evdev.h backlight_if.h
.include <bsd.kmod.mk>