Add the ACPI Panasonic extras driver.

Submitted by:	OGAWA Takaya <t-ogawa@triaez.kaisei.org> and nyan
This commit is contained in:
Yoshihiro Takahashi 2004-07-21 14:47:54 +00:00
parent a24d9b9413
commit ee6020c993
8 changed files with 996 additions and 1 deletions

View File

@ -1,6 +1,7 @@
# $FreeBSD$
MAN= acpi_asus.4 \
acpi_panasonic.4 \
acpi_toshiba.4 \
aic.4 \
alpm.4 \

View File

@ -0,0 +1,161 @@
.\"
.\" Copyright (c) 2004 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
.\" 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<n>
pressed. 0x81 corresponds to
.Sy \&Fn+F1 ,
0x82 corresponds to
.Sy \&Fn+F2 ,
and so on.
.It 0x01-0x07, 0x09, 0x1a
.Sy \&Fn+F<n>
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 .

View File

@ -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 \

View File

@ -0,0 +1,410 @@
/*-
* Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
* Copyright (c) 2004 Yoshihiro TAKAHASHI <nyan@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include "acpi.h"
#include <dev/acpica/acpivar.h>
/* 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;
}
}

View File

@ -0,0 +1,410 @@
/*-
* Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
* Copyright (c) 2004 Yoshihiro TAKAHASHI <nyan@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include "acpi.h"
#include <dev/acpica/acpivar.h>
/* 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;
}
}

View File

@ -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

View File

@ -1,5 +1,5 @@
# $FreeBSD$
SUBDIR= acpi acpi_asus acpi_toshiba acpi_video
SUBDIR= acpi acpi_asus acpi_panasonic acpi_toshiba acpi_video
.include <bsd.subdir.mk>

View File

@ -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 <bsd.kmod.mk>