Author: iwasaki
Date: Sat Jun 23 18:43:54 2012
New Revision: 237493
URL: http://svn.freebsd.org/changeset/base/237493

Log:
  Add in-driver event handler.
  
  MFC after:    3 days

Modified:
  head/sys/dev/acpi_support/acpi_ibm.c

Modified: head/sys/dev/acpi_support/acpi_ibm.c
==============================================================================
--- head/sys/dev/acpi_support/acpi_ibm.c        Sat Jun 23 18:43:11 2012        
(r237492)
+++ head/sys/dev/acpi_support/acpi_ibm.c        Sat Jun 23 18:43:54 2012        
(r237493)
@@ -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 ac
 
 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_soft
 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_soft
                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_sof
                        return (TRUE);
                }
                return (FALSE);
+
+       case ACPI_IBM_METHOD_HANDLEREVENTS:
+               return (TRUE);
        }
        return (FALSE);
 }
@@ -937,6 +896,328 @@ acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_A
        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 no
                                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;
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to