acpi_lid.c revision 1.36
1/* $NetBSD: acpi_lid.c,v 1.36 2010/03/05 14:00:16 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 Lid Switch driver. 40 */ 41 42#include <sys/cdefs.h> 43__KERNEL_RCSID(0, "$NetBSD: acpi_lid.c,v 1.36 2010/03/05 14:00:16 jruoho Exp $"); 44 45#include <sys/param.h> 46#include <sys/device.h> 47#include <sys/module.h> 48#include <sys/systm.h> 49 50#include <dev/acpi/acpireg.h> 51#include <dev/acpi/acpivar.h> 52 53#define _COMPONENT ACPI_LID_COMPONENT 54ACPI_MODULE_NAME ("acpi_lid") 55 56struct acpilid_softc { 57 struct acpi_devnode *sc_node; 58 struct sysmon_pswitch sc_smpsw; 59 uint64_t sc_status; 60}; 61 62static const char * const lid_hid[] = { 63 "PNP0C0D", 64 NULL 65}; 66 67static int acpilid_match(device_t, cfdata_t, void *); 68static void acpilid_attach(device_t, device_t, void *); 69static int acpilid_detach(device_t, int); 70static void acpilid_status_changed(void *); 71static void acpilid_notify_handler(ACPI_HANDLE, UINT32, void *); 72static void acpilid_wake_event(device_t, bool); 73static bool acpilid_suspend(device_t, const pmf_qual_t *); 74 75CFATTACH_DECL_NEW(acpilid, sizeof(struct acpilid_softc), 76 acpilid_match, acpilid_attach, acpilid_detach, NULL); 77 78/* 79 * acpilid_match: 80 * 81 * Autoconfiguration `match' routine. 82 */ 83static int 84acpilid_match(device_t parent, cfdata_t match, void *aux) 85{ 86 struct acpi_attach_args *aa = aux; 87 88 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 89 return 0; 90 91 return acpi_match_hid(aa->aa_node->ad_devinfo, lid_hid); 92} 93 94/* 95 * acpilid_attach: 96 * 97 * Autoconfiguration `attach' routine. 98 */ 99static void 100acpilid_attach(device_t parent, device_t self, void *aux) 101{ 102 struct acpilid_softc *sc = device_private(self); 103 struct acpi_attach_args *aa = aux; 104 ACPI_STATUS rv; 105 106 aprint_naive(": ACPI Lid Switch\n"); 107 aprint_normal(": ACPI Lid Switch\n"); 108 109 sc->sc_node = aa->aa_node; 110 111 sc->sc_smpsw.smpsw_name = device_xname(self); 112 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_LID; 113 114 (void)sysmon_pswitch_register(&sc->sc_smpsw); 115 (void)pmf_device_register(self, acpilid_suspend, NULL); 116 117 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 118 ACPI_DEVICE_NOTIFY, acpilid_notify_handler, self); 119 120 if (ACPI_FAILURE(rv)) 121 aprint_error_dev(self, "failed to register notify handler\n"); 122} 123 124static int 125acpilid_detach(device_t self, int flags) 126{ 127 struct acpilid_softc *sc = device_private(self); 128 ACPI_STATUS rv; 129 130 rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle, 131 ACPI_DEVICE_NOTIFY, acpilid_notify_handler); 132 133 if (ACPI_FAILURE(rv)) 134 return EBUSY; 135 136 pmf_device_deregister(self); 137 sysmon_pswitch_unregister(&sc->sc_smpsw); 138 139 return 0; 140} 141 142static void 143acpilid_wake_event(device_t dv, bool enable) 144{ 145 struct acpilid_softc *sc = device_private(dv); 146 ACPI_OBJECT_LIST arg; 147 ACPI_OBJECT obj[3]; 148 ACPI_STATUS rv; 149 150 /* 151 * First try to call the Device Sleep Wake control method, _DSW. 152 * Only if this is not available, resort to to the Power State 153 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 154 */ 155 obj[0].Integer.Value = enable ? 1 : 0; 156 obj[1].Integer.Value = obj[2].Integer.Value = 0; 157 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 158 159 arg.Count = 3; 160 arg.Pointer = obj; 161 162 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "_DSW", &arg, NULL); 163 164 if (ACPI_SUCCESS(rv)) 165 return; 166 167 if (rv != AE_NOT_FOUND) 168 goto fail; 169 170 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "_PSW", 171 enable ? 1 : 0); 172 173 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 174 goto fail; 175 176 return; 177 178fail: 179 aprint_error_dev(dv, "unable to evaluate wake control method: %s\n", 180 AcpiFormatException(rv)); 181} 182 183/* 184 * acpilid_status_changed: 185 * 186 * Get, and possibly display, the lid status, and take action. 187 */ 188static void 189acpilid_status_changed(void *arg) 190{ 191 device_t dv = arg; 192 struct acpilid_softc *sc = device_private(dv); 193 ACPI_STATUS rv; 194 195 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_LID", &sc->sc_status); 196 197 if (ACPI_FAILURE(rv)) 198 return; 199 200 sysmon_pswitch_event(&sc->sc_smpsw, (sc->sc_status == 0) ? 201 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 202} 203 204/* 205 * acpilid_notify_handler: 206 * 207 * Callback from ACPI interrupt handler to notify us of an event. 208 */ 209static void 210acpilid_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context) 211{ 212 static const int handler = OSL_NOTIFY_HANDLER; 213 device_t dv = context; 214 215 switch (notify) { 216 217 case ACPI_NOTIFY_LidStatusChanged: 218 (void)AcpiOsExecute(handler, acpilid_status_changed, dv); 219 break; 220 221 default: 222 aprint_error_dev(dv, "unknown notify 0x%02X\n", notify); 223 } 224} 225 226static bool 227acpilid_suspend(device_t dv, const pmf_qual_t *qual) 228{ 229 struct acpilid_softc *sc = device_private(dv); 230 231 acpilid_wake_event(dv, sc->sc_status == 0); 232 233 return true; 234} 235 236#ifdef _MODULE 237 238MODULE(MODULE_CLASS_DRIVER, acpilid, NULL); 239CFDRIVER_DECL(acpilid, DV_DULL, NULL); 240 241static int acpilidloc[] = { -1 }; 242extern struct cfattach acpilid_ca; 243 244static struct cfparent acpiparent = { 245 "acpinodebus", NULL, DVUNIT_ANY 246}; 247 248static struct cfdata acpilid_cfdata[] = { 249 { 250 .cf_name = "acpilid", 251 .cf_atname = "acpilid", 252 .cf_unit = 0, 253 .cf_fstate = FSTATE_STAR, 254 .cf_loc = acpilidloc, 255 .cf_flags = 0, 256 .cf_pspec = &acpiparent, 257 }, 258 259 { NULL } 260}; 261 262static int 263acpilid_modcmd(modcmd_t cmd, void *context) 264{ 265 int err; 266 267 switch (cmd) { 268 269 case MODULE_CMD_INIT: 270 271 err = config_cfdriver_attach(&acpilid_cd); 272 273 if (err != 0) 274 return err; 275 276 err = config_cfattach_attach("acpilid", &acpilid_ca); 277 278 if (err != 0) { 279 config_cfdriver_detach(&acpilid_cd); 280 return err; 281 } 282 283 err = config_cfdata_attach(acpilid_cfdata, 1); 284 285 if (err != 0) { 286 config_cfattach_detach("acpilid", &acpilid_ca); 287 config_cfdriver_detach(&acpilid_cd); 288 return err; 289 } 290 291 return 0; 292 293 case MODULE_CMD_FINI: 294 295 err = config_cfdata_detach(acpilid_cfdata); 296 297 if (err != 0) 298 return err; 299 300 config_cfattach_detach("acpilid", &acpilid_ca); 301 config_cfdriver_detach(&acpilid_cd); 302 303 return 0; 304 305 default: 306 return ENOTTY; 307 } 308} 309 310#endif /* _MODULE */ 311