diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile index 23a3a0803448..2568e3d96170 100644 --- a/share/man/man4/man4.i386/Makefile +++ b/share/man/man4/man4.i386/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ MAN= acpi_asus.4 \ + acpi_panasonic.4 \ acpi_toshiba.4 \ aic.4 \ alpm.4 \ diff --git a/share/man/man4/man4.i386/acpi_panasonic.4 b/share/man/man4/man4.i386/acpi_panasonic.4 new file mode 100644 index 000000000000..76ee5546b6e7 --- /dev/null +++ b/share/man/man4/man4.i386/acpi_panasonic.4 @@ -0,0 +1,161 @@ +.\" +.\" Copyright (c) 2004 OGAWA Takaya +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd July 11, 2004 +.Dt ACPI_PANASONIC 4 i386 +.Os +.Sh NAME +.Nm acpi_panasonic +.Nd "ACPI hotkey driver for Panasonic laptops" +.Sh SYNOPSIS +.Cd "device acpi_panasonic" +.Sh DESCRIPTION +The +.Nm +driver enables such hotkey facilities of various Panasonic laptops as +changing LCD brightness, controlling mixer volumes, entering sleep or +suspended state and so on. +On the following models it is reported to work: Let's note (or +Toughbook, outside Japan) CF-R1N, CF-R2A and CF-R3. +It may also work on other models as well. +.Pp +The driver consists of three functionalities. +The first is to detect hotkey events and take corresponding actions, +which include changing LCD luminance and speaker mute state. +The second role is to notify occurences of the event by way of +.Xr devctl 4 +and eventually to +.Xr devd 8 . +The third and last is to provide a way to adjust LCD brightness and +sound mute state via sysctl. +.Ss Hotkeys +There are 9 hotkeys available on the supported hardwares: +.Pp +.Bl -tag -compact -offset indent +.It Sy \&Fn+F1 +Make LCD backlight darker. +.It Sy \&Fn+F2 +Make LCD backlight brighter. +.It Sy \&Fn+F3 +Switch video output between LCD and CRT. +Not supported by the +.Nm +driver. +.It Sy \&Fn+F4 +Toggle muting the speaker. +.It Sy \&Fn+F5 +Turn the mixer volume down. +.It Sy \&Fn+F6 +Turn the mixer volume up. +.It Sy \&Fn+F7 +Enter suspend-to-RAM state. +.It Sy \&Fn+F9 +Show battery status. +.It Sy \&Fn+F10 +Enter suspend-to-disk state. +.El +.Pp +Actions are automatically taken within the driver for +.Sy \&Fn+F1 , +.Sy \&Fn+F2 +and +.Sy \&Fn+F4 . +For the other events such as +mixer control and showing battery status, +.Xr devd 8 +should take the role as described below. +.Ss Devd Events +When notified to +.Xr devd 8 , +the hotkey event provides the following information: +.Pp +.Bl -tag -compact -offset indent +.It system +.Qq ACPI +.It subsystem +.Qq Panasonic +.It type +The source of the event in ACPI namespace. +The value depends on the model but typically +.Qq \e_SB_.HKEY . +.It notify +Event code (see below). +.El +.Pp +Event codes to be generated are assigned as follows: +.Bl -tag -offset indent +.It 0x81-0x86, 0x89 +.Sy \&Fn+F +pressed. 0x81 corresponds to +.Sy \&Fn+F1 , +0x82 corresponds to +.Sy \&Fn+F2 , +and so on. +.It 0x01-0x07, 0x09, 0x1a +.Sy \&Fn+F +released. 0x01 corresponds to +.Sy \&Fn+F1 , +0x02 corresponds to +.Sy \&Fn+F2 , +and so on. +.El +.Sh SYSCTLS +The following read-write MIBs are available: +.Bl -tag -width indent +.It Va hw.acpi.panasonic.lcd_brightness_max +The max level of brightness. +The value ranges from 0 to 255. +CF-R1N and CF-R2A should be 255, CF-R3 is 31. +.It Va hw.acpi.panasonic.lcd_brightness +Current brightness level of the LCD. +The value ranges from 0 to +.Va hw.acpi.panasonic.lcd_brightness_max . +.It Va hw.acpi.panasonic.sound_mute +A boolean flag to control whether to mute the speaker. +The value 1 means to mute and 0 not. +.El +.Sh SEE ALSO +.Xr acpi 4 , +.Xr sysctl 8 , +.Xr devd 8 , +.Xr devd.conf 5 +.\" .Sh HISTORY +.\" The +.\" .Nm +.\" driver first appeared in +.\" .Fx 5.3 . +.Sh AUTHORS +The +.Nm +driver and this manual page were written by +.An OGAWA Takaya Aq t\-ogawa@triaez.kaisei.org +and +.An TAKAHASHI Yoshihiro Aq nyan@FreeBSD.org . +.Sh BUGS +The code is written in trial-and-error manner. +There is no way to confirm the correctness except by +.Dq it just works here . diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index ba0b330600fc..d569b44644a6 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -182,6 +182,7 @@ dev/acpica/acpi_if.m standard i386/acpica/OsdEnvironment.c optional acpi i386/acpica/acpi_asus.c optional acpi_asus acpi i386/acpica/acpi_machdep.c optional acpi +i386/acpica/acpi_panasonic.c optional acpi_panasonic acpi i386/acpica/acpi_toshiba.c optional acpi_toshiba acpi i386/acpica/acpi_wakeup.c optional acpi acpi_wakecode.h optional acpi \ diff --git a/sys/dev/acpi_support/acpi_panasonic.c b/sys/dev/acpi_support/acpi_panasonic.c new file mode 100644 index 000000000000..b92e4a507a32 --- /dev/null +++ b/sys/dev/acpi_support/acpi_panasonic.c @@ -0,0 +1,410 @@ +/*- + * Copyright (c) 2003 OGAWA Takaya + * Copyright (c) 2004 Yoshihiro TAKAHASHI + * 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 "acpi.h" +#include + +/* Debug */ +#undef ACPI_PANASONIC_DEBUG + +/* Operations */ +#define HKEY_SET 0 +#define HKEY_GET 1 + +/* Functions */ +#define HKEY_REG_LCD_BRIGHTNESS 0x04 +#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; +}; + +/* 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_sysctl(SYSCTL_HANDLER_ARGS); +static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index); +static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, + ACPI_INTEGER val); +static hkey_fn_t hkey_lcd_brightness_max; +static hkey_fn_t hkey_lcd_brightness; +static hkey_fn_t hkey_sound_mute; +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); + +/* 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", 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), + + {0, 0} +}; + +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 lcd_brightness_max = 255; + +static int +acpi_panasonic_probe(device_t dev) +{ + static char *mat_ids[] = { "MAT0019", NULL }; + + if (acpi_disabled("panasonic") || + ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL || + device_get_unit(dev) != 0) + return (ENXIO); + + device_set_desc(dev, "Panasonic Notebook Hotkeys"); + return (0); +} + +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, 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, + 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); + } + + return (0); +} + +static int +acpi_panasonic_detach(device_t dev) +{ + struct acpi_panasonic_softc *sc; + + sc = device_get_softc(dev); + + /* 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_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. */ + error = handler(sc->handle, HKEY_GET, &arg); + if (error != 0) + return (error); + + /* 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) + return (error); + + /* Set the new value via the appropriate function. */ + error = handler(sc->handle, HKEY_SET, &arg); + + return (error); +} + +static ACPI_INTEGER +acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index) +{ + ACPI_BUFFER buf; + ACPI_OBJECT *res; + ACPI_INTEGER ret; + + 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, ACPI_INTEGER index, ACPI_INTEGER val) +{ + ACPI_OBJECT_LIST args; + ACPI_OBJECT obj[2]; + + 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) +{ + + switch (op) { + case HKEY_SET: + if (*val < 0 || *val > 255) + return (EINVAL); + lcd_brightness_max = *val; + break; + case HKEY_GET: + *val = lcd_brightness_max; + break; + } + + return (0); +} + +static int +hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) +{ + + switch (op) { + case HKEY_SET: + if (*val < 0 || *val > lcd_brightness_max) + return (EINVAL); + acpi_panasonic_sset(h, HKEY_REG_LCD_BRIGHTNESS, *val); + break; + case HKEY_GET: + *val = acpi_panasonic_sinf(h, HKEY_REG_LCD_BRIGHTNESS); + break; + } + + return (0); +} + +static int +hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) +{ + + 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; + ACPI_INTEGER val; + int status; + + 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) +{ + int arg; + + switch (key) { + case 1: + /* Decrease LCD brightness. */ + hkey_lcd_brightness(h, HKEY_GET, &arg); + arg -= lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; + if (arg < 0) + arg = 0; + else if (arg > lcd_brightness_max) + arg = lcd_brightness_max; + hkey_lcd_brightness(h, HKEY_SET, &arg); + break; + case 2: + /* Increase LCD brightness. */ + hkey_lcd_brightness(h, HKEY_GET, &arg); + arg += lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; + if (arg < 0) + arg = 0; + else if (arg > lcd_brightness_max) + arg = lcd_brightness_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; + } +} + +static void +acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) +{ + struct acpi_panasonic_softc *sc; + UINT32 key; + + sc = (struct acpi_panasonic_softc *)context; + + switch (notify) { + case 0x80: + if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { + acpi_panasonic_hkey_action(sc, h, key); + acpi_UserNotify("Panasonic", h, (uint8_t)key); + } + break; + default: + device_printf(sc->dev, "unknown Notify: 0x%x\n", notify); + break; + } +} diff --git a/sys/i386/acpica/acpi_panasonic.c b/sys/i386/acpica/acpi_panasonic.c new file mode 100644 index 000000000000..b92e4a507a32 --- /dev/null +++ b/sys/i386/acpica/acpi_panasonic.c @@ -0,0 +1,410 @@ +/*- + * Copyright (c) 2003 OGAWA Takaya + * Copyright (c) 2004 Yoshihiro TAKAHASHI + * 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 "acpi.h" +#include + +/* Debug */ +#undef ACPI_PANASONIC_DEBUG + +/* Operations */ +#define HKEY_SET 0 +#define HKEY_GET 1 + +/* Functions */ +#define HKEY_REG_LCD_BRIGHTNESS 0x04 +#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; +}; + +/* 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_sysctl(SYSCTL_HANDLER_ARGS); +static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index); +static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, + ACPI_INTEGER val); +static hkey_fn_t hkey_lcd_brightness_max; +static hkey_fn_t hkey_lcd_brightness; +static hkey_fn_t hkey_sound_mute; +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); + +/* 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", 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), + + {0, 0} +}; + +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 lcd_brightness_max = 255; + +static int +acpi_panasonic_probe(device_t dev) +{ + static char *mat_ids[] = { "MAT0019", NULL }; + + if (acpi_disabled("panasonic") || + ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL || + device_get_unit(dev) != 0) + return (ENXIO); + + device_set_desc(dev, "Panasonic Notebook Hotkeys"); + return (0); +} + +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, 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, + 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); + } + + return (0); +} + +static int +acpi_panasonic_detach(device_t dev) +{ + struct acpi_panasonic_softc *sc; + + sc = device_get_softc(dev); + + /* 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_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. */ + error = handler(sc->handle, HKEY_GET, &arg); + if (error != 0) + return (error); + + /* 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) + return (error); + + /* Set the new value via the appropriate function. */ + error = handler(sc->handle, HKEY_SET, &arg); + + return (error); +} + +static ACPI_INTEGER +acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index) +{ + ACPI_BUFFER buf; + ACPI_OBJECT *res; + ACPI_INTEGER ret; + + 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, ACPI_INTEGER index, ACPI_INTEGER val) +{ + ACPI_OBJECT_LIST args; + ACPI_OBJECT obj[2]; + + 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) +{ + + switch (op) { + case HKEY_SET: + if (*val < 0 || *val > 255) + return (EINVAL); + lcd_brightness_max = *val; + break; + case HKEY_GET: + *val = lcd_brightness_max; + break; + } + + return (0); +} + +static int +hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) +{ + + switch (op) { + case HKEY_SET: + if (*val < 0 || *val > lcd_brightness_max) + return (EINVAL); + acpi_panasonic_sset(h, HKEY_REG_LCD_BRIGHTNESS, *val); + break; + case HKEY_GET: + *val = acpi_panasonic_sinf(h, HKEY_REG_LCD_BRIGHTNESS); + break; + } + + return (0); +} + +static int +hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) +{ + + 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; + ACPI_INTEGER val; + int status; + + 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) +{ + int arg; + + switch (key) { + case 1: + /* Decrease LCD brightness. */ + hkey_lcd_brightness(h, HKEY_GET, &arg); + arg -= lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; + if (arg < 0) + arg = 0; + else if (arg > lcd_brightness_max) + arg = lcd_brightness_max; + hkey_lcd_brightness(h, HKEY_SET, &arg); + break; + case 2: + /* Increase LCD brightness. */ + hkey_lcd_brightness(h, HKEY_GET, &arg); + arg += lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; + if (arg < 0) + arg = 0; + else if (arg > lcd_brightness_max) + arg = lcd_brightness_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; + } +} + +static void +acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) +{ + struct acpi_panasonic_softc *sc; + UINT32 key; + + sc = (struct acpi_panasonic_softc *)context; + + switch (notify) { + case 0x80: + if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { + acpi_panasonic_hkey_action(sc, h, key); + acpi_UserNotify("Panasonic", h, (uint8_t)key); + } + break; + default: + device_printf(sc->dev, "unknown Notify: 0x%x\n", notify); + break; + } +} diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 249cd52c44ac..c1d05f5a98d6 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -448,6 +448,9 @@ options ACPI_MAX_THREADS=1 # ACPI Asus Extras (LCD backlight/brightness, video output, etc.) device acpi_asus +# ACPI Panasonic Extras (LCD backlight/brightness, video output, etc.) +device acpi_panasonic + # ACPI Toshiba Extras (LCD backlight/brightness, video output, etc.) device acpi_toshiba diff --git a/sys/modules/acpi/Makefile b/sys/modules/acpi/Makefile index 80823606092f..9009d0019d93 100644 --- a/sys/modules/acpi/Makefile +++ b/sys/modules/acpi/Makefile @@ -1,5 +1,5 @@ # $FreeBSD$ -SUBDIR= acpi acpi_asus acpi_toshiba acpi_video +SUBDIR= acpi acpi_asus acpi_panasonic acpi_toshiba acpi_video .include diff --git a/sys/modules/acpi/acpi_panasonic/Makefile b/sys/modules/acpi/acpi_panasonic/Makefile new file mode 100644 index 000000000000..fbf6ed893349 --- /dev/null +++ b/sys/modules/acpi/acpi_panasonic/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../i386/acpica + +KMOD= acpi_panasonic +CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica +SRCS= acpi_panasonic.c opt_acpi.h acpi_if.h bus_if.h device_if.h + +.include