diff --git a/sys/dev/acpi_support/acpi_asus.c b/sys/dev/acpi_support/acpi_asus.c index aa9f41cd6cd9..4f7ebdcf3c8c 100644 --- a/sys/dev/acpi_support/acpi_asus.c +++ b/sys/dev/acpi_support/acpi_asus.c @@ -1,1280 +1,1280 @@ /*- * Copyright (c) 2004, 2005 Philip Paeps * 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 (hotkeys, leds, etc) found on * recent Asus (and Medion) laptops. Inspired by the acpi4asus project which * implements these features in the Linux kernel. * * * * Currently should support most features, but could use some more testing. * Particularly the display-switching stuff is a bit hairy. If you have an * Asus laptop which doesn't appear to be supported, or strange things happen * when using this driver, please report to . */ #include "opt_acpi.h" #include #include #include #include #include #include #include #include #include /* Methods */ #define ACPI_ASUS_METHOD_BRN 1 #define ACPI_ASUS_METHOD_DISP 2 #define ACPI_ASUS_METHOD_LCD 3 #define ACPI_ASUS_METHOD_CAMERA 4 #define ACPI_ASUS_METHOD_CARDRD 5 #define ACPI_ASUS_METHOD_WLAN 6 #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("ASUS") struct acpi_asus_model { char *name; char *bled_set; char *dled_set; char *gled_set; char *mled_set; char *tled_set; char *wled_set; char *brn_get; char *brn_set; char *brn_up; char *brn_dn; char *lcd_get; char *lcd_set; char *disp_get; char *disp_set; char *cam_get; char *cam_set; char *crd_get; char *crd_set; char *wlan_get; char *wlan_set; void (*n_func)(ACPI_HANDLE, UINT32, void *); char *lcdd; void (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *); }; struct acpi_asus_led { struct acpi_asus_softc *sc; struct cdev *cdev; int busy; int state; enum { ACPI_ASUS_LED_BLED, ACPI_ASUS_LED_DLED, ACPI_ASUS_LED_GLED, ACPI_ASUS_LED_MLED, ACPI_ASUS_LED_TLED, ACPI_ASUS_LED_WLED, } type; }; struct acpi_asus_softc { device_t dev; ACPI_HANDLE handle; ACPI_HANDLE lcdd_handle; struct acpi_asus_model *model; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; struct acpi_asus_led s_bled; struct acpi_asus_led s_dled; struct acpi_asus_led s_gled; struct acpi_asus_led s_mled; struct acpi_asus_led s_tled; struct acpi_asus_led s_wled; int s_brn; int s_disp; int s_lcd; int s_cam; int s_crd; int s_wlan; }; static void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context); /* * We can identify Asus laptops from the string they return * as a result of calling the ATK0100 'INIT' method. */ static struct acpi_asus_model acpi_asus_models[] = { { .name = "xxN", .mled_set = "MLED", .wled_set = "WLED", .lcd_get = "\\BKLT", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\ADVG", .disp_set = "SDSP" }, { .name = "A1x", .mled_set = "MLED", .lcd_get = "\\BKLI", .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E", .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F" }, { .name = "A2x", .mled_set = "MLED", .wled_set = "WLED", .lcd_get = "\\BAOF", .lcd_set = "\\Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\INFB", .disp_set = "SDSP" }, { .name = "A3E", .mled_set = "MLED", .wled_set = "WLED", .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x67)", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", .disp_set = "SDSP" }, { .name = "A3F", .mled_set = "MLED", .wled_set = "WLED", .bled_set = "BLED", .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x11)", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\SSTE", .disp_set = "SDSP" }, { .name = "A3N", .mled_set = "MLED", .bled_set = "BLED", .wled_set = "WLED", .lcd_get = "\\BKLT", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", .disp_set = "SDSP" }, { .name = "A4D", .mled_set = "MLED", .brn_up = "\\_SB_.PCI0.SBRG.EC0._Q0E", .brn_dn = "\\_SB_.PCI0.SBRG.EC0._Q0F", .brn_get = "GPLV", .brn_set = "SPLV", #ifdef notyet .disp_get = "\\_SB_.PCI0.SBRG.EC0._Q10", .disp_set = "\\_SB_.PCI0.SBRG.EC0._Q11" #endif }, { .name = "A6V", .bled_set = "BLED", .mled_set = "MLED", .wled_set = "WLED", .lcd_get = NULL, .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", .disp_set = "SDSP" }, { .name = "A8SR", .bled_set = "BLED", .mled_set = "MLED", .wled_set = "WLED", .lcd_get = NULL, .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", .disp_set = "SDSP", .lcdd = "\\_SB.PCI0.P0P1.VGA.LCDD", .lcdd_n_func = acpi_asus_lcdd_notify }, { .name = "D1x", .mled_set = "MLED", .lcd_get = "\\GP11", .lcd_set = "\\Q0D", .brn_up = "\\Q0C", .brn_dn = "\\Q0B", .disp_get = "\\INFB", .disp_set = "SDSP" }, { .name = "G2K", .bled_set = "BLED", .dled_set = "DLED", .gled_set = "GLED", .mled_set = "MLED", .tled_set = "TLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "GBTL", .lcd_set = "SBTL", .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD", .disp_set = "SDSP", }, { .name = "L2D", .mled_set = "MLED", .wled_set = "WLED", .brn_up = "\\Q0E", .brn_dn = "\\Q0F", .lcd_get = "\\SGP0", .lcd_set = "\\Q10" }, { .name = "L3C", .mled_set = "MLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "\\GL32", .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" }, { .name = "L3D", .mled_set = "MLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "\\BKLG", .lcd_set = "\\Q10" }, { .name = "L3H", .mled_set = "MLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "\\_SB.PCI0.PM.PBC", .lcd_set = "EHK", .disp_get = "\\_SB.INFB", .disp_set = "SDSP" }, { .name = "L4R", .mled_set = "MLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "\\_SB.PCI0.SBSM.SEO4", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", .disp_set = "SDSP" }, { .name = "L5x", .mled_set = "MLED", .tled_set = "TLED", .lcd_get = "\\BAOF", .lcd_set = "\\Q0D", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\INFB", .disp_set = "SDSP" }, { .name = "L8L" /* Only has hotkeys, apparently */ }, { .name = "M1A", .mled_set = "MLED", .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", .lcd_get = "\\PNOF", .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" }, { .name = "M2E", .mled_set = "MLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "\\GP06", .lcd_set = "\\Q10" }, { .name = "M6N", .mled_set = "MLED", .wled_set = "WLED", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .lcd_get = "\\_SB.BKLT", .brn_set = "SPLV", .brn_get = "GPLV", .disp_set = "SDSP", .disp_get = "\\SSTE" }, { .name = "M6R", .mled_set = "MLED", .wled_set = "WLED", .brn_get = "GPLV", .brn_set = "SPLV", .lcd_get = "\\_SB.PCI0.SBSM.SEO4", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .disp_get = "\\SSTE", .disp_set = "SDSP" }, { .name = "S1x", .mled_set = "MLED", .wled_set = "WLED", .lcd_get = "\\PNOF", .lcd_set = "\\_SB.PCI0.PX40.Q10", .brn_get = "GPLV", .brn_set = "SPLV" }, { .name = "S2x", .mled_set = "MLED", .lcd_get = "\\BKLI", .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B", .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A" }, { .name = "V6V", .bled_set = "BLED", .tled_set = "TLED", .wled_set = "WLED", .lcd_get = "\\BKLT", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", .disp_set = "SDSP" }, { .name = "W5A", .bled_set = "BLED", .lcd_get = "\\BKLT", .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", .brn_get = "GPLV", .brn_set = "SPLV", .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", .disp_set = "SDSP" }, { .name = NULL } }; /* * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, * but they can't be probed quite the same way as Asus laptops. */ static struct acpi_asus_model acpi_samsung_models[] = { { .name = "P30", .wled_set = "WLED", .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", .lcd_get = "\\BKLT", .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" }, { .name = NULL } }; static void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context); /* * EeePC have an Asus ASUS010 gadget interface, * but they can't be probed quite the same way as Asus laptops. */ static struct acpi_asus_model acpi_eeepc_models[] = { { .name = "EEE", .brn_get = "\\_SB.ATKD.PBLG", .brn_set = "\\_SB.ATKD.PBLS", .cam_get = "\\_SB.ATKD.CAMG", .cam_set = "\\_SB.ATKD.CAMS", .crd_set = "\\_SB.ATKD.CRDS", .crd_get = "\\_SB.ATKD.CRDG", .wlan_get = "\\_SB.ATKD.WLDG", .wlan_set = "\\_SB.ATKD.WLDS", .n_func = acpi_asus_eeepc_notify }, { .name = NULL } }; static struct { char *name; char *description; int method; int flag_anybody; } acpi_asus_sysctls[] = { { .name = "lcd_backlight", .method = ACPI_ASUS_METHOD_LCD, .description = "state of the lcd backlight", .flag_anybody = 1 }, { .name = "lcd_brightness", .method = ACPI_ASUS_METHOD_BRN, .description = "brightness of the lcd panel", .flag_anybody = 1 }, { .name = "video_output", .method = ACPI_ASUS_METHOD_DISP, .description = "display output state", }, { .name = "camera", .method = ACPI_ASUS_METHOD_CAMERA, .description = "internal camera state", }, { .name = "cardreader", .method = ACPI_ASUS_METHOD_CARDRD, .description = "internal card reader state", }, { .name = "wlan", .method = ACPI_ASUS_METHOD_WLAN, .description = "wireless lan state", }, { .name = NULL } }; ACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); /* Function prototypes */ static int acpi_asus_probe(device_t dev); static int acpi_asus_attach(device_t dev); static int acpi_asus_detach(device_t dev); static void acpi_asus_led(struct acpi_asus_led *led, int state); static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); static device_method_t acpi_asus_methods[] = { DEVMETHOD(device_probe, acpi_asus_probe), DEVMETHOD(device_attach, acpi_asus_attach), DEVMETHOD(device_detach, acpi_asus_detach), { 0, 0 } }; static driver_t acpi_asus_driver = { "acpi_asus", acpi_asus_methods, sizeof(struct acpi_asus_softc) }; static devclass_t acpi_asus_devclass; DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); static int acpi_asus_probe(device_t dev) { struct acpi_asus_model *model; struct acpi_asus_softc *sc; struct sbuf *sb; ACPI_BUFFER Buf; ACPI_OBJECT Arg, *Obj; ACPI_OBJECT_LIST Args; static char *asus_ids[] = { "ATK0100", "ASUS010", NULL }; int rv; char *rstr; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (acpi_disabled("asus")) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids, &rstr); if (rv > 0) { return (rv); } sc = device_get_softc(dev); sc->dev = dev; sc->handle = acpi_get_handle(dev); Arg.Type = ACPI_TYPE_INTEGER; Arg.Integer.Value = 0; Args.Count = 1; Args.Pointer = &Arg; Buf.Pointer = NULL; Buf.Length = ACPI_ALLOCATE_BUFFER; AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); Obj = Buf.Pointer; /* * The Samsung P30 returns a null-pointer from INIT, we * can identify it from the 'ODEM' string in the DSDT. */ if (Obj->String.Pointer == NULL) { ACPI_STATUS status; ACPI_TABLE_HEADER th; status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th); if (ACPI_FAILURE(status)) { device_printf(dev, "Unsupported (Samsung?) laptop\n"); AcpiOsFree(Buf.Pointer); return (ENXIO); } if (strncmp("ODEM", th.OemTableId, 4) == 0) { sc->model = &acpi_samsung_models[0]; device_set_desc(dev, "Samsung P30 Laptop Extras"); AcpiOsFree(Buf.Pointer); return (rv); } /* EeePC */ if (strncmp("ASUS010", rstr, 7) == 0) { sc->model = &acpi_eeepc_models[0]; device_set_desc(dev, "ASUS EeePC"); AcpiOsFree(Buf.Pointer); return (rv); } } sb = sbuf_new_auto(); if (sb == NULL) return (ENOMEM); /* * Asus laptops are simply identified by name, easy! */ for (model = acpi_asus_models; model->name != NULL; model++) { if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { good: sbuf_printf(sb, "Asus %s Laptop Extras", Obj->String.Pointer); sbuf_finish(sb); sc->model = model; device_set_desc_copy(dev, sbuf_data(sb)); sbuf_delete(sb); AcpiOsFree(Buf.Pointer); return (rv); } /* * Some models look exactly the same as other models, but have * their own ids. If we spot these, set them up with the same * details as the models they're like, possibly dealing with * small differences. * * XXX: there must be a prettier way to do this! */ else if (strncmp(model->name, "xxN", 3) == 0 && (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || strncmp(Obj->String.Pointer, "S1N", 3) == 0)) goto good; else if (strncmp(model->name, "A1x", 3) == 0 && strncmp(Obj->String.Pointer, "A1", 2) == 0) goto good; else if (strncmp(model->name, "A2x", 3) == 0 && strncmp(Obj->String.Pointer, "A2", 2) == 0) goto good; else if (strncmp(model->name, "A3F", 3) == 0 && strncmp(Obj->String.Pointer, "A6F", 3) == 0) goto good; else if (strncmp(model->name, "D1x", 3) == 0 && strncmp(Obj->String.Pointer, "D1", 2) == 0) goto good; else if (strncmp(model->name, "L3H", 3) == 0 && strncmp(Obj->String.Pointer, "L2E", 3) == 0) goto good; else if (strncmp(model->name, "L5x", 3) == 0 && strncmp(Obj->String.Pointer, "L5", 2) == 0) goto good; else if (strncmp(model->name, "M2E", 3) == 0 && (strncmp(Obj->String.Pointer, "M2", 2) == 0 || strncmp(Obj->String.Pointer, "L4E", 3) == 0)) goto good; else if (strncmp(model->name, "S1x", 3) == 0 && (strncmp(Obj->String.Pointer, "L8", 2) == 0 || strncmp(Obj->String.Pointer, "S1", 2) == 0)) goto good; else if (strncmp(model->name, "S2x", 3) == 0 && (strncmp(Obj->String.Pointer, "J1", 2) == 0 || strncmp(Obj->String.Pointer, "S2", 2) == 0)) goto good; /* L2B is like L3C but has no lcd_get method */ else if (strncmp(model->name, "L3C", 3) == 0 && strncmp(Obj->String.Pointer, "L2B", 3) == 0) { model->lcd_get = NULL; goto good; } /* A3G is like M6R but with a different lcd_get method */ else if (strncmp(model->name, "M6R", 3) == 0 && strncmp(Obj->String.Pointer, "A3G", 3) == 0) { model->lcd_get = "\\BLFG"; goto good; } /* M2N and W1N are like xxN with added WLED */ else if (strncmp(model->name, "xxN", 3) == 0 && (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { model->wled_set = "WLED"; goto good; } /* M5N and S5N are like xxN without MLED */ else if (strncmp(model->name, "xxN", 3) == 0 && (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { model->mled_set = NULL; goto good; } } sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); sbuf_finish(sb); device_printf(dev, "%s", sbuf_data(sb)); sbuf_delete(sb); AcpiOsFree(Buf.Pointer); return (ENXIO); } static int acpi_asus_attach(device_t dev) { struct acpi_asus_softc *sc; struct acpi_softc *acpi_sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc(dev); acpi_sc = acpi_device_get_parent_softc(dev); /* Build sysctl tree */ sysctl_ctx_init(&sc->sysctl_ctx); sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "asus", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); /* Hook up nodes */ for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) continue; if (acpi_asus_sysctls[i].flag_anybody != 0) { SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_asus_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | - CTLFLAG_NEEDGIANT, sc, i, acpi_asus_sysctl, "I", + CTLFLAG_MPSAFE, sc, i, acpi_asus_sysctl, "I", acpi_asus_sysctls[i].description); } else { SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_asus_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, i, acpi_asus_sysctl, "I", acpi_asus_sysctls[i].description); } } /* Attach leds */ if (sc->model->bled_set) { sc->s_bled.busy = 0; sc->s_bled.sc = sc; sc->s_bled.type = ACPI_ASUS_LED_BLED; sc->s_bled.cdev = led_create_state((led_t *)acpi_asus_led, &sc->s_bled, "bled", 1); } if (sc->model->dled_set) { sc->s_dled.busy = 0; sc->s_dled.sc = sc; sc->s_dled.type = ACPI_ASUS_LED_DLED; sc->s_dled.cdev = led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled"); } if (sc->model->gled_set) { sc->s_gled.busy = 0; sc->s_gled.sc = sc; sc->s_gled.type = ACPI_ASUS_LED_GLED; sc->s_gled.cdev = led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled"); } if (sc->model->mled_set) { sc->s_mled.busy = 0; sc->s_mled.sc = sc; sc->s_mled.type = ACPI_ASUS_LED_MLED; sc->s_mled.cdev = led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); } if (sc->model->tled_set) { sc->s_tled.busy = 0; sc->s_tled.sc = sc; sc->s_tled.type = ACPI_ASUS_LED_TLED; sc->s_tled.cdev = led_create_state((led_t *)acpi_asus_led, &sc->s_tled, "tled", 1); } if (sc->model->wled_set) { sc->s_wled.busy = 0; sc->s_wled.sc = sc; sc->s_wled.type = ACPI_ASUS_LED_WLED; sc->s_wled.cdev = led_create_state((led_t *)acpi_asus_led, &sc->s_wled, "wled", 1); } /* Activate hotkeys */ AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); /* Handle notifies */ if (sc->model->n_func == NULL) sc->model->n_func = acpi_asus_notify; AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, sc->model->n_func, dev); /* Find and hook the 'LCDD' object */ if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) { ACPI_STATUS res; sc->lcdd_handle = NULL; res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ? NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle)); if (ACPI_SUCCESS(res)) { AcpiInstallNotifyHandler((sc->lcdd_handle), ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev); } else { printf("%s: unable to find LCD device '%s'\n", __func__, sc->model->lcdd); } } return (0); } static int acpi_asus_detach(device_t dev) { struct acpi_asus_softc *sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc(dev); /* Turn the lights off */ if (sc->model->bled_set) led_destroy(sc->s_bled.cdev); if (sc->model->dled_set) led_destroy(sc->s_dled.cdev); if (sc->model->gled_set) led_destroy(sc->s_gled.cdev); if (sc->model->mled_set) led_destroy(sc->s_mled.cdev); if (sc->model->tled_set) led_destroy(sc->s_tled.cdev); if (sc->model->wled_set) led_destroy(sc->s_wled.cdev); /* Remove notify handler */ AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, acpi_asus_notify); if (sc->lcdd_handle) { KASSERT(sc->model->lcdd_n_func != NULL, ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero")); AcpiRemoveNotifyHandler((sc->lcdd_handle), ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func); } /* Free sysctl tree */ sysctl_ctx_free(&sc->sysctl_ctx); return (0); } static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) { struct acpi_asus_softc *sc; char *method; int state; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = led->sc; switch (led->type) { case ACPI_ASUS_LED_BLED: method = sc->model->bled_set; state = led->state; break; case ACPI_ASUS_LED_DLED: method = sc->model->dled_set; state = led->state; break; case ACPI_ASUS_LED_GLED: method = sc->model->gled_set; state = led->state + 1; /* 1: off, 2: on */ break; case ACPI_ASUS_LED_MLED: method = sc->model->mled_set; state = !led->state; /* inverted */ break; case ACPI_ASUS_LED_TLED: method = sc->model->tled_set; state = led->state; break; case ACPI_ASUS_LED_WLED: method = sc->model->wled_set; state = led->state; break; default: printf("acpi_asus_led: invalid LED type %d\n", (int)led->type); return; } acpi_SetInteger(sc->handle, method, state); led->busy = 0; } static void acpi_asus_led(struct acpi_asus_led *led, int state) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (led->busy) return; led->busy = 1; led->state = state; AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led); } static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_asus_softc *sc; int arg; int error = 0; int function; int method; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_asus_softc *)oidp->oid_arg1; function = oidp->oid_arg2; method = acpi_asus_sysctls[function].method; ACPI_SERIAL_BEGIN(asus); arg = acpi_asus_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_asus_sysctl_set(sc, method, arg); out: ACPI_SERIAL_END(asus); return (error); } static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) { int val = 0; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(asus); switch (method) { case ACPI_ASUS_METHOD_BRN: val = sc->s_brn; break; case ACPI_ASUS_METHOD_DISP: val = sc->s_disp; break; case ACPI_ASUS_METHOD_LCD: val = sc->s_lcd; break; case ACPI_ASUS_METHOD_CAMERA: val = sc->s_cam; break; case ACPI_ASUS_METHOD_CARDRD: val = sc->s_crd; break; case ACPI_ASUS_METHOD_WLAN: val = sc->s_wlan; break; } return (val); } static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) { ACPI_STATUS status = AE_OK; ACPI_OBJECT_LIST acpiargs; ACPI_OBJECT acpiarg[1]; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(asus); acpiargs.Count = 1; acpiargs.Pointer = acpiarg; acpiarg[0].Type = ACPI_TYPE_INTEGER; acpiarg[0].Integer.Value = arg; switch (method) { case ACPI_ASUS_METHOD_BRN: if (arg < 0 || arg > 15) return (EINVAL); if (sc->model->brn_set) status = acpi_SetInteger(sc->handle, sc->model->brn_set, arg); else { while (arg != 0) { status = AcpiEvaluateObject(sc->handle, (arg > 0) ? sc->model->brn_up : sc->model->brn_dn, NULL, NULL); (arg > 0) ? arg-- : arg++; } } if (ACPI_SUCCESS(status)) sc->s_brn = arg; break; case ACPI_ASUS_METHOD_DISP: if (arg < 0 || arg > 7) return (EINVAL); status = acpi_SetInteger(sc->handle, sc->model->disp_set, arg); if (ACPI_SUCCESS(status)) sc->s_disp = arg; break; case ACPI_ASUS_METHOD_LCD: if (arg < 0 || arg > 1) return (EINVAL); if (strncmp(sc->model->name, "L3H", 3) != 0) status = AcpiEvaluateObject(sc->handle, sc->model->lcd_set, NULL, NULL); else status = acpi_SetInteger(sc->handle, sc->model->lcd_set, 0x7); if (ACPI_SUCCESS(status)) sc->s_lcd = arg; break; case ACPI_ASUS_METHOD_CAMERA: if (arg < 0 || arg > 1) return (EINVAL); status = AcpiEvaluateObject(sc->handle, sc->model->cam_set, &acpiargs, NULL); if (ACPI_SUCCESS(status)) sc->s_cam = arg; break; case ACPI_ASUS_METHOD_CARDRD: if (arg < 0 || arg > 1) return (EINVAL); status = AcpiEvaluateObject(sc->handle, sc->model->crd_set, &acpiargs, NULL); if (ACPI_SUCCESS(status)) sc->s_crd = arg; break; case ACPI_ASUS_METHOD_WLAN: if (arg < 0 || arg > 1) return (EINVAL); status = AcpiEvaluateObject(sc->handle, sc->model->wlan_set, &acpiargs, NULL); if (ACPI_SUCCESS(status)) sc->s_wlan = arg; break; } return (0); } static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) { ACPI_STATUS status; switch (method) { case ACPI_ASUS_METHOD_BRN: if (sc->model->brn_get) { /* GPLV/SPLV models */ status = acpi_GetInteger(sc->handle, sc->model->brn_get, &sc->s_brn); if (ACPI_SUCCESS(status)) return (TRUE); } else if (sc->model->brn_up) { /* Relative models */ status = AcpiEvaluateObject(sc->handle, sc->model->brn_up, NULL, NULL); if (ACPI_FAILURE(status)) return (FALSE); status = AcpiEvaluateObject(sc->handle, sc->model->brn_dn, NULL, NULL); if (ACPI_FAILURE(status)) return (FALSE); return (TRUE); } return (FALSE); case ACPI_ASUS_METHOD_DISP: if (sc->model->disp_get) { status = acpi_GetInteger(sc->handle, sc->model->disp_get, &sc->s_disp); if (ACPI_SUCCESS(status)) return (TRUE); } return (FALSE); case ACPI_ASUS_METHOD_LCD: if (sc->model->lcd_get) { if (strncmp(sc->model->name, "L3H", 3) == 0) { ACPI_BUFFER Buf; ACPI_OBJECT Arg[2], Obj; ACPI_OBJECT_LIST Args; /* L3H is a bit special */ Arg[0].Type = ACPI_TYPE_INTEGER; Arg[0].Integer.Value = 0x02; Arg[1].Type = ACPI_TYPE_INTEGER; Arg[1].Integer.Value = 0x03; Args.Count = 2; Args.Pointer = Arg; Buf.Length = sizeof(Obj); Buf.Pointer = &Obj; status = AcpiEvaluateObject(sc->handle, sc->model->lcd_get, &Args, &Buf); if (ACPI_SUCCESS(status) && Obj.Type == ACPI_TYPE_INTEGER) { sc->s_lcd = Obj.Integer.Value >> 8; return (TRUE); } } else { status = acpi_GetInteger(sc->handle, sc->model->lcd_get, &sc->s_lcd); if (ACPI_SUCCESS(status)) return (TRUE); } } return (FALSE); case ACPI_ASUS_METHOD_CAMERA: if (sc->model->cam_get) { status = acpi_GetInteger(sc->handle, sc->model->cam_get, &sc->s_cam); if (ACPI_SUCCESS(status)) return (TRUE); } return (FALSE); case ACPI_ASUS_METHOD_CARDRD: if (sc->model->crd_get) { status = acpi_GetInteger(sc->handle, sc->model->crd_get, &sc->s_crd); if (ACPI_SUCCESS(status)) return (TRUE); } return (FALSE); case ACPI_ASUS_METHOD_WLAN: if (sc->model->wlan_get) { status = acpi_GetInteger(sc->handle, sc->model->wlan_get, &sc->s_wlan); if (ACPI_SUCCESS(status)) return (TRUE); } return (FALSE); } return (FALSE); } static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_asus_softc *sc; struct acpi_softc *acpi_sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc((device_t)context); acpi_sc = acpi_device_get_parent_softc(sc->dev); ACPI_SERIAL_BEGIN(asus); if ((notify & ~0x10) <= 15) { sc->s_brn = notify & ~0x10; ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); } else if ((notify & ~0x20) <= 15) { sc->s_brn = notify & ~0x20; ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); } else if (notify == 0x33) { sc->s_lcd = 1; ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); } else if (notify == 0x34) { sc->s_lcd = 0; ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); } else if (notify == 0x86) { acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); } else if (notify == 0x87) { acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); } else { /* Notify devd(8) */ acpi_UserNotify("ASUS", h, notify); } ACPI_SERIAL_END(asus); } static void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_asus_softc *sc; struct acpi_softc *acpi_sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc((device_t)context); acpi_sc = acpi_device_get_parent_softc(sc->dev); ACPI_SERIAL_BEGIN(asus); switch (notify) { case 0x87: acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); break; case 0x86: acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); break; } ACPI_SERIAL_END(asus); } static void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_asus_softc *sc; struct acpi_softc *acpi_sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = device_get_softc((device_t)context); acpi_sc = acpi_device_get_parent_softc(sc->dev); ACPI_SERIAL_BEGIN(asus); if ((notify & ~0x20) <= 15) { sc->s_brn = notify & ~0x20; ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased/decreased\n"); } else { /* Notify devd(8) */ acpi_UserNotify("ASUS-Eee", h, notify); } ACPI_SERIAL_END(asus); } diff --git a/sys/dev/acpi_support/acpi_asus_wmi.c b/sys/dev/acpi_support/acpi_asus_wmi.c index 0f67d65ec872..b675bb3a7ffb 100644 --- a/sys/dev/acpi_support/acpi_asus_wmi.c +++ b/sys/dev/acpi_support/acpi_asus_wmi.c @@ -1,636 +1,636 @@ /*- * Copyright (c) 2012 Alexander Motin * 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 #include #include #include #include #include "acpi_wmi_if.h" #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("ASUS-WMI") #define ACPI_ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" #define ACPI_ASUS_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" #define ACPI_EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" /* WMI Methods */ #define ASUS_WMI_METHODID_SPEC 0x43455053 #define ASUS_WMI_METHODID_SFUN 0x4E554653 #define ASUS_WMI_METHODID_DSTS 0x53544344 #define ASUS_WMI_METHODID_DSTS2 0x53545344 #define ASUS_WMI_METHODID_DEVS 0x53564544 #define ASUS_WMI_METHODID_INIT 0x54494E49 #define ASUS_WMI_METHODID_HKEY 0x59454B48 #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE /* Wireless */ #define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 #define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 #define ASUS_WMI_DEVID_CWAP 0x00010003 #define ASUS_WMI_DEVID_WLAN 0x00010011 #define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 #define ASUS_WMI_DEVID_GPS 0x00010015 #define ASUS_WMI_DEVID_WIMAX 0x00010017 #define ASUS_WMI_DEVID_WWAN3G 0x00010019 #define ASUS_WMI_DEVID_UWB 0x00010021 /* LEDs */ #define ASUS_WMI_DEVID_LED1 0x00020011 #define ASUS_WMI_DEVID_LED2 0x00020012 #define ASUS_WMI_DEVID_LED3 0x00020013 #define ASUS_WMI_DEVID_LED4 0x00020014 #define ASUS_WMI_DEVID_LED5 0x00020015 #define ASUS_WMI_DEVID_LED6 0x00020016 /* Backlight and Brightness */ #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 #define ASUS_WMI_DEVID_CARDREADER 0x00080013 #define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 #define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 #define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 #define ASUS_WMI_DSTS_USER_BIT 0x00020000 #define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 struct acpi_asus_wmi_softc { device_t dev; device_t wmi_dev; const char *notify_guid; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; int dsts_id; int handle_keys; }; static struct { char *name; int dev_id; char *description; int flag_rdonly; } acpi_asus_wmi_sysctls[] = { { .name = "hw_switch", .dev_id = ASUS_WMI_DEVID_HW_SWITCH, .description = "hw_switch", }, { .name = "wireless_led", .dev_id = ASUS_WMI_DEVID_WIRELESS_LED, .description = "Wireless LED control", }, { .name = "cwap", .dev_id = ASUS_WMI_DEVID_CWAP, .description = "Alt+F2 function", }, { .name = "wlan", .dev_id = ASUS_WMI_DEVID_WLAN, .description = "WLAN power control", }, { .name = "bluetooth", .dev_id = ASUS_WMI_DEVID_BLUETOOTH, .description = "Bluetooth power control", }, { .name = "gps", .dev_id = ASUS_WMI_DEVID_GPS, .description = "GPS power control", }, { .name = "wimax", .dev_id = ASUS_WMI_DEVID_WIMAX, .description = "WiMAX power control", }, { .name = "wwan3g", .dev_id = ASUS_WMI_DEVID_WWAN3G, .description = "WWAN-3G power control", }, { .name = "uwb", .dev_id = ASUS_WMI_DEVID_UWB, .description = "UWB power control", }, { .name = "led1", .dev_id = ASUS_WMI_DEVID_LED1, .description = "LED1 control", }, { .name = "led2", .dev_id = ASUS_WMI_DEVID_LED2, .description = "LED2 control", }, { .name = "led3", .dev_id = ASUS_WMI_DEVID_LED3, .description = "LED3 control", }, { .name = "led4", .dev_id = ASUS_WMI_DEVID_LED4, .description = "LED4 control", }, { .name = "led5", .dev_id = ASUS_WMI_DEVID_LED5, .description = "LED5 control", }, { .name = "led6", .dev_id = ASUS_WMI_DEVID_LED6, .description = "LED6 control", }, { .name = "backlight", .dev_id = ASUS_WMI_DEVID_BACKLIGHT, .description = "LCD backlight on/off control", }, { .name = "brightness", .dev_id = ASUS_WMI_DEVID_BRIGHTNESS, .description = "LCD backlight brightness control", }, { .name = "kbd_backlight", .dev_id = ASUS_WMI_DEVID_KBD_BACKLIGHT, .description = "Keyboard backlight brightness control", }, { .name = "light_sensor", .dev_id = ASUS_WMI_DEVID_LIGHT_SENSOR, .description = "Ambient light sensor", }, { .name = "camera", .dev_id = ASUS_WMI_DEVID_CAMERA, .description = "Camera power control", }, { .name = "cardreader", .dev_id = ASUS_WMI_DEVID_CARDREADER, .description = "Cardreader power control", }, { .name = "touchpad", .dev_id = ASUS_WMI_DEVID_TOUCHPAD, .description = "Touchpad control", }, { .name = "touchpad_led", .dev_id = ASUS_WMI_DEVID_TOUCHPAD_LED, .description = "Touchpad LED control", }, { .name = "themperature", .dev_id = ASUS_WMI_DEVID_THERMAL_CTRL, .description = "Temperature (C)", .flag_rdonly = 1 }, { .name = "fan_speed", .dev_id = ASUS_WMI_DEVID_FAN_CTRL, .description = "Fan speed (0-3)", .flag_rdonly = 1 }, { .name = "processor_state", .dev_id = ASUS_WMI_DEVID_PROCESSOR_STATE, .flag_rdonly = 1 }, { NULL, 0, NULL, 0 } }; ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device"); static void acpi_asus_wmi_identify(driver_t *driver, device_t parent); static int acpi_asus_wmi_probe(device_t dev); static int acpi_asus_wmi_attach(device_t dev); static int acpi_asus_wmi_detach(device_t dev); static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg); static int acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id); static int acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method, UINT32 arg0, UINT32 arg1, UINT32 *retval); static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 *retval); static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval); static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context); static device_method_t acpi_asus_wmi_methods[] = { DEVMETHOD(device_identify, acpi_asus_wmi_identify), DEVMETHOD(device_probe, acpi_asus_wmi_probe), DEVMETHOD(device_attach, acpi_asus_wmi_attach), DEVMETHOD(device_detach, acpi_asus_wmi_detach), DEVMETHOD_END }; static driver_t acpi_asus_wmi_driver = { "acpi_asus_wmi", acpi_asus_wmi_methods, sizeof(struct acpi_asus_wmi_softc), }; static devclass_t acpi_asus_wmi_devclass; DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver, acpi_asus_wmi_devclass, 0, 0); MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1); MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1); static void acpi_asus_wmi_identify(driver_t *driver, device_t parent) { /* Don't do anything if driver is disabled. */ if (acpi_disabled("asus_wmi")) return; /* Add only a single device instance. */ if (device_find_child(parent, "acpi_asus_wmi", -1) != NULL) return; /* Check management GUID to see whether system is compatible. */ if (!ACPI_WMI_PROVIDES_GUID_STRING(parent, ACPI_ASUS_WMI_MGMT_GUID)) return; if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", -1) == NULL) device_printf(parent, "add acpi_asus_wmi child failed\n"); } static int acpi_asus_wmi_probe(device_t dev) { if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev), ACPI_ASUS_WMI_MGMT_GUID)) return (EINVAL); device_set_desc(dev, "ASUS WMI device"); return (0); } static int acpi_asus_wmi_attach(device_t dev) { struct acpi_asus_wmi_softc *sc; UINT32 val; int dev_id, i; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); sc = device_get_softc(dev); sc->dev = dev; sc->wmi_dev = device_get_parent(dev); sc->handle_keys = 1; /* Check management GUID. */ if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_ASUS_WMI_MGMT_GUID)) { device_printf(dev, "WMI device does not provide the ASUS management GUID\n"); return (EINVAL); } /* Find proper DSTS method. */ sc->dsts_id = ASUS_WMI_METHODID_DSTS; next: for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) { dev_id = acpi_asus_wmi_sysctls[i].dev_id; if (acpi_wpi_asus_get_devstate(sc, dev_id, &val)) continue; break; } if (acpi_asus_wmi_sysctls[i].name == NULL) { if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) { sc->dsts_id = ASUS_WMI_METHODID_DSTS2; goto next; } else { device_printf(dev, "Can not detect DSTS method ID\n"); return (EINVAL); } } /* Find proper and attach to notufy GUID. */ if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_ASUS_WMI_EVENT_GUID)) sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID; else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_EEEPC_WMI_EVENT_GUID)) sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID; else sc->notify_guid = NULL; if (sc->notify_guid != NULL) { if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev, sc->notify_guid, acpi_asus_wmi_notify, dev)) sc->notify_guid = NULL; } if (sc->notify_guid == NULL) device_printf(dev, "Could not install event handler!\n"); /* Initialize. */ if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_INIT, 0, 0, &val) && bootverbose) device_printf(dev, "Initialization: %#x\n", val); if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_SPEC, 0, 0x9, &val) && bootverbose) device_printf(dev, "WMI BIOS version: %d.%d\n", val >> 16, val & 0xFF); if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_SFUN, 0, 0, &val) && bootverbose) device_printf(dev, "SFUN value: %#x\n", val); ACPI_SERIAL_BEGIN(asus_wmi); 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, "handle_keys", CTLFLAG_RW, &sc->handle_keys, 0, "Handle some hardware keys inside the driver"); for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) { dev_id = acpi_asus_wmi_sysctls[i].dev_id; if (acpi_wpi_asus_get_devstate(sc, dev_id, &val)) continue; switch (dev_id) { case ASUS_WMI_DEVID_THERMAL_CTRL: case ASUS_WMI_DEVID_PROCESSOR_STATE: case ASUS_WMI_DEVID_FAN_CTRL: case ASUS_WMI_DEVID_BRIGHTNESS: if (val == 0) continue; break; default: if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0) continue; break; } if (acpi_asus_wmi_sysctls[i].flag_rdonly != 0) { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_asus_wmi_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, i, acpi_asus_wmi_sysctl, "I", acpi_asus_wmi_sysctls[i].description); } else { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_asus_wmi_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, i, acpi_asus_wmi_sysctl, "I", acpi_asus_wmi_sysctls[i].description); } } ACPI_SERIAL_END(asus_wmi); return (0); } static int acpi_asus_wmi_detach(device_t dev) { struct acpi_asus_wmi_softc *sc = device_get_softc(dev); ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); if (sc->notify_guid) ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid); return (0); } static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_asus_wmi_softc *sc; int arg; int oldarg; int error = 0; int function; int dev_id; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1; function = oidp->oid_arg2; dev_id = acpi_asus_wmi_sysctls[function].dev_id; ACPI_SERIAL_BEGIN(asus_wmi); arg = acpi_asus_wmi_sysctl_get(sc, dev_id); oldarg = arg; error = sysctl_handle_int(oidp, &arg, 0, req); if (!error && req->newptr != NULL) error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg); ACPI_SERIAL_END(asus_wmi); return (error); } static int acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id) { UINT32 val = 0; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(asus_wmi); acpi_wpi_asus_get_devstate(sc, dev_id, &val); switch(dev_id) { case ASUS_WMI_DEVID_THERMAL_CTRL: val = (val - 2731 + 5) / 10; break; case ASUS_WMI_DEVID_PROCESSOR_STATE: case ASUS_WMI_DEVID_FAN_CTRL: break; case ASUS_WMI_DEVID_BRIGHTNESS: val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK; break; case ASUS_WMI_DEVID_KBD_BACKLIGHT: val &= 0x7; break; default: if (val & ASUS_WMI_DSTS_UNKNOWN_BIT) val = -1; else val = !!(val & ASUS_WMI_DSTS_STATUS_BIT); break; } return (val); } static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(asus_wmi); switch(dev_id) { case ASUS_WMI_DEVID_KBD_BACKLIGHT: arg = min(0x7, arg); if (arg != 0) arg |= 0x80; break; } acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL); return (0); } static __inline void acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) { if (buf && buf->Pointer) { AcpiOsFree(buf->Pointer); } } static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context) { device_t dev = context; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); UINT32 val; int code = 0; struct acpi_asus_wmi_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_INTEGER) { code = obj->Integer.Value; acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT, code); } if (code && sc->handle_keys) { /* Keyboard backlight control. */ if (code == 0xc4 || code == 0xc5) { acpi_wpi_asus_get_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT, &val); val &= 0x7; if (code == 0xc4) { if (val < 0x7) val++; } else if (val > 0) val--; if (val != 0) val |= 0x80; acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL); } /* Touchpad control. */ if (code == 0x6b) { acpi_wpi_asus_get_devstate(sc, ASUS_WMI_DEVID_TOUCHPAD, &val); val = !(val & 1); acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_TOUCHPAD, val, NULL); } } acpi_asus_wmi_free_buffer(&response); } static int acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method, UINT32 arg0, UINT32 arg1, UINT32 *retval) { UINT32 params[2] = { arg0, arg1 }; UINT32 result; ACPI_OBJECT *obj; ACPI_BUFFER in = { sizeof(params), ¶ms }; ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) { acpi_asus_wmi_free_buffer(&out); return (-EINVAL); } obj = out.Pointer; if (obj && obj->Type == ACPI_TYPE_INTEGER) result = (UINT32) obj->Integer.Value; else result = 0; acpi_asus_wmi_free_buffer(&out); if (retval) *retval = result; return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0); } static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 *retval) { return (acpi_asus_wmi_evaluate_method(sc->wmi_dev, sc->dsts_id, dev_id, 0, retval)); } static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval) { return (acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval)); } diff --git a/sys/dev/acpi_support/acpi_fujitsu.c b/sys/dev/acpi_support/acpi_fujitsu.c index ebfe00bdca91..8d0f2c5a8d27 100644 --- a/sys/dev/acpi_support/acpi_fujitsu.c +++ b/sys/dev/acpi_support/acpi_fujitsu.c @@ -1,764 +1,764 @@ /*- * Copyright (c) 2002 Sean Bullington * 2003-2008 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 #define METHOD_GBLS 9 /* 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 */ gbls, /* get brightness state */ 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), DEVMETHOD_END }; 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 = "lcd_brightness", .method = METHOD_GBLS, .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]; int rv; rv = ACPI_ID_PROBE(device_get_parent(dev), dev, fujitsu_ids, &name); if (acpi_disabled("fujitsu") || rv > 0 || device_get_unit(dev) > 1) return (ENXIO); sprintf(buffer, "Fujitsu Function Hotkeys %s", name); device_set_desc_copy(dev, buffer); return (rv); } 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->gbls.name = "GBLS"; 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 | CTLFLAG_MPSAFE, 0, ""); for (i = 0; sysctl_table[i].name != NULL; i++) { switch(sysctl_table[i].method) { case METHOD_GMOU: exists = sc->gmou.exists; break; case METHOD_GBLL: exists = sc->gbll.exists; break; case METHOD_GBLS: exists = sc->gbls.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 | - CTLFLAG_NEEDGIANT, sc, i, acpi_fujitsu_sysctl, "I", + CTLFLAG_MPSAFE, 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_GBLS: nv = sc->gbls; 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_GBLS: changed = BRIGHT_CHANGED; control = "SBL2"; nv = sc->gbls; 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; 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->gbls.name, &val))) { sc->gbls.exists = 0; } else { sc->gbls.exists = 1; } // don't add if we can use the new method if (sc->gbls.exists || 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; /* Set the value in case it is not hardware controlled */ acpi_fujitsu_method_set(sc, METHOD_GMOU, sc->gmou.value); 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 P8XXX */ if(sc->gbls.exists) { if (ACPI_FAILURE(acpi_GetInteger(sc->handle, sc->gbls.name, &(sc->gbls.value)))) { device_printf(sc->dev, "Couldn't query P8XXX brightness level\n"); return (FALSE); } if (changed & BRIGHT_CHANGED) { /* No state to record here. */ /* Clear the modification bit */ sc->gbls.value &= BRIGHTNESS_SETTING_BITS; /* Set the value in case it is not hardware controlled */ acpi_fujitsu_method_set(sc, METHOD_GBLS, sc->gbls.value); acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS); ACPI_VPRINT(sc->dev, acpi_sc, "P8XXX Brightness level is now %d\n", sc->gbls.value); } } /* 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); } diff --git a/sys/dev/acpi_support/acpi_hp.c b/sys/dev/acpi_support/acpi_hp.c index 0b77475a6bad..d22c9844638e 100644 --- a/sys/dev/acpi_support/acpi_hp.c +++ b/sys/dev/acpi_support/acpi_hp.c @@ -1,1292 +1,1292 @@ /*- * 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_WMI_BIOS_COMMAND 0x9 #define ACPI_HP_WMI_FEATURE_COMMAND 0xb #define ACPI_HP_WMI_HOTKEY_COMMAND 0xc #define ACPI_HP_WMI_FEATURE2_COMMAND 0xd #define ACPI_HP_WMI_WIRELESS2_COMMAND 0x1b #define ACPI_HP_WMI_POSTCODEERROR_COMMAND 0x2a #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_EVENT_DOCK 0x01 #define ACPI_HP_EVENT_PARK_HDD 0x02 #define ACPI_HP_EVENT_SMART_ADAPTER 0x03 #define ACPI_HP_EVENT_BEZEL_BUTTON 0x04 #define ACPI_HP_EVENT_WIRELESS 0x05 #define ACPI_HP_EVENT_CPU_BATTERY_THROTTLE 0x06 #define ACPI_HP_EVENT_LOCK_SWITCH 0x07 #define ACPI_HP_EVENT_LID_SWITCH 0x08 #define ACPI_HP_EVENT_SCREEN_ROTATION 0x09 #define ACPI_HP_EVENT_COOLSENSE_SYSTEM_MOBILE 0x0A #define ACPI_HP_EVENT_COOLSENSE_SYSTEM_HOT 0x0B #define ACPI_HP_EVENT_PROXIMITY_SENSOR 0x0C #define ACPI_HP_EVENT_BACKLIT_KB_BRIGHTNESS 0x0D #define ACPI_HP_EVENT_PEAKSHIFT_PERIOD 0x0F #define ACPI_HP_EVENT_BATTERY_CHARGE_PERIOD 0x10 #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 #define ACPI_HP_WMI_RET_WRONG_SIGNATURE 0x02 #define ACPI_HP_WMI_RET_UNKNOWN_COMMAND 0x03 #define ACPI_HP_WMI_RET_UNKNOWN_CMDTYPE 0x04 #define ACPI_HP_WMI_RET_INVALID_PARAMETERS 0x05 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; device_t wmi_dev; int has_notify; /* notification GUID found */ int has_cmi; /* CMI GUID found */ int has_wireless; /* Wireless command 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 flag_rdonly; } acpi_hp_sysctls[] = { { .name = "wlan_enabled", .method = ACPI_HP_METHOD_WLAN_ENABLED, .description = "Enable/Disable WLAN (WiFi)", }, { .name = "wlan_radio", .method = ACPI_HP_METHOD_WLAN_RADIO, .description = "WLAN radio status", .flag_rdonly = 1 }, { .name = "wlan_on_air", .method = ACPI_HP_METHOD_WLAN_ON_AIR, .description = "WLAN radio ready to use (enabled and radio)", .flag_rdonly = 1 }, { .name = "wlan_enable_if_radio_on", .method = ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON, .description = "Enable WLAN if radio is turned on", }, { .name = "wlan_disable_if_radio_off", .method = ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF, .description = "Disable WLAN if radio is turned off", }, { .name = "bt_enabled", .method = ACPI_HP_METHOD_BLUETOOTH_ENABLED, .description = "Enable/Disable Bluetooth", }, { .name = "bt_radio", .method = ACPI_HP_METHOD_BLUETOOTH_RADIO, .description = "Bluetooth radio status", .flag_rdonly = 1 }, { .name = "bt_on_air", .method = ACPI_HP_METHOD_BLUETOOTH_ON_AIR, .description = "Bluetooth radio ready to use" " (enabled and radio)", .flag_rdonly = 1 }, { .name = "bt_enable_if_radio_on", .method = ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON, .description = "Enable bluetooth if radio is turned on", }, { .name = "bt_disable_if_radio_off", .method = ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF, .description = "Disable bluetooth if radio is turned off", }, { .name = "wwan_enabled", .method = ACPI_HP_METHOD_WWAN_ENABLED, .description = "Enable/Disable WWAN (UMTS)", }, { .name = "wwan_radio", .method = ACPI_HP_METHOD_WWAN_RADIO, .description = "WWAN radio status", .flag_rdonly = 1 }, { .name = "wwan_on_air", .method = ACPI_HP_METHOD_WWAN_ON_AIR, .description = "WWAN radio ready to use (enabled and radio)", .flag_rdonly = 1 }, { .name = "wwan_enable_if_radio_on", .method = ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON, .description = "Enable WWAN if radio is turned on", }, { .name = "wwan_disable_if_radio_off", .method = ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF, .description = "Disable WWAN if radio is turned off", }, { .name = "als_enabled", .method = ACPI_HP_METHOD_ALS, .description = "Enable/Disable ALS (Ambient light sensor)", }, { .name = "display", .method = ACPI_HP_METHOD_DISPLAY, .description = "Display status", .flag_rdonly = 1 }, { .name = "hdd_temperature", .method = ACPI_HP_METHOD_HDDTEMP, .description = "HDD temperature", .flag_rdonly = 1 }, { .name = "is_docked", .method = ACPI_HP_METHOD_DOCK, .description = "Docking station status", .flag_rdonly = 1 }, { .name = "cmi_detail", .method = ACPI_HP_METHOD_CMI_DETAIL, .description = "Details shown in CMI output " "(cat /dev/hpcmi)", }, { .name = "verbose", .method = ACPI_HP_METHOD_VERBOSE, .description = "Verbosity level", }, { NULL, 0, NULL, 0 } }; ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping"); static void acpi_hp_identify(driver_t *driver, device_t parent); 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, int *retval); 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_identify, acpi_hp_identify), DEVMETHOD(device_probe, acpi_hp_probe), DEVMETHOD(device_attach, acpi_hp_attach), DEVMETHOD(device_detach, acpi_hp_detach), DEVMETHOD_END }; 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_wmi, 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 res; int wireless; int new_wlan_status; int new_bluetooth_status; int new_wwan_status; res = acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &wireless); if (res != 0) { device_printf(sc->wmi_dev, "Wireless command error %x\n", res); return; } 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, NULL); 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, NULL); 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, NULL); 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, NULL); 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, NULL); 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, NULL); 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", ACPI_ROOT_OBJECT, 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", ACPI_ROOT_OBJECT, 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", ACPI_ROOT_OBJECT, 0xe0+sc->was_wwan_on_air); } } } static void acpi_hp_identify(driver_t *driver, device_t parent) { /* Don't do anything if driver is disabled. */ if (acpi_disabled("hp")) return; /* Add only a single device instance. */ if (device_find_child(parent, "acpi_hp", -1) != NULL) return; /* Check BIOS GUID to see whether system is compatible. */ if (!ACPI_WMI_PROVIDES_GUID_STRING(parent, ACPI_HP_WMI_BIOS_GUID)) return; if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL) device_printf(parent, "add acpi_hp child failed\n"); } static int acpi_hp_probe(device_t dev) { device_set_desc(dev, "HP ACPI-WMI Mapping"); return (0); } static int acpi_hp_attach(device_t dev) { struct acpi_hp_softc *sc; int arg; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); sc = device_get_softc(dev); sc->dev = 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 = bootverbose; memset(sc->cmi_order, 0, sizeof(sc->cmi_order)); sc->wmi_dev = device_get_parent(dev); 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; } if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, NULL) == 0) sc->has_wireless = 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 || !sc->has_wireless) && (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; } if (acpi_hp_sysctls[i].flag_rdonly != 0) { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_hp_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, i, acpi_hp_sysctl, "I", acpi_hp_sysctls[i].description); } else { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_hp_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 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) { struct acpi_hp_softc *sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); sc = device_get_softc(dev); if (sc->has_cmi && sc->hpcmi_open_pid != 0) return (EBUSY); if (sc->has_notify) { ACPI_WMI_REMOVE_EVENT_HANDLER(sc->wmi_dev, ACPI_HP_WMI_EVENT_GUID); } if (sc->has_cmi) { 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); } return (0); } 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); val = ((val & HP_MASK_WLAN_ENABLED) != 0); break; case ACPI_HP_METHOD_WLAN_RADIO: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); val = ((val & HP_MASK_WLAN_RADIO) != 0); break; case ACPI_HP_METHOD_WLAN_ON_AIR: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0); break; case ACPI_HP_METHOD_BLUETOOTH_RADIO: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0); break; case ACPI_HP_METHOD_BLUETOOTH_ON_AIR: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); val = ((val & HP_MASK_WWAN_ENABLED) != 0); break; case ACPI_HP_METHOD_WWAN_RADIO: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); val = ((val & HP_MASK_WWAN_RADIO) != 0); break; case ACPI_HP_METHOD_WWAN_ON_AIR: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val)) return (-EINVAL); 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_ALS_COMMAND, 0, 0, &val)) return (-EINVAL); break; case ACPI_HP_METHOD_DISPLAY: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0, &val)) return (-EINVAL); break; case ACPI_HP_METHOD_HDDTEMP: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0, &val)) return (-EINVAL); break; case ACPI_HP_METHOD_DOCK: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_DOCK_COMMAND, 0, 0, &val)) return (-EINVAL); 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, arg?0x101:0x100, NULL)) return (-EINVAL); break; 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, arg?0x202:0x200, NULL)) return (-EINVAL); break; 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_WIRELESS_COMMAND, 1, arg?0x404:0x400, NULL)) return (-EINVAL); break; 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: if (acpi_hp_exec_wmi_command(sc->wmi_dev, ACPI_HP_WMI_ALS_COMMAND, 1, arg?1:0, NULL)) return (-EINVAL); break; 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) { switch (*((UINT8 *) obj->Buffer.Pointer)) { case ACPI_HP_EVENT_WIRELESS: acpi_hp_evaluate_auto_on_off(sc); break; default: if (sc->verbose) { device_printf(sc->dev, "Event %02x\n", *((UINT8 *) obj->Buffer.Pointer)); } break; } } acpi_hp_free_buffer(&response); } static int acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write, int val, int *retval) { UINT32 params[4+32] = { 0x55434553, is_write ? 2 : 1, command, 4, val}; UINT32* result; ACPI_OBJECT *obj; ACPI_BUFFER in = { sizeof(params), ¶ms }; ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; int res; 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; res = result[1]; if (res == 0 && retval != NULL) *retval = result[2]; acpi_hp_free_buffer(&out); return (res); } 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 has_enums = 0; int valuebase = 0; char string_buffer[255]; 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); } /* Check if first 6 bytes matches our expectations. */ if (obj->Package.Count < 8 || 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[7].Type != ACPI_TYPE_INTEGER) { acpi_hp_free_buffer(&out); return (-EINVAL); } /* Skip prerequisites and optionally array. */ valuebase = 8 + obj->Package.Elements[7].Integer.Value; if (obj->Package.Count <= valuebase) { acpi_hp_free_buffer(&out); return (-EINVAL); } if (obj->Package.Elements[valuebase].Type == ACPI_TYPE_INTEGER) valuebase += 1 + obj->Package.Elements[valuebase].Integer.Value; /* Check if we have value and enum. */ if (obj->Package.Count <= valuebase + 1 || obj->Package.Elements[valuebase].Type != ACPI_TYPE_STRING || obj->Package.Elements[valuebase+1].Type != ACPI_TYPE_INTEGER) { acpi_hp_free_buffer(&out); return (-EINVAL); } enumbase = valuebase + 1; if (obj->Package.Count <= valuebase + obj->Package.Elements[enumbase].Integer.Value) { acpi_hp_free_buffer(&out); return (-EINVAL); } if (detail & ACPI_HP_CMI_DETAIL_PATHS) { strlcat(outbuf, acpi_hp_get_string_from_object( &obj->Package.Elements[2], string_buffer, sizeof(string_buffer)), outsize); outlen += 48; while (strlen(outbuf) < outlen) strlcat(outbuf, " ", outsize); } strlcat(outbuf, acpi_hp_get_string_from_object( &obj->Package.Elements[0], string_buffer, sizeof(string_buffer)), outsize); outlen += 43; while (strlen(outbuf) < outlen) strlcat(outbuf, " ", outsize); strlcat(outbuf, acpi_hp_get_string_from_object( &obj->Package.Elements[valuebase], string_buffer, sizeof(string_buffer)), 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, sizeof(string_buffer)); 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 (rounddown((int)length, 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; char line[1025]; 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, sizeof(line), &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, sizeof(line), &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); } diff --git a/sys/dev/acpi_support/acpi_ibm.c b/sys/dev/acpi_support/acpi_ibm.c index 99df9efd2522..84b17dbe9bdd 100644 --- a/sys/dev/acpi_support/acpi_ibm.c +++ b/sys/dev/acpi_support/acpi_ibm.c @@ -1,1514 +1,1514 @@ /*- * Copyright (c) 2004 Takanori Watanabe * Copyright (c) 2005 Markus Brueffer * All rights reserved. * Copyright (c) 2020 Ali Abdallah * * 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 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 #include "acpi_if.h" #include #include #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 #define ACPI_IBM_METHOD_HANDLEREVENTS 14 #define ACPI_IBM_METHOD_MIC_LED 15 #define ACPI_IBM_METHOD_PRIVACYGUARD 16 /* 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_FANUNTHROTTLED (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_PRIVACYGUARD_GET "GSSS" #define IBM_NAME_PRIVACYGUARD_SET "SSSS" #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" /* Event Code */ #define IBM_EVENT_LCD_BACKLIGHT 0x03 #define IBM_EVENT_SUSPEND_TO_RAM 0x04 #define IBM_EVENT_BLUETOOTH 0x05 #define IBM_EVENT_SCREEN_EXPAND 0x07 #define IBM_EVENT_SUSPEND_TO_DISK 0x0c #define IBM_EVENT_BRIGHTNESS_UP 0x10 #define IBM_EVENT_BRIGHTNESS_DOWN 0x11 #define IBM_EVENT_THINKLIGHT 0x12 #define IBM_EVENT_ZOOM 0x14 #define IBM_EVENT_VOLUME_UP 0x15 #define IBM_EVENT_VOLUME_DOWN 0x16 #define IBM_EVENT_MUTE 0x17 #define IBM_EVENT_ACCESS_IBM_BUTTON 0x18 /* Device-specific register flags */ #define IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT 0x10000 #define IBM_FLAG_PRIVACYGUARD_ON 0x1 #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; /* Mic led handle */ ACPI_HANDLE mic_led_handle; int mic_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; unsigned int handler_events; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; }; static struct { char *name; int method; char *description; int flag_rdonly; } acpi_ibm_sysctls[] = { { .name = "events", .method = ACPI_IBM_METHOD_EVENTS, .description = "ACPI events enable", }, { .name = "eventmask", .method = ACPI_IBM_METHOD_EVENTMASK, .description = "ACPI eventmask", }, { .name = "hotkey", .method = ACPI_IBM_METHOD_HOTKEY, .description = "Key Status", .flag_rdonly = 1 }, { .name = "lcd_brightness", .method = ACPI_IBM_METHOD_BRIGHTNESS, .description = "LCD Brightness", }, { .name = "volume", .method = ACPI_IBM_METHOD_VOLUME, .description = "Volume", }, { .name = "mute", .method = ACPI_IBM_METHOD_MUTE, .description = "Mute", }, { .name = "thinklight", .method = ACPI_IBM_METHOD_THINKLIGHT, .description = "Thinklight enable", }, { .name = "bluetooth", .method = ACPI_IBM_METHOD_BLUETOOTH, .description = "Bluetooth enable", }, { .name = "wlan", .method = ACPI_IBM_METHOD_WLAN, .description = "WLAN enable", .flag_rdonly = 1 }, { .name = "fan_speed", .method = ACPI_IBM_METHOD_FANSPEED, .description = "Fan speed", .flag_rdonly = 1 }, { .name = "fan_level", .method = ACPI_IBM_METHOD_FANLEVEL, .description = "Fan level, 0-7 (recommended max), " "8 (unthrottled, full-speed)", }, { .name = "fan", .method = ACPI_IBM_METHOD_FANSTATUS, .description = "Fan enable", }, { .name = "mic_led", .method = ACPI_IBM_METHOD_MIC_LED, .description = "Mic led", }, { .name = "privacyguard", .method = ACPI_IBM_METHOD_PRIVACYGUARD, .description = "PrivacyGuard enable", }, { NULL, 0, NULL, 0 } }; /* * Per-model default list of event mask. */ #define ACPI_IBM_HKEY_RFKILL_MASK (1 << 4) #define ACPI_IBM_HKEY_DSWITCH_MASK (1 << 6) #define ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK (1 << 15) #define ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK (1 << 16) #define ACPI_IBM_HKEY_SEARCH_MASK (1 << 18) #define ACPI_IBM_HKEY_MICMUTE_MASK (1 << 26) #define ACPI_IBM_HKEY_SETTINGS_MASK (1 << 28) #define ACPI_IBM_HKEY_VIEWOPEN_MASK (1 << 30) #define ACPI_IBM_HKEY_VIEWALL_MASK (1 << 31) struct acpi_ibm_models { const char *maker; const char *product; uint32_t eventmask; } acpi_ibm_models[] = { { "LENOVO", "20BSCTO1WW", ACPI_IBM_HKEY_RFKILL_MASK | ACPI_IBM_HKEY_DSWITCH_MASK | ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK | ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK | ACPI_IBM_HKEY_SEARCH_MASK | ACPI_IBM_HKEY_MICMUTE_MASK | ACPI_IBM_HKEY_SETTINGS_MASK | ACPI_IBM_HKEY_VIEWOPEN_MASK | ACPI_IBM_HKEY_VIEWALL_MASK } }; ACPI_SERIAL_DECL(ibm, "ThinkPad ACPI 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 int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); static int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg); static int acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc); static ACPI_STATUS acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg); static ACPI_STATUS acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg); static int acpi_status_to_errno(ACPI_STATUS status); 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), DEVMETHOD_END }; 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", "LEN0068", "LEN0268", NULL}; static int acpi_status_to_errno(ACPI_STATUS status) { switch (status) { case AE_OK: return (0); case AE_BAD_PARAMETER: return (EINVAL); default: return (ENODEV); } } static void ibm_led(void *softc, int onoff) { struct acpi_ibm_softc *sc = 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_mic_led_set(struct acpi_ibm_softc *sc, int arg) { ACPI_OBJECT_LIST input; ACPI_OBJECT params[1]; ACPI_STATUS status; if (arg < 0 || arg > 1) return (EINVAL); if (sc->mic_led_handle) { params[0].Type = ACPI_TYPE_INTEGER; params[0].Integer.Value = 0; /* mic led: 0 off, 2 on */ if (arg == 1) params[0].Integer.Value = 2; input.Pointer = params; input.Count = 1; status = AcpiEvaluateObject(sc->handle, "MMTS", &input, NULL); if (ACPI_SUCCESS(status)) sc->mic_led_state = arg; return (status); } return (0); } static int acpi_ibm_probe(device_t dev) { int rv; if (acpi_disabled("ibm") || device_get_unit(dev) != 0) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids, NULL); if (rv <= 0) device_set_desc(dev, "ThinkPad ACPI Extras"); return (rv); } static int acpi_ibm_attach(device_t dev) { int i; int hkey; struct acpi_ibm_softc *sc; char *maker, *product; ACPI_OBJECT_LIST input; ACPI_OBJECT params[1]; ACPI_OBJECT out_obj; ACPI_BUFFER result; 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_UINT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "initialmask", CTLFLAG_RD, &sc->events_initialmask, 0, "Initial eventmask"); if (ACPI_SUCCESS (acpi_GetInteger(sc->handle, "MHKV", &hkey))) { device_printf(dev, "Firmware version is 0x%X\n", hkey); switch (hkey >> 8) { case 1: /* 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; break; case 2: result.Length = sizeof(out_obj); result.Pointer = &out_obj; params[0].Type = ACPI_TYPE_INTEGER; params[0].Integer.Value = 1; input.Pointer = params; input.Count = 1; sc->events_availmask = 0xffffffff; if (ACPI_SUCCESS(AcpiEvaluateObject (sc->handle, IBM_NAME_EVENTS_AVAILMASK, &input, &result))) sc->events_availmask = out_obj.Integer.Value; break; default: device_printf(dev, "Unknown firmware version 0x%x\n", hkey); break; } } else sc->events_availmask = 0xffffffff; SYSCTL_ADD_UINT(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; if (acpi_ibm_sysctls[i].flag_rdonly != 0) { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_ibm_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, i, acpi_ibm_sysctl, "I", acpi_ibm_sysctls[i].description); } else { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_ibm_sysctls[i].name, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 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_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, acpi_ibm_thermal_sysctl, "I", "Thermal zones"); } /* Hook up handlerevents node */ if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "handlerevents", - CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, acpi_ibm_handlerevents_sysctl, "I", "devd(8) events handled by acpi_ibm"); } /* 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 ? 1 : 0)); /* Enable per-model events. */ maker = kern_getenv("smbios.system.maker"); product = kern_getenv("smbios.system.product"); if (maker == NULL || product == NULL) goto nosmbios; for (i = 0; i < nitems(acpi_ibm_models); i++) { if (strcmp(maker, acpi_ibm_models[i].maker) == 0 && strcmp(product, acpi_ibm_models[i].product) == 0) { ACPI_SERIAL_BEGIN(ibm); acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, acpi_ibm_models[i].eventmask); ACPI_SERIAL_END(ibm); } } nosmbios: freeenv(maker); freeenv(product); /* Enable events by default. */ ACPI_SERIAL_BEGIN(ibm); acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 1); ACPI_SERIAL_END(ibm); 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; val = acpi_ibm_sysctl_get(sc, i); if (acpi_ibm_sysctls[i].flag_rdonly != 0) continue; acpi_ibm_sysctl_set(sc, i, val); } ACPI_SERIAL_END(ibm); /* The mic led does not turn back on when sysctl_set is called in the above loop */ acpi_ibm_mic_led_set(sc, sc->mic_led_state); 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); if (val_ec & IBM_EC_MASK_FANUNTHROTTLED) val = 8; else 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; case ACPI_IBM_METHOD_MIC_LED: if (sc->mic_led_handle) return sc->mic_led_state; else val = -1; break; case ACPI_IBM_METHOD_PRIVACYGUARD: val = acpi_ibm_privacyguard_get(sc); break; } return (val); } static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) { int val; UINT64 val_ec; 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: return acpi_ibm_brightness_set(sc, arg); break; case ACPI_IBM_METHOD_VOLUME: return acpi_ibm_volume_set(sc, arg); break; case ACPI_IBM_METHOD_MUTE: return acpi_ibm_mute_set(sc, arg); break; case ACPI_IBM_METHOD_MIC_LED: return acpi_ibm_mic_led_set(sc, arg); break; case ACPI_IBM_METHOD_THINKLIGHT: return acpi_ibm_thinklight_set(sc, arg); break; case ACPI_IBM_METHOD_BLUETOOTH: return acpi_ibm_bluetooth_set(sc, arg); break; case ACPI_IBM_METHOD_PRIVACYGUARD: return (acpi_status_to_errno(acpi_ibm_privacyguard_set(sc, arg))); break; case ACPI_IBM_METHOD_FANLEVEL: if (arg < 0 || arg > 8) return (EINVAL); if (!sc->fan_handle) { /* Read the current fan status. */ ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); val = val_ec & ~(IBM_EC_MASK_FANLEVEL | IBM_EC_MASK_FANUNTHROTTLED); if (arg == 8) /* Full speed, set the unthrottled bit. */ val |= 7 | IBM_EC_MASK_FANUNTHROTTLED; else val |= arg; return (ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val, 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: 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 already checked before */ return (TRUE); case ACPI_IBM_METHOD_MIC_LED: if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "MMTS", &sc->mic_led_handle))) { /* Turn off mic led by default */ acpi_ibm_mic_led_set(sc, 0); return (TRUE); } else sc->mic_led_handle = NULL; return (FALSE); 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); case ACPI_IBM_METHOD_HANDLEREVENTS: return (TRUE); case ACPI_IBM_METHOD_PRIVACYGUARD: return (acpi_ibm_privacyguard_get(sc) != -1); } 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] - 2731 + 5) / 10; } error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); ACPI_SERIAL_END(ibm); return (error); } static int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_ibm_softc *sc; int error = 0; struct sbuf sb; char *cp, *ep; int l, val; unsigned int handler_events; char temp[128]; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_ibm_softc *)oidp->oid_arg1; if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) return (ENOMEM); ACPI_SERIAL_BEGIN(ibm); /* Get old values if this is a get request. */ if (req->newptr == NULL) { for (int i = 0; i < 8 * sizeof(sc->handler_events); i++) if (sc->handler_events & (1 << i)) sbuf_printf(&sb, "0x%02x ", i + 1); if (sbuf_len(&sb) == 0) sbuf_printf(&sb, "NONE"); } sbuf_trim(&sb); sbuf_finish(&sb); strlcpy(temp, sbuf_data(&sb), sizeof(temp)); sbuf_delete(&sb); error = sysctl_handle_string(oidp, temp, sizeof(temp), req); /* Check for error or no change */ if (error != 0 || req->newptr == NULL) goto out; /* If the user is setting a string, parse it. */ handler_events = 0; cp = temp; while (*cp) { if (isspace(*cp)) { cp++; continue; } ep = cp; while (*ep && !isspace(*ep)) ep++; l = ep - cp; if (l == 0) break; if (strncmp(cp, "NONE", 4) == 0) { cp = ep; continue; } if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x')) val = strtoul(cp, &ep, 16); else val = strtoul(cp, &ep, 10); if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) { cp[l] = '\0'; device_printf(sc->dev, "invalid event code: %s\n", cp); error = EINVAL; goto out; } handler_events |= 1 << (val - 1); cp = ep; } sc->handler_events = handler_events; out: ACPI_SERIAL_END(ibm); return (error); } static int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, 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); if (arg < 0 || arg > 7) return (EINVAL); /* Read the current brightness */ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); if (ACPI_FAILURE(status)) return (status); if (sc->cmos_handle) { 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)) { /* Record the last value */ if (i != val) { ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, i - step, 1); } return (status); } } } return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); } static int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg) { int val; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(ibm); 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); } static int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg) { ACPI_OBJECT Arg; ACPI_OBJECT_LIST Args; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(ibm); 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); } return (0); } /* * Helper function to make a get or set ACPI call to the PrivacyGuard handle. * Only meant to be used internally by the get/set functions below. */ static ACPI_STATUS acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg) { ACPI_OBJECT Arg; ACPI_OBJECT_LIST Args; ACPI_STATUS status; ACPI_OBJECT out_obj; ACPI_BUFFER result; Arg.Type = ACPI_TYPE_INTEGER; Arg.Integer.Value = (write ? *arg : 0); Args.Count = 1; Args.Pointer = &Arg; result.Length = sizeof(out_obj); result.Pointer = &out_obj; status = AcpiEvaluateObject(sc->handle, (write ? IBM_NAME_PRIVACYGUARD_SET : IBM_NAME_PRIVACYGUARD_GET), &Args, &result); if (ACPI_SUCCESS(status) && !write) *arg = out_obj.Integer.Value; return (status); } /* * Returns -1 if the device is not present. */ static int acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc) { ACPI_STATUS status; int val; status = acpi_ibm_privacyguard_acpi_call(sc, false, &val); if (ACPI_SUCCESS(status) && (val & IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT)) return (val & IBM_FLAG_PRIVACYGUARD_ON); return (-1); } static ACPI_STATUS acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg) { if (arg < 0 || arg > 1) return (AE_BAD_PARAMETER); return (acpi_ibm_privacyguard_acpi_call(sc, true, &arg)); } static int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, 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); if (arg < 0 || arg > 14) return (EINVAL); /* Read the current volume */ 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)) { /* Record the last value */ if (i != val) { val_ec = i - step + (val_ec & (~IBM_EC_MASK_VOL)); ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); } return (status); } } } val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL)); return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); } static int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg) { UINT64 val_ec; ACPI_OBJECT Arg; ACPI_OBJECT_LIST Args; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(ibm); 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) { 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)) return (status); } val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE); return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); } static void acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg) { int val; UINT64 val_ec; ACPI_STATUS status; ACPI_SERIAL_BEGIN(ibm); switch (arg) { case IBM_EVENT_SUSPEND_TO_RAM: power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); break; case IBM_EVENT_BLUETOOTH: acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0)); break; case IBM_EVENT_BRIGHTNESS_UP: case IBM_EVENT_BRIGHTNESS_DOWN: /* Read the current brightness */ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); if (ACPI_FAILURE(status)) return; val = val_ec & IBM_EC_MASK_BRI; val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1; acpi_ibm_brightness_set(sc, val); break; case IBM_EVENT_THINKLIGHT: acpi_ibm_thinklight_set(sc, (sc->light_val == 0)); break; case IBM_EVENT_VOLUME_UP: case IBM_EVENT_VOLUME_DOWN: /* Read the current volume */ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); if (ACPI_FAILURE(status)) return; val = val_ec & IBM_EC_MASK_VOL; val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1; acpi_ibm_volume_set(sc, val); break; case IBM_EVENT_MUTE: /* Read the current value */ status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); if (ACPI_FAILURE(status)) return; val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); acpi_ibm_mute_set(sc, (val == 0)); break; default: break; } ACPI_SERIAL_END(ibm); } 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; } /* Execute event handler */ if (sc->handler_events & (1 << (arg - 1))) acpi_ibm_eventhandler(sc, (arg & 0xff)); /* Notify devd(8) */ acpi_UserNotify("IBM", h, (arg & 0xff)); break; default: break; } } } diff --git a/sys/dev/acpi_support/acpi_panasonic.c b/sys/dev/acpi_support/acpi_panasonic.c index 2dd5bf19d7d9..9c32ab9fad21 100644 --- a/sys/dev/acpi_support/acpi_panasonic.c +++ b/sys/dev/acpi_support/acpi_panasonic.c @@ -1,522 +1,522 @@ /*- * Copyright (c) 2003 OGAWA Takaya * Copyright (c) 2004 TAKAHASHI Yoshihiro * 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 #include #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("Panasonic") /* Debug */ #undef ACPI_PANASONIC_DEBUG /* Operations */ #define HKEY_SET 0 #define HKEY_GET 1 /* Functions */ #define HKEY_REG_LCD_BRIGHTNESS_MAX_AC 0x02 #define HKEY_REG_LCD_BRIGHTNESS_MIN_AC 0x03 #define HKEY_REG_LCD_BRIGHTNESS_AC 0x04 #define HKEY_REG_LCD_BRIGHTNESS_MAX_DC 0x05 #define HKEY_REG_LCD_BRIGHTNESS_MIN_DC 0x06 #define HKEY_REG_LCD_BRIGHTNESS_DC 0x07 #define HKEY_REG_SOUND_MUTE 0x08 /* Field definitions */ #define HKEY_LCD_BRIGHTNESS_BITS 4 #define HKEY_LCD_BRIGHTNESS_DIV ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1) struct acpi_panasonic_softc { device_t dev; ACPI_HANDLE handle; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; eventhandler_tag power_evh; }; /* Prototype for HKEY functions for getting/setting a value. */ typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *); static int acpi_panasonic_probe(device_t dev); static int acpi_panasonic_attach(device_t dev); static int acpi_panasonic_detach(device_t dev); static int acpi_panasonic_shutdown(device_t dev); static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS); static UINT64 acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index); static void acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index, UINT64 val); static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, UINT32 *arg); static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, UINT32 key); static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context); static void acpi_panasonic_power_profile(void *arg); static hkey_fn_t hkey_lcd_brightness_max; static hkey_fn_t hkey_lcd_brightness_min; static hkey_fn_t hkey_lcd_brightness; static hkey_fn_t hkey_sound_mute; ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras"); /* Table of sysctl names and HKEY functions to call. */ static struct { char *name; hkey_fn_t *handler; } sysctl_table[] = { /* name, handler */ {"lcd_brightness_max", hkey_lcd_brightness_max}, {"lcd_brightness_min", hkey_lcd_brightness_min}, {"lcd_brightness", hkey_lcd_brightness}, {"sound_mute", hkey_sound_mute}, {NULL, NULL} }; static device_method_t acpi_panasonic_methods[] = { DEVMETHOD(device_probe, acpi_panasonic_probe), DEVMETHOD(device_attach, acpi_panasonic_attach), DEVMETHOD(device_detach, acpi_panasonic_detach), DEVMETHOD(device_shutdown, acpi_panasonic_shutdown), DEVMETHOD_END }; static driver_t acpi_panasonic_driver = { "acpi_panasonic", acpi_panasonic_methods, sizeof(struct acpi_panasonic_softc), }; static devclass_t acpi_panasonic_devclass; DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, acpi_panasonic_devclass, 0, 0); MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1); static int acpi_panasonic_probe(device_t dev) { static char *mat_ids[] = { "MAT0019", NULL }; int rv; if (acpi_disabled("panasonic") || device_get_unit(dev) != 0) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids, NULL); if (rv <= 0) device_set_desc(dev, "Panasonic Notebook Hotkeys"); return (rv); } static int acpi_panasonic_attach(device_t dev) { struct acpi_panasonic_softc *sc; struct acpi_softc *acpi_sc; ACPI_STATUS status; int i; sc = device_get_softc(dev); sc->dev = dev; sc->handle = acpi_get_handle(dev); acpi_sc = acpi_device_get_parent_softc(dev); /* Build sysctl tree */ sysctl_ctx_init(&sc->sysctl_ctx); sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, "panasonic", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); for (i = 0; sysctl_table[i].name != NULL; i++) { SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, sysctl_table[i].name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | - CTLFLAG_NEEDGIANT, sc, i, acpi_panasonic_sysctl, "I", ""); + CTLFLAG_MPSAFE, sc, i, acpi_panasonic_sysctl, "I", ""); } #if 0 /* Activate hotkeys */ status = AcpiEvaluateObject(sc->handle, "", NULL, NULL); if (ACPI_FAILURE(status)) { device_printf(dev, "enable FN keys failed\n"); sysctl_ctx_free(&sc->sysctl_ctx); return (ENXIO); } #endif /* Handle notifies */ status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_panasonic_notify, sc); if (ACPI_FAILURE(status)) { device_printf(dev, "couldn't install notify handler - %s\n", AcpiFormatException(status)); sysctl_ctx_free(&sc->sysctl_ctx); return (ENXIO); } /* Install power profile event handler */ sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change, acpi_panasonic_power_profile, sc->handle, 0); return (0); } static int acpi_panasonic_detach(device_t dev) { struct acpi_panasonic_softc *sc; sc = device_get_softc(dev); /* Remove power profile event handler */ EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh); /* Remove notify handler */ AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_panasonic_notify); /* Free sysctl tree */ sysctl_ctx_free(&sc->sysctl_ctx); return (0); } static int acpi_panasonic_shutdown(device_t dev) { struct acpi_panasonic_softc *sc; int mute; /* Mute the main audio during reboot to prevent static burst to speaker. */ sc = device_get_softc(dev); mute = 1; hkey_sound_mute(sc->handle, HKEY_SET, &mute); return (0); } static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_panasonic_softc *sc; UINT32 arg; int function, error; hkey_fn_t *handler; sc = (struct acpi_panasonic_softc *)oidp->oid_arg1; function = oidp->oid_arg2; handler = sysctl_table[function].handler; /* Get the current value from the appropriate function. */ ACPI_SERIAL_BEGIN(panasonic); error = handler(sc->handle, HKEY_GET, &arg); if (error != 0) goto out; /* Send the current value to the user and return if no new value. */ error = sysctl_handle_int(oidp, &arg, 0, req); if (error != 0 || req->newptr == NULL) goto out; /* Set the new value via the appropriate function. */ error = handler(sc->handle, HKEY_SET, &arg); out: ACPI_SERIAL_END(panasonic); return (error); } static UINT64 acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index) { ACPI_BUFFER buf; ACPI_OBJECT *res; UINT64 ret; ACPI_SERIAL_ASSERT(panasonic); ret = -1; buf.Length = ACPI_ALLOCATE_BUFFER; buf.Pointer = NULL; AcpiEvaluateObject(h, "SINF", NULL, &buf); res = (ACPI_OBJECT *)buf.Pointer; if (res->Type == ACPI_TYPE_PACKAGE) ret = res->Package.Elements[index].Integer.Value; AcpiOsFree(buf.Pointer); return (ret); } static void acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index, UINT64 val) { ACPI_OBJECT_LIST args; ACPI_OBJECT obj[2]; ACPI_SERIAL_ASSERT(panasonic); obj[0].Type = ACPI_TYPE_INTEGER; obj[0].Integer.Value = index; obj[1].Type = ACPI_TYPE_INTEGER; obj[1].Integer.Value = val; args.Count = 2; args.Pointer = obj; AcpiEvaluateObject(h, "SSET", &args, NULL); } static int hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val) { int reg; ACPI_SERIAL_ASSERT(panasonic); reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC; switch (op) { case HKEY_SET: return (EPERM); break; case HKEY_GET: *val = acpi_panasonic_sinf(h, reg); break; } return (0); } static int hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val) { int reg; ACPI_SERIAL_ASSERT(panasonic); reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC; switch (op) { case HKEY_SET: return (EPERM); break; case HKEY_GET: *val = acpi_panasonic_sinf(h, reg); break; } return (0); } static int hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) { int reg; UINT32 max, min; reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC; ACPI_SERIAL_ASSERT(panasonic); switch (op) { case HKEY_SET: hkey_lcd_brightness_max(h, HKEY_GET, &max); hkey_lcd_brightness_min(h, HKEY_GET, &min); if (*val < min || *val > max) return (EINVAL); acpi_panasonic_sset(h, reg, *val); break; case HKEY_GET: *val = acpi_panasonic_sinf(h, reg); break; } return (0); } static int hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) { ACPI_SERIAL_ASSERT(panasonic); switch (op) { case HKEY_SET: if (*val != 0 && *val != 1) return (EINVAL); acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val); break; case HKEY_GET: *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE); break; } return (0); } static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, UINT32 *arg) { ACPI_BUFFER buf; ACPI_OBJECT *res; UINT64 val; int status; ACPI_SERIAL_ASSERT(panasonic); status = ENXIO; buf.Length = ACPI_ALLOCATE_BUFFER; buf.Pointer = NULL; AcpiEvaluateObject(h, "HINF", NULL, &buf); res = (ACPI_OBJECT *)buf.Pointer; if (res->Type != ACPI_TYPE_INTEGER) { device_printf(sc->dev, "HINF returned non-integer\n"); goto end; } val = res->Integer.Value; #ifdef ACPI_PANASONIC_DEBUG device_printf(sc->dev, "%s button Fn+F%d\n", (val & 0x80) ? "Pressed" : "Released", (int)(val & 0x7f)); #endif if ((val & 0x7f) > 0 && (val & 0x7f) < 11) { *arg = val; status = 0; } end: if (buf.Pointer) AcpiOsFree(buf.Pointer); return (status); } static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, UINT32 key) { struct acpi_softc *acpi_sc; int arg, max, min; acpi_sc = acpi_device_get_parent_softc(sc->dev); ACPI_SERIAL_ASSERT(panasonic); switch (key) { case 1: /* Decrease LCD brightness. */ hkey_lcd_brightness_max(h, HKEY_GET, &max); hkey_lcd_brightness_min(h, HKEY_GET, &min); hkey_lcd_brightness(h, HKEY_GET, &arg); arg -= max / HKEY_LCD_BRIGHTNESS_DIV; if (arg < min) arg = min; else if (arg > max) arg = max; hkey_lcd_brightness(h, HKEY_SET, &arg); break; case 2: /* Increase LCD brightness. */ hkey_lcd_brightness_max(h, HKEY_GET, &max); hkey_lcd_brightness_min(h, HKEY_GET, &min); hkey_lcd_brightness(h, HKEY_GET, &arg); arg += max / HKEY_LCD_BRIGHTNESS_DIV; if (arg < min) arg = min; else if (arg > max) arg = max; hkey_lcd_brightness(h, HKEY_SET, &arg); break; case 4: /* Toggle sound mute. */ hkey_sound_mute(h, HKEY_GET, &arg); if (arg) arg = 0; else arg = 1; hkey_sound_mute(h, HKEY_SET, &arg); break; case 7: /* Suspend. */ acpi_event_sleep_button_sleep(acpi_sc); break; } } static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_panasonic_softc *sc; UINT32 key = 0; sc = (struct acpi_panasonic_softc *)context; switch (notify) { case 0x80: ACPI_SERIAL_BEGIN(panasonic); if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { acpi_panasonic_hkey_action(sc, h, key); acpi_UserNotify("Panasonic", h, (uint8_t)key); } ACPI_SERIAL_END(panasonic); break; case 0x81: if (!bootverbose) break; /* FALLTHROUGH */ default: device_printf(sc->dev, "unknown notify: %#x\n", notify); break; } } static void acpi_panasonic_power_profile(void *arg) { ACPI_HANDLE handle; UINT32 brightness; handle = (ACPI_HANDLE)arg; /* Reset current brightness according to new power state. */ ACPI_SERIAL_BEGIN(panasonic); hkey_lcd_brightness(handle, HKEY_GET, &brightness); hkey_lcd_brightness(handle, HKEY_SET, &brightness); ACPI_SERIAL_END(panasonic); } diff --git a/sys/dev/acpi_support/acpi_rapidstart.c b/sys/dev/acpi_support/acpi_rapidstart.c index 0724abdefcac..eb47f347785f 100644 --- a/sys/dev/acpi_support/acpi_rapidstart.c +++ b/sys/dev/acpi_support/acpi_rapidstart.c @@ -1,142 +1,142 @@ /*- * Copyright (c) 2013 Takanori Watanabe * 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 "acpi_if.h" #include #include #include static int sysctl_acpi_rapidstart_gen_handler(SYSCTL_HANDLER_ARGS); static struct acpi_rapidstart_name_list { char *nodename; char *getmethod; char *setmethod; char *comment; } acpi_rapidstart_oids[] ={ {"ffs","GFFS","SFFS","Flash Fast Store Flag"}, {"ftv","GFTV","SFTV","Time value"}, {NULL, NULL, NULL, NULL} }; struct acpi_rapidstart_softc { struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; }; static char *rapidstart_ids[] = {"INT3392", NULL}; static int acpi_rapidstart_probe(device_t dev) { int rv; if (acpi_disabled("rapidstart") || device_get_unit(dev) != 0) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, rapidstart_ids, NULL); if (rv <= 0) device_set_desc(dev, "Intel Rapid Start ACPI device"); return (rv); } static int acpi_rapidstart_attach(device_t dev) { struct acpi_rapidstart_softc *sc; int i; sc = device_get_softc(dev); sc->sysctl_ctx = device_get_sysctl_ctx(dev); sc->sysctl_tree = device_get_sysctl_tree(dev); for (i = 0 ; acpi_rapidstart_oids[i].nodename != NULL; i++){ if (acpi_rapidstart_oids[i].setmethod != NULL) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), i, acpi_rapidstart_oids[i].nodename, - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, i, sysctl_acpi_rapidstart_gen_handler, "I", acpi_rapidstart_oids[i].comment); } else { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), i, acpi_rapidstart_oids[i].nodename, - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, i, sysctl_acpi_rapidstart_gen_handler, "I", acpi_rapidstart_oids[i].comment); } } return (0); } static int sysctl_acpi_rapidstart_gen_handler(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; int function = oidp->oid_arg2; int error = 0, val; acpi_GetInteger(acpi_get_handle(dev), acpi_rapidstart_oids[function].getmethod, &val); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr || !acpi_rapidstart_oids[function].setmethod) return (error); acpi_SetInteger(acpi_get_handle(dev), acpi_rapidstart_oids[function].setmethod, val); return (0); } static device_method_t acpi_rapidstart_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_rapidstart_probe), DEVMETHOD(device_attach, acpi_rapidstart_attach), DEVMETHOD_END }; static driver_t acpi_rapidstart_driver = { "acpi_rapidstart", acpi_rapidstart_methods, sizeof(struct acpi_rapidstart_softc), }; static devclass_t acpi_rapidstart_devclass; DRIVER_MODULE(acpi_rapidstart, acpi, acpi_rapidstart_driver, acpi_rapidstart_devclass, 0, 0); MODULE_DEPEND(acpi_rapidstart, acpi, 1, 1, 1); diff --git a/sys/dev/acpi_support/acpi_sony.c b/sys/dev/acpi_support/acpi_sony.c index 0cfb312b552f..724f04a36301 100644 --- a/sys/dev/acpi_support/acpi_sony.c +++ b/sys/dev/acpi_support/acpi_sony.c @@ -1,191 +1,191 @@ /*- * Copyright (c) 2004 Takanori Watanabe * 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 "acpi_if.h" #include #include #include #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("Sony") #define ACPI_SONY_GET_PID "GPID" /* * SNY5001 * This is the ACPI handle for the "Sony Notebook Control" driver under * Windows. * It provides several methods within the ACPI namespace, including: * [GS]BRT [GS]PBR [GS]CTR [GS]PCR [GS]CMI [CDPW GCDP]? GWDP PWAK PWRN * * SNY6001 * This is the ACPI handle for the "Sony Programmable I/O" driver under * Windows. * It is not yet supported by this driver, but provides control over the * power to the bluetooth, built-in camera and HSDPA modem devices in some * laptops, and also allows some control of the fan speed. */ struct acpi_sony_softc { int pid; }; static struct acpi_sony_name_list { char *nodename; char *getmethod; char *setmethod; char *comment; } acpi_sony_oids[] = { { "brightness", "GBRT", "SBRT", "Display Brightness"}, { "brightness_default", "GPBR", "SPBR", "Default Display Brightness"}, { "contrast", "GCTR", "SCTR", "Display Contrast"}, { "bass_gain", "GMGB", "SMGB", "Multimedia Bass Gain"}, { "pcr", "GPCR", "SPCR", "???"}, #if 0 { "cmi", "GCMI", "SCMI", "???"}, #endif { "wdp", "GWDP", NULL, "???"}, { "cdp", "GCDP", "CDPW", "CD Power"}, /*shares [\GL03]&0x8 flag*/ { "azp", "GAZP", "AZPW", "Audio Power"}, { "lnp", "GLNP", "LNPW", "LAN Power"}, { NULL, NULL, NULL } }; static int acpi_sony_probe(device_t dev); static int acpi_sony_attach(device_t dev); static int acpi_sony_detach(device_t dev); static int sysctl_acpi_sony_gen_handler(SYSCTL_HANDLER_ARGS); static device_method_t acpi_sony_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_sony_probe), DEVMETHOD(device_attach, acpi_sony_attach), DEVMETHOD(device_detach, acpi_sony_detach), DEVMETHOD_END }; static driver_t acpi_sony_driver = { "acpi_sony", acpi_sony_methods, sizeof(struct acpi_sony_softc), }; static devclass_t acpi_sony_devclass; DRIVER_MODULE(acpi_sony, acpi, acpi_sony_driver, acpi_sony_devclass, 0, 0); MODULE_DEPEND(acpi_sony, acpi, 1, 1, 1); static char *sny_id[] = {"SNY5001", NULL}; static int acpi_sony_probe(device_t dev) { int ret; ret = ACPI_ID_PROBE(device_get_parent(dev), dev, sny_id, NULL); if (ret <= 0) { device_set_desc(dev, "Sony notebook controller"); } return (ret); } static int acpi_sony_attach(device_t dev) { struct acpi_sony_softc *sc; int i; sc = device_get_softc(dev); acpi_GetInteger(acpi_get_handle(dev), ACPI_SONY_GET_PID, &sc->pid); device_printf(dev, "PID %x\n", sc->pid); for (i = 0 ; acpi_sony_oids[i].nodename != NULL; i++) { if (acpi_sony_oids[i].setmethod != NULL) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), i, acpi_sony_oids[i].nodename , - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, i, sysctl_acpi_sony_gen_handler, "I", acpi_sony_oids[i].comment); } else { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), i, acpi_sony_oids[i].nodename , - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, i, sysctl_acpi_sony_gen_handler, "I", acpi_sony_oids[i].comment); } } return (0); } static int acpi_sony_detach(device_t dev) { return (0); } #if 0 static int acpi_sony_suspend(device_t dev) { struct acpi_sony_softc *sc = device_get_softc(dev); return (0); } static int acpi_sony_resume(device_t dev) { return (0); } #endif static int sysctl_acpi_sony_gen_handler(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; int function = oidp->oid_arg2; int error = 0, val; acpi_GetInteger(acpi_get_handle(dev), acpi_sony_oids[function].getmethod, &val); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr || !acpi_sony_oids[function].setmethod) return (error); acpi_SetInteger(acpi_get_handle(dev), acpi_sony_oids[function].setmethod, val); return (0); } diff --git a/sys/dev/acpi_support/acpi_toshiba.c b/sys/dev/acpi_support/acpi_toshiba.c index a9b537edf993..c5233579d560 100644 --- a/sys/dev/acpi_support/acpi_toshiba.c +++ b/sys/dev/acpi_support/acpi_toshiba.c @@ -1,570 +1,570 @@ /*- * Copyright (c) 2003 Hiroyuki Aizu * 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 #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("Toshiba") /* * Toshiba HCI interface definitions * * HCI is Toshiba's "Hardware Control Interface" which is supposed to * be uniform across all their models. Ideally we would just call * dedicated ACPI methods instead of using this primitive interface. * However, the ACPI methods seem to be incomplete in some areas (for * example they allow setting, but not reading, the LCD brightness * value), so this is still useful. */ #define METHOD_HCI "GHCI" #define METHOD_HCI_ENABLE "ENAB" #define METHOD_VIDEO "DSSX" /* Operations */ #define HCI_SET 0xFF00 #define HCI_GET 0xFE00 /* Return codes */ #define HCI_SUCCESS 0x0000 #define HCI_FAILURE 0x1000 #define HCI_NOT_SUPPORTED 0x8000 #define HCI_EMPTY 0x8C00 /* Functions */ #define HCI_REG_LCD_BACKLIGHT 0x0002 #define HCI_REG_FAN 0x0004 #define HCI_REG_SYSTEM_EVENT 0x0016 #define HCI_REG_VIDEO_OUTPUT 0x001C #define HCI_REG_HOTKEY_EVENT 0x001E #define HCI_REG_LCD_BRIGHTNESS 0x002A #define HCI_REG_CPU_SPEED 0x0032 /* Field definitions */ #define HCI_FAN_SHIFT 7 #define HCI_LCD_BRIGHTNESS_BITS 3 #define HCI_LCD_BRIGHTNESS_SHIFT (16 - HCI_LCD_BRIGHTNESS_BITS) #define HCI_LCD_BRIGHTNESS_MAX ((1 << HCI_LCD_BRIGHTNESS_BITS) - 1) #define HCI_VIDEO_OUTPUT_FLAG 0x0100 #define HCI_VIDEO_OUTPUT_LCD 0x1 #define HCI_VIDEO_OUTPUT_CRT 0x2 #define HCI_VIDEO_OUTPUT_TV 0x4 #define HCI_CPU_SPEED_BITS 3 #define HCI_CPU_SPEED_SHIFT (16 - HCI_CPU_SPEED_BITS) #define HCI_CPU_SPEED_MAX ((1 << HCI_CPU_SPEED_BITS) - 1) /* Key press/release events. */ #define FN_F1_PRESS 0x013B #define FN_F1_RELEASE 0x01BB #define FN_F2_PRESS 0x013C #define FN_F2_RELEASE 0x01BC #define FN_F3_PRESS 0x013D #define FN_F3_RELEASE 0x01BD #define FN_F4_PRESS 0x013E #define FN_F4_RELEASE 0x01BE #define FN_F5_PRESS 0x013F #define FN_F5_RELEASE 0x01BF #define FN_F6_PRESS 0x0140 #define FN_F6_RELEASE 0x01C0 #define FN_F7_PRESS 0x0141 #define FN_F7_RELEASE 0x01C1 #define FN_F8_PRESS 0x0142 #define FN_F8_RELEASE 0x01C2 #define FN_F9_PRESS 0x0143 #define FN_F9_RELEASE 0x01C3 #define FN_BS_PRESS 0x010E #define FN_BS_RELEASE 0x018E #define FN_ESC_PRESS 0x0101 #define FN_ESC_RELEASE 0x0181 #define FN_KNJ_PRESS 0x0129 #define FN_KNJ_RELEASE 0x01A9 /* HCI register definitions. */ #define HCI_WORDS 6 /* Number of registers */ #define HCI_REG_AX 0 /* Operation, then return value */ #define HCI_REG_BX 1 /* Function */ #define HCI_REG_CX 2 /* Argument (in or out) */ #define HCI_REG_DX 3 /* Unused? */ #define HCI_REG_SI 4 /* Unused? */ #define HCI_REG_DI 5 /* Unused? */ struct acpi_toshiba_softc { device_t dev; ACPI_HANDLE handle; ACPI_HANDLE video_handle; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; }; /* Prototype for HCI functions for getting/setting a value. */ typedef int hci_fn_t(ACPI_HANDLE, int, UINT32 *); static int acpi_toshiba_probe(device_t dev); static int acpi_toshiba_attach(device_t dev); static int acpi_toshiba_detach(device_t dev); static int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS); static hci_fn_t hci_force_fan; static hci_fn_t hci_video_output; static hci_fn_t hci_lcd_brightness; static hci_fn_t hci_lcd_backlight; static hci_fn_t hci_cpu_speed; static int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg); static void hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key); static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context); static int acpi_toshiba_video_probe(device_t dev); static int acpi_toshiba_video_attach(device_t dev); ACPI_SERIAL_DECL(toshiba, "ACPI Toshiba Extras"); /* Table of sysctl names and HCI functions to call. */ static struct { char *name; hci_fn_t *handler; } sysctl_table[] = { /* name, handler */ {"force_fan", hci_force_fan}, {"video_output", hci_video_output}, {"lcd_brightness", hci_lcd_brightness}, {"lcd_backlight", hci_lcd_backlight}, {"cpu_speed", hci_cpu_speed}, {NULL, NULL} }; static device_method_t acpi_toshiba_methods[] = { DEVMETHOD(device_probe, acpi_toshiba_probe), DEVMETHOD(device_attach, acpi_toshiba_attach), DEVMETHOD(device_detach, acpi_toshiba_detach), DEVMETHOD_END }; static driver_t acpi_toshiba_driver = { "acpi_toshiba", acpi_toshiba_methods, sizeof(struct acpi_toshiba_softc), }; static devclass_t acpi_toshiba_devclass; DRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass, 0, 0); MODULE_DEPEND(acpi_toshiba, acpi, 1, 1, 1); static device_method_t acpi_toshiba_video_methods[] = { DEVMETHOD(device_probe, acpi_toshiba_video_probe), DEVMETHOD(device_attach, acpi_toshiba_video_attach), DEVMETHOD_END }; static driver_t acpi_toshiba_video_driver = { "acpi_toshiba_video", acpi_toshiba_video_methods, 0, }; static devclass_t acpi_toshiba_video_devclass; DRIVER_MODULE(acpi_toshiba_video, acpi, acpi_toshiba_video_driver, acpi_toshiba_video_devclass, 0, 0); MODULE_DEPEND(acpi_toshiba_video, acpi, 1, 1, 1); static int enable_fn_keys = 1; TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys); /* * HID Model * ------------------------------------- * TOS6200 Libretto L Series * Dynabook Satellite 2455 * Dynabook SS 3500 * TOS6207 Dynabook SS2110 Series * TOS6208 SPA40 */ static int acpi_toshiba_probe(device_t dev) { static char *tosh_ids[] = { "TOS6200", "TOS6207", "TOS6208", NULL }; int rv; if (acpi_disabled("toshiba") || device_get_unit(dev) != 0) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, tosh_ids, NULL); if (rv <= 0) device_set_desc(dev, "Toshiba HCI Extras"); return (rv); } static int acpi_toshiba_attach(device_t dev) { struct acpi_toshiba_softc *sc; struct acpi_softc *acpi_sc; ACPI_STATUS status; int i; sc = device_get_softc(dev); sc->dev = dev; sc->handle = acpi_get_handle(dev); acpi_sc = acpi_device_get_parent_softc(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, "toshiba", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); for (i = 0; sysctl_table[i].name != NULL; i++) { SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, sysctl_table[i].name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | - CTLFLAG_NEEDGIANT, sc, i, acpi_toshiba_sysctl, "I", ""); + CTLFLAG_MPSAFE, sc, i, acpi_toshiba_sysctl, "I", ""); } if (enable_fn_keys != 0) { status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE, NULL, NULL); if (ACPI_FAILURE(status)) { device_printf(dev, "enable FN keys failed\n"); sysctl_ctx_free(&sc->sysctl_ctx); return (ENXIO); } AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_toshiba_notify, sc); } return (0); } static int acpi_toshiba_detach(device_t dev) { struct acpi_toshiba_softc *sc; sc = device_get_softc(dev); if (enable_fn_keys != 0) { AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_toshiba_notify); } sysctl_ctx_free(&sc->sysctl_ctx); return (0); } static int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_toshiba_softc *sc; UINT32 arg; int function, error = 0; hci_fn_t *handler; sc = (struct acpi_toshiba_softc *)oidp->oid_arg1; function = oidp->oid_arg2; handler = sysctl_table[function].handler; /* Get the current value from the appropriate function. */ ACPI_SERIAL_BEGIN(toshiba); error = handler(sc->handle, HCI_GET, &arg); if (error != 0) goto out; /* Send the current value to the user and return if no new value. */ error = sysctl_handle_int(oidp, &arg, 0, req); if (error != 0 || req->newptr == NULL) goto out; /* Set the new value via the appropriate function. */ error = handler(sc->handle, HCI_SET, &arg); out: ACPI_SERIAL_END(toshiba); return (error); } static int hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state) { int ret; ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*state > 1) return (EINVAL); *state <<= HCI_FAN_SHIFT; } ret = hci_call(h, op, HCI_REG_FAN, state); if (ret == 0 && op == HCI_GET) *state >>= HCI_FAN_SHIFT; return (ret); } static int hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output) { int ret; ACPI_STATUS status; ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*video_output < 1 || *video_output > 7) return (EINVAL); if (h == NULL) return (ENXIO); *video_output |= HCI_VIDEO_OUTPUT_FLAG; status = acpi_SetInteger(h, METHOD_VIDEO, *video_output); if (ACPI_SUCCESS(status)) ret = 0; else ret = ENXIO; } else { ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output); if (ret == 0) *video_output &= 0xff; } return (ret); } static int hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness) { int ret; ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*brightness > HCI_LCD_BRIGHTNESS_MAX) return (EINVAL); *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT; } ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness); if (ret == 0 && op == HCI_GET) *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT; return (ret); } static int hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight) { ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*backlight > 1) return (EINVAL); } return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight)); } static int hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed) { int ret; ACPI_SERIAL_ASSERT(toshiba); if (op == HCI_SET) { if (*speed > HCI_CPU_SPEED_MAX) return (EINVAL); *speed <<= HCI_CPU_SPEED_SHIFT; } ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed); if (ret == 0 && op == HCI_GET) *speed >>= HCI_CPU_SPEED_SHIFT; return (ret); } static int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg) { ACPI_OBJECT_LIST args; ACPI_BUFFER results; ACPI_OBJECT obj[HCI_WORDS]; ACPI_OBJECT *res; int status, i, ret; ACPI_SERIAL_ASSERT(toshiba); status = ENXIO; for (i = 0; i < HCI_WORDS; i++) { obj[i].Type = ACPI_TYPE_INTEGER; obj[i].Integer.Value = 0; } obj[HCI_REG_AX].Integer.Value = op; obj[HCI_REG_BX].Integer.Value = function; if (op == HCI_SET) obj[HCI_REG_CX].Integer.Value = *arg; args.Count = HCI_WORDS; args.Pointer = obj; results.Pointer = NULL; results.Length = ACPI_ALLOCATE_BUFFER; if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results))) goto end; res = (ACPI_OBJECT *)results.Pointer; if (!ACPI_PKG_VALID(res, HCI_WORDS)) { printf("toshiba: invalid package!\n"); return (ENXIO); } acpi_PkgInt32(res, HCI_REG_AX, &ret); if (ret == HCI_SUCCESS) { if (op == HCI_GET) acpi_PkgInt32(res, HCI_REG_CX, arg); status = 0; } else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET && ret == HCI_NOT_SUPPORTED) { /* * Sometimes system events are disabled without us requesting * it. This workaround attempts to re-enable them. * * XXX This call probably shouldn't be recursive. Queueing * a task via AcpiOsQueueForExecution() might be better. */ i = 1; hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i); } end: if (results.Pointer != NULL) AcpiOsFree(results.Pointer); return (status); } /* * Perform a few actions based on the keypress. Users can extend this * functionality by reading the keystrokes we send to devd(8). */ static void hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key) { UINT32 arg; ACPI_SERIAL_ASSERT(toshiba); switch (key) { case FN_F6_RELEASE: /* Decrease LCD brightness. */ hci_lcd_brightness(h, HCI_GET, &arg); if (arg-- == 0) arg = 0; else hci_lcd_brightness(h, HCI_SET, &arg); break; case FN_F7_RELEASE: /* Increase LCD brightness. */ hci_lcd_brightness(h, HCI_GET, &arg); if (arg++ == 7) arg = 7; else hci_lcd_brightness(h, HCI_SET, &arg); break; case FN_F5_RELEASE: /* Cycle through video outputs. */ hci_video_output(h, HCI_GET, &arg); arg = (arg + 1) % 7; hci_video_output(sc->video_handle, HCI_SET, &arg); break; case FN_F8_RELEASE: /* Toggle LCD backlight. */ hci_lcd_backlight(h, HCI_GET, &arg); arg = (arg != 0) ? 0 : 1; hci_lcd_backlight(h, HCI_SET, &arg); break; case FN_ESC_RELEASE: /* Toggle forcing fan on. */ hci_force_fan(h, HCI_GET, &arg); arg = (arg != 0) ? 0 : 1; hci_force_fan(h, HCI_SET, &arg); break; } } static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_toshiba_softc *sc; UINT32 key; sc = (struct acpi_toshiba_softc *)context; if (notify == 0x80) { ACPI_SERIAL_BEGIN(toshiba); while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) { hci_key_action(sc, h, key); acpi_UserNotify("TOSHIBA", h, (uint8_t)key); } ACPI_SERIAL_END(toshiba); } else device_printf(sc->dev, "unknown notify: 0x%x\n", notify); } /* * Toshiba video pseudo-device to provide the DSSX method. * * HID Model * ------------------------------------- * TOS6201 Libretto L Series */ static int acpi_toshiba_video_probe(device_t dev) { static char *vid_ids[] = { "TOS6201", NULL }; int rv; if (acpi_disabled("toshiba") || device_get_unit(dev) != 0) return (ENXIO); device_quiet(dev); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, vid_ids, NULL); if (rv <= 0) device_set_desc(dev, "Toshiba Video"); return (rv); } static int acpi_toshiba_video_attach(device_t dev) { struct acpi_toshiba_softc *sc; sc = devclass_get_softc(acpi_toshiba_devclass, 0); if (sc == NULL) return (ENXIO); sc->video_handle = acpi_get_handle(dev); return (0); } diff --git a/sys/dev/acpi_support/atk0110.c b/sys/dev/acpi_support/atk0110.c index 202421120e60..3aac45624089 100644 --- a/sys/dev/acpi_support/atk0110.c +++ b/sys/dev/acpi_support/atk0110.c @@ -1,584 +1,584 @@ /* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */ /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */ /* * Copyright (c) 2009, 2010 Constantine A. Murenin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include /* * ASUSTeK AI Booster (ACPI ASOC ATK0110). * * This code was originally written for OpenBSD after the techniques * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's * acpi_aiboost.c were verified to be accurate on the actual hardware kindly * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9). * * -- Constantine A. Murenin */ #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("aibs"); ACPI_SERIAL_DECL(aibs, "aibs"); #define AIBS_MORE_SENSORS #define AIBS_VERBOSE #define AIBS_GROUP_SENSORS 0x06 #define AIBS_SENS_TYPE(x) (((x) >> 16) & 0xff) #define AIBS_SENS_TYPE_VOLT 2 #define AIBS_SENS_TYPE_TEMP 3 #define AIBS_SENS_TYPE_FAN 4 #define AIBS_SENS_TYPE_VOLT_NAME "volt" #define AIBS_SENS_TYPE_VOLT_TEMP "temp" #define AIBS_SENS_TYPE_VOLT_FAN "fan" struct aibs_sensor { ACPI_INTEGER v; ACPI_INTEGER i; ACPI_INTEGER l; ACPI_INTEGER h; int t; }; struct aibs_softc { device_t sc_dev; ACPI_HANDLE sc_ah; struct aibs_sensor *sc_asens_volt; struct aibs_sensor *sc_asens_temp; struct aibs_sensor *sc_asens_fan; struct aibs_sensor *sc_asens_all; struct sysctl_oid *sc_volt_sysctl; struct sysctl_oid *sc_temp_sysctl; struct sysctl_oid *sc_fan_sysctl; bool sc_ggrp_method; }; static int aibs_probe(device_t); static int aibs_attach(device_t); static int aibs_detach(device_t); static int aibs_sysctl(SYSCTL_HANDLER_ARGS); static int aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS); static int aibs_attach_ggrp(struct aibs_softc *); static int aibs_attach_sif(struct aibs_softc *, int); static device_method_t aibs_methods[] = { DEVMETHOD(device_probe, aibs_probe), DEVMETHOD(device_attach, aibs_attach), DEVMETHOD(device_detach, aibs_detach), { NULL, NULL } }; static driver_t aibs_driver = { "aibs", aibs_methods, sizeof(struct aibs_softc) }; static devclass_t aibs_devclass; DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL); MODULE_DEPEND(aibs, acpi, 1, 1, 1); static char* aibs_hids[] = { "ATK0110", NULL }; static int aibs_probe(device_t dev) { int rv; if (acpi_disabled("aibs")) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids, NULL); if (rv <= 0 ) device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)"); return (rv); } static int aibs_attach(device_t dev) { struct aibs_softc *sc = device_get_softc(dev); int err; sc->sc_dev = dev; sc->sc_ah = acpi_get_handle(dev); sc->sc_ggrp_method = false; err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT); if (err == 0) err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP); if (err == 0) err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN); if (err == 0) return (0); /* Clean up whatever was allocated earlier. */ if (sc->sc_volt_sysctl != NULL) sysctl_remove_oid(sc->sc_volt_sysctl, true, true); if (sc->sc_temp_sysctl != NULL) sysctl_remove_oid(sc->sc_temp_sysctl, true, true); if (sc->sc_fan_sysctl != NULL) sysctl_remove_oid(sc->sc_fan_sysctl, true, true); aibs_detach(dev); sc->sc_ggrp_method = true; err = aibs_attach_ggrp(sc); return (err); } static int aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o, struct aibs_sensor* sensor, const char ** descr) { int off; /* * Packages for the old and new methods are quite * similar except that the new package has two * new (unknown / unused) fields after the name field. */ if (sc->sc_ggrp_method) off = 4; else off = 2; if (o->Type != ACPI_TYPE_PACKAGE) { device_printf(sc->sc_dev, "sensor object is not a package: %i type\n", o->Type); return (ENXIO); } if (o[0].Package.Count != (off + 3) || o->Package.Elements[0].Type != ACPI_TYPE_INTEGER || o->Package.Elements[1].Type != ACPI_TYPE_STRING || o->Package.Elements[off].Type != ACPI_TYPE_INTEGER || o->Package.Elements[off + 1].Type != ACPI_TYPE_INTEGER || o->Package.Elements[off + 2].Type != ACPI_TYPE_INTEGER) { device_printf(sc->sc_dev, "unexpected package content\n"); return (ENXIO); } sensor->i = o->Package.Elements[0].Integer.Value; *descr = o->Package.Elements[1].String.Pointer; sensor->l = o->Package.Elements[off].Integer.Value; sensor->h = o->Package.Elements[off + 1].Integer.Value; /* For the new method the second value is a range size. */ if (sc->sc_ggrp_method) sensor->h += sensor->l; sensor->t = AIBS_SENS_TYPE(sensor->i); switch (sensor->t) { case AIBS_SENS_TYPE_VOLT: case AIBS_SENS_TYPE_TEMP: case AIBS_SENS_TYPE_FAN: return (0); default: device_printf(sc->sc_dev, "unknown sensor type 0x%x", sensor->t); return (ENXIO); } } static void aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so, const char *type_name, int idx, struct aibs_sensor *sensor, const char *descr) { char sysctl_name[8]; snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx); #ifdef AIBS_VERBOSE device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n", type_name[0], idx, (uintmax_t)sensor->i, descr, (intmax_t)sensor->l, (intmax_t)sensor->h); #endif SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev), SYSCTL_CHILDREN(so), idx, sysctl_name, - CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, (uintptr_t)sensor, + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, (uintptr_t)sensor, sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl, sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr); } static int aibs_attach_ggrp(struct aibs_softc *sc) { ACPI_STATUS s; ACPI_BUFFER buf; ACPI_HANDLE h; ACPI_OBJECT id; ACPI_OBJECT *bp; ACPI_OBJECT_LIST arg; int i; int t, v, f; int err; int *s_idx; const char *name; const char *descr; struct aibs_sensor *sensor; struct sysctl_oid **so; /* First see if GITM is available. */ s = AcpiGetHandle(sc->sc_ah, "GITM", &h); if (ACPI_FAILURE(s)) { if (bootverbose) device_printf(sc->sc_dev, "GITM not found\n"); return (ENXIO); } /* * Now call GGRP with the appropriate argument to list sensors. * The method lists different groups of entities depending on * the argument. */ id.Integer.Value = AIBS_GROUP_SENSORS; id.Type = ACPI_TYPE_INTEGER; arg.Count = 1; arg.Pointer = &id; buf.Length = ACPI_ALLOCATE_BUFFER; buf.Pointer = NULL; s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf, ACPI_TYPE_PACKAGE); if (ACPI_FAILURE(s)) { device_printf(sc->sc_dev, "GGRP not found\n"); return (ENXIO); } bp = buf.Pointer; sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count, M_DEVBUF, M_WAITOK | M_ZERO); v = t = f = 0; for (i = 0; i < bp->Package.Count; i++) { sensor = &sc->sc_asens_all[i]; err = aibs_add_sensor(sc, &bp->Package.Elements[i], sensor, &descr); if (err != 0) continue; switch (sensor->t) { case AIBS_SENS_TYPE_VOLT: name = "volt"; so = &sc->sc_volt_sysctl; s_idx = &v; break; case AIBS_SENS_TYPE_TEMP: name = "temp"; so = &sc->sc_temp_sysctl; s_idx = &t; break; case AIBS_SENS_TYPE_FAN: name = "fan"; so = &sc->sc_fan_sysctl; s_idx = &f; break; default: panic("add_sensor succeeded for unknown sensor type %d", sensor->t); } if (*so == NULL) { /* sysctl subtree for sensors of this type */ *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), sensor->t, name, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, NULL); } aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr); *s_idx += 1; } AcpiOsFree(buf.Pointer); return (0); } static int aibs_attach_sif(struct aibs_softc *sc, int st) { char name[] = "?SIF"; ACPI_STATUS s; ACPI_BUFFER b; ACPI_OBJECT *bp, *o; const char *node; struct aibs_sensor *as; struct sysctl_oid **so; int i, n; int err; switch (st) { case AIBS_SENS_TYPE_VOLT: node = "volt"; name[0] = 'V'; so = &sc->sc_volt_sysctl; break; case AIBS_SENS_TYPE_TEMP: node = "temp"; name[0] = 'T'; so = &sc->sc_temp_sysctl; break; case AIBS_SENS_TYPE_FAN: node = "fan"; name[0] = 'F'; so = &sc->sc_fan_sysctl; break; default: panic("Unsupported sensor type %d", st); } b.Length = ACPI_ALLOCATE_BUFFER; s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, ACPI_TYPE_PACKAGE); if (ACPI_FAILURE(s)) { device_printf(sc->sc_dev, "%s not found\n", name); return (ENXIO); } bp = b.Pointer; o = bp->Package.Elements; if (o[0].Type != ACPI_TYPE_INTEGER) { device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); AcpiOsFree(b.Pointer); return (ENXIO); } n = o[0].Integer.Value; if (bp->Package.Count - 1 < n) { device_printf(sc->sc_dev, "%s: invalid package\n", name); AcpiOsFree(b.Pointer); return (ENXIO); } else if (bp->Package.Count - 1 > n) { int on = n; #ifdef AIBS_MORE_SENSORS n = bp->Package.Count - 1; #endif device_printf(sc->sc_dev, "%s: malformed package: %i/%i" ", assume %i\n", name, on, bp->Package.Count - 1, n); } if (n < 1) { device_printf(sc->sc_dev, "%s: no members in the package\n", name); AcpiOsFree(b.Pointer); return (ENXIO); } as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO); switch (st) { case AIBS_SENS_TYPE_VOLT: sc->sc_asens_volt = as; break; case AIBS_SENS_TYPE_TEMP: sc->sc_asens_temp = as; break; case AIBS_SENS_TYPE_FAN: sc->sc_asens_fan = as; break; } /* sysctl subtree for sensors of this type */ *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st, node, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, NULL); for (i = 0, o++; i < n; i++, o++) { const char *descr; err = aibs_add_sensor(sc, o, &as[i], &descr); if (err == 0) aibs_sensor_added(sc, *so, node, i, &as[i], descr); } AcpiOsFree(b.Pointer); return (0); } static int aibs_detach(device_t dev) { struct aibs_softc *sc = device_get_softc(dev); if (sc->sc_asens_volt != NULL) free(sc->sc_asens_volt, M_DEVBUF); if (sc->sc_asens_temp != NULL) free(sc->sc_asens_temp, M_DEVBUF); if (sc->sc_asens_fan != NULL) free(sc->sc_asens_fan, M_DEVBUF); if (sc->sc_asens_all != NULL) free(sc->sc_asens_all, M_DEVBUF); return (0); } #ifdef AIBS_VERBOSE #define ddevice_printf(x...) device_printf(x) #else #define ddevice_printf(x...) #endif static int aibs_sysctl(SYSCTL_HANDLER_ARGS) { struct aibs_softc *sc = arg1; struct aibs_sensor *sensor = (void *)(intptr_t)arg2; int i = oidp->oid_number; ACPI_STATUS rs; ACPI_OBJECT p, *bp; ACPI_OBJECT_LIST mp; ACPI_BUFFER b; char *name; ACPI_INTEGER v, l, h; int so[3]; switch (sensor->t) { case AIBS_SENS_TYPE_VOLT: name = "RVLT"; break; case AIBS_SENS_TYPE_TEMP: name = "RTMP"; break; case AIBS_SENS_TYPE_FAN: name = "RFAN"; break; default: return (ENOENT); } l = sensor->l; h = sensor->h; p.Type = ACPI_TYPE_INTEGER; p.Integer.Value = sensor->i; mp.Count = 1; mp.Pointer = &p; b.Length = ACPI_ALLOCATE_BUFFER; ACPI_SERIAL_BEGIN(aibs); rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b, ACPI_TYPE_INTEGER); if (ACPI_FAILURE(rs)) { ddevice_printf(sc->sc_dev, "%s: %i: evaluation failed\n", name, i); ACPI_SERIAL_END(aibs); return (EIO); } bp = b.Pointer; v = bp->Integer.Value; AcpiOsFree(b.Pointer); ACPI_SERIAL_END(aibs); switch (sensor->t) { case AIBS_SENS_TYPE_VOLT: break; case AIBS_SENS_TYPE_TEMP: v += 2731; l += 2731; h += 2731; break; case AIBS_SENS_TYPE_FAN: break; } so[0] = v; so[1] = l; so[2] = h; return (sysctl_handle_opaque(oidp, &so, sizeof(so), req)); } static int aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS) { struct aibs_softc *sc = arg1; struct aibs_sensor *sensor = (void *)(intptr_t)arg2; ACPI_STATUS rs; ACPI_OBJECT p, *bp; ACPI_OBJECT_LIST arg; ACPI_BUFFER buf; ACPI_INTEGER v, l, h; int so[3]; uint32_t *ret; uint32_t cmd[3]; cmd[0] = sensor->i; cmd[1] = 0; cmd[2] = 0; p.Type = ACPI_TYPE_BUFFER; p.Buffer.Pointer = (void *)cmd; p.Buffer.Length = sizeof(cmd); arg.Count = 1; arg.Pointer = &p; buf.Pointer = NULL; buf.Length = ACPI_ALLOCATE_BUFFER; ACPI_SERIAL_BEGIN(aibs); rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf, ACPI_TYPE_BUFFER); ACPI_SERIAL_END(aibs); if (ACPI_FAILURE(rs)) { device_printf(sc->sc_dev, "GITM evaluation failed\n"); return (EIO); } bp = buf.Pointer; if (bp->Buffer.Length < 8) { device_printf(sc->sc_dev, "GITM returned short buffer\n"); return (EIO); } ret = (uint32_t *)bp->Buffer.Pointer; if (ret[0] == 0) { device_printf(sc->sc_dev, "GITM returned error status\n"); return (EINVAL); } v = ret[1]; AcpiOsFree(buf.Pointer); l = sensor->l; h = sensor->h; switch (sensor->t) { case AIBS_SENS_TYPE_VOLT: break; case AIBS_SENS_TYPE_TEMP: v += 2731; l += 2731; h += 2731; break; case AIBS_SENS_TYPE_FAN: break; } so[0] = v; so[1] = l; so[2] = h; return (sysctl_handle_opaque(oidp, &so, sizeof(so), req)); }