1 /* $NetBSD: acpi_acad.c,v 1.34 2009/05/12 09:29:46 cegger Exp $ */ 2 3 /* 4 * Copyright 2001 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 #if 0 39 #define ACPI_ACAD_DEBUG 40 #endif 41 42 /* 43 * ACPI AC Adapter driver. 44 */ 45 46 #include <sys/cdefs.h> 47 __KERNEL_RCSID(0, "$NetBSD: acpi_acad.c,v 1.34 2009/05/12 09:29:46 cegger Exp $"); 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/device.h> 52 #include <sys/mutex.h> 53 54 #include <dev/acpi/acpica.h> 55 #include <dev/acpi/acpireg.h> 56 #include <dev/acpi/acpivar.h> 57 58 #include <dev/sysmon/sysmonvar.h> 59 60 struct acpiacad_softc { 61 struct acpi_devnode *sc_node; /* our ACPI devnode */ 62 int sc_flags; /* see below */ 63 int sc_status; /* status changed/not changed */ 64 int sc_notifysent; /* notify message sent */ 65 66 struct sysmon_envsys *sc_sme; 67 struct sysmon_pswitch sc_smpsw; /* our sysmon glue */ 68 envsys_data_t sc_sensor; 69 70 kmutex_t sc_mtx; 71 }; 72 73 static const char * const acad_hid[] = { 74 "ACPI0003", 75 NULL 76 }; 77 78 #define AACAD_F_VERBOSE 0x01 /* verbose events */ 79 #define AACAD_F_AVAILABLE 0x02 /* information is available */ 80 #define AACAD_F_STCHANGED 0x04 /* status changed */ 81 82 #define AACAD_SET(sc, f) (void)((sc)->sc_flags |= (f)) 83 #define AACAD_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f)) 84 #define AACAD_ISSET(sc, f) ((sc)->sc_flags & (f)) 85 86 static int acpiacad_match(device_t, cfdata_t, void *); 87 static void acpiacad_attach(device_t, device_t, void *); 88 89 CFATTACH_DECL_NEW(acpiacad, sizeof(struct acpiacad_softc), 90 acpiacad_match, acpiacad_attach, NULL, NULL); 91 92 static void acpiacad_get_status(void *); 93 static void acpiacad_clear_status(struct acpiacad_softc *); 94 static void acpiacad_notify_handler(ACPI_HANDLE, UINT32, void *); 95 static void acpiacad_init_envsys(device_t); 96 static void acpiacad_refresh(struct sysmon_envsys *, envsys_data_t *); 97 static bool acpiacad_resume(device_t PMF_FN_PROTO); 98 99 /* 100 * acpiacad_match: 101 * 102 * Autoconfiguration `match' routine. 103 */ 104 static int 105 acpiacad_match(device_t parent, cfdata_t match, void *aux) 106 { 107 struct acpi_attach_args *aa = aux; 108 109 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 110 return 0; 111 112 return acpi_match_hid(aa->aa_node->ad_devinfo, acad_hid); 113 } 114 115 /* 116 * acpiacad_attach: 117 * 118 * Autoconfiguration `attach' routine. 119 */ 120 static void 121 acpiacad_attach(device_t parent, device_t self, void *aux) 122 { 123 struct acpiacad_softc *sc = device_private(self); 124 struct acpi_attach_args *aa = aux; 125 ACPI_STATUS rv; 126 127 aprint_naive(": ACPI AC Adapter\n"); 128 aprint_normal(": ACPI AC Adapter\n"); 129 130 sc->sc_node = aa->aa_node; 131 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); 132 133 sc->sc_smpsw.smpsw_name = device_xname(self); 134 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER; 135 if (sysmon_pswitch_register(&sc->sc_smpsw) != 0) { 136 aprint_error_dev(self, "unable to register with sysmon\n"); 137 return; 138 } 139 140 sc->sc_status = -1; 141 142 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 143 ACPI_ALL_NOTIFY, acpiacad_notify_handler, self); 144 if (ACPI_FAILURE(rv)) { 145 aprint_error_dev(self, "unable to register DEVICE and SYSTEM " 146 "NOTIFY handler: %s\n", AcpiFormatException(rv)); 147 return; 148 } 149 150 #ifdef ACPI_ACAD_DEBUG 151 /* Display the current state. */ 152 sc->sc_flags = AACAD_F_VERBOSE; 153 #endif 154 155 if (!pmf_device_register(self, NULL, acpiacad_resume)) 156 aprint_error_dev(self, "couldn't establish power handler\n"); 157 158 acpiacad_init_envsys(self); 159 } 160 161 /* 162 * acpiacad_resume: 163 * 164 * Clear status after resuming to fetch new status. 165 */ 166 static bool 167 acpiacad_resume(device_t dv PMF_FN_ARGS) 168 { 169 struct acpiacad_softc *sc = device_private(dv); 170 171 acpiacad_clear_status(sc); 172 return true; 173 } 174 175 /* 176 * acpiacad_get_status: 177 * 178 * Get, and possibly display, the current AC line status. 179 */ 180 static void 181 acpiacad_get_status(void *arg) 182 { 183 device_t dv = arg; 184 struct acpiacad_softc *sc = device_private(dv); 185 ACPI_INTEGER status; 186 ACPI_STATUS rv; 187 188 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 189 if (ACPI_FAILURE(rv)) 190 return; 191 192 mutex_enter(&sc->sc_mtx); 193 sc->sc_notifysent = 0; 194 if (sc->sc_status != status) { 195 sc->sc_status = status; 196 if (status) 197 sc->sc_sensor.value_cur = 1; 198 else 199 sc->sc_sensor.value_cur = 0; 200 AACAD_SET(sc, AACAD_F_STCHANGED); 201 } 202 203 sc->sc_sensor.state = ENVSYS_SVALID; 204 AACAD_SET(sc, AACAD_F_AVAILABLE); 205 /* 206 * If status has changed, send the event. 207 * 208 * PSWITCH_EVENT_RELEASED : AC offline 209 * PSWITCH_EVENT_PRESSED : AC online 210 */ 211 if (AACAD_ISSET(sc, AACAD_F_STCHANGED)) { 212 sysmon_pswitch_event(&sc->sc_smpsw, status ? 213 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 214 if (AACAD_ISSET(sc, AACAD_F_VERBOSE)) 215 aprint_verbose_dev(dv, "AC adapter %sconnected\n", 216 status == 0 ? "not " : ""); 217 } 218 mutex_exit(&sc->sc_mtx); 219 } 220 221 /* 222 * Clear status 223 */ 224 static void 225 acpiacad_clear_status(struct acpiacad_softc *sc) 226 { 227 sc->sc_sensor.state = ENVSYS_SINVALID; 228 AACAD_CLEAR(sc, AACAD_F_AVAILABLE); 229 AACAD_CLEAR(sc, AACAD_F_STCHANGED); 230 } 231 232 /* 233 * acpiacad_notify_handler: 234 * 235 * Callback from ACPI interrupt handler to notify us of an event. 236 */ 237 static void 238 acpiacad_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 239 { 240 device_t dv = context; 241 struct acpiacad_softc *sc = device_private(dv); 242 int rv; 243 244 switch (notify) { 245 /* 246 * XXX So, BusCheck is not exactly what I would expect, 247 * but at least my IBM T21 sends it on AC adapter status 248 * change. --thorpej@wasabisystems.com 249 */ 250 /* 251 * XXX My Acer TravelMate 291 sends DeviceCheck on AC 252 * adapter status change. 253 * --rpaulo@NetBSD.org 254 */ 255 /* 256 * XXX Sony VAIO VGN-N250E sends BatteryInformationChanged on AC 257 * adapter status change. 258 * --jmcneill@NetBSD.org 259 */ 260 case ACPI_NOTIFY_BusCheck: 261 case ACPI_NOTIFY_DeviceCheck: 262 case ACPI_NOTIFY_PowerSourceStatusChanged: 263 case ACPI_NOTIFY_BatteryInformationChanged: 264 mutex_enter(&sc->sc_mtx); 265 acpiacad_clear_status(sc); 266 mutex_exit(&sc->sc_mtx); 267 if (sc->sc_status == -1 || !sc->sc_notifysent) { 268 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 269 acpiacad_get_status, dv); 270 if (ACPI_FAILURE(rv)) 271 aprint_error_dev(dv, 272 "unable to queue status check: %s\n", 273 AcpiFormatException(rv)); 274 sc->sc_notifysent = 1; 275 #ifdef ACPI_ACAD_DEBUG 276 aprint_debug_dev(dv, "received notify message: 0x%x\n", 277 notify); 278 #endif 279 } 280 break; 281 282 default: 283 aprint_error_dev(dv, "received unknown notify message: 0x%x\n", 284 notify); 285 } 286 } 287 288 static void 289 acpiacad_init_envsys(device_t dv) 290 { 291 struct acpiacad_softc *sc = device_private(dv); 292 293 sc->sc_sme = sysmon_envsys_create(); 294 sc->sc_sensor.state = ENVSYS_SVALID; 295 sc->sc_sensor.units = ENVSYS_INDICATOR; 296 strlcpy(sc->sc_sensor.desc, "connected", sizeof(sc->sc_sensor.desc)); 297 298 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 299 aprint_error_dev(dv, "unable to add sensor\n"); 300 sysmon_envsys_destroy(sc->sc_sme); 301 return; 302 } 303 304 sc->sc_sme->sme_name = device_xname(dv); 305 sc->sc_sme->sme_cookie = dv; 306 sc->sc_sme->sme_refresh = acpiacad_refresh; 307 sc->sc_sme->sme_class = SME_CLASS_ACADAPTER; 308 sc->sc_sme->sme_flags = SME_INIT_REFRESH; 309 310 if (sysmon_envsys_register(sc->sc_sme)) { 311 aprint_error_dev(dv, "unable to register with sysmon\n"); 312 sysmon_envsys_destroy(sc->sc_sme); 313 } 314 } 315 316 static void 317 acpiacad_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 318 { 319 device_t dv = sme->sme_cookie; 320 struct acpiacad_softc *sc = device_private(dv); 321 322 if (!AACAD_ISSET(sc, AACAD_F_AVAILABLE)) 323 acpiacad_get_status(dv); 324 } 325