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,10 @@ #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 +#define ACPI_IBM_METHOD_MICMUTE_LED 18 /* Hotkeys/Buttons */ #define IBM_RTC_HOTKEY1 0x64 @@ -129,7 +134,18 @@ #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" + +#define IBM_NAME_MICMUTE_LED_SET "MMTS" +#define IBM_NAME_MICMUTE_LED_OFF 0x0 +#define IBM_NAME_MICMUTE_LED_ON 0x2 +#define IBM_NAME_MICMUTE_LED_BLINK 0x3 + /* Event Code */ #define IBM_EVENT_LCD_BACKLIGHT 0x03 #define IBM_EVENT_SUSPEND_TO_RAM 0x04 @@ -170,11 +186,31 @@ 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; + + /* micmute led */ + int micmute_led_supported; + int micmute_led_val; + + /* led(4) interface for micmute led */ + struct cdev *micmute_led_dev; + int micmute_led_busy; + int micmute_led_state; + int wlan_bt_flags; int thermal_updt_supported; @@ -258,6 +294,26 @@ .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", + }, + { + .name = "micmute_led", + .method = ACPI_IBM_METHOD_MICMUTE_LED, + .description = "Control micmute LED", + }, { NULL, 0, NULL, 0 } }; @@ -303,6 +359,13 @@ 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 void ibm_micmute_led(void *softc, int onoff); +static void ibm_micmute_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 +381,8 @@ 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 int acpi_ibm_micmute_led_set(struct acpi_ibm_softc *sc, int arg); static device_method_t acpi_ibm_methods[] = { /* Device interface */ @@ -340,7 +405,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 +435,66 @@ 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 void +ibm_micmute_led(void *softc, int onoff) +{ + struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (sc->micmute_led_busy) + return; + + sc->micmute_led_busy = 1; + sc->micmute_led_state = onoff; + + AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_micmute_led_task, sc); +} + +static void +ibm_micmute_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_MICMUTE_LED, + sc->micmute_led_state); + ACPI_SERIAL_END(ibm); + + sc->micmute_led_busy = 0; +} + static int acpi_ibm_probe(device_t dev) { @@ -386,7 +511,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 +552,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 (MHKV) + * 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 +640,23 @@ 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)); + + if (sc->micmute_led_supported) + sc->micmute_led_dev = led_create_state(ibm_micmute_led, sc, + "micmute", 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 +697,12 @@ if (sc->led_dev != NULL) led_destroy(sc->led_dev); + if (sc->kbd_backlight_led_dev != NULL) + led_destroy(sc->kbd_backlight_led_dev); + + if (sc->micmute_led_dev != NULL) + led_destroy(sc->micmute_led_dev); + return (0); } @@ -739,6 +913,26 @@ 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; + + case ACPI_IBM_METHOD_MICMUTE_LED: + val = sc->micmute_led_val; + break; } return (val); @@ -816,6 +1010,27 @@ (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; + case ACPI_IBM_METHOD_MICMUTE_LED: + return acpi_ibm_micmute_led_set(sc, arg); + break; } return (0); @@ -911,6 +1126,37 @@ 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); + + case ACPI_IBM_METHOD_MICMUTE_LED: + sc->micmute_led_supported = ACPI_SUCCESS(acpi_SetInteger( + sc->handle, IBM_NAME_MICMUTE_LED_SET, + IBM_NAME_MICMUTE_LED_OFF)); + if (sc->micmute_led_supported) + return (TRUE); + return (FALSE); } return (FALSE); } @@ -1210,6 +1456,61 @@ 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 int +acpi_ibm_micmute_led_set(struct acpi_ibm_softc *sc, int arg) +{ + int led_arg; + ACPI_STATUS status; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(ibm); + + if (arg < 0 || arg > 2) + return (EINVAL); + + switch (arg) { + case 0: + led_arg = IBM_NAME_MICMUTE_LED_OFF; + break; + case 1: + led_arg = IBM_NAME_MICMUTE_LED_ON; + break; + case 2: + led_arg = IBM_NAME_MICMUTE_LED_BLINK; + break; + } + + if (sc->micmute_led_supported) { + status = acpi_SetInteger(sc->handle, IBM_NAME_MICMUTE_LED_SET, led_arg); + if (ACPI_SUCCESS(status)) + sc->micmute_led_val = arg; + return (status); + } + + return (0); } static void