Index: share/man/man4/acpi_asus.4 =================================================================== --- share/man/man4/acpi_asus.4 +++ share/man/man4/acpi_asus.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 8, 2010 +.Dd August 7, 2020 .Dt ACPI_ASUS 4 .Os .Sh NAME @@ -49,7 +49,7 @@ The .Nm driver provides support for the extra ACPI-controlled gadgets, such as hotkeys -and leds, found on recent Asus (and Medion) laptops. +and LEDs, found on older Asus (and Medion) laptops. It allows one to use the .Xr sysctl 8 interface to manipulate the brightness of the LCD panel and the display output @@ -124,10 +124,20 @@ .Em ATK0100 interface found in .Em Samsung P30/P35 -laptops. +laptops and other Asus laptops models such as +.Em G73Jh . .Sh SYSCTL VARIABLES The following sysctls are currently implemented: .Bl -tag -width indent +.It Va hw.acpi.asus.kbd_backlight +Controls the keyboard backlight brightness. +This value is an index in a table of brightness levels defined in the ACPI +firmware. +0 is off, the max is +.Va kbd_backlight_max . +.It Va hw.acpi.asus.kbd_backlight_max +Read-only maximum value allowed by firmware for +.Va kbd_backlight . .It Va hw.acpi.asus.lcd_brightness Makes the LCD backlight brighter or dimmer (higher values are brighter). .It Va hw.acpi.asus.lcd_backlight Index: sys/dev/acpi_support/acpi_asus.c =================================================================== --- sys/dev/acpi_support/acpi_asus.c +++ sys/dev/acpi_support/acpi_asus.c @@ -60,6 +60,13 @@ #define ACPI_ASUS_METHOD_CAMERA 4 #define ACPI_ASUS_METHOD_CARDRD 5 #define ACPI_ASUS_METHOD_WLAN 6 +#define ACPI_ASUS_METHOD_KBRN 7 + +#define KB_BACKLIGHT_OFF 0x00 +#define KB_BACKLIGHT_ON 0x80 +#define KB_BACKLIGHT_MAX_MAX (KB_BACKLIGHT_ON - 1) +#define KB_BACKLIGHT_LEVEL(lvl) \ + ((lvl) == KB_BACKLIGHT_OFF ? KB_BACKLIGHT_OFF : (KB_BACKLIGHT_ON | (lvl))) #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("ASUS") @@ -79,6 +86,10 @@ char *brn_up; char *brn_dn; + char *kbrn_get; + char *kbrn_set; + char *kbrn_levels; + char *lcd_get; char *lcd_set; @@ -132,6 +143,7 @@ struct acpi_asus_led s_wled; int s_brn; + int s_kbrn; int s_disp; int s_lcd; int s_cam; @@ -143,10 +155,32 @@ void *context); /* - * We can identify Asus laptops from the string they return + * We can identify some Asus laptops from the string they return * as a result of calling the ATK0100 'INIT' method. */ static struct acpi_asus_model acpi_asus_models[] = { + { /* + * G73Jh returns an empty name from 'INIT' and has these + * methods in the DSDT. Other laptops probably do, too. + * The Linux driver is less picky and doesn't even try matching + * the name, but it's harder for us to get away with that + * without some significant restructuring. For one thing, we + * have LCD backlight and display switching controls mixed in + * with this driver, not just ATKD methods. + */ + .name = "", + .bled_set = "BLED", + .wled_set = "WLED", + .brn_get = "GPLV", + .brn_set = "SPLV", + .kbrn_get = "GLKB", + .kbrn_set = "SLKB", + .kbrn_levels = "PWKB", + .lcd_get = "GBTL", + .lcd_set = "SBTL", + .disp_get = "\\ADVG", + .disp_set = "SDSP" + }, { .name = "xxN", .mled_set = "MLED", @@ -421,6 +455,9 @@ { .name = NULL } }; +static int acpi_asus_kbrn_max(struct acpi_asus_softc *sc); +static int acpi_asus_kbrn_max_sysctl(SYSCTL_HANDLER_ARGS); + /* * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, * but they can't be probed quite the same way as Asus laptops. @@ -479,6 +516,12 @@ .description = "brightness of the lcd panel", .flag_anybody = 1 }, + { + .name = "kbd_backlight", + .method = ACPI_ASUS_METHOD_KBRN, + .description = "brightness of the keyboard backlight", + .flag_anybody = 1 + }, { .name = "video_output", .method = ACPI_ASUS_METHOD_DISP, @@ -733,6 +776,11 @@ SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "asus", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); + SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), + OID_AUTO, "kbd_backlight_max", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, KB_BACKLIGHT_OFF, + acpi_asus_kbrn_max_sysctl, "I", "Max keyboard backlight level"); + /* Hook up nodes */ for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) @@ -881,6 +929,65 @@ return (0); } +static int +acpi_asus_kbrn_max(struct acpi_asus_softc *sc) +{ + ACPI_BUFFER buf; + ACPI_OBJECT *levels; + ACPI_STATUS status; + int max; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (sc->model->kbrn_levels == NULL) + return (KB_BACKLIGHT_OFF); + + /* + * kbrn_levels is a lookup table of brightness levels for the + * keyboard backlight. + */ + buf.Pointer = NULL; + buf.Length = ACPI_ALLOCATE_BUFFER; + status = AcpiEvaluateObject(sc->handle, sc->model->kbrn_levels, + NULL, &buf); + levels = buf.Pointer; + + /* + * If that didn't work we might have the wrong path to the table. + * The SLKB method commonly takes values up to 127, we'll just get + * an error message on the console if we go past the end of the table. + */ + if (ACPI_FAILURE(status)) + return (KB_BACKLIGHT_MAX_MAX); + if (levels->Type != ACPI_TYPE_BUFFER) { + AcpiOsFree(levels); + return (KB_BACKLIGHT_MAX_MAX); + } + + max = levels->Buffer.Length - 1; + AcpiOsFree(levels); + return (MIN(max, KB_BACKLIGHT_MAX_MAX)); +} + +static int +acpi_asus_kbrn_max_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct acpi_asus_softc *sc; + int arg; + int error; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + sc = (struct acpi_asus_softc *)oidp->oid_arg1; + + ACPI_SERIAL_BEGIN(asus); + arg = acpi_asus_kbrn_max(sc); + error = sysctl_handle_int(oidp, &arg, 0, req); + ACPI_SERIAL_END(asus); + + return (error); +} + static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) { @@ -985,6 +1092,9 @@ case ACPI_ASUS_METHOD_BRN: val = sc->s_brn; break; + case ACPI_ASUS_METHOD_KBRN: + val = sc->s_kbrn; + break; case ACPI_ASUS_METHOD_DISP: val = sc->s_disp; break; @@ -1040,6 +1150,18 @@ if (ACPI_SUCCESS(status)) sc->s_brn = arg; + break; + case ACPI_ASUS_METHOD_KBRN: + if (arg < 0 || arg > acpi_asus_kbrn_max(sc)) + return (EINVAL); + + if (sc->model->kbrn_set) + status = acpi_SetInteger(sc->handle, + sc->model->kbrn_set, KB_BACKLIGHT_LEVEL(arg)); + + if (ACPI_SUCCESS(status)) + sc->s_kbrn = arg; + break; case ACPI_ASUS_METHOD_DISP: if (arg < 0 || arg > 7) @@ -1130,6 +1252,27 @@ return (TRUE); } return (FALSE); + case ACPI_ASUS_METHOD_KBRN: + if (sc->model->kbrn_get) { + ACPI_OBJECT_LIST args; + ACPI_OBJECT arg0, param; + ACPI_BUFFER buf; + + arg0.Type = ACPI_TYPE_INTEGER; + arg0.Integer.Value = 1; + args.Count = 1; + args.Pointer = &arg0; + buf.Pointer = ¶m; + buf.Length = sizeof(param); + status = AcpiEvaluateObject(sc->handle, + sc->model->kbrn_get, &args, &buf); + if (ACPI_SUCCESS(status) && + param.Type == ACPI_TYPE_INTEGER) { + sc->s_kbrn = param.Integer.Value; + return (TRUE); + } + } + return (FALSE); case ACPI_ASUS_METHOD_DISP: if (sc->model->disp_get) { status = acpi_GetInteger(sc->handle, @@ -1230,6 +1373,14 @@ } else if (notify == 0x87) { acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); + } else if (notify == 0xC4) { + acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_KBRN, sc->s_kbrn+1); + ACPI_VPRINT(sc->dev, acpi_sc, + "Keyboard backlight brightness increased\n"); + } else if (notify == 0xC5) { + acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_KBRN, sc->s_kbrn-1); + ACPI_VPRINT(sc->dev, acpi_sc, + "Keyboard backlight brightness decreased\n"); } else { /* Notify devd(8) */ acpi_UserNotify("ASUS", h, notify);