acpi_button.c revision 1.33
1/* $NetBSD: acpi_button.c,v 1.33 2010/03/04 23:06:36 jruoho Exp $ */ 2 3/* 4 * Copyright 2001, 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38/* 39 * ACPI Button driver. 40 */ 41 42#include <sys/cdefs.h> 43__KERNEL_RCSID(0, "$NetBSD: acpi_button.c,v 1.33 2010/03/04 23:06:36 jruoho Exp $"); 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/device.h> 48#include <sys/module.h> 49 50#include <dev/acpi/acpireg.h> 51#include <dev/acpi/acpivar.h> 52 53#include <dev/sysmon/sysmonvar.h> 54 55#define _COMPONENT ACPI_BUTTON_COMPONENT 56ACPI_MODULE_NAME ("acpi_button") 57 58struct acpibut_softc { 59 struct acpi_devnode *sc_node; 60 struct sysmon_pswitch sc_smpsw; 61}; 62 63static const char * const power_button_hid[] = { 64 "PNP0C0C", 65 NULL 66}; 67 68static const char * const sleep_button_hid[] = { 69 "PNP0C0E", 70 NULL 71}; 72 73static int acpibut_match(device_t, cfdata_t, void *); 74static void acpibut_attach(device_t, device_t, void *); 75static int acpibut_detach(device_t, int); 76static void acpibut_pressed_event(void *); 77static void acpibut_notify_handler(ACPI_HANDLE, uint32_t, void *); 78 79CFATTACH_DECL_NEW(acpibut, sizeof(struct acpibut_softc), 80 acpibut_match, acpibut_attach, acpibut_detach, NULL); 81 82/* 83 * acpibut_match: 84 * 85 * Autoconfiguration `match' routine. 86 */ 87static int 88acpibut_match(device_t parent, cfdata_t match, void *aux) 89{ 90 struct acpi_attach_args *aa = aux; 91 92 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 93 return 0; 94 95 if (acpi_match_hid(aa->aa_node->ad_devinfo, power_button_hid)) 96 return 1; 97 98 if (acpi_match_hid(aa->aa_node->ad_devinfo, sleep_button_hid)) 99 return 1; 100 101 return 0; 102} 103 104/* 105 * acpibut_attach: 106 * 107 * Autoconfiguration `attach' routine. 108 */ 109static void 110acpibut_attach(device_t parent, device_t self, void *aux) 111{ 112 struct acpibut_softc *sc = device_private(self); 113 struct acpi_attach_args *aa = aux; 114 const char *desc; 115 ACPI_STATUS rv; 116 117 sc->sc_smpsw.smpsw_name = device_xname(self); 118 119 if (acpi_match_hid(aa->aa_node->ad_devinfo, power_button_hid)) { 120 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER; 121 desc = "Power"; 122 } else if (acpi_match_hid(aa->aa_node->ad_devinfo, sleep_button_hid)) { 123 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_SLEEP; 124 desc = "Sleep"; 125 } else 126 panic("%s: impossible", __func__); 127 128 aprint_naive(": ACPI %s Button\n", desc); 129 aprint_normal(": ACPI %s Button\n", desc); 130 131 sc->sc_node = aa->aa_node; 132 133 (void)pmf_device_register(self, NULL, NULL); 134 (void)sysmon_pswitch_register(&sc->sc_smpsw); 135 136 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 137 ACPI_DEVICE_NOTIFY, acpibut_notify_handler, self); 138 139 if (ACPI_FAILURE(rv)) 140 aprint_error_dev(self, "failed to install notify handler\n"); 141} 142 143/* 144 * acpibut_detach: 145 * 146 * Autoconfiguration `detach' routine. 147 */ 148static int 149acpibut_detach(device_t self, int flags) 150{ 151 struct acpibut_softc *sc = device_private(self); 152 ACPI_STATUS rv; 153 154 rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle, 155 ACPI_DEVICE_NOTIFY, acpibut_notify_handler); 156 157 if (ACPI_FAILURE(rv)) 158 return EBUSY; 159 160 pmf_device_deregister(self); 161 sysmon_pswitch_unregister(&sc->sc_smpsw); 162 163 return 0; 164} 165 166/* 167 * acpibut_pressed_event: 168 * 169 * Deal with a button being pressed. 170 */ 171static void 172acpibut_pressed_event(void *arg) 173{ 174 device_t dv = arg; 175 struct acpibut_softc *sc = device_private(dv); 176 177 aprint_debug_dev(dv, "button pressed\n"); 178 sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED); 179} 180 181/* 182 * acpibut_notify_handler: 183 * 184 * Callback from ACPI interrupt handler to notify us of an event. 185 */ 186static void 187acpibut_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context) 188{ 189 static const int handler = OSL_NOTIFY_HANDLER; 190 device_t dv = context; 191 192 switch (notify) { 193 194 /* case ACPI_NOTIFY_S0SleepButtonPressed: */ 195 case ACPI_NOTIFY_S0PowerButtonPressed: 196 (void)AcpiOsExecute(handler, acpibut_pressed_event, dv); 197 break; 198 199 default: 200 aprint_error_dev(dv, "unknown notify 0x%02X\n", notify); 201 } 202} 203 204#ifdef _MODULE 205 206MODULE(MODULE_CLASS_DRIVER, acpibut, NULL); 207CFDRIVER_DECL(acpibut, DV_DULL, NULL); 208 209static int acpibutloc[] = { -1 }; 210extern struct cfattach acpibut_ca; 211 212static struct cfparent acpiparent = { 213 "acpinodebus", NULL, DVUNIT_ANY 214}; 215 216static struct cfdata acpibut_cfdata[] = { 217 { 218 .cf_name = "acpibut", 219 .cf_atname = "acpibut", 220 .cf_unit = 0, 221 .cf_fstate = FSTATE_STAR, 222 .cf_loc = acpibutloc, 223 .cf_flags = 0, 224 .cf_pspec = &acpiparent, 225 }, 226 227 { NULL } 228}; 229 230static int 231acpibut_modcmd(modcmd_t cmd, void *context) 232{ 233 int err; 234 235 switch (cmd) { 236 237 case MODULE_CMD_INIT: 238 239 err = config_cfdriver_attach(&acpibut_cd); 240 241 if (err != 0) 242 return err; 243 244 err = config_cfattach_attach("acpibut", &acpibut_ca); 245 246 if (err != 0) { 247 config_cfdriver_detach(&acpibut_cd); 248 return err; 249 } 250 251 err = config_cfdata_attach(acpibut_cfdata, 1); 252 253 if (err != 0) { 254 config_cfattach_detach("acpibut", &acpibut_ca); 255 config_cfdriver_detach(&acpibut_cd); 256 return err; 257 } 258 259 return 0; 260 261 case MODULE_CMD_FINI: 262 263 err = config_cfdata_detach(acpibut_cfdata); 264 265 if (err != 0) 266 return err; 267 268 config_cfattach_detach("acpibut", &acpibut_ca); 269 config_cfdriver_detach(&acpibut_cd); 270 271 return 0; 272 273 default: 274 return ENOTTY; 275 } 276} 277 278#endif /* _MODULE */ 279