1 /* $NetBSD: acpi_acad.c,v 1.31 2007/12/09 20:27:52 jmcneill 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.31 2007/12/09 20:27:52 jmcneill 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, struct cfdata *, 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 98 /* 99 * acpiacad_match: 100 * 101 * Autoconfiguration `match' routine. 102 */ 103 static int 104 acpiacad_match(device_t parent, struct cfdata *match, void *aux) 105 { 106 struct acpi_attach_args *aa = aux; 107 108 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 109 return 0; 110 111 return acpi_match_hid(aa->aa_node->ad_devinfo, acad_hid); 112 } 113 114 /* 115 * acpiacad_attach: 116 * 117 * Autoconfiguration `attach' routine. 118 */ 119 static void 120 acpiacad_attach(device_t parent, device_t self, void *aux) 121 { 122 struct acpiacad_softc *sc = device_private(self); 123 struct acpi_attach_args *aa = aux; 124 ACPI_STATUS rv; 125 126 aprint_naive(": ACPI AC Adapter\n"); 127 aprint_normal(": ACPI AC Adapter\n"); 128 129 sc->sc_node = aa->aa_node; 130 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); 131 132 sc->sc_smpsw.smpsw_name = device_xname(self); 133 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER; 134 if (sysmon_pswitch_register(&sc->sc_smpsw) != 0) { 135 aprint_error_dev(self, "unable to register with sysmon\n"); 136 return; 137 } 138 139 sc->sc_status = -1; 140 141 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 142 ACPI_ALL_NOTIFY, acpiacad_notify_handler, self); 143 if (ACPI_FAILURE(rv)) { 144 aprint_error_dev(self, "unable to register DEVICE and SYSTEM " 145 "NOTIFY handler: %s\n", AcpiFormatException(rv)); 146 return; 147 } 148 149 #ifdef ACPI_ACAD_DEBUG 150 /* Display the current state. */ 151 sc->sc_flags = AACAD_F_VERBOSE; 152 #endif 153 154 if (!pmf_device_register(self, NULL, NULL)) 155 aprint_error_dev(self, "couldn't establish power handler\n"); 156 157 acpiacad_init_envsys(self); 158 } 159 160 /* 161 * acpiacad_get_status: 162 * 163 * Get, and possibly display, the current AC line status. 164 */ 165 static void 166 acpiacad_get_status(void *arg) 167 { 168 device_t dv = arg; 169 struct acpiacad_softc *sc = device_private(dv); 170 ACPI_INTEGER status; 171 ACPI_STATUS rv; 172 173 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 174 if (ACPI_FAILURE(rv)) 175 return; 176 177 mutex_enter(&sc->sc_mtx); 178 if (sc->sc_status != status) { 179 sc->sc_status = status; 180 if (status) 181 sc->sc_sensor.value_cur = 1; 182 else 183 sc->sc_sensor.value_cur = 0; 184 AACAD_SET(sc, AACAD_F_STCHANGED); 185 sc->sc_notifysent = 0; 186 } 187 188 sc->sc_sensor.state = ENVSYS_SVALID; 189 AACAD_SET(sc, AACAD_F_AVAILABLE); 190 /* 191 * If status has changed, send the event. 192 * 193 * PSWITCH_EVENT_RELEASED : AC offline 194 * PSWITCH_EVENT_PRESSED : AC online 195 */ 196 if (AACAD_ISSET(sc, AACAD_F_STCHANGED)) { 197 sysmon_pswitch_event(&sc->sc_smpsw, status ? 198 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 199 if (AACAD_ISSET(sc, AACAD_F_VERBOSE)) 200 aprint_verbose_dev(dv, "AC adapter %sconnected\n", 201 status == 0 ? "not " : ""); 202 } 203 mutex_exit(&sc->sc_mtx); 204 } 205 206 /* 207 * Clear status 208 */ 209 static void 210 acpiacad_clear_status(struct acpiacad_softc *sc) 211 { 212 sc->sc_sensor.state = ENVSYS_SINVALID; 213 AACAD_CLEAR(sc, AACAD_F_AVAILABLE); 214 AACAD_CLEAR(sc, AACAD_F_STCHANGED); 215 } 216 217 /* 218 * acpiacad_notify_handler: 219 * 220 * Callback from ACPI interrupt handler to notify us of an event. 221 */ 222 static void 223 acpiacad_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 224 { 225 device_t dv = context; 226 struct acpiacad_softc *sc = device_private(dv); 227 int rv; 228 229 switch (notify) { 230 /* 231 * XXX So, BusCheck is not exactly what I would expect, 232 * but at least my IBM T21 sends it on AC adapter status 233 * change. --thorpej@wasabisystems.com 234 */ 235 /* 236 * XXX My Acer TravelMate 291 sends DeviceCheck on AC 237 * adapter status change. 238 * --rpaulo@NetBSD.org 239 */ 240 /* 241 * XXX Sony VAIO VGN-N250E sends BatteryInformationChanged on AC 242 * adapter status change. 243 * --jmcneill@NetBSD.org 244 */ 245 case ACPI_NOTIFY_BusCheck: 246 case ACPI_NOTIFY_DeviceCheck: 247 case ACPI_NOTIFY_PowerSourceStatusChanged: 248 case ACPI_NOTIFY_BatteryInformationChanged: 249 mutex_enter(&sc->sc_mtx); 250 acpiacad_clear_status(sc); 251 mutex_exit(&sc->sc_mtx); 252 if (sc->sc_status == -1 || !sc->sc_notifysent) { 253 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, 254 acpiacad_get_status, dv); 255 if (ACPI_FAILURE(rv)) 256 aprint_error_dev(dv, 257 "unable to queue status check: %s\n", 258 AcpiFormatException(rv)); 259 sc->sc_notifysent = 1; 260 #ifdef ACPI_ACAD_DEBUG 261 aprint_debug_dev(dv, "received notify message: 0x%x\n", 262 notify); 263 #endif 264 } 265 break; 266 267 default: 268 aprint_error_dev(dv, "received unknown notify message: 0x%x\n", 269 notify); 270 } 271 } 272 273 static void 274 acpiacad_init_envsys(device_t dv) 275 { 276 struct acpiacad_softc *sc = device_private(dv); 277 278 sc->sc_sme = sysmon_envsys_create(); 279 sc->sc_sensor.state = ENVSYS_SVALID; 280 sc->sc_sensor.units = ENVSYS_INDICATOR; 281 strlcpy(sc->sc_sensor.desc, "connected", sizeof(sc->sc_sensor.desc)); 282 283 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 284 aprint_error_dev(dv, "unable to add sensor\n"); 285 sysmon_envsys_destroy(sc->sc_sme); 286 return; 287 } 288 289 sc->sc_sme->sme_name = device_xname(dv); 290 sc->sc_sme->sme_cookie = dv; 291 sc->sc_sme->sme_refresh = acpiacad_refresh; 292 sc->sc_sme->sme_class = SME_CLASS_ACADAPTER; 293 294 if (sysmon_envsys_register(sc->sc_sme)) { 295 aprint_error_dev(dv, "unable to register with sysmon\n"); 296 sysmon_envsys_destroy(sc->sc_sme); 297 } 298 } 299 300 static void 301 acpiacad_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 302 { 303 device_t dv = sme->sme_cookie; 304 struct acpiacad_softc *sc = device_private(dv); 305 306 if (!AACAD_ISSET(sc, AACAD_F_AVAILABLE)) 307 acpiacad_get_status(dv); 308 } 309