Index: sys/dev/acpi_support/acpi_ibm.c =================================================================== --- sys/dev/acpi_support/acpi_ibm.c +++ sys/dev/acpi_support/acpi_ibm.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 2004 Takanori Watanabe * Copyright (c) 2005 Markus Brueffer + * Copyright (c) 2018 Michael Gmelin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -74,6 +75,9 @@ #define ACPI_IBM_METHOD_FANSTATUS 12 #define ACPI_IBM_METHOD_THERMAL 13 #define ACPI_IBM_METHOD_HANDLEREVENTS 14 +#define ACPI_IBM_METHOD_KBD_BACKLIGHT 15 +#define ACPI_IBM_METHOD_KBD_BL_LEDON 16 +#define ACPI_IBM_METHOD_KBD_BL_LEDOFF 17 /* Hotkeys/Buttons */ #define IBM_RTC_HOTKEY1 0x64 @@ -129,7 +133,13 @@ #define IBM_NAME_EVENTS_MASK_SET "MHKM" #define IBM_NAME_EVENTS_GET "MHKP" #define IBM_NAME_EVENTS_AVAILMASK "MHKA" +#define IBM_NAME_EVENTS_VERSION "MHKV" +#define IBM_NAME_KBD_BACKLIGHT_GET "MLCG" +#define IBM_NAME_MASK_KBD_BL_SUPPORT (1 << 9) +#define IBM_NAME_MASK_KBD_BL_LEVEL 0x3 +#define IBM_NAME_KBD_BACKLIGHT_SET "MLCS" + /* Event Code */ #define IBM_EVENT_LCD_BACKLIGHT 0x03 #define IBM_EVENT_SUSPEND_TO_RAM 0x04 @@ -170,11 +180,22 @@ int light_get_supported; int light_set_supported; - /* led(4) interface */ + /* led(4) interface for thinklight */ struct cdev *led_dev; int led_busy; int led_state; + /* Keyboard backlight support */ + int kbd_backlight_supported; + int kbd_backlight_val; + + /* led(4) interface for keyboard backlight */ + struct cdev *kbd_backlight_led_dev; + int kbd_backlight_led_busy; + int kbd_backlight_led_state; + int kbd_backlight_ledon; + int kbd_backlight_ledoff; + int wlan_bt_flags; int thermal_updt_supported; @@ -258,6 +279,21 @@ .method = ACPI_IBM_METHOD_FANSTATUS, .description = "Fan enable", }, + { + .name = "kbd_backlight", + .method = ACPI_IBM_METHOD_KBD_BACKLIGHT, + .description = "Keyboard backlight level", + }, + { + .name = "kbd_backlight_ledon", + .method = ACPI_IBM_METHOD_KBD_BL_LEDON, + .description = "Keyboard backlight led-on level", + }, + { + .name = "kbd_backlight_ledoff", + .method = ACPI_IBM_METHOD_KBD_BL_LEDOFF, + .description = "Keyboard backlight led-off level", + }, { NULL, 0, NULL, 0 } }; @@ -303,6 +339,10 @@ static void ibm_led(void *softc, int onoff); static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); +static void ibm_kbd_backlight_led(void *softc, int onoff); +static void ibm_kbd_backlight_led_task(struct acpi_ibm_softc *sc, + int pending __unused); + static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); @@ -318,6 +358,7 @@ static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg); +static int acpi_ibm_kbd_backlight_set(struct acpi_ibm_softc *sc, int arg); static device_method_t acpi_ibm_methods[] = { /* Device interface */ @@ -340,7 +381,7 @@ DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 0, 0); MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); -static char *ibm_ids[] = {"IBM0068", "LEN0068", NULL}; +static char *ibm_ids[] = {"IBM0068", "LEN0068", "LEN0268", NULL}; static void ibm_led(void *softc, int onoff) @@ -370,6 +411,37 @@ sc->led_busy = 0; } +static void +ibm_kbd_backlight_led(void *softc, int onoff) +{ + struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (sc->kbd_backlight_led_busy) + return; + + sc->kbd_backlight_led_busy = 1; + sc->kbd_backlight_led_state = onoff; + + AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_kbd_backlight_led_task, + sc); +} + +static void +ibm_kbd_backlight_led_task(struct acpi_ibm_softc *sc, int pending __unused) +{ + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + ACPI_SERIAL_BEGIN(ibm); + acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_KBD_BACKLIGHT, + sc->kbd_backlight_led_state ? + sc->kbd_backlight_ledon : sc->kbd_backlight_ledoff); + ACPI_SERIAL_END(ibm); + + sc->kbd_backlight_led_busy = 0; +} + static int acpi_ibm_probe(device_t dev) { @@ -386,7 +458,10 @@ static int acpi_ibm_attach(device_t dev) { - int i; + ACPI_OBJECT p, *bufp; + ACPI_OBJECT_LIST args; + ACPI_BUFFER buf; + int hkey_version; struct acpi_ibm_softc *sc; char *maker, *product; devclass_t ec_devclass; @@ -424,11 +499,41 @@ "initialmask", CTLFLAG_RD, &sc->events_initialmask, 0, "Initial eventmask"); - /* The availmask is the bitmask of supported events */ + /* + * The availmask is the bitmask of supported events, it's determined + * by calling MHKA. Old pre-0x100 versions of the hkey interface (MKHV) + * take no parameter to determine the hotkey mask. Newer models + * (post-2015, like T460, T470s, Carbon X1) take one integer parameter: + * 1: Retrieve availmask like before + * 2: Retrieve adaptive keyboard mask (currently not supported + * by this driver), only supported on models featuring + * the adaptive keyboard, otherwise returns 0 + */ if (ACPI_FAILURE(acpi_GetInteger(sc->handle, - IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) - sc->events_availmask = 0xffffffff; + IBM_NAME_EVENTS_VERSION, &hkey_version))) + hkey_version = 0x100; /* default to version 1 of the hkey interface */ + if ((hkey_version >> 8) < 2) { + if (ACPI_FAILURE(acpi_GetInteger(sc->handle, + IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) + sc->events_availmask = 0xffffffff; + } else { + p.Type = ACPI_TYPE_INTEGER; + p.Integer.Value = 1; + args.Count = 1; + args.Pointer = &p; + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE(AcpiEvaluateObjectTyped(sc->handle, + IBM_NAME_EVENTS_AVAILMASK, &args, &buf, + ACPI_TYPE_INTEGER))) + sc->events_availmask = 0xffffffff; + else { + bufp = buf.Pointer; + sc->events_availmask= bufp->Integer.Value; + AcpiOsFree(buf.Pointer); + } + } + SYSCTL_ADD_UINT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "availmask", CTLFLAG_RD, @@ -482,13 +587,19 @@ sc->led_dev = led_create_state(ibm_led, sc, "thinklight", (sc->light_val ? 1 : 0)); + /* Hook up keyboard backlight to led(4) */ + if (sc->kbd_backlight_supported) + sc->kbd_backlight_led_dev = led_create_state( + ibm_kbd_backlight_led, sc, "kbd_backlight", + (sc->kbd_backlight_val ? 1 : 0)); + /* Enable per-model events. */ maker = kern_getenv("smbios.system.maker"); product = kern_getenv("smbios.system.product"); if (maker == NULL || product == NULL) goto nosmbios; - for (i = 0; i < nitems(acpi_ibm_models); i++) { + for (int i = 0; i < nitems(acpi_ibm_models); i++) { if (strcmp(maker, acpi_ibm_models[i].maker) == 0 && strcmp(product, acpi_ibm_models[i].product) == 0) { ACPI_SERIAL_BEGIN(ibm); @@ -529,6 +640,9 @@ if (sc->led_dev != NULL) led_destroy(sc->led_dev); + if (sc->kbd_backlight_led_dev != NULL) + led_destroy(sc->kbd_backlight_led_dev); + return (0); } @@ -739,6 +853,22 @@ else val = -1; break; + + case ACPI_IBM_METHOD_KBD_BACKLIGHT: + if (sc->kbd_backlight_supported) + acpi_GetInteger(sc->handle, IBM_NAME_KBD_BACKLIGHT_GET, &val); + else + val = sc->kbd_backlight_val; + val &= IBM_NAME_MASK_KBD_BL_LEVEL; + break; + + case ACPI_IBM_METHOD_KBD_BL_LEDON: + val = sc->kbd_backlight_ledon; + break; + + case ACPI_IBM_METHOD_KBD_BL_LEDOFF: + val = sc->kbd_backlight_ledoff; + break; } return (val); @@ -816,6 +946,24 @@ (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); } break; + + case ACPI_IBM_METHOD_KBD_BACKLIGHT: + return acpi_ibm_kbd_backlight_set(sc, arg); + break; + + case ACPI_IBM_METHOD_KBD_BL_LEDON: + if (arg < 0 || arg > 2) + return (EINVAL); + + sc->kbd_backlight_ledon = arg; + break; + + case ACPI_IBM_METHOD_KBD_BL_LEDOFF: + if (arg < 0 || arg > 2) + return (EINVAL); + + sc->kbd_backlight_ledoff = arg; + break; } return (0); @@ -911,6 +1059,29 @@ case ACPI_IBM_METHOD_HANDLEREVENTS: return (TRUE); + + case ACPI_IBM_METHOD_KBD_BACKLIGHT: + if (ACPI_FAILURE(acpi_GetInteger(sc->handle, IBM_NAME_KBD_BACKLIGHT_GET, + &sc->kbd_backlight_val))) + sc->kbd_backlight_val = 0; + sc->kbd_backlight_supported = sc->kbd_backlight_val & + IBM_NAME_MASK_KBD_BL_SUPPORT; + sc->kbd_backlight_val &= IBM_NAME_MASK_KBD_BL_LEVEL; + if (sc->kbd_backlight_supported) + return (TRUE); + return (FALSE); + + case ACPI_IBM_METHOD_KBD_BL_LEDON: + sc->kbd_backlight_ledon = 1; + if (sc->kbd_backlight_supported) + return (TRUE); + return (FALSE); + + case ACPI_IBM_METHOD_KBD_BL_LEDOFF: + sc->kbd_backlight_ledoff = 0; + if (sc->kbd_backlight_supported) + return (TRUE); + return (FALSE); } return (FALSE); } @@ -1210,6 +1381,27 @@ val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE); return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); +} + +static int +acpi_ibm_kbd_backlight_set(struct acpi_ibm_softc *sc, int arg) +{ + ACPI_STATUS status; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(ibm); + + if (arg < 0 || arg > 2) + return (EINVAL); + + if (sc->kbd_backlight_supported) { + status = acpi_SetInteger(sc->handle, IBM_NAME_KBD_BACKLIGHT_SET, arg); + if (ACPI_SUCCESS(status)) + sc->kbd_backlight_val = arg; + return (status); + } + + return (0); } static void