Index: head/sys/dev/acpi_support/acpi_fujitsu.c =================================================================== --- head/sys/dev/acpi_support/acpi_fujitsu.c (revision 209063) +++ head/sys/dev/acpi_support/acpi_fujitsu.c (revision 209064) @@ -1,718 +1,714 @@ /*- * Copyright (c) 2002 Sean Bullington * 2003-2006 Anish Mistry * 2004 Mark Santcroos * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include #include #include #include #include #include #include #include /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("Fujitsu") /* Change and update bits for the hotkeys */ #define VOLUME_MUTE_BIT 0x40000000 /* Values of settings */ #define GENERAL_SETTING_BITS 0x0fffffff #define MOUSE_SETTING_BITS GENERAL_SETTING_BITS #define VOLUME_SETTING_BITS GENERAL_SETTING_BITS #define BRIGHTNESS_SETTING_BITS GENERAL_SETTING_BITS /* Possible state changes */ /* * These are NOT arbitrary values. They are the * GHKS return value from the device that says which * hotkey is active. They should match up with a bit * from the GSIF bitmask. */ #define BRIGHT_CHANGED 0x01 #define VOLUME_CHANGED 0x04 #define MOUSE_CHANGED 0x08 /* * It is unknown which hotkey this bit is supposed to indicate, but * according to values from GSIF this is a valid flag. */ #define UNKNOWN_CHANGED 0x10 /* sysctl values */ #define FN_MUTE 0 #define FN_POINTER_ENABLE 1 #define FN_LCD_BRIGHTNESS 2 #define FN_VOLUME 3 /* Methods */ #define METHOD_GBLL 1 #define METHOD_GMOU 2 #define METHOD_GVOL 3 #define METHOD_MUTE 4 #define METHOD_RBLL 5 #define METHOD_RVOL 6 #define METHOD_GSIF 7 #define METHOD_GHKS 8 /* Notify event */ #define ACPI_NOTIFY_STATUS_CHANGED 0x80 /* * Holds a control method name and its associated integer value. * Only used for no-argument control methods which return a value. */ struct int_nameval { char *name; int value; int exists; }; /* * Driver extension for the FUJITSU ACPI driver. */ struct acpi_fujitsu_softc { device_t dev; ACPI_HANDLE handle; /* Control methods */ struct int_nameval _sta, /* unused */ gbll, /* brightness */ ghks, /* hotkey selector */ gbuf, /* unused (buffer?) */ gmou, /* mouse */ gsif, /* function key bitmask */ gvol, /* volume */ rbll, /* number of brightness levels (radix) */ rvol; /* number of volume levels (radix) */ /* State variables */ uint8_t bIsMuted; /* Is volume muted */ uint8_t bIntPtrEnabled; /* Is internal ptr enabled */ uint32_t lastValChanged; /* The last value updated */ /* sysctl tree */ struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; }; /* Driver entry point forward declarations. */ static int acpi_fujitsu_probe(device_t dev); static int acpi_fujitsu_attach(device_t dev); static int acpi_fujitsu_detach(device_t dev); static int acpi_fujitsu_suspend(device_t dev); static int acpi_fujitsu_resume(device_t dev); static void acpi_fujitsu_notify_status_changed(void *arg); static void acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context); static int acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS); /* Utility function declarations */ static uint8_t acpi_fujitsu_update(struct acpi_fujitsu_softc *sc); static uint8_t acpi_fujitsu_init(struct acpi_fujitsu_softc *sc); static uint8_t acpi_fujitsu_check_hardware(struct acpi_fujitsu_softc *sc); /* Driver/Module specific structure definitions. */ static device_method_t acpi_fujitsu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_fujitsu_probe), DEVMETHOD(device_attach, acpi_fujitsu_attach), DEVMETHOD(device_detach, acpi_fujitsu_detach), DEVMETHOD(device_suspend, acpi_fujitsu_suspend), DEVMETHOD(device_resume, acpi_fujitsu_resume), {0, 0} }; static driver_t acpi_fujitsu_driver = { "acpi_fujitsu", acpi_fujitsu_methods, sizeof(struct acpi_fujitsu_softc), }; /* Prototype for function hotkeys for getting/setting a value. */ static int acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method); static int acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value); static char *fujitsu_ids[] = { "FUJ02B1", NULL }; ACPI_SERIAL_DECL(fujitsu, "Fujitsu Function Hotkeys"); /* sysctl names and function calls */ static struct { char *name; int method; char *description; } sysctl_table[] = { { .name = "mute", .method = METHOD_MUTE, .description = "Speakers/headphones mute status" }, { .name = "pointer_enable", .method = METHOD_GMOU, .description = "Enable and disable the internal pointer" }, { .name = "lcd_brightness", .method = METHOD_GBLL, .description = "Brightness level of the LCD panel" }, { .name = "volume", .method = METHOD_GVOL, .description = "Speakers/headphones volume level" }, { .name = "volume_radix", .method = METHOD_RVOL, .description = "Number of volume level steps" }, { .name = "lcd_brightness_radix", .method = METHOD_RBLL, .description = "Number of brightness level steps" }, { NULL, 0, NULL } }; static devclass_t acpi_fujitsu_devclass; DRIVER_MODULE(acpi_fujitsu, acpi, acpi_fujitsu_driver, acpi_fujitsu_devclass, 0, 0); MODULE_DEPEND(acpi_fujitsu, acpi, 1, 1, 1); MODULE_VERSION(acpi_fujitsu, 1); static int acpi_fujitsu_probe(device_t dev) { char *name; char buffer[64]; name = ACPI_ID_PROBE(device_get_parent(dev), dev, fujitsu_ids); if (acpi_disabled("fujitsu") || name == NULL || device_get_unit(dev) > 1) return (ENXIO); sprintf(buffer, "Fujitsu Function Hotkeys %s", name); device_set_desc_copy(dev, buffer); return (0); } static int acpi_fujitsu_attach(device_t dev) { struct acpi_fujitsu_softc *sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc(dev); sc->dev = dev; sc->handle = acpi_get_handle(dev); /* Install notification handler */ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_fujitsu_notify_handler, sc); /* Snag our default values for the hotkeys / hotkey states. */ ACPI_SERIAL_BEGIN(fujitsu); if (!acpi_fujitsu_init(sc)) device_printf(dev, "Couldn't initialize hotkey states!\n"); ACPI_SERIAL_END(fujitsu); return (0); } /* * Called when the system is being suspended, simply * set an event to be signalled when we wake up. */ static int acpi_fujitsu_suspend(device_t dev) { return (0); } static int acpi_fujitsu_resume(device_t dev) { struct acpi_fujitsu_softc *sc; ACPI_STATUS status; sc = device_get_softc(dev); /* * The pointer needs to be re-enabled for * some revisions of the P series (2120). */ ACPI_SERIAL_BEGIN(fujitsu); if(sc->gmou.exists) { status = acpi_SetInteger(sc->handle, "SMOU", 1); if (ACPI_FAILURE(status)) device_printf(sc->dev, "Couldn't enable pointer\n"); } ACPI_SERIAL_END(fujitsu); return (0); } static void acpi_fujitsu_notify_status_changed(void *arg) { struct acpi_fujitsu_softc *sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_fujitsu_softc *)arg; /* * Since our notify function is called, we know something has * happened. So the only reason for acpi_fujitsu_update to fail * is if we can't find what has changed or an error occurs. */ ACPI_SERIAL_BEGIN(fujitsu); acpi_fujitsu_update(sc); ACPI_SERIAL_END(fujitsu); } static void acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context) { struct acpi_fujitsu_softc *sc; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); sc = (struct acpi_fujitsu_softc *)context; switch (notify) { case ACPI_NOTIFY_STATUS_CHANGED: AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_fujitsu_notify_status_changed, sc); break; default: /* unknown notification value */ break; } } static int acpi_fujitsu_detach(device_t dev) { struct acpi_fujitsu_softc *sc; sc = device_get_softc(dev); AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_fujitsu_notify_handler); sysctl_ctx_free(&sc->sysctl_ctx); return (0); } /* * Initializes the names of the ACPI control methods and grabs * the current state of all of the ACPI hotkeys into the softc. */ static uint8_t acpi_fujitsu_init(struct acpi_fujitsu_softc *sc) { struct acpi_softc *acpi_sc; int i, exists; ACPI_SERIAL_ASSERT(fujitsu); /* Setup all of the names for each control method */ sc->_sta.name = "_STA"; sc->gbll.name = "GBLL"; sc->ghks.name = "GHKS"; sc->gmou.name = "GMOU"; sc->gsif.name = "GSIF"; sc->gvol.name = "GVOL"; sc->ghks.name = "GHKS"; sc->gsif.name = "GSIF"; sc->rbll.name = "RBLL"; sc->rvol.name = "RVOL"; /* Determine what hardware functionality is available */ acpi_fujitsu_check_hardware(sc); /* Build the sysctl tree */ acpi_sc = acpi_device_get_parent_softc(sc->dev); sysctl_ctx_init(&sc->sysctl_ctx); sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "fujitsu", CTLFLAG_RD, 0, ""); for (i = 0; sysctl_table[i].name != NULL; i++) { - exists = 0; switch(sysctl_table[i].method) { case METHOD_GMOU: exists = sc->gmou.exists; break; case METHOD_GBLL: exists = sc->gbll.exists; break; case METHOD_GVOL: case METHOD_MUTE: exists = sc->gvol.exists; break; case METHOD_RVOL: exists = sc->rvol.exists; break; case METHOD_RBLL: exists = sc->rbll.exists; break; default: /* Allow by default */ exists = 1; break; } if(!exists) continue; SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, sysctl_table[i].name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc, i, acpi_fujitsu_sysctl, "I", sysctl_table[i].description); } /* Set the hotkeys to their initial states */ if (!acpi_fujitsu_update(sc)) { device_printf(sc->dev, "Couldn't init hotkey states\n"); return (FALSE); } return (TRUE); } static int acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_fujitsu_softc *sc; int method; int arg; int function_num, error = 0; sc = (struct acpi_fujitsu_softc *)oidp->oid_arg1; function_num = oidp->oid_arg2; method = sysctl_table[function_num].method; ACPI_SERIAL_BEGIN(fujitsu); /* Get the current value */ arg = acpi_fujitsu_method_get(sc, method); error = sysctl_handle_int(oidp, &arg, 0, req); if (error != 0 || req->newptr == NULL) goto out; /* Update the value */ error = acpi_fujitsu_method_set(sc, method, arg); out: ACPI_SERIAL_END(fujitsu); return (error); } static int acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method) { struct int_nameval nv; ACPI_STATUS status; ACPI_SERIAL_ASSERT(fujitsu); switch (method) { case METHOD_GBLL: nv = sc->gbll; break; case METHOD_GMOU: nv = sc->gmou; break; case METHOD_GVOL: case METHOD_MUTE: nv = sc->gvol; break; case METHOD_GHKS: nv = sc->ghks; break; case METHOD_GSIF: nv = sc->gsif; break; case METHOD_RBLL: nv = sc->rbll; break; case METHOD_RVOL: nv = sc->rvol; break; default: return (FALSE); } if(!nv.exists) return (EINVAL); status = acpi_GetInteger(sc->handle, nv.name, &nv.value); if (ACPI_FAILURE(status)) { device_printf(sc->dev, "Couldn't query method (%s)\n", nv.name); return (FALSE); } if (method == METHOD_MUTE) { sc->bIsMuted = (uint8_t)((nv.value & VOLUME_MUTE_BIT) != 0); return (sc->bIsMuted); } nv.value &= GENERAL_SETTING_BITS; return (nv.value); } static int acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value) { struct int_nameval nv; ACPI_STATUS status; char *control; int changed; ACPI_SERIAL_ASSERT(fujitsu); switch (method) { case METHOD_GBLL: changed = BRIGHT_CHANGED; control = "SBLL"; nv = sc->gbll; break; case METHOD_GMOU: changed = MOUSE_CHANGED; control = "SMOU"; nv = sc->gmou; break; case METHOD_GVOL: case METHOD_MUTE: changed = VOLUME_CHANGED; control = "SVOL"; nv = sc->gvol; break; default: return (EINVAL); } if(!nv.exists) return (EINVAL); if (method == METHOD_MUTE) { if (value == 1) value = nv.value | VOLUME_MUTE_BIT; else if (value == 0) value = nv.value & ~VOLUME_MUTE_BIT; else return (EINVAL); } status = acpi_SetInteger(sc->handle, control, value); if (ACPI_FAILURE(status)) { device_printf(sc->dev, "Couldn't update %s\n", control); return (FALSE); } sc->lastValChanged = changed; return (0); } /* * Query the get methods to determine what functionality is available * from the hardware function hotkeys. */ static uint8_t acpi_fujitsu_check_hardware(struct acpi_fujitsu_softc *sc) { int val; - struct acpi_softc *acpi_sc; - - acpi_sc = acpi_device_get_parent_softc(sc->dev); ACPI_SERIAL_ASSERT(fujitsu); /* save the hotkey bitmask */ if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gsif.name, &(sc->gsif.value)))) { sc->gsif.exists = 0; device_printf(sc->dev, "Couldn't query bitmask value\n"); } else { sc->gsif.exists = 1; } /* System Volume Level */ if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gvol.name, &val))) { sc->gvol.exists = 0; } else { sc->gvol.exists = 1; } if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gbll.name, &val))) { sc->gbll.exists = 0; } else { sc->gbll.exists = 1; } if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->ghks.name, &val))) { sc->ghks.exists = 0; } else { sc->ghks.exists = 1; } if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gmou.name, &val))) { sc->gmou.exists = 0; } else { sc->gmou.exists = 1; } if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->rbll.name, &val))) { sc->rbll.exists = 0; } else { sc->rbll.exists = 1; } if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->rvol.name, &val))) { sc->rvol.exists = 0; } else { sc->rvol.exists = 1; } return (TRUE); } /* * Query each of the ACPI control methods that contain information we're * interested in. We check the return values from the control methods and * adjust any state variables if they should be adjusted. */ static uint8_t acpi_fujitsu_update(struct acpi_fujitsu_softc *sc) { int changed; struct acpi_softc *acpi_sc; acpi_sc = acpi_device_get_parent_softc(sc->dev); ACPI_SERIAL_ASSERT(fujitsu); if(sc->gsif.exists) changed = sc->gsif.value & acpi_fujitsu_method_get(sc,METHOD_GHKS); else changed = 0; /* System Volume Level */ if(sc->gvol.exists) { if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gvol.name, &(sc->gvol.value)))) { device_printf(sc->dev, "Couldn't query volume level\n"); return (FALSE); } if (changed & VOLUME_CHANGED) { sc->bIsMuted = (uint8_t)((sc->gvol.value & VOLUME_MUTE_BIT) != 0); /* Clear the modification bit */ sc->gvol.value &= VOLUME_SETTING_BITS; if (sc->bIsMuted) { acpi_UserNotify("FUJITSU", sc->handle, FN_MUTE); ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now mute\n"); } else ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now %d\n", sc->gvol.value); acpi_UserNotify("FUJITSU", sc->handle, FN_VOLUME); } } /* Internal mouse pointer (eraserhead) */ if(sc->gmou.exists) { if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gmou.name, &(sc->gmou.value)))) { device_printf(sc->dev, "Couldn't query pointer state\n"); return (FALSE); } if (changed & MOUSE_CHANGED) { sc->bIntPtrEnabled = (uint8_t)(sc->gmou.value & 0x1); /* Clear the modification bit */ sc->gmou.value &= MOUSE_SETTING_BITS; acpi_UserNotify("FUJITSU", sc->handle, FN_POINTER_ENABLE); ACPI_VPRINT(sc->dev, acpi_sc, "Internal pointer is now %s\n", (sc->bIntPtrEnabled) ? "enabled" : "disabled"); } } /* Screen Brightness Level */ if(sc->gbll.exists) { if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gbll.name, &(sc->gbll.value)))) { device_printf(sc->dev, "Couldn't query brightness level\n"); return (FALSE); } if (changed & BRIGHT_CHANGED) { /* No state to record here. */ /* Clear the modification bit */ sc->gbll.value &= BRIGHTNESS_SETTING_BITS; acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS); ACPI_VPRINT(sc->dev, acpi_sc, "Brightness level is now %d\n", sc->gbll.value); } } sc->lastValChanged = changed; return (TRUE); } Index: head/sys/dev/acpi_support/acpi_hp.c =================================================================== --- head/sys/dev/acpi_support/acpi_hp.c (revision 209063) +++ head/sys/dev/acpi_support/acpi_hp.c (revision 209064) @@ -1,1215 +1,1213 @@ /*- * Copyright (c) 2009 Michael Gmelin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for extra ACPI-controlled features found on HP laptops * that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p). * Allows to control and read status of integrated hardware and read * BIOS settings through CMI. * Inspired by the hp-wmi driver, which implements a subset of these * features (hotkeys) on Linux. * * HP CMI whitepaper: * http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf * wmi-hp for Linux: * http://www.kernel.org * WMI and ACPI: * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx */ #include "opt_acpi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "acpi_wmi_if.h" #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("HP") #define ACPI_HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" #define ACPI_HP_WMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" #define ACPI_HP_WMI_CMI_GUID "2D114B49-2DFB-4130-B8FE-4A3C09E75133" #define ACPI_HP_WMI_DISPLAY_COMMAND 0x1 #define ACPI_HP_WMI_HDDTEMP_COMMAND 0x2 #define ACPI_HP_WMI_ALS_COMMAND 0x3 #define ACPI_HP_WMI_DOCK_COMMAND 0x4 #define ACPI_HP_WMI_WIRELESS_COMMAND 0x5 #define ACPI_HP_METHOD_WLAN_ENABLED 1 #define ACPI_HP_METHOD_WLAN_RADIO 2 #define ACPI_HP_METHOD_WLAN_ON_AIR 3 #define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON 4 #define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF 5 #define ACPI_HP_METHOD_BLUETOOTH_ENABLED 6 #define ACPI_HP_METHOD_BLUETOOTH_RADIO 7 #define ACPI_HP_METHOD_BLUETOOTH_ON_AIR 8 #define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON 9 #define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF 10 #define ACPI_HP_METHOD_WWAN_ENABLED 11 #define ACPI_HP_METHOD_WWAN_RADIO 12 #define ACPI_HP_METHOD_WWAN_ON_AIR 13 #define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON 14 #define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF 15 #define ACPI_HP_METHOD_ALS 16 #define ACPI_HP_METHOD_DISPLAY 17 #define ACPI_HP_METHOD_HDDTEMP 18 #define ACPI_HP_METHOD_DOCK 19 #define ACPI_HP_METHOD_CMI_DETAIL 20 #define ACPI_HP_METHOD_VERBOSE 21 #define HP_MASK_WWAN_ON_AIR 0x1000000 #define HP_MASK_BLUETOOTH_ON_AIR 0x10000 #define HP_MASK_WLAN_ON_AIR 0x100 #define HP_MASK_WWAN_RADIO 0x8000000 #define HP_MASK_BLUETOOTH_RADIO 0x80000 #define HP_MASK_WLAN_RADIO 0x800 #define HP_MASK_WWAN_ENABLED 0x2000000 #define HP_MASK_BLUETOOTH_ENABLED 0x20000 #define HP_MASK_WLAN_ENABLED 0x200 #define ACPI_HP_CMI_DETAIL_PATHS 0x01 #define ACPI_HP_CMI_DETAIL_ENUMS 0x02 #define ACPI_HP_CMI_DETAIL_FLAGS 0x04 #define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE 0x08 struct acpi_hp_inst_seq_pair { UINT32 sequence; /* sequence number as suggested by cmi bios */ UINT8 instance; /* object instance on guid */ }; struct acpi_hp_softc { device_t dev; ACPI_HANDLE handle; device_t wmi_dev; int has_notify; /* notification GUID found */ int has_cmi; /* CMI GUID found */ int cmi_detail; /* CMI detail level (set by sysctl) */ int verbose; /* add debug output */ int wlan_enable_if_radio_on; /* set by sysctl */ int wlan_disable_if_radio_off; /* set by sysctl */ int bluetooth_enable_if_radio_on; /* set by sysctl */ int bluetooth_disable_if_radio_off; /* set by sysctl */ int wwan_enable_if_radio_on; /* set by sysctl */ int wwan_disable_if_radio_off; /* set by sysctl */ int was_wlan_on_air; /* last known WLAN on air status */ int was_bluetooth_on_air; /* last known BT on air status */ int was_wwan_on_air; /* last known WWAN on air status */ struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; struct cdev *hpcmi_dev_t; /* hpcmi device handle */ struct sbuf hpcmi_sbuf; /* /dev/hpcmi output sbuf */ pid_t hpcmi_open_pid; /* pid operating on /dev/hpcmi */ int hpcmi_bufptr; /* current pointer position in /dev/hpcmi output buffer */ int cmi_order_size; /* size of cmi_order list */ struct acpi_hp_inst_seq_pair cmi_order[128]; /* list of CMI instances ordered by BIOS suggested sequence */ }; static struct { char *name; int method; char *description; int access; } acpi_hp_sysctls[] = { { .name = "wlan_enabled", .method = ACPI_HP_METHOD_WLAN_ENABLED, .description = "Enable/Disable WLAN (WiFi)", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wlan_radio", .method = ACPI_HP_METHOD_WLAN_RADIO, .description = "WLAN radio status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "wlan_on_air", .method = ACPI_HP_METHOD_WLAN_ON_AIR, .description = "WLAN radio ready to use (enabled and radio)", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "wlan_enable_if_radio_on", .method = ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON, .description = "Enable WLAN if radio is turned on", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wlan_disable_if_radio_off", .method = ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF, .description = "Disable WLAN if radio is turned off", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "bt_enabled", .method = ACPI_HP_METHOD_BLUETOOTH_ENABLED, .description = "Enable/Disable Bluetooth", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "bt_radio", .method = ACPI_HP_METHOD_BLUETOOTH_RADIO, .description = "Bluetooth radio status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "bt_on_air", .method = ACPI_HP_METHOD_BLUETOOTH_ON_AIR, .description = "Bluetooth radio ready to use" " (enabled and radio)", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "bt_enable_if_radio_on", .method = ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON, .description = "Enable bluetooth if radio is turned on", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "bt_disable_if_radio_off", .method = ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF, .description = "Disable bluetooth if radio is turned off", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wwan_enabled", .method = ACPI_HP_METHOD_WWAN_ENABLED, .description = "Enable/Disable WWAN (UMTS)", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wwan_radio", .method = ACPI_HP_METHOD_WWAN_RADIO, .description = "WWAN radio status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "wwan_on_air", .method = ACPI_HP_METHOD_WWAN_ON_AIR, .description = "WWAN radio ready to use (enabled and radio)", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "wwan_enable_if_radio_on", .method = ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON, .description = "Enable WWAN if radio is turned on", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wwan_disable_if_radio_off", .method = ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF, .description = "Disable WWAN if radio is turned off", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "als_enabled", .method = ACPI_HP_METHOD_ALS, .description = "Enable/Disable ALS (Ambient light sensor)", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "display", .method = ACPI_HP_METHOD_DISPLAY, .description = "Display status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "hdd_temperature", .method = ACPI_HP_METHOD_HDDTEMP, .description = "HDD temperature", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "is_docked", .method = ACPI_HP_METHOD_DOCK, .description = "Docking station status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "cmi_detail", .method = ACPI_HP_METHOD_CMI_DETAIL, .description = "Details shown in CMI output " "(cat /dev/hpcmi)", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "verbose", .method = ACPI_HP_METHOD_VERBOSE, .description = "Verbosity level", .access = CTLTYPE_INT | CTLFLAG_RW }, { NULL, 0, NULL, 0 } }; ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping"); static int acpi_hp_probe(device_t dev); static int acpi_hp_attach(device_t dev); static int acpi_hp_detach(device_t dev); static void acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc); static int acpi_hp_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg); static int acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method); static int acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val); static void acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context); static int acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance, char* outbuf, size_t outsize, UINT32* sequence, int detail); static void acpi_hp_hex_decode(char* buffer); static d_open_t acpi_hp_hpcmi_open; static d_close_t acpi_hp_hpcmi_close; static d_read_t acpi_hp_hpcmi_read; /* handler /dev/hpcmi device */ static struct cdevsw hpcmi_cdevsw = { .d_version = D_VERSION, .d_open = acpi_hp_hpcmi_open, .d_close = acpi_hp_hpcmi_close, .d_read = acpi_hp_hpcmi_read, .d_name = "hpcmi", }; static device_method_t acpi_hp_methods[] = { DEVMETHOD(device_probe, acpi_hp_probe), DEVMETHOD(device_attach, acpi_hp_attach), DEVMETHOD(device_detach, acpi_hp_detach), {0, 0} }; static driver_t acpi_hp_driver = { "acpi_hp", acpi_hp_methods, sizeof(struct acpi_hp_softc), }; static devclass_t acpi_hp_devclass; DRIVER_MODULE(acpi_hp, acpi, acpi_hp_driver, acpi_hp_devclass, 0, 0); MODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1); MODULE_DEPEND(acpi_hp, acpi, 1, 1, 1); static void acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc) { int wireless; int new_wlan_status; int new_bluetooth_status; int new_wwan_status; wireless = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); new_wlan_status = -1; new_bluetooth_status = -1; new_wwan_status = -1; if (sc->verbose) device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless); if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO) && (wireless & HP_MASK_WLAN_ENABLED)) { acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100); new_wlan_status = 0; } else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO) && !(wireless & HP_MASK_WLAN_ENABLED)) { acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101); new_wlan_status = 1; } if (sc->bluetooth_disable_if_radio_off && !(wireless & HP_MASK_BLUETOOTH_RADIO) && (wireless & HP_MASK_BLUETOOTH_ENABLED)) { acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200); new_bluetooth_status = 0; } else if (sc->bluetooth_enable_if_radio_on && (wireless & HP_MASK_BLUETOOTH_RADIO) && !(wireless & HP_MASK_BLUETOOTH_ENABLED)) { acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202); new_bluetooth_status = 1; } if (sc->wwan_disable_if_radio_off && !(wireless & HP_MASK_WWAN_RADIO) && (wireless & HP_MASK_WWAN_ENABLED)) { acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400); new_wwan_status = 0; } else if (sc->wwan_enable_if_radio_on && (wireless & HP_MASK_WWAN_RADIO) && !(wireless & HP_MASK_WWAN_ENABLED)) { acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404); new_wwan_status = 1; } if (new_wlan_status == -1) { new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR); if ((new_wlan_status?1:0) != sc->was_wlan_on_air) { sc->was_wlan_on_air = sc->was_wlan_on_air?0:1; if (sc->verbose) device_printf(sc->wmi_dev, "WLAN on air changed to %i " "(new_wlan_status is %i)\n", sc->was_wlan_on_air, new_wlan_status); acpi_UserNotify("HP", sc->handle, 0xc0+sc->was_wlan_on_air); } } if (new_bluetooth_status == -1) { new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR); if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) { sc->was_bluetooth_on_air = sc->was_bluetooth_on_air? 0:1; if (sc->verbose) device_printf(sc->wmi_dev, "BLUETOOTH on air changed" " to %i (new_bluetooth_status is %i)\n", sc->was_bluetooth_on_air, new_bluetooth_status); acpi_UserNotify("HP", sc->handle, 0xd0+sc->was_bluetooth_on_air); } } if (new_wwan_status == -1) { new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR); if ((new_wwan_status?1:0) != sc->was_wwan_on_air) { sc->was_wwan_on_air = sc->was_wwan_on_air?0:1; if (sc->verbose) device_printf(sc->wmi_dev, "WWAN on air changed to %i" " (new_wwan_status is %i)\n", sc->was_wwan_on_air, new_wwan_status); acpi_UserNotify("HP", sc->handle, 0xe0+sc->was_wwan_on_air); } } } static int acpi_hp_probe(device_t dev) { if (acpi_disabled("hp") || device_get_unit(dev) != 0) return (ENXIO); device_set_desc(dev, "HP ACPI-WMI Mapping"); return (0); } static int acpi_hp_attach(device_t dev) { struct acpi_hp_softc *sc; - struct acpi_softc *acpi_sc; devclass_t wmi_devclass; int arg; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); sc = device_get_softc(dev); sc->dev = dev; sc->handle = acpi_get_handle(dev); sc->has_notify = 0; sc->has_cmi = 0; sc->bluetooth_enable_if_radio_on = 0; sc->bluetooth_disable_if_radio_off = 0; sc->wlan_enable_if_radio_on = 0; sc->wlan_disable_if_radio_off = 0; sc->wlan_enable_if_radio_on = 0; sc->wlan_disable_if_radio_off = 0; sc->was_wlan_on_air = 0; sc->was_bluetooth_on_air = 0; sc->was_wwan_on_air = 0; sc->cmi_detail = 0; sc->cmi_order_size = -1; sc->verbose = 0; memset(sc->cmi_order, 0, sizeof(sc->cmi_order)); - acpi_sc = acpi_device_get_parent_softc(dev); if (!(wmi_devclass = devclass_find ("acpi_wmi"))) { device_printf(dev, "Couldn't find acpi_wmi devclass\n"); return (EINVAL); } if (!(sc->wmi_dev = devclass_get_device(wmi_devclass, 0))) { device_printf(dev, "Couldn't find acpi_wmi device\n"); return (EINVAL); } if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_BIOS_GUID)) { device_printf(dev, "WMI device does not provide the HP BIOS GUID\n"); return (EINVAL); } if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_EVENT_GUID)) { device_printf(dev, "HP event GUID detected, installing event handler\n"); if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev, ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) { device_printf(dev, "Could not install notification handler!\n"); } else { sc->has_notify = 1; } } if ((sc->has_cmi = ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID) )) { device_printf(dev, "HP CMI GUID detected\n"); } if (sc->has_cmi) { sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644, "hpcmi"); sc->hpcmi_dev_t->si_drv1 = sc; sc->hpcmi_open_pid = 0; sc->hpcmi_bufptr = -1; } ACPI_SERIAL_BEGIN(hp); sc->sysctl_ctx = device_get_sysctl_ctx(dev); sc->sysctl_tree = device_get_sysctl_tree(dev); for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) { arg = 0; if ((!sc->has_notify && (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON || acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF || acpi_hp_sysctls[i].method == ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON || acpi_hp_sysctls[i].method == ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF || acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON || acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) || (arg = acpi_hp_sysctl_get(sc, acpi_hp_sysctls[i].method)) < 0) { continue; } if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) { sc->was_wlan_on_air = arg; } else if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_BLUETOOTH_ON_AIR) { sc->was_bluetooth_on_air = arg; } else if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WWAN_ON_AIR) { sc->was_wwan_on_air = arg; } SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_hp_sysctls[i].name, acpi_hp_sysctls[i].access, sc, i, acpi_hp_sysctl, "I", acpi_hp_sysctls[i].description); } ACPI_SERIAL_END(hp); return (0); } static int acpi_hp_detach(device_t dev) { int ret; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); struct acpi_hp_softc *sc = device_get_softc(dev); if (sc->has_cmi && sc->hpcmi_open_pid != 0) { ret = EBUSY; } else { if (sc->has_notify) { ACPI_WMI_REMOVE_EVENT_HANDLER(dev, ACPI_HP_WMI_EVENT_GUID); } if (sc->hpcmi_bufptr != -1) { sbuf_delete(&sc->hpcmi_sbuf); sc->hpcmi_bufptr = -1; } sc->hpcmi_open_pid = 0; destroy_dev(sc->hpcmi_dev_t); ret = 0; } return (ret); } static int acpi_hp_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_hp_softc *sc; int arg; int oldarg; int error = 0; int function; int method; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_hp_softc *)oidp->oid_arg1; function = oidp->oid_arg2; method = acpi_hp_sysctls[function].method; ACPI_SERIAL_BEGIN(hp); arg = acpi_hp_sysctl_get(sc, method); oldarg = arg; error = sysctl_handle_int(oidp, &arg, 0, req); if (!error && req->newptr != NULL) { error = acpi_hp_sysctl_set(sc, method, arg, oldarg); } ACPI_SERIAL_END(hp); return (error); } static int acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method) { int val = 0; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(hp); switch (method) { case ACPI_HP_METHOD_WLAN_ENABLED: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_WLAN_ENABLED) != 0); break; case ACPI_HP_METHOD_WLAN_RADIO: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_WLAN_RADIO) != 0); break; case ACPI_HP_METHOD_WLAN_ON_AIR: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_WLAN_ON_AIR) != 0); break; case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON: val = sc->wlan_enable_if_radio_on; break; case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF: val = sc->wlan_disable_if_radio_off; break; case ACPI_HP_METHOD_BLUETOOTH_ENABLED: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0); break; case ACPI_HP_METHOD_BLUETOOTH_RADIO: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0); break; case ACPI_HP_METHOD_BLUETOOTH_ON_AIR: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0); break; case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON: val = sc->bluetooth_enable_if_radio_on; break; case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF: val = sc->bluetooth_disable_if_radio_off; break; case ACPI_HP_METHOD_WWAN_ENABLED: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_WWAN_ENABLED) != 0); break; case ACPI_HP_METHOD_WWAN_RADIO: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_WWAN_RADIO) != 0); break; case ACPI_HP_METHOD_WWAN_ON_AIR: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0); val = ((val & HP_MASK_WWAN_ON_AIR) != 0); break; case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON: val = sc->wwan_enable_if_radio_on; break; case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF: val = sc->wwan_disable_if_radio_off; break; case ACPI_HP_METHOD_ALS: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_ALS_COMMAND, 0, 0); break; case ACPI_HP_METHOD_DISPLAY: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0); break; case ACPI_HP_METHOD_HDDTEMP: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0); break; case ACPI_HP_METHOD_DOCK: val = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_DOCK_COMMAND, 0, 0); break; case ACPI_HP_METHOD_CMI_DETAIL: val = sc->cmi_detail; break; case ACPI_HP_METHOD_VERBOSE: val = sc->verbose; break; } return (val); } static int acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(hp); if (method != ACPI_HP_METHOD_CMI_DETAIL && method != ACPI_HP_METHOD_VERBOSE) arg = arg?1:0; if (arg != oldarg) { switch (method) { case ACPI_HP_METHOD_WLAN_ENABLED: return (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, arg?0x101:0x100)); case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON: sc->wlan_enable_if_radio_on = arg; acpi_hp_evaluate_auto_on_off(sc); break; case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF: sc->wlan_disable_if_radio_off = arg; acpi_hp_evaluate_auto_on_off(sc); break; case ACPI_HP_METHOD_BLUETOOTH_ENABLED: return (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, arg?0x202:0x200)); case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON: sc->bluetooth_enable_if_radio_on = arg; acpi_hp_evaluate_auto_on_off(sc); break; case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF: sc->bluetooth_disable_if_radio_off = arg?1:0; acpi_hp_evaluate_auto_on_off(sc); break; case ACPI_HP_METHOD_WWAN_ENABLED: return (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, arg?0x404:0x400)); case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON: sc->wwan_enable_if_radio_on = arg?1:0; acpi_hp_evaluate_auto_on_off(sc); break; case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF: sc->wwan_disable_if_radio_off = arg?1:0; acpi_hp_evaluate_auto_on_off(sc); break; case ACPI_HP_METHOD_ALS: return (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_ALS_COMMAND, 1, arg?1:0)); case ACPI_HP_METHOD_CMI_DETAIL: sc->cmi_detail = arg; if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) != (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) { sc->cmi_order_size = -1; } break; case ACPI_HP_METHOD_VERBOSE: sc->verbose = arg; break; } } return (0); } static __inline void acpi_hp_free_buffer(ACPI_BUFFER* buf) { if (buf && buf->Pointer) { AcpiOsFree(buf->Pointer); } } static void acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context) { device_t dev = context; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); struct acpi_hp_softc *sc = device_get_softc(dev); ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL }; ACPI_OBJECT *obj; ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response); obj = (ACPI_OBJECT*) response.Pointer; if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) { if (*((UINT8 *) obj->Buffer.Pointer) == 0x5) { acpi_hp_evaluate_auto_on_off(sc); } } acpi_hp_free_buffer(&response); } static int acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val) { UINT32 params[5] = { 0x55434553, is_write?2:1, command, is_write?4:0, val}; UINT32* result; ACPI_OBJECT *obj; ACPI_BUFFER in = { sizeof(params), ¶ms }; ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; int retval; if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID, 0, 0x3, &in, &out))) { acpi_hp_free_buffer(&out); return (-EINVAL); } obj = out.Pointer; if (!obj || obj->Type != ACPI_TYPE_BUFFER) { acpi_hp_free_buffer(&out); return (-EINVAL); } result = (UINT32*) obj->Buffer.Pointer; retval = result[2]; if (result[1] > 0) { retval = result[1]; } acpi_hp_free_buffer(&out); return (retval); } static __inline char* acpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) { int length; dst[0] = 0; if (obj->Type == ACPI_TYPE_STRING) { length = obj->String.Length+1; if (length > size) { length = size - 1; } strlcpy(dst, obj->String.Pointer, length); acpi_hp_hex_decode(dst); } return (dst); } /* * Read BIOS Setting block in instance "instance". * The block returned is ACPI_TYPE_PACKAGE which should contain the following * elements: * Index Meaning * 0 Setting Name [string] * 1 Value (comma separated, asterisk marks the current value) [string] * 2 Path within the bios hierarchy [string] * 3 IsReadOnly [int] * 4 DisplayInUI [int] * 5 RequiresPhysicalPresence [int] * 6 Sequence for ordering within the bios settings (absolute) [int] * 7 Length of prerequisites array [int] * 8..8+[7] PrerequisiteN [string] * 9+[7] Current value (in case of enum) [string] / Array length [int] * 10+[7] Enum length [int] / Array values * 11+[7]ff Enum value at index x [string] */ static int acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance, char* outbuf, size_t outsize, UINT32* sequence, int detail) { ACPI_OBJECT *obj; ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; int i; int outlen; int size = 255; int has_enums = 0; int valuebase = 0; char string_buffer[size]; int enumbase; outlen = 0; outbuf[0] = 0; if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) { acpi_hp_free_buffer(&out); return (-EINVAL); } obj = out.Pointer; if (!obj || obj->Type != ACPI_TYPE_PACKAGE) { acpi_hp_free_buffer(&out); return (-EINVAL); } if (obj->Package.Count >= 8 && obj->Package.Elements[7].Type == ACPI_TYPE_INTEGER) { valuebase = 8 + obj->Package.Elements[7].Integer.Value; } /* check if this matches our expectations based on limited knowledge */ if (valuebase > 7 && obj->Package.Count > valuebase + 1 && obj->Package.Elements[0].Type == ACPI_TYPE_STRING && obj->Package.Elements[1].Type == ACPI_TYPE_STRING && obj->Package.Elements[2].Type == ACPI_TYPE_STRING && obj->Package.Elements[3].Type == ACPI_TYPE_INTEGER && obj->Package.Elements[4].Type == ACPI_TYPE_INTEGER && obj->Package.Elements[5].Type == ACPI_TYPE_INTEGER && obj->Package.Elements[6].Type == ACPI_TYPE_INTEGER && obj->Package.Elements[valuebase].Type == ACPI_TYPE_STRING && obj->Package.Elements[valuebase+1].Type == ACPI_TYPE_INTEGER && obj->Package.Count > valuebase + obj->Package.Elements[valuebase+1].Integer.Value ) { enumbase = valuebase + 1; if (detail & ACPI_HP_CMI_DETAIL_PATHS) { strlcat(outbuf, acpi_hp_get_string_from_object( &obj->Package.Elements[2], string_buffer, size), outsize); outlen += 48; while (strlen(outbuf) < outlen) strlcat(outbuf, " ", outsize); } strlcat(outbuf, acpi_hp_get_string_from_object( &obj->Package.Elements[0], string_buffer, size), outsize); outlen += 43; while (strlen(outbuf) < outlen) strlcat(outbuf, " ", outsize); strlcat(outbuf, acpi_hp_get_string_from_object( &obj->Package.Elements[valuebase], string_buffer, size), outsize); outlen += 21; while (strlen(outbuf) < outlen) strlcat(outbuf, " ", outsize); for (i = 0; i < strlen(outbuf); ++i) if (outbuf[i] == '\\') outbuf[i] = '/'; if (detail & ACPI_HP_CMI_DETAIL_ENUMS) { for (i = enumbase + 1; i < enumbase + 1 + obj->Package.Elements[enumbase].Integer.Value; ++i) { acpi_hp_get_string_from_object( &obj->Package.Elements[i], string_buffer, size); if (strlen(string_buffer) > 1 || (strlen(string_buffer) == 1 && string_buffer[0] != ' ')) { if (has_enums) strlcat(outbuf, "/", outsize); else strlcat(outbuf, " (", outsize); strlcat(outbuf, string_buffer, outsize); has_enums = 1; } } } if (has_enums) strlcat(outbuf, ")", outsize); if (detail & ACPI_HP_CMI_DETAIL_FLAGS) { strlcat(outbuf, obj->Package.Elements[3].Integer.Value? " [ReadOnly]":"", outsize); strlcat(outbuf, obj->Package.Elements[4].Integer.Value? "":" [NOUI]", outsize); strlcat(outbuf, obj->Package.Elements[5].Integer.Value? " [RPP]":"", outsize); } *sequence = (UINT32) obj->Package.Elements[6].Integer.Value; } acpi_hp_free_buffer(&out); return (0); } /* * Convert given two digit hex string (hexin) to an UINT8 referenced * by byteout. * Return != 0 if the was a problem (invalid input) */ static __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout) { unsigned int hi; unsigned int lo; hi = hexin[0]; lo = hexin[1]; if ('0' <= hi && hi <= '9') hi -= '0'; else if ('A' <= hi && hi <= 'F') hi -= ('A' - 10); else if ('a' <= hi && hi <= 'f') hi -= ('a' - 10); else return (1); if ('0' <= lo && lo <= '9') lo -= '0'; else if ('A' <= lo && lo <= 'F') lo -= ('A' - 10); else if ('a' <= lo && lo <= 'f') lo -= ('a' - 10); else return (1); *byteout = (hi << 4) + lo; return (0); } static void acpi_hp_hex_decode(char* buffer) { int i; int length = strlen(buffer); UINT8 *uin; UINT8 uout; if (((int)length/2)*2 == length || length < 10) return; for (i = 0; i= '0' && buffer[i] <= '9') || (buffer[i] >= 'A' && buffer[i] <= 'F'))) return; } for (i = 0; isi_drv1 == NULL) return (EBADF); sc = dev->si_drv1; ACPI_SERIAL_BEGIN(hp); if (sc->hpcmi_open_pid != 0) { ret = EBUSY; } else { if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { ret = ENXIO; } else { sc->hpcmi_open_pid = td->td_proc->p_pid; sc->hpcmi_bufptr = 0; ret = 0; } } ACPI_SERIAL_END(hp); return (ret); } /* * close hpcmi device */ static int acpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td) { struct acpi_hp_softc *sc; int ret; if (dev == NULL || dev->si_drv1 == NULL) return (EBADF); sc = dev->si_drv1; ACPI_SERIAL_BEGIN(hp); if (sc->hpcmi_open_pid == 0) { ret = EBADF; } else { if (sc->hpcmi_bufptr != -1) { sbuf_delete(&sc->hpcmi_sbuf); sc->hpcmi_bufptr = -1; } sc->hpcmi_open_pid = 0; ret = 0; } ACPI_SERIAL_END(hp); return (ret); } /* * Read from hpcmi bios information */ static int acpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag) { struct acpi_hp_softc *sc; int pos, i, l, ret; UINT8 instance; UINT8 maxInstance; UINT32 sequence; int linesize = 1025; char line[linesize]; if (dev == NULL || dev->si_drv1 == NULL) return (EBADF); sc = dev->si_drv1; ACPI_SERIAL_BEGIN(hp); if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid || sc->hpcmi_bufptr == -1) { ret = EBADF; } else { if (!sbuf_done(&sc->hpcmi_sbuf)) { if (sc->cmi_order_size < 0) { maxInstance = sc->has_cmi; if (!(sc->cmi_detail & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) && maxInstance > 0) { maxInstance--; } sc->cmi_order_size = 0; for (instance = 0; instance < maxInstance; ++instance) { if (acpi_hp_get_cmi_block(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID, instance, line, linesize, &sequence, sc->cmi_detail)) { instance = maxInstance; } else { pos = sc->cmi_order_size; for (i=0; icmi_order_size && i<127; ++i) { if (sc->cmi_order[i].sequence > sequence) { pos = i; break; } } for (i=sc->cmi_order_size; i>pos; --i) { sc->cmi_order[i].sequence = sc->cmi_order[i-1].sequence; sc->cmi_order[i].instance = sc->cmi_order[i-1].instance; } sc->cmi_order[pos].sequence = sequence; sc->cmi_order[pos].instance = instance; sc->cmi_order_size++; } } } for (i=0; icmi_order_size; ++i) { if (!acpi_hp_get_cmi_block(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID, sc->cmi_order[i].instance, line, linesize, &sequence, sc->cmi_detail)) { sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line); } } sbuf_finish(&sc->hpcmi_sbuf); } if (sbuf_len(&sc->hpcmi_sbuf) <= 0) { sbuf_delete(&sc->hpcmi_sbuf); sc->hpcmi_bufptr = -1; sc->hpcmi_open_pid = 0; ret = ENOMEM; } else { l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) - sc->hpcmi_bufptr); ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) + sc->hpcmi_bufptr, l, buf) : 0; sc->hpcmi_bufptr += l; } } ACPI_SERIAL_END(hp); return (ret); } Index: head/sys/dev/acpi_support/acpi_ibm.c =================================================================== --- head/sys/dev/acpi_support/acpi_ibm.c (revision 209063) +++ head/sys/dev/acpi_support/acpi_ibm.c (revision 209064) @@ -1,977 +1,975 @@ /*- * Copyright (c) 2004 Takanori Watanabe * Copyright (c) 2005 Markus Brueffer * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops. * Inspired by the ibm-acpi and tpb projects which implement these features * on Linux. * * acpi-ibm: * tpb: */ #include "opt_acpi.h" #include #include #include #include #include #include #include "acpi_if.h" #include #include #include #include #include #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("IBM") /* Internal methods */ #define ACPI_IBM_METHOD_EVENTS 1 #define ACPI_IBM_METHOD_EVENTMASK 2 #define ACPI_IBM_METHOD_HOTKEY 3 #define ACPI_IBM_METHOD_BRIGHTNESS 4 #define ACPI_IBM_METHOD_VOLUME 5 #define ACPI_IBM_METHOD_MUTE 6 #define ACPI_IBM_METHOD_THINKLIGHT 7 #define ACPI_IBM_METHOD_BLUETOOTH 8 #define ACPI_IBM_METHOD_WLAN 9 #define ACPI_IBM_METHOD_FANSPEED 10 #define ACPI_IBM_METHOD_FANLEVEL 11 #define ACPI_IBM_METHOD_FANSTATUS 12 #define ACPI_IBM_METHOD_THERMAL 13 /* Hotkeys/Buttons */ #define IBM_RTC_HOTKEY1 0x64 #define IBM_RTC_MASK_HOME (1 << 0) #define IBM_RTC_MASK_SEARCH (1 << 1) #define IBM_RTC_MASK_MAIL (1 << 2) #define IBM_RTC_MASK_WLAN (1 << 5) #define IBM_RTC_HOTKEY2 0x65 #define IBM_RTC_MASK_THINKPAD (1 << 3) #define IBM_RTC_MASK_ZOOM (1 << 5) #define IBM_RTC_MASK_VIDEO (1 << 6) #define IBM_RTC_MASK_HIBERNATE (1 << 7) #define IBM_RTC_THINKLIGHT 0x66 #define IBM_RTC_MASK_THINKLIGHT (1 << 4) #define IBM_RTC_SCREENEXPAND 0x67 #define IBM_RTC_MASK_SCREENEXPAND (1 << 5) #define IBM_RTC_BRIGHTNESS 0x6c #define IBM_RTC_MASK_BRIGHTNESS (1 << 5) #define IBM_RTC_VOLUME 0x6e #define IBM_RTC_MASK_VOLUME (1 << 7) /* Embedded Controller registers */ #define IBM_EC_BRIGHTNESS 0x31 #define IBM_EC_MASK_BRI 0x7 #define IBM_EC_VOLUME 0x30 #define IBM_EC_MASK_VOL 0xf #define IBM_EC_MASK_MUTE (1 << 6) #define IBM_EC_FANSTATUS 0x2F #define IBM_EC_MASK_FANLEVEL 0x3f #define IBM_EC_MASK_FANDISENGAGED (1 << 6) #define IBM_EC_MASK_FANSTATUS (1 << 7) #define IBM_EC_FANSPEED 0x84 /* CMOS Commands */ #define IBM_CMOS_VOLUME_DOWN 0 #define IBM_CMOS_VOLUME_UP 1 #define IBM_CMOS_VOLUME_MUTE 2 #define IBM_CMOS_BRIGHTNESS_UP 4 #define IBM_CMOS_BRIGHTNESS_DOWN 5 /* ACPI methods */ #define IBM_NAME_KEYLIGHT "KBLT" #define IBM_NAME_WLAN_BT_GET "GBDC" #define IBM_NAME_WLAN_BT_SET "SBDC" #define IBM_NAME_MASK_BT (1 << 1) #define IBM_NAME_MASK_WLAN (1 << 2) #define IBM_NAME_THERMAL_GET "TMP7" #define IBM_NAME_THERMAL_UPDT "UPDT" #define IBM_NAME_EVENTS_STATUS_GET "DHKC" #define IBM_NAME_EVENTS_MASK_GET "DHKN" #define IBM_NAME_EVENTS_STATUS_SET "MHKC" #define IBM_NAME_EVENTS_MASK_SET "MHKM" #define IBM_NAME_EVENTS_GET "MHKP" #define IBM_NAME_EVENTS_AVAILMASK "MHKA" #define ABS(x) (((x) < 0)? -(x) : (x)) struct acpi_ibm_softc { device_t dev; ACPI_HANDLE handle; /* Embedded controller */ device_t ec_dev; ACPI_HANDLE ec_handle; /* CMOS */ ACPI_HANDLE cmos_handle; /* Fan status */ ACPI_HANDLE fan_handle; int fan_levels; /* Keylight commands and states */ ACPI_HANDLE light_handle; int light_cmd_on; int light_cmd_off; int light_val; int light_get_supported; int light_set_supported; /* led(4) interface */ struct cdev *led_dev; int led_busy; int led_state; int wlan_bt_flags; int thermal_updt_supported; unsigned int events_availmask; unsigned int events_initialmask; int events_mask_supported; int events_enable; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; }; static struct { char *name; int method; char *description; int access; } acpi_ibm_sysctls[] = { { .name = "events", .method = ACPI_IBM_METHOD_EVENTS, .description = "ACPI events enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "eventmask", .method = ACPI_IBM_METHOD_EVENTMASK, .description = "ACPI eventmask", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "hotkey", .method = ACPI_IBM_METHOD_HOTKEY, .description = "Key Status", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "lcd_brightness", .method = ACPI_IBM_METHOD_BRIGHTNESS, .description = "LCD Brightness", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "volume", .method = ACPI_IBM_METHOD_VOLUME, .description = "Volume", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "mute", .method = ACPI_IBM_METHOD_MUTE, .description = "Mute", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "thinklight", .method = ACPI_IBM_METHOD_THINKLIGHT, .description = "Thinklight enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "bluetooth", .method = ACPI_IBM_METHOD_BLUETOOTH, .description = "Bluetooth enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "wlan", .method = ACPI_IBM_METHOD_WLAN, .description = "WLAN enable", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "fan_speed", .method = ACPI_IBM_METHOD_FANSPEED, .description = "Fan speed", .access = CTLTYPE_INT | CTLFLAG_RD }, { .name = "fan_level", .method = ACPI_IBM_METHOD_FANLEVEL, .description = "Fan level", .access = CTLTYPE_INT | CTLFLAG_RW }, { .name = "fan", .method = ACPI_IBM_METHOD_FANSTATUS, .description = "Fan enable", .access = CTLTYPE_INT | CTLFLAG_RW }, { NULL, 0, NULL, 0 } }; ACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); static int acpi_ibm_probe(device_t dev); static int acpi_ibm_attach(device_t dev); static int acpi_ibm_detach(device_t dev); static int acpi_ibm_resume(device_t dev); static void ibm_led(void *softc, int onoff); static void ibm_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); static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); static device_method_t acpi_ibm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_ibm_probe), DEVMETHOD(device_attach, acpi_ibm_attach), DEVMETHOD(device_detach, acpi_ibm_detach), DEVMETHOD(device_resume, acpi_ibm_resume), {0, 0} }; static driver_t acpi_ibm_driver = { "acpi_ibm", acpi_ibm_methods, sizeof(struct acpi_ibm_softc), }; static devclass_t acpi_ibm_devclass; 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", NULL}; static void ibm_led(void *softc, int onoff) { struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (sc->led_busy) return; sc->led_busy = 1; sc->led_state = onoff; AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); } static void ibm_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_THINKLIGHT, sc->led_state); ACPI_SERIAL_END(ibm); sc->led_busy = 0; } static int acpi_ibm_probe(device_t dev) { if (acpi_disabled("ibm") || ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || device_get_unit(dev) != 0) return (ENXIO); device_set_desc(dev, "IBM ThinkPad ACPI Extras"); return (0); } static int acpi_ibm_attach(device_t dev) { struct acpi_ibm_softc *sc; devclass_t ec_devclass; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); sc = device_get_softc(dev); sc->dev = dev; sc->handle = acpi_get_handle(dev); /* Look for the first embedded controller */ if (!(ec_devclass = devclass_find ("acpi_ec"))) { if (bootverbose) device_printf(dev, "Couldn't find acpi_ec devclass\n"); return (EINVAL); } if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { if (bootverbose) device_printf(dev, "Couldn't find acpi_ec device\n"); return (EINVAL); } sc->ec_handle = acpi_get_handle(sc->ec_dev); /* Get the sysctl tree */ sc->sysctl_ctx = device_get_sysctl_ctx(dev); sc->sysctl_tree = device_get_sysctl_tree(dev); /* Look for event mask and hook up the nodes */ sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); if (sc->events_mask_supported) { SYSCTL_ADD_INT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "initialmask", CTLFLAG_RD, &sc->events_initialmask, 0, "Initial eventmask"); /* The availmask is the bitmask of supported events */ if (ACPI_FAILURE(acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) sc->events_availmask = 0xffffffff; SYSCTL_ADD_INT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "availmask", CTLFLAG_RD, &sc->events_availmask, 0, "Mask of supported events"); } /* Hook up proc nodes */ for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) continue; SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, sc, i, acpi_ibm_sysctl, "I", acpi_ibm_sysctls[i].description); } /* Hook up thermal node */ if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "thermal", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, acpi_ibm_thermal_sysctl, "I", "Thermal zones"); } /* Handle notifies */ AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify, dev); /* Hook up light to led(4) */ if (sc->light_set_supported) sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); return (0); } static int acpi_ibm_detach(device_t dev) { ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); struct acpi_ibm_softc *sc = device_get_softc(dev); /* Disable events and restore eventmask */ ACPI_SERIAL_BEGIN(ibm); acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); ACPI_SERIAL_END(ibm); AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); if (sc->led_dev != NULL) led_destroy(sc->led_dev); return (0); } static int acpi_ibm_resume(device_t dev) { struct acpi_ibm_softc *sc = device_get_softc(dev); ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); ACPI_SERIAL_BEGIN(ibm); for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { int val; if ((acpi_ibm_sysctls[i].access & CTLFLAG_RD) == 0) { continue; } val = acpi_ibm_sysctl_get(sc, i); if ((acpi_ibm_sysctls[i].access & CTLFLAG_WR) == 0) { continue; } acpi_ibm_sysctl_set(sc, i, val); } ACPI_SERIAL_END(ibm); return (0); } static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) { ACPI_OBJECT arg[2]; ACPI_OBJECT_LIST args; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(ibm); args.Count = 2; args.Pointer = arg; arg[0].Type = ACPI_TYPE_INTEGER; arg[1].Type = ACPI_TYPE_INTEGER; for (int i = 0; i < 32; ++i) { arg[0].Integer.Value = i+1; arg[1].Integer.Value = (((1 << i) & val) != 0); status = AcpiEvaluateObject(sc->handle, IBM_NAME_EVENTS_MASK_SET, &args, NULL); if (ACPI_FAILURE(status)) return (status); } return (0); } static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_ibm_softc *sc; int arg; int error = 0; int function; int method; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_ibm_softc *)oidp->oid_arg1; function = oidp->oid_arg2; method = acpi_ibm_sysctls[function].method; ACPI_SERIAL_BEGIN(ibm); arg = acpi_ibm_sysctl_get(sc, method); error = sysctl_handle_int(oidp, &arg, 0, req); /* Sanity check */ if (error != 0 || req->newptr == NULL) goto out; /* Update */ error = acpi_ibm_sysctl_set(sc, method, arg); out: ACPI_SERIAL_END(ibm); return (error); } static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) { UINT64 val_ec; int val = 0, key; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(ibm); switch (method) { case ACPI_IBM_METHOD_EVENTS: acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); break; case ACPI_IBM_METHOD_EVENTMASK: if (sc->events_mask_supported) acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); break; case ACPI_IBM_METHOD_HOTKEY: /* * Construct the hotkey as a bitmask as illustrated below. * Note that whenever a key was pressed, the respecting bit * toggles and nothing else changes. * +--+--+-+-+-+-+-+-+-+-+-+-+ * |11|10|9|8|7|6|5|4|3|2|1|0| * +--+--+-+-+-+-+-+-+-+-+-+-+ * | | | | | | | | | | | | * | | | | | | | | | | | +- Home Button * | | | | | | | | | | +--- Search Button * | | | | | | | | | +----- Mail Button * | | | | | | | | +------- Thinkpad Button * | | | | | | | +--------- Zoom (Fn + Space) * | | | | | | +----------- WLAN Button * | | | | | +------------- Video Button * | | | | +--------------- Hibernate Button * | | | +----------------- Thinklight Button * | | +------------------- Screen expand (Fn + F8) * | +--------------------- Brightness * +------------------------ Volume/Mute */ key = rtcin(IBM_RTC_HOTKEY1); val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; key = rtcin(IBM_RTC_HOTKEY2); val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; val |= (IBM_RTC_MASK_ZOOM & key) >> 1; key = rtcin(IBM_RTC_THINKLIGHT); val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; key = rtcin(IBM_RTC_SCREENEXPAND); val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; key = rtcin(IBM_RTC_BRIGHTNESS); val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; key = rtcin(IBM_RTC_VOLUME); val |= (IBM_RTC_MASK_VOLUME & key) << 4; break; case ACPI_IBM_METHOD_BRIGHTNESS: ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); val = val_ec & IBM_EC_MASK_BRI; break; case ACPI_IBM_METHOD_VOLUME: ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); val = val_ec & IBM_EC_MASK_VOL; break; case ACPI_IBM_METHOD_MUTE: ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); break; case ACPI_IBM_METHOD_THINKLIGHT: if (sc->light_get_supported) acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); else val = sc->light_val; break; case ACPI_IBM_METHOD_BLUETOOTH: acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); sc->wlan_bt_flags = val; val = ((val & IBM_NAME_MASK_BT) != 0); break; case ACPI_IBM_METHOD_WLAN: acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); sc->wlan_bt_flags = val; val = ((val & IBM_NAME_MASK_WLAN) != 0); break; case ACPI_IBM_METHOD_FANSPEED: if (sc->fan_handle) { if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) val = -1; } else { ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); val = val_ec; } break; case ACPI_IBM_METHOD_FANLEVEL: /* * The IBM_EC_FANSTATUS register works as follows: * Bit 0-5 indicate the level at which the fan operates. Only * values between 0 and 7 have an effect. Everything * above 7 is treated the same as level 7 * Bit 6 overrides the fan speed limit if set to 1 * Bit 7 indicates at which mode the fan operates: * manual (0) or automatic (1) */ if (!sc->fan_handle) { ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); val = val_ec & IBM_EC_MASK_FANLEVEL; } break; case ACPI_IBM_METHOD_FANSTATUS: if (!sc->fan_handle) { ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; } else val = -1; break; } return (val); } static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) { int val, step; UINT64 val_ec; ACPI_OBJECT Arg; ACPI_OBJECT_LIST Args; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(ibm); switch (method) { case ACPI_IBM_METHOD_EVENTS: if (arg < 0 || arg > 1) return (EINVAL); status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); if (ACPI_FAILURE(status)) return (status); if (sc->events_mask_supported) return acpi_ibm_eventmask_set(sc, sc->events_availmask); break; case ACPI_IBM_METHOD_EVENTMASK: if (sc->events_mask_supported) return acpi_ibm_eventmask_set(sc, arg); break; case ACPI_IBM_METHOD_BRIGHTNESS: if (arg < 0 || arg > 7) return (EINVAL); if (sc->cmos_handle) { /* Read the current brightness */ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); val = val_ec & IBM_EC_MASK_BRI; Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN; step = (arg > val) ? 1 : -1; for (int i = val; i != arg; i += step) { status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); if (ACPI_FAILURE(status)) break; } } return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); break; case ACPI_IBM_METHOD_VOLUME: if (arg < 0 || arg > 14) return (EINVAL); status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); if (sc->cmos_handle) { val = val_ec & IBM_EC_MASK_VOL; Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN; step = (arg > val) ? 1 : -1; for (int i = val; i != arg; i += step) { status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); if (ACPI_FAILURE(status)) break; } } return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1); break; case ACPI_IBM_METHOD_MUTE: if (arg < 0 || arg > 1) return (EINVAL); status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); if (sc->cmos_handle) { - val = val_ec & IBM_EC_MASK_VOL; - Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); if (ACPI_FAILURE(status)) break; } return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1); break; case ACPI_IBM_METHOD_THINKLIGHT: if (arg < 0 || arg > 1) return (EINVAL); if (sc->light_set_supported) { Args.Count = 1; Args.Pointer = &Arg; Arg.Type = ACPI_TYPE_INTEGER; Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL); if (ACPI_SUCCESS(status)) sc->light_val = arg; return (status); } break; case ACPI_IBM_METHOD_BLUETOOTH: if (arg < 0 || arg > 1) return (EINVAL); val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); break; case ACPI_IBM_METHOD_FANLEVEL: if (arg < 0 || arg > 7) return (EINVAL); if (!sc->fan_handle) { /* Read the current fanstatus */ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); val = val_ec & (~IBM_EC_MASK_FANLEVEL); return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); } break; case ACPI_IBM_METHOD_FANSTATUS: if (arg < 0 || arg > 1) return (EINVAL); if (!sc->fan_handle) { /* Read the current fanstatus */ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); } break; } return (0); } static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) { int dummy; ACPI_OBJECT_TYPE cmos_t; ACPI_HANDLE ledb_handle; switch (method) { case ACPI_IBM_METHOD_EVENTS: /* Events are disabled by default */ return (TRUE); case ACPI_IBM_METHOD_EVENTMASK: return (sc->events_mask_supported); case ACPI_IBM_METHOD_HOTKEY: case ACPI_IBM_METHOD_BRIGHTNESS: case ACPI_IBM_METHOD_VOLUME: case ACPI_IBM_METHOD_MUTE: /* EC is required here, which was aready checked before */ return (TRUE); case ACPI_IBM_METHOD_THINKLIGHT: sc->cmos_handle = NULL; sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && cmos_t == ACPI_TYPE_METHOD) { sc->light_cmd_on = 0x0c; sc->light_cmd_off = 0x0d; sc->cmos_handle = sc->light_handle; } else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { sc->light_cmd_on = 1; sc->light_cmd_off = 0; } else sc->light_handle = NULL; sc->light_set_supported = (sc->light_handle && ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); if (sc->light_get_supported) return (TRUE); if (sc->light_set_supported) { sc->light_val = 0; return (TRUE); } return (FALSE); case ACPI_IBM_METHOD_BLUETOOTH: case ACPI_IBM_METHOD_WLAN: if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) return (TRUE); return (FALSE); case ACPI_IBM_METHOD_FANSPEED: /* * Some models report the fan speed in levels from 0-7 * Newer models report it contiguously */ sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); return (TRUE); case ACPI_IBM_METHOD_FANLEVEL: case ACPI_IBM_METHOD_FANSTATUS: /* * Fan status is only supported on those models, * which report fan RPM contiguously, not in levels */ if (sc->fan_levels) return (FALSE); return (TRUE); case ACPI_IBM_METHOD_THERMAL: if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); return (TRUE); } return (FALSE); } return (FALSE); } static int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_ibm_softc *sc; int error = 0; char temp_cmd[] = "TMP0"; int temp[8]; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_ibm_softc *)oidp->oid_arg1; ACPI_SERIAL_BEGIN(ibm); for (int i = 0; i < 8; ++i) { temp_cmd[3] = '0' + i; /* * The TMPx methods seem to return +/- 128 or 0 * when the respecting sensor is not available */ if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) temp[i] = -1; else if (sc->thermal_updt_supported) /* Temperature is reported in tenth of Kelvin */ temp[i] = (temp[i] - 2732 + 5) / 10; } error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); ACPI_SERIAL_END(ibm); return (error); } static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) { int event, arg, type; device_t dev = context; struct acpi_ibm_softc *sc = device_get_softc(dev); ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); if (notify != 0x80) device_printf(dev, "Unknown notify\n"); for (;;) { acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); if (event == 0) break; type = (event >> 12) & 0xf; arg = event & 0xfff; switch (type) { case 1: if (!(sc->events_availmask & (1 << (arg - 1)))) { device_printf(dev, "Unknown key %d\n", arg); break; } /* Notify devd(8) */ acpi_UserNotify("IBM", h, (arg & 0xff)); break; default: break; } } } Index: head/sys/dev/acpica/acpi_dock.c =================================================================== --- head/sys/dev/acpica/acpi_dock.c (revision 209063) +++ head/sys/dev/acpica/acpi_dock.c (revision 209064) @@ -1,540 +1,537 @@ /*- * Copyright (c) 2005-2006 Mitsuru IWASAKI * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_acpi.h" #include #include #include #include #include #include #include #include /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_DOCK ACPI_MODULE_NAME("DOCK") /* For Docking status */ #define ACPI_DOCK_STATUS_UNKNOWN -1 #define ACPI_DOCK_STATUS_UNDOCKED 0 #define ACPI_DOCK_STATUS_DOCKED 1 #define ACPI_DOCK_UNLOCK 0 /* Allow device to be ejected */ #define ACPI_DOCK_LOCK 1 /* Prevent dev from being removed */ #define ACPI_DOCK_ISOLATE 0 /* Isolate from dock connector */ #define ACPI_DOCK_CONNECT 1 /* Connect to dock */ struct acpi_dock_softc { int _sta; int _bdn; int _uid; int status; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; }; ACPI_SERIAL_DECL(dock, "ACPI Docking Station"); /* * Utility functions */ static void acpi_dock_get_info(device_t dev) { struct acpi_dock_softc *sc; ACPI_HANDLE h; sc = device_get_softc(dev); h = acpi_get_handle(dev); if (ACPI_FAILURE(acpi_GetInteger(h, "_STA", &sc->_sta))) sc->_sta = ACPI_DOCK_STATUS_UNKNOWN; if (ACPI_FAILURE(acpi_GetInteger(h, "_BDN", &sc->_bdn))) sc->_bdn = ACPI_DOCK_STATUS_UNKNOWN; if (ACPI_FAILURE(acpi_GetInteger(h, "_UID", &sc->_uid))) sc->_uid = ACPI_DOCK_STATUS_UNKNOWN; ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "_STA: %04x, _BDN: %04x, _UID: %04x\n", sc->_sta, sc->_bdn, sc->_uid); } static int acpi_dock_execute_dck(device_t dev, int dock) { ACPI_HANDLE h; ACPI_OBJECT argobj; ACPI_OBJECT_LIST args; ACPI_BUFFER buf; ACPI_OBJECT retobj; ACPI_STATUS status; h = acpi_get_handle(dev); argobj.Type = ACPI_TYPE_INTEGER; argobj.Integer.Value = dock; args.Count = 1; args.Pointer = &argobj; buf.Pointer = &retobj; buf.Length = sizeof(retobj); status = AcpiEvaluateObject(h, "_DCK", &args, &buf); /* * When _DCK is called with 0, OSPM will ignore the return value. */ if (dock == ACPI_DOCK_ISOLATE) return (0); /* If _DCK returned 1, the request succeeded. */ if (ACPI_SUCCESS(status) && retobj.Type == ACPI_TYPE_INTEGER && retobj.Integer.Value == 1) return (0); return (-1); } /* Lock devices while docked to prevent surprise removal. */ static void acpi_dock_execute_lck(device_t dev, int lock) { ACPI_HANDLE h; h = acpi_get_handle(dev); acpi_SetInteger(h, "_LCK", lock); } /* Eject a device (i.e., motorized). */ static int acpi_dock_execute_ejx(device_t dev, int eject, int state) { ACPI_HANDLE h; ACPI_STATUS status; char ejx[5]; h = acpi_get_handle(dev); snprintf(ejx, sizeof(ejx), "_EJ%d", state); status = acpi_SetInteger(h, ejx, eject); if (ACPI_SUCCESS(status)) return (0); return (-1); } /* Find dependent devices. When their parent is removed, so are they. */ static int acpi_dock_is_ejd_device(ACPI_HANDLE dock_handle, ACPI_HANDLE handle) { int ret; ACPI_STATUS ret_status; ACPI_BUFFER ejd_buffer; ACPI_OBJECT *obj; ret = 0; ejd_buffer.Pointer = NULL; ejd_buffer.Length = ACPI_ALLOCATE_BUFFER; ret_status = AcpiEvaluateObject(handle, "_EJD", NULL, &ejd_buffer); if (ACPI_FAILURE(ret_status)) goto out; obj = (ACPI_OBJECT *)ejd_buffer.Pointer; if (dock_handle == acpi_GetReference(NULL, obj)) ret = 1; out: if (ejd_buffer.Pointer != NULL) AcpiOsFree(ejd_buffer.Pointer); return (ret); } /* * Docking functions */ static void acpi_dock_attach_later(void *context) { device_t dev; dev = (device_t)context; if (!device_is_enabled(dev)) device_enable(dev); mtx_lock(&Giant); device_probe_and_attach(dev); mtx_unlock(&Giant); } static ACPI_STATUS acpi_dock_insert_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) { device_t dock_dev, dev; ACPI_HANDLE dock_handle; dock_dev = (device_t)context; dock_handle = acpi_get_handle(dock_dev); if (!acpi_dock_is_ejd_device(dock_handle, handle)) goto out; ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev), "inserting device for %s\n", acpi_name(handle)); #if 0 /* * If the system boot up w/o Docking, the devices under the dock * still un-initialized, also control methods such as _INI, _STA * are not executed. * Normal devices are initialized at booting by calling * AcpiInitializeObjects(), however the devices under the dock * need to be initialized here on the scheme of ACPICA. */ ACPI_INIT_WALK_INFO Info; AcpiNsWalkNamespace(ACPI_TYPE_ANY, handle, 100, TRUE, AcpiNsInitOneDevice, NULL, &Info, NULL); #endif dev = acpi_get_device(handle); if (dev == NULL) { device_printf(dock_dev, "error: %s has no associated device\n", acpi_name(handle)); goto out; } AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_dock_attach_later, dev); out: return (AE_OK); } static void acpi_dock_insert_children(device_t dev) { ACPI_STATUS status; ACPI_HANDLE sb_handle; status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle); if (ACPI_SUCCESS(status)) { AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 100, acpi_dock_insert_child, NULL, dev, NULL); } } static void acpi_dock_insert(device_t dev) { struct acpi_dock_softc *sc; - ACPI_HANDLE h; ACPI_SERIAL_ASSERT(dock); sc = device_get_softc(dev); - h = acpi_get_handle(dev); if (sc->status == ACPI_DOCK_STATUS_UNDOCKED || sc->status == ACPI_DOCK_STATUS_UNKNOWN) { acpi_dock_execute_lck(dev, ACPI_DOCK_LOCK); if (acpi_dock_execute_dck(dev, ACPI_DOCK_CONNECT) != 0) { device_printf(dev, "_DCK failed\n"); return; } if (!cold) acpi_dock_insert_children(dev); sc->status = ACPI_DOCK_STATUS_DOCKED; } } /* * Undock */ static ACPI_STATUS acpi_dock_eject_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status) { device_t dock_dev, dev; ACPI_HANDLE dock_handle; dock_dev = *(device_t *)context; dock_handle = acpi_get_handle(dock_dev); if (!acpi_dock_is_ejd_device(dock_handle, handle)) goto out; ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev), "ejecting device for %s\n", acpi_name(handle)); dev = acpi_get_device(handle); if (dev != NULL && device_is_attached(dev)) { mtx_lock(&Giant); device_detach(dev); mtx_unlock(&Giant); } acpi_SetInteger(handle, "_EJ0", 0); out: return (AE_OK); } static void acpi_dock_eject_children(device_t dev) { ACPI_HANDLE sb_handle; ACPI_STATUS status; status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle); if (ACPI_SUCCESS(status)) { AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle, 100, acpi_dock_eject_child, NULL, &dev, NULL); } } static void acpi_dock_removal(device_t dev) { struct acpi_dock_softc *sc; ACPI_SERIAL_ASSERT(dock); sc = device_get_softc(dev); if (sc->status == ACPI_DOCK_STATUS_DOCKED || sc->status == ACPI_DOCK_STATUS_UNKNOWN) { acpi_dock_eject_children(dev); if (acpi_dock_execute_dck(dev, ACPI_DOCK_ISOLATE) != 0) return; acpi_dock_execute_lck(dev, ACPI_DOCK_UNLOCK); if (acpi_dock_execute_ejx(dev, 1, 0) != 0) { device_printf(dev, "_EJ0 failed\n"); return; } sc->status = ACPI_DOCK_STATUS_UNDOCKED; } acpi_dock_get_info(dev); if (sc->_sta != 0) device_printf(dev, "mechanical failure (%#x).\n", sc->_sta); } /* * Device/Bus check */ static void acpi_dock_device_check(device_t dev) { struct acpi_dock_softc *sc; ACPI_SERIAL_ASSERT(dock); sc = device_get_softc(dev); acpi_dock_get_info(dev); /* * If the _STA method indicates 'present' and 'functioning', the * system is docked. If _STA does not exist for this device, it * is always present. */ if (sc->_sta == ACPI_DOCK_STATUS_UNKNOWN || ACPI_DEVICE_PRESENT(sc->_sta)) acpi_dock_insert(dev); else if (sc->_sta == 0) acpi_dock_removal(dev); } /* * Notify Handler */ static void acpi_dock_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) { device_t dev; dev = (device_t) context; ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "got notification %#x\n", notify); ACPI_SERIAL_BEGIN(dock); switch (notify) { case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: acpi_dock_device_check(dev); break; case ACPI_NOTIFY_EJECT_REQUEST: acpi_dock_removal(dev); break; default: device_printf(dev, "unknown notify %#x\n", notify); break; } ACPI_SERIAL_END(dock); } static int acpi_dock_status_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_dock_softc *sc; device_t dev; int status, err; - err = 0; dev = (device_t)arg1; sc = device_get_softc(dev); status = sc->status; ACPI_SERIAL_BEGIN(dock); err = sysctl_handle_int(oidp, &status, 0, req); if (err != 0 || req->newptr == NULL) goto out; if (status != ACPI_DOCK_STATUS_UNDOCKED && status != ACPI_DOCK_STATUS_DOCKED) { err = EINVAL; goto out; } if (status == sc->status) goto out; switch (status) { case ACPI_DOCK_STATUS_UNDOCKED: acpi_dock_removal(dev); break; case ACPI_DOCK_STATUS_DOCKED: acpi_dock_device_check(dev); break; default: err = EINVAL; break; } out: ACPI_SERIAL_END(dock); return (err); } static int acpi_dock_probe(device_t dev) { ACPI_HANDLE h, tmp; h = acpi_get_handle(dev); if (acpi_disabled("dock") || ACPI_FAILURE(AcpiGetHandle(h, "_DCK", &tmp))) return (ENXIO); device_set_desc(dev, "ACPI Docking Station"); /* * XXX Somewhere else in the kernel panics on "sysctl kern" if we * return a negative value here (reprobe ok). */ return (0); } static int acpi_dock_attach(device_t dev) { struct acpi_dock_softc *sc; ACPI_HANDLE h; sc = device_get_softc(dev); h = acpi_get_handle(dev); if (sc == NULL || h == NULL) return (ENXIO); sc->status = ACPI_DOCK_STATUS_UNKNOWN; AcpiEvaluateObject(h, "_INI", NULL, NULL); ACPI_SERIAL_BEGIN(dock); acpi_dock_device_check(dev); /* Get the sysctl tree */ sc->sysctl_ctx = device_get_sysctl_ctx(dev); sc->sysctl_tree = device_get_sysctl_tree(dev); SYSCTL_ADD_INT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "_sta", CTLFLAG_RD, &sc->_sta, 0, "Dock _STA"); SYSCTL_ADD_INT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "_bdn", CTLFLAG_RD, &sc->_bdn, 0, "Dock _BDN"); SYSCTL_ADD_INT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "_uid", CTLFLAG_RD, &sc->_uid, 0, "Dock _UID"); SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "status", CTLTYPE_INT|CTLFLAG_RW, dev, 0, acpi_dock_status_sysctl, "I", "Dock/Undock operation"); ACPI_SERIAL_END(dock); AcpiInstallNotifyHandler(h, ACPI_ALL_NOTIFY, acpi_dock_notify_handler, dev); return (0); } static device_method_t acpi_dock_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_dock_probe), DEVMETHOD(device_attach, acpi_dock_attach), {0, 0} }; static driver_t acpi_dock_driver = { "acpi_dock", acpi_dock_methods, sizeof(struct acpi_dock_softc), }; static devclass_t acpi_dock_devclass; DRIVER_MODULE(acpi_dock, acpi, acpi_dock_driver, acpi_dock_devclass, 0, 0); MODULE_DEPEND(acpi_dock, acpi, 1, 1, 1); Index: head/sys/dev/acpica/acpi_video.c =================================================================== --- head/sys/dev/acpica/acpi_video.c (revision 209063) +++ head/sys/dev/acpica/acpi_video.c (revision 209064) @@ -1,1060 +1,1054 @@ /*- * Copyright (c) 2002-2003 Taku YAMAMOTO * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: acpi_vid.c,v 1.4 2003/10/13 10:07:36 taku Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include /* ACPI video extension driver. */ struct acpi_video_output { ACPI_HANDLE handle; UINT32 adr; STAILQ_ENTRY(acpi_video_output) vo_next; struct { int num; STAILQ_ENTRY(acpi_video_output) next; } vo_unit; int vo_brightness; int vo_fullpower; int vo_economy; int vo_numlevels; int *vo_levels; struct sysctl_ctx_list vo_sysctl_ctx; struct sysctl_oid *vo_sysctl_tree; }; STAILQ_HEAD(acpi_video_output_queue, acpi_video_output); struct acpi_video_softc { device_t device; ACPI_HANDLE handle; struct acpi_video_output_queue vid_outputs; eventhandler_tag vid_pwr_evh; }; /* interfaces */ static int acpi_video_modevent(struct module*, int, void *); static void acpi_video_identify(driver_t *driver, device_t parent); static int acpi_video_probe(device_t); static int acpi_video_attach(device_t); static int acpi_video_detach(device_t); static int acpi_video_shutdown(device_t); static void acpi_video_notify_handler(ACPI_HANDLE, UINT32, void *); static void acpi_video_power_profile(void *); static void acpi_video_bind_outputs(struct acpi_video_softc *); static struct acpi_video_output *acpi_video_vo_init(UINT32); static void acpi_video_vo_bind(struct acpi_video_output *, ACPI_HANDLE); static void acpi_video_vo_destroy(struct acpi_video_output *); static int acpi_video_vo_check_level(struct acpi_video_output *, int); static void acpi_video_vo_notify_handler(ACPI_HANDLE, UINT32, void *); static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS); /* operations */ static void vid_set_switch_policy(ACPI_HANDLE, UINT32); static int vid_enum_outputs(ACPI_HANDLE, void(*)(ACPI_HANDLE, UINT32, void *), void *); static int vo_get_brightness_levels(ACPI_HANDLE, int **); static int vo_get_brightness(ACPI_HANDLE); static void vo_set_brightness(ACPI_HANDLE, int); static UINT32 vo_get_device_status(ACPI_HANDLE); static UINT32 vo_get_graphics_state(ACPI_HANDLE); static void vo_set_device_state(ACPI_HANDLE, UINT32); /* events */ #define VID_NOTIFY_SWITCHED 0x80 #define VID_NOTIFY_REPROBE 0x81 #define VID_NOTIFY_CYCLE_BRN 0x85 #define VID_NOTIFY_INC_BRN 0x86 #define VID_NOTIFY_DEC_BRN 0x87 #define VID_NOTIFY_ZERO_BRN 0x88 /* _DOS (Enable/Disable Output Switching) argument bits */ #define DOS_SWITCH_MASK 3 #define DOS_SWITCH_BY_OSPM 0 #define DOS_SWITCH_BY_BIOS 1 #define DOS_SWITCH_LOCKED 2 #define DOS_BRIGHTNESS_BY_OSPM (1 << 2) /* _DOD and subdev's _ADR */ #define DOD_DEVID_MASK 0x0f00 #define DOD_DEVID_MASK_FULL 0xffff #define DOD_DEVID_MASK_DISPIDX 0x000f #define DOD_DEVID_MASK_DISPPORT 0x00f0 #define DOD_DEVID_MONITOR 0x0100 #define DOD_DEVID_LCD 0x0110 #define DOD_DEVID_TV 0x0200 #define DOD_DEVID_EXT 0x0300 #define DOD_DEVID_INTDFP 0x0400 #define DOD_BIOS (1 << 16) #define DOD_NONVGA (1 << 17) #define DOD_HEAD_ID_SHIFT 18 #define DOD_HEAD_ID_BITS 3 #define DOD_HEAD_ID_MASK \ (((1 << DOD_HEAD_ID_BITS) - 1) << DOD_HEAD_ID_SHIFT) #define DOD_DEVID_SCHEME_STD (1 << 31) /* _BCL related constants */ #define BCL_FULLPOWER 0 #define BCL_ECONOMY 1 /* _DCS (Device Currrent Status) value bits and masks. */ #define DCS_EXISTS (1 << 0) #define DCS_ACTIVE (1 << 1) #define DCS_READY (1 << 2) #define DCS_FUNCTIONAL (1 << 3) #define DCS_ATTACHED (1 << 4) /* _DSS (Device Set Status) argument bits and masks. */ #define DSS_INACTIVE 0 #define DSS_ACTIVE (1 << 0) #define DSS_SETNEXT (1 << 30) #define DSS_COMMIT (1 << 31) static device_method_t acpi_video_methods[] = { DEVMETHOD(device_identify, acpi_video_identify), DEVMETHOD(device_probe, acpi_video_probe), DEVMETHOD(device_attach, acpi_video_attach), DEVMETHOD(device_detach, acpi_video_detach), DEVMETHOD(device_shutdown, acpi_video_shutdown), { 0, 0 } }; static driver_t acpi_video_driver = { "acpi_video", acpi_video_methods, sizeof(struct acpi_video_softc), }; static devclass_t acpi_video_devclass; DRIVER_MODULE(acpi_video, vgapci, acpi_video_driver, acpi_video_devclass, acpi_video_modevent, NULL); MODULE_DEPEND(acpi_video, acpi, 1, 1, 1); static struct sysctl_ctx_list acpi_video_sysctl_ctx; static struct sysctl_oid *acpi_video_sysctl_tree; static struct acpi_video_output_queue crt_units, tv_units, ext_units, lcd_units, other_units; /* * The 'video' lock protects the hierarchy of video output devices * (the video "bus"). The 'video_output' lock protects per-output * data is equivalent to a softc lock for each video output. */ ACPI_SERIAL_DECL(video, "ACPI video"); ACPI_SERIAL_DECL(video_output, "ACPI video output"); MALLOC_DEFINE(M_ACPIVIDEO, "acpivideo", "ACPI video extension"); static int acpi_video_modevent(struct module *mod __unused, int evt, void *cookie __unused) { int err; err = 0; switch (evt) { case MOD_LOAD: sysctl_ctx_init(&acpi_video_sysctl_ctx); STAILQ_INIT(&crt_units); STAILQ_INIT(&tv_units); STAILQ_INIT(&ext_units); STAILQ_INIT(&lcd_units); STAILQ_INIT(&other_units); break; case MOD_UNLOAD: sysctl_ctx_free(&acpi_video_sysctl_ctx); acpi_video_sysctl_tree = NULL; break; default: err = EINVAL; } return (err); } static void acpi_video_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "acpi_video", -1) == NULL) device_add_child(parent, "acpi_video", -1); } static int acpi_video_probe(device_t dev) { ACPI_HANDLE devh, h; ACPI_OBJECT_TYPE t_dos; devh = acpi_get_handle(dev); if (acpi_disabled("video") || ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h)) || ACPI_FAILURE(AcpiGetType(h, &t_dos)) || t_dos != ACPI_TYPE_METHOD) return (ENXIO); device_set_desc(dev, "ACPI video extension"); return (0); } static int acpi_video_attach(device_t dev) { struct acpi_softc *acpi_sc; struct acpi_video_softc *sc; sc = device_get_softc(dev); acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); if (acpi_sc == NULL) return (ENXIO); ACPI_SERIAL_BEGIN(video); if (acpi_video_sysctl_tree == NULL) { acpi_video_sysctl_tree = SYSCTL_ADD_NODE(&acpi_video_sysctl_ctx, SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "video", CTLFLAG_RD, 0, "video extension control"); } ACPI_SERIAL_END(video); sc->device = dev; sc->handle = acpi_get_handle(dev); STAILQ_INIT(&sc->vid_outputs); AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_video_notify_handler, sc); sc->vid_pwr_evh = EVENTHANDLER_REGISTER(power_profile_change, acpi_video_power_profile, sc, 0); ACPI_SERIAL_BEGIN(video); acpi_video_bind_outputs(sc); ACPI_SERIAL_END(video); /* * Notify the BIOS that we want to switch both active outputs and * brightness levels. */ vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_OSPM | DOS_BRIGHTNESS_BY_OSPM); acpi_video_power_profile(sc); return (0); } static int acpi_video_detach(device_t dev) { struct acpi_video_softc *sc; struct acpi_video_output *vo, *vn; sc = device_get_softc(dev); vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); EVENTHANDLER_DEREGISTER(power_profile_change, sc->vid_pwr_evh); AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_video_notify_handler); ACPI_SERIAL_BEGIN(video); STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vn) { acpi_video_vo_destroy(vo); } ACPI_SERIAL_END(video); return (0); } static int acpi_video_shutdown(device_t dev) { struct acpi_video_softc *sc; sc = device_get_softc(dev); vid_set_switch_policy(sc->handle, DOS_SWITCH_BY_BIOS); return (0); } static void acpi_video_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) { struct acpi_video_softc *sc; struct acpi_video_output *vo, *vo_tmp; ACPI_HANDLE lasthand; UINT32 dcs, dss, dss_p; sc = (struct acpi_video_softc *)context; switch (notify) { case VID_NOTIFY_SWITCHED: dss_p = 0; lasthand = NULL; ACPI_SERIAL_BEGIN(video); ACPI_SERIAL_BEGIN(video_output); STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { dss = vo_get_graphics_state(vo->handle); dcs = vo_get_device_status(vo->handle); if (!(dcs & DCS_READY)) dss = DSS_INACTIVE; if (((dcs & DCS_ACTIVE) && dss == DSS_INACTIVE) || (!(dcs & DCS_ACTIVE) && dss == DSS_ACTIVE)) { if (lasthand != NULL) vo_set_device_state(lasthand, dss_p); dss_p = dss; lasthand = vo->handle; } } if (lasthand != NULL) vo_set_device_state(lasthand, dss_p|DSS_COMMIT); ACPI_SERIAL_END(video_output); ACPI_SERIAL_END(video); break; case VID_NOTIFY_REPROBE: ACPI_SERIAL_BEGIN(video); STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) vo->handle = NULL; acpi_video_bind_outputs(sc); STAILQ_FOREACH_SAFE(vo, &sc->vid_outputs, vo_next, vo_tmp) { if (vo->handle == NULL) { STAILQ_REMOVE(&sc->vid_outputs, vo, acpi_video_output, vo_next); acpi_video_vo_destroy(vo); } } ACPI_SERIAL_END(video); break; default: device_printf(sc->device, "unknown notify event 0x%x\n", notify); } } static void acpi_video_power_profile(void *context) { int state; struct acpi_video_softc *sc; struct acpi_video_output *vo; sc = context; state = power_profile_get_state(); if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY) return; ACPI_SERIAL_BEGIN(video); ACPI_SERIAL_BEGIN(video_output); STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { if (vo->vo_levels != NULL && vo->vo_brightness == -1) vo_set_brightness(vo->handle, state == POWER_PROFILE_ECONOMY ? vo->vo_economy : vo->vo_fullpower); } ACPI_SERIAL_END(video_output); ACPI_SERIAL_END(video); } static void acpi_video_bind_outputs_subr(ACPI_HANDLE handle, UINT32 adr, void *context) { struct acpi_video_softc *sc; struct acpi_video_output *vo; ACPI_SERIAL_ASSERT(video); sc = context; STAILQ_FOREACH(vo, &sc->vid_outputs, vo_next) { if (vo->adr == adr) { acpi_video_vo_bind(vo, handle); return; } } vo = acpi_video_vo_init(adr); if (vo != NULL) { acpi_video_vo_bind(vo, handle); STAILQ_INSERT_TAIL(&sc->vid_outputs, vo, vo_next); } } static void acpi_video_bind_outputs(struct acpi_video_softc *sc) { ACPI_SERIAL_ASSERT(video); vid_enum_outputs(sc->handle, acpi_video_bind_outputs_subr, sc); } static struct acpi_video_output * acpi_video_vo_init(UINT32 adr) { struct acpi_video_output *vn, *vo, *vp; int n, x; - int display_index; - int display_port; char name[8], env[32]; const char *type, *desc; struct acpi_video_output_queue *voqh; ACPI_SERIAL_ASSERT(video); - display_index = adr & DOD_DEVID_MASK_DISPIDX; - display_port = (adr & DOD_DEVID_MASK_DISPPORT) >> 4; switch (adr & DOD_DEVID_MASK) { case DOD_DEVID_MONITOR: if ((adr & DOD_DEVID_MASK_FULL) == DOD_DEVID_LCD) { /* DOD_DEVID_LCD is a common, backward compatible ID */ desc = "Internal/Integrated Digital Flat Panel"; type = "lcd"; voqh = &lcd_units; } else { desc = "VGA CRT or VESA Compatible Analog Monitor"; type = "crt"; voqh = &crt_units; } break; case DOD_DEVID_TV: desc = "TV/HDTV or Analog-Video Monitor"; type = "tv"; voqh = &tv_units; break; case DOD_DEVID_EXT: desc = "External Digital Monitor"; type = "ext"; voqh = &ext_units; break; case DOD_DEVID_INTDFP: desc = "Internal/Integrated Digital Flat Panel"; type = "lcd"; voqh = &lcd_units; break; default: desc = "unknown output"; type = "out"; voqh = &other_units; } n = 0; - vn = vp = NULL; + vp = NULL; STAILQ_FOREACH(vn, voqh, vo_unit.next) { if (vn->vo_unit.num != n) break; vp = vn; n++; } snprintf(name, sizeof(name), "%s%d", type, n); vo = malloc(sizeof(*vo), M_ACPIVIDEO, M_NOWAIT); if (vo != NULL) { vo->handle = NULL; vo->adr = adr; vo->vo_unit.num = n; vo->vo_brightness = -1; vo->vo_fullpower = -1; /* TODO: override with tunables */ vo->vo_economy = -1; vo->vo_numlevels = 0; vo->vo_levels = NULL; snprintf(env, sizeof(env), "hw.acpi.video.%s.fullpower", name); if (getenv_int(env, &x)) vo->vo_fullpower = x; snprintf(env, sizeof(env), "hw.acpi.video.%s.economy", name); if (getenv_int(env, &x)) vo->vo_economy = x; sysctl_ctx_init(&vo->vo_sysctl_ctx); if (vp != NULL) STAILQ_INSERT_AFTER(voqh, vp, vo, vo_unit.next); else STAILQ_INSERT_TAIL(voqh, vo, vo_unit.next); if (acpi_video_sysctl_tree != NULL) vo->vo_sysctl_tree = SYSCTL_ADD_NODE(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(acpi_video_sysctl_tree), OID_AUTO, name, CTLFLAG_RD, 0, desc); if (vo->vo_sysctl_tree != NULL) { SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "active", CTLTYPE_INT|CTLFLAG_RW, vo, 0, acpi_video_vo_active_sysctl, "I", "current activity of this device"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "brightness", CTLTYPE_INT|CTLFLAG_RW, vo, 0, acpi_video_vo_bright_sysctl, "I", "current brightness level"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "fullpower", CTLTYPE_INT|CTLFLAG_RW, vo, POWER_PROFILE_PERFORMANCE, acpi_video_vo_presets_sysctl, "I", "preset level for full power mode"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "economy", CTLTYPE_INT|CTLFLAG_RW, vo, POWER_PROFILE_ECONOMY, acpi_video_vo_presets_sysctl, "I", "preset level for economy mode"); SYSCTL_ADD_PROC(&vo->vo_sysctl_ctx, SYSCTL_CHILDREN(vo->vo_sysctl_tree), OID_AUTO, "levels", CTLTYPE_OPAQUE|CTLFLAG_RD, vo, 0, acpi_video_vo_levels_sysctl, "I", "supported brightness levels"); } else printf("%s: sysctl node creation failed\n", type); } else printf("%s: softc allocation failed\n", type); if (bootverbose) { printf("found %s(%x)", desc, adr & DOD_DEVID_MASK_FULL); printf(", idx#%x", adr & DOD_DEVID_MASK_DISPIDX); printf(", port#%x", (adr & DOD_DEVID_MASK_DISPPORT) >> 4); if (adr & DOD_BIOS) printf(", detectable by BIOS"); if (adr & DOD_NONVGA) printf(" (Non-VGA output device whose power " "is related to the VGA device)"); printf(", head #%d\n", (adr & DOD_HEAD_ID_MASK) >> DOD_HEAD_ID_SHIFT); } return (vo); } static void acpi_video_vo_bind(struct acpi_video_output *vo, ACPI_HANDLE handle) { ACPI_SERIAL_BEGIN(video_output); if (vo->vo_levels != NULL) AcpiOsFree(vo->vo_levels); vo->handle = handle; vo->vo_numlevels = vo_get_brightness_levels(handle, &vo->vo_levels); if (vo->vo_numlevels >= 2) { if (vo->vo_fullpower == -1 || acpi_video_vo_check_level(vo, vo->vo_fullpower) != 0) /* XXX - can't deal with rebinding... */ vo->vo_fullpower = vo->vo_levels[BCL_FULLPOWER]; if (vo->vo_economy == -1 || acpi_video_vo_check_level(vo, vo->vo_economy) != 0) /* XXX - see above. */ vo->vo_economy = vo->vo_levels[BCL_ECONOMY]; } if (vo->vo_levels != NULL) AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, acpi_video_vo_notify_handler, vo); ACPI_SERIAL_END(video_output); } static void acpi_video_vo_destroy(struct acpi_video_output *vo) { struct acpi_video_output_queue *voqh; ACPI_SERIAL_ASSERT(video); if (vo->vo_sysctl_tree != NULL) { vo->vo_sysctl_tree = NULL; sysctl_ctx_free(&vo->vo_sysctl_ctx); } if (vo->vo_levels != NULL) { AcpiRemoveNotifyHandler(vo->handle, ACPI_DEVICE_NOTIFY, acpi_video_vo_notify_handler); AcpiOsFree(vo->vo_levels); } switch (vo->adr & DOD_DEVID_MASK) { case DOD_DEVID_MONITOR: voqh = &crt_units; break; case DOD_DEVID_TV: voqh = &tv_units; break; case DOD_DEVID_EXT: voqh = &ext_units; break; case DOD_DEVID_INTDFP: voqh = &lcd_units; break; default: voqh = &other_units; } STAILQ_REMOVE(voqh, vo, acpi_video_output, vo_unit.next); free(vo, M_ACPIVIDEO); } static int acpi_video_vo_check_level(struct acpi_video_output *vo, int level) { int i; ACPI_SERIAL_ASSERT(video_output); if (vo->vo_levels == NULL) return (ENODEV); for (i = 0; i < vo->vo_numlevels; i++) if (vo->vo_levels[i] == level) return (0); return (EINVAL); } static void acpi_video_vo_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) { struct acpi_video_output *vo; int i, j, level, new_level; vo = context; ACPI_SERIAL_BEGIN(video_output); if (vo->handle != handle) goto out; switch (notify) { case VID_NOTIFY_CYCLE_BRN: if (vo->vo_numlevels <= 3) goto out; /* FALLTHROUGH */ case VID_NOTIFY_INC_BRN: case VID_NOTIFY_DEC_BRN: case VID_NOTIFY_ZERO_BRN: if (vo->vo_levels == NULL) goto out; level = vo_get_brightness(handle); if (level < 0) goto out; break; default: printf("unknown notify event 0x%x from %s\n", notify, acpi_name(handle)); goto out; } new_level = level; switch (notify) { case VID_NOTIFY_CYCLE_BRN: for (i = 2; i < vo->vo_numlevels; i++) if (vo->vo_levels[i] == level) { new_level = vo->vo_numlevels > i + 1 ? vo->vo_levels[i + 1] : vo->vo_levels[2]; break; } break; case VID_NOTIFY_INC_BRN: case VID_NOTIFY_DEC_BRN: for (i = 0; i < vo->vo_numlevels; i++) { j = vo->vo_levels[i]; if (notify == VID_NOTIFY_INC_BRN) { if (j > level && (j < new_level || level == new_level)) new_level = j; } else { if (j < level && (j > new_level || level == new_level)) new_level = j; } } break; case VID_NOTIFY_ZERO_BRN: for (i = 0; i < vo->vo_numlevels; i++) if (vo->vo_levels[i] == 0) { new_level = 0; break; } break; } if (new_level != level) { vo_set_brightness(handle, new_level); vo->vo_brightness = new_level; } out: ACPI_SERIAL_END(video_output); } /* ARGSUSED */ static int acpi_video_vo_active_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int state, err; vo = (struct acpi_video_output *)arg1; if (vo->handle == NULL) return (ENXIO); ACPI_SERIAL_BEGIN(video_output); state = (vo_get_device_status(vo->handle) & DCS_ACTIVE) ? 1 : 0; err = sysctl_handle_int(oidp, &state, 0, req); if (err != 0 || req->newptr == NULL) goto out; vo_set_device_state(vo->handle, DSS_COMMIT | (state ? DSS_ACTIVE : DSS_INACTIVE)); out: ACPI_SERIAL_END(video_output); return (err); } /* ARGSUSED */ static int acpi_video_vo_bright_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int level, preset, err; vo = (struct acpi_video_output *)arg1; ACPI_SERIAL_BEGIN(video_output); if (vo->handle == NULL) { err = ENXIO; goto out; } if (vo->vo_levels == NULL) { err = ENODEV; goto out; } preset = (power_profile_get_state() == POWER_PROFILE_ECONOMY) ? vo->vo_economy : vo->vo_fullpower; level = vo->vo_brightness; if (level == -1) level = preset; err = sysctl_handle_int(oidp, &level, 0, req); if (err != 0 || req->newptr == NULL) goto out; if (level < -1 || level > 100) { err = EINVAL; goto out; } if (level != -1 && (err = acpi_video_vo_check_level(vo, level))) goto out; vo->vo_brightness = level; vo_set_brightness(vo->handle, (level == -1) ? preset : level); out: ACPI_SERIAL_END(video_output); return (err); } static int acpi_video_vo_presets_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int i, level, *preset, err; - err = 0; vo = (struct acpi_video_output *)arg1; ACPI_SERIAL_BEGIN(video_output); if (vo->handle == NULL) { err = ENXIO; goto out; } if (vo->vo_levels == NULL) { err = ENODEV; goto out; } preset = (arg2 == POWER_PROFILE_ECONOMY) ? &vo->vo_economy : &vo->vo_fullpower; level = *preset; err = sysctl_handle_int(oidp, &level, 0, req); if (err != 0 || req->newptr == NULL) goto out; if (level < -1 || level > 100) { err = EINVAL; goto out; } if (level == -1) { i = (arg2 == POWER_PROFILE_ECONOMY) ? BCL_ECONOMY : BCL_FULLPOWER; level = vo->vo_levels[i]; } else if ((err = acpi_video_vo_check_level(vo, level)) != 0) goto out; if (vo->vo_brightness == -1 && (power_profile_get_state() == arg2)) vo_set_brightness(vo->handle, level); *preset = level; out: ACPI_SERIAL_END(video_output); return (err); } /* ARGSUSED */ static int acpi_video_vo_levels_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_video_output *vo; int err; vo = (struct acpi_video_output *)arg1; ACPI_SERIAL_BEGIN(video_output); if (vo->vo_levels == NULL) { err = ENODEV; goto out; } if (req->newptr != NULL) { err = EPERM; goto out; } err = sysctl_handle_opaque(oidp, vo->vo_levels, vo->vo_numlevels * sizeof(*vo->vo_levels), req); out: ACPI_SERIAL_END(video_output); return (err); } static void vid_set_switch_policy(ACPI_HANDLE handle, UINT32 policy) { ACPI_STATUS status; status = acpi_SetInteger(handle, "_DOS", policy); if (ACPI_FAILURE(status)) printf("can't evaluate %s._DOS - %s\n", acpi_name(handle), AcpiFormatException(status)); } struct enum_callback_arg { void (*callback)(ACPI_HANDLE, UINT32, void *); void *context; ACPI_OBJECT *dod_pkg; int count; }; static ACPI_STATUS vid_enum_outputs_subr(ACPI_HANDLE handle, UINT32 level __unused, void *context, void **retp __unused) { ACPI_STATUS status; UINT32 adr, val; struct enum_callback_arg *argset; size_t i; ACPI_SERIAL_ASSERT(video); argset = context; status = acpi_GetInteger(handle, "_ADR", &adr); if (ACPI_FAILURE(status)) return (AE_OK); for (i = 0; i < argset->dod_pkg->Package.Count; i++) { if (acpi_PkgInt32(argset->dod_pkg, i, &val) == 0 && (val & DOD_DEVID_MASK_FULL) == adr) { argset->callback(handle, val, argset->context); argset->count++; } } return (AE_OK); } static int vid_enum_outputs(ACPI_HANDLE handle, void (*callback)(ACPI_HANDLE, UINT32, void *), void *context) { ACPI_STATUS status; ACPI_BUFFER dod_buf; ACPI_OBJECT *res; struct enum_callback_arg argset; ACPI_SERIAL_ASSERT(video); dod_buf.Length = ACPI_ALLOCATE_BUFFER; dod_buf.Pointer = NULL; status = AcpiEvaluateObject(handle, "_DOD", NULL, &dod_buf); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) printf("can't evaluate %s._DOD - %s\n", acpi_name(handle), AcpiFormatException(status)); argset.count = -1; goto out; } res = (ACPI_OBJECT *)dod_buf.Pointer; if (!ACPI_PKG_VALID(res, 1)) { printf("evaluation of %s._DOD makes no sense\n", acpi_name(handle)); argset.count = -1; goto out; } if (callback == NULL) { argset.count = res->Package.Count; goto out; } argset.callback = callback; argset.context = context; argset.dod_pkg = res; argset.count = 0; status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, vid_enum_outputs_subr, NULL, &argset, NULL); if (ACPI_FAILURE(status)) printf("failed walking down %s - %s\n", acpi_name(handle), AcpiFormatException(status)); out: if (dod_buf.Pointer != NULL) AcpiOsFree(dod_buf.Pointer); return (argset.count); } static int vo_get_brightness_levels(ACPI_HANDLE handle, int **levelp) { ACPI_STATUS status; ACPI_BUFFER bcl_buf; ACPI_OBJECT *res; int num, i, n, *levels; - num = 0; bcl_buf.Length = ACPI_ALLOCATE_BUFFER; bcl_buf.Pointer = NULL; status = AcpiEvaluateObject(handle, "_BCL", NULL, &bcl_buf); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) printf("can't evaluate %s._BCL - %s\n", acpi_name(handle), AcpiFormatException(status)); num = -1; goto out; } res = (ACPI_OBJECT *)bcl_buf.Pointer; if (!ACPI_PKG_VALID(res, 2)) { printf("evaluation of %s._BCL makes no sense\n", acpi_name(handle)); num = -1; goto out; } num = res->Package.Count; if (levelp == NULL) goto out; levels = AcpiOsAllocate(num * sizeof(*levels)); if (levels == NULL) { num = -1; goto out; } for (i = 0, n = 0; i < num; i++) if (acpi_PkgInt32(res, i, &levels[n]) == 0) n++; if (n < 2) { num = -1; AcpiOsFree(levels); } else { num = n; *levelp = levels; } out: if (bcl_buf.Pointer != NULL) AcpiOsFree(bcl_buf.Pointer); return (num); } static int vo_get_brightness(ACPI_HANDLE handle) { UINT32 level; ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); status = acpi_GetInteger(handle, "_BQC", &level); if (ACPI_FAILURE(status)) { printf("can't evaluate %s._BQC - %s\n", acpi_name(handle), AcpiFormatException(status)); return (-1); } if (level > 100) return (-1); return (level); } static void vo_set_brightness(ACPI_HANDLE handle, int level) { ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); status = acpi_SetInteger(handle, "_BCM", level); if (ACPI_FAILURE(status)) printf("can't evaluate %s._BCM - %s\n", acpi_name(handle), AcpiFormatException(status)); } static UINT32 vo_get_device_status(ACPI_HANDLE handle) { UINT32 dcs; ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); dcs = 0; status = acpi_GetInteger(handle, "_DCS", &dcs); if (ACPI_FAILURE(status)) printf("can't evaluate %s._DCS - %s\n", acpi_name(handle), AcpiFormatException(status)); return (dcs); } static UINT32 vo_get_graphics_state(ACPI_HANDLE handle) { UINT32 dgs; ACPI_STATUS status; dgs = 0; status = acpi_GetInteger(handle, "_DGS", &dgs); if (ACPI_FAILURE(status)) printf("can't evaluate %s._DGS - %s\n", acpi_name(handle), AcpiFormatException(status)); return (dgs); } static void vo_set_device_state(ACPI_HANDLE handle, UINT32 state) { ACPI_STATUS status; ACPI_SERIAL_ASSERT(video_output); status = acpi_SetInteger(handle, "_DSS", state); if (ACPI_FAILURE(status)) printf("can't evaluate %s._DSS - %s\n", acpi_name(handle), AcpiFormatException(status)); }