aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/acpi_support
diff options
context:
space:
mode:
authorMitsuru IWASAKI <iwasaki@FreeBSD.org>2012-06-23 18:43:54 +0000
committerMitsuru IWASAKI <iwasaki@FreeBSD.org>2012-06-23 18:43:54 +0000
commitae2f080bc490564644934cb543fb7424319d93db (patch)
tree89a7d575721f20df4cbde7fd3d95b1140c82d716 /sys/dev/acpi_support
parente1f4f1f860f4263140020f98393712bb1f3111ae (diff)
downloadsrc-ae2f080bc490564644934cb543fb7424319d93db.tar.gz
src-ae2f080bc490564644934cb543fb7424319d93db.zip
Add in-driver event handler.
MFC after: 3 days
Notes
Notes: svn path=/head/; revision=237493
Diffstat (limited to 'sys/dev/acpi_support')
-rw-r--r--sys/dev/acpi_support/acpi_ibm.c457
1 files changed, 371 insertions, 86 deletions
diff --git a/sys/dev/acpi_support/acpi_ibm.c b/sys/dev/acpi_support/acpi_ibm.c
index c8f3b45b7c22..c6b52471cb7a 100644
--- a/sys/dev/acpi_support/acpi_ibm.c
+++ b/sys/dev/acpi_support/acpi_ibm.c
@@ -50,6 +50,8 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <dev/acpica/acpivar.h>
#include <dev/led/led.h>
+#include <sys/power.h>
+#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <isa/rtc.h>
@@ -70,6 +72,7 @@ ACPI_MODULE_NAME("IBM")
#define ACPI_IBM_METHOD_FANLEVEL 11
#define ACPI_IBM_METHOD_FANSTATUS 12
#define ACPI_IBM_METHOD_THERMAL 13
+#define ACPI_IBM_METHOD_HANDLEREVENTS 14
/* Hotkeys/Buttons */
#define IBM_RTC_HOTKEY1 0x64
@@ -126,6 +129,21 @@ ACPI_MODULE_NAME("IBM")
#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
+
#define ABS(x) (((x) < 0)? -(x) : (x))
struct acpi_ibm_softc {
@@ -164,6 +182,8 @@ struct acpi_ibm_softc {
int events_mask_supported;
int events_enable;
+ unsigned int handler_events;
+
struct sysctl_ctx_list *sysctl_ctx;
struct sysctl_oid *sysctl_tree;
};
@@ -267,8 +287,15 @@ 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 device_method_t acpi_ibm_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, acpi_ibm_probe),
@@ -404,6 +431,15 @@ acpi_ibm_attach(device_t dev)
"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,
+ 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);
@@ -656,10 +692,8 @@ 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 arg)
{
- int val, step;
+ int val;
UINT64 val_ec;
- ACPI_OBJECT Arg;
- ACPI_OBJECT_LIST Args;
ACPI_STATUS status;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
@@ -683,101 +717,23 @@ acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
break;
case ACPI_IBM_METHOD_BRIGHTNESS:
- if (arg < 0 || arg > 7)
- return (EINVAL);
-
- if (sc->cmos_handle) {
- /* Read the current brightness */
- status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
- if (ACPI_FAILURE(status))
- return (status);
- val = val_ec & IBM_EC_MASK_BRI;
-
- Args.Count = 1;
- Args.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN;
-
- step = (arg > val) ? 1 : -1;
- for (int i = val; i != arg; i += step) {
- status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
- if (ACPI_FAILURE(status))
- break;
- }
- }
- return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
+ return acpi_ibm_brightness_set(sc, arg);
break;
case ACPI_IBM_METHOD_VOLUME:
- if (arg < 0 || arg > 14)
- return (EINVAL);
-
- status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
- if (ACPI_FAILURE(status))
- return (status);
-
- if (sc->cmos_handle) {
- val = val_ec & IBM_EC_MASK_VOL;
-
- Args.Count = 1;
- Args.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN;
-
- step = (arg > val) ? 1 : -1;
- for (int i = val; i != arg; i += step) {
- status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
- if (ACPI_FAILURE(status))
- break;
- }
- }
- return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1);
+ return acpi_ibm_volume_set(sc, arg);
break;
case ACPI_IBM_METHOD_MUTE:
- if (arg < 0 || arg > 1)
- return (EINVAL);
-
- status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
- if (ACPI_FAILURE(status))
- return (status);
-
- if (sc->cmos_handle) {
- Args.Count = 1;
- Args.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
-
- status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
- if (ACPI_FAILURE(status))
- break;
- }
- return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1);
+ return acpi_ibm_mute_set(sc, arg);
break;
case ACPI_IBM_METHOD_THINKLIGHT:
- if (arg < 0 || arg > 1)
- return (EINVAL);
-
- if (sc->light_set_supported) {
- Args.Count = 1;
- Args.Pointer = &Arg;
- Arg.Type = ACPI_TYPE_INTEGER;
- Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
-
- status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL);
- if (ACPI_SUCCESS(status))
- sc->light_val = arg;
- return (status);
- }
+ return acpi_ibm_thinklight_set(sc, arg);
break;
case ACPI_IBM_METHOD_BLUETOOTH:
- if (arg < 0 || arg > 1)
- return (EINVAL);
-
- val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
- return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
+ return acpi_ibm_bluetooth_set(sc, arg);
break;
case ACPI_IBM_METHOD_FANLEVEL:
@@ -898,6 +854,9 @@ acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
return (TRUE);
}
return (FALSE);
+
+ case ACPI_IBM_METHOD_HANDLEREVENTS:
+ return (TRUE);
}
return (FALSE);
}
@@ -937,6 +896,328 @@ acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
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;
+
+ 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);
+
+ /* Copy out the old values to the user. */
+ error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
+ sbuf_delete(&sb);
+
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /* If the user is setting a string, parse it. */
+ handler_events = 0;
+ cp = (char *)req->newptr;
+ 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);
+}
+
+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)
{
@@ -965,6 +1246,10 @@ acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
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;