Home | History | Annotate | Line # | Download | only in wmi
wmi_msi.c revision 1.2
      1 /*	$NetBSD: wmi_msi.c,v 1.2 2010/10/24 16:25:31 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jukka Ruohonen.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30  * SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 __KERNEL_RCSID(0, "$NetBSD: wmi_msi.c,v 1.2 2010/10/24 16:25:31 jmcneill Exp $");
     35 
     36 #include <sys/param.h>
     37 #include <sys/device.h>
     38 #include <sys/module.h>
     39 
     40 #include <dev/acpi/acpireg.h>
     41 #include <dev/acpi/acpivar.h>
     42 #include <dev/acpi/wmi/wmi_acpivar.h>
     43 
     44 #define _COMPONENT			ACPI_RESOURCE_COMPONENT
     45 ACPI_MODULE_NAME			("wmi_msi")
     46 
     47 #define WMI_MSI_HOTKEY_BRIGHTNESS_UP	0xD0
     48 #define WMI_MSI_HOTKEY_BRIGHTNESS_DOWN	0xD1
     49 #define WMI_MSI_HOTKEY_VOLUME_UP	0xD2
     50 #define WMI_MSI_HOTKEY_VOLUME_DOWN	0xD3
     51 /*      WMI_MSI_HOTKEY_UNKNOWN		0xXXXX */
     52 
     53 #define WMI_MSI_GUID_EVENT		"B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
     54 
     55 struct wmi_msi_softc {
     56 	device_t		sc_dev;
     57 	device_t		sc_parent;
     58 };
     59 
     60 static int	wmi_msi_match(device_t, cfdata_t, void *);
     61 static void	wmi_msi_attach(device_t, device_t, void *);
     62 static int	wmi_msi_detach(device_t, int);
     63 static void	wmi_msi_notify_handler(ACPI_HANDLE, uint32_t, void *);
     64 static bool	wmi_msi_suspend(device_t, const pmf_qual_t *);
     65 static bool	wmi_msi_resume(device_t, const pmf_qual_t *);
     66 
     67 CFATTACH_DECL_NEW(wmimsi, sizeof(struct wmi_msi_softc),
     68     wmi_msi_match, wmi_msi_attach, wmi_msi_detach, NULL);
     69 
     70 static int
     71 wmi_msi_match(device_t parent, cfdata_t match, void *aux)
     72 {
     73 	return acpi_wmi_guid_match(parent, WMI_MSI_GUID_EVENT);
     74 }
     75 
     76 static void
     77 wmi_msi_attach(device_t parent, device_t self, void *aux)
     78 {
     79 	struct wmi_msi_softc *sc = device_private(self);
     80 	ACPI_STATUS rv;
     81 
     82 	sc->sc_dev = self;
     83 	sc->sc_parent = parent;
     84 
     85 	rv = acpi_wmi_event_register(parent, wmi_msi_notify_handler);
     86 
     87 	if (ACPI_FAILURE(rv)) {
     88 		aprint_error(": failed to install WMI notify handler\n");
     89 		return;
     90 	}
     91 
     92 	aprint_naive("\n");
     93 	aprint_normal(": MSI WMI mappings\n");
     94 
     95 	(void)pmf_device_register(self, wmi_msi_suspend, wmi_msi_resume);
     96 }
     97 
     98 static int
     99 wmi_msi_detach(device_t self, int flags)
    100 {
    101 	struct wmi_msi_softc *sc = device_private(self);
    102 	device_t parent = sc->sc_parent;
    103 
    104 	(void)pmf_device_deregister(self);
    105 	(void)acpi_wmi_event_deregister(parent);
    106 
    107 	return 0;
    108 }
    109 
    110 static bool
    111 wmi_msi_suspend(device_t self, const pmf_qual_t *qual)
    112 {
    113 	struct wmi_msi_softc *sc = device_private(self);
    114 	device_t parent = sc->sc_parent;
    115 
    116 	(void)acpi_wmi_event_deregister(parent);
    117 
    118 	return true;
    119 }
    120 
    121 static bool
    122 wmi_msi_resume(device_t self, const pmf_qual_t *qual)
    123 {
    124 	struct wmi_msi_softc *sc = device_private(self);
    125 	device_t parent = sc->sc_parent;
    126 
    127 	(void)acpi_wmi_event_register(parent, wmi_msi_notify_handler);
    128 
    129 	return true;
    130 }
    131 
    132 static void
    133 wmi_msi_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
    134 {
    135 	struct wmi_msi_softc *sc;
    136 	device_t self = aux;
    137 	ACPI_OBJECT *obj;
    138 	ACPI_BUFFER buf;
    139 	ACPI_STATUS rv;
    140 	uint32_t val;
    141 
    142 	buf.Pointer = NULL;
    143 
    144 	sc = device_private(self);
    145 	rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf);
    146 
    147 	if (ACPI_FAILURE(rv))
    148 		goto out;
    149 
    150 	obj = buf.Pointer;
    151 
    152 	if (obj->Type != ACPI_TYPE_INTEGER) {
    153 		rv = AE_TYPE;
    154 		goto out;
    155 	}
    156 
    157 	if (obj->Integer.Value > UINT32_MAX) {
    158 		rv = AE_AML_NUMERIC_OVERFLOW;
    159 		goto out;
    160 	}
    161 
    162 	val = obj->Integer.Value;
    163 
    164 	switch (val) {
    165 
    166 	case WMI_MSI_HOTKEY_BRIGHTNESS_DOWN:
    167 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
    168 		break;
    169 
    170 	case WMI_MSI_HOTKEY_BRIGHTNESS_UP:
    171 		pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
    172 		break;
    173 
    174 	case WMI_MSI_HOTKEY_VOLUME_DOWN:
    175 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN);
    176 		break;
    177 
    178 	case WMI_MSI_HOTKEY_VOLUME_UP:
    179 		pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP);
    180 		break;
    181 
    182 	default:
    183 		aprint_normal_dev(sc->sc_dev,
    184 		    "unknown key 0x%02X for event 0x%02X\n", val, evt);
    185 		break;
    186 	}
    187 
    188 out:
    189 	if (buf.Pointer != NULL)
    190 		ACPI_FREE(buf.Pointer);
    191 
    192 	if (ACPI_FAILURE(rv))
    193 		aprint_error_dev(sc->sc_dev, "failed to get data for "
    194 		    "event 0x%02X: %s\n", evt, AcpiFormatException(rv));
    195 }
    196 
    197 #ifdef _MODULE
    198 
    199 MODULE(MODULE_CLASS_DRIVER, wmimsi, NULL);
    200 CFDRIVER_DECL(wmimsi, DV_DULL, NULL);
    201 
    202 static int wmimsiloc[] = { -1 };
    203 extern struct cfattach wmimsi_ca;
    204 
    205 static struct cfparent wmiparent = {
    206 	"acpiwmibus", NULL, DVUNIT_ANY
    207 };
    208 
    209 static struct cfdata wmimsi_cfdata[] = {
    210 	{
    211 		.cf_name = "wmimsi",
    212 		.cf_atname = "wmimsi",
    213 		.cf_unit = 0,
    214 		.cf_fstate = FSTATE_STAR,
    215 		.cf_loc = wmimsiloc,
    216 		.cf_flags = 0,
    217 		.cf_pspec = &wmiparent,
    218 	},
    219 
    220 	{ NULL }
    221 };
    222 
    223 static int
    224 wmimsi_modcmd(modcmd_t cmd, void *opaque)
    225 {
    226 	int err;
    227 
    228 	switch (cmd) {
    229 
    230 	case MODULE_CMD_INIT:
    231 
    232 		err = config_cfdriver_attach(&wmimsi_cd);
    233 
    234 		if (err != 0)
    235 			return err;
    236 
    237 		err = config_cfattach_attach("wmimsi", &wmimsi_ca);
    238 
    239 		if (err != 0) {
    240 			config_cfdriver_detach(&wmimsi_cd);
    241 			return err;
    242 		}
    243 
    244 		err = config_cfdata_attach(wmimsi_cfdata, 1);
    245 
    246 		if (err != 0) {
    247 			config_cfattach_detach("wmimsi", &wmimsi_ca);
    248 			config_cfdriver_detach(&wmimsi_cd);
    249 			return err;
    250 		}
    251 
    252 		return 0;
    253 
    254 	case MODULE_CMD_FINI:
    255 
    256 		err = config_cfdata_detach(wmimsi_cfdata);
    257 
    258 		if (err != 0)
    259 			return err;
    260 
    261 		config_cfattach_detach("wmimsi", &wmimsi_ca);
    262 		config_cfdriver_detach(&wmimsi_cd);
    263 
    264 		return 0;
    265 
    266 	default:
    267 		return ENOTTY;
    268 	}
    269 }
    270 
    271 #endif	/* _MODULE */
    272