1 /* $NetBSD: acpi_acad.c,v 1.29 2007/11/16 08:00:13 xtraeme 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.29 2007/11/16 08:00:13 xtraeme 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_DRIVER, 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 acpiacad_init_envsys(self); 155 } 156 157 /* 158 * acpiacad_get_status: 159 * 160 * Get, and possibly display, the current AC line status. 161 */ 162 static void 163 acpiacad_get_status(void *arg) 164 { 165 device_t dv = arg; 166 struct acpiacad_softc *sc = device_private(dv); 167 ACPI_INTEGER status; 168 ACPI_STATUS rv; 169 170 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 171 if (ACPI_FAILURE(rv)) 172 return; 173 174 mutex_enter(&sc->sc_mtx); 175 if (sc->sc_status != status) { 176 sc->sc_status = status; 177 if (status) 178 sc->sc_sensor.value_cur = 1; 179 else 180 sc->sc_sensor.value_cur = 0; 181 AACAD_SET(sc, AACAD_F_STCHANGED); 182 sc->sc_notifysent = 0; 183 } 184 185 sc->sc_sensor.state = ENVSYS_SVALID; 186 AACAD_SET(sc, AACAD_F_AVAILABLE); 187 /* 188 * If status has changed, send the event. 189 * 190 * PSWITCH_EVENT_RELEASED : AC offline 191 * PSWITCH_EVENT_PRESSED : AC online 192 */ 193 if (AACAD_ISSET(sc, AACAD_F_STCHANGED)) { 194 sysmon_pswitch_event(&sc->sc_smpsw, status ? 195 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 196 if (AACAD_ISSET(sc, AACAD_F_VERBOSE)) 197 aprint_verbose_dev(dv, "AC adapter %sconnected\n", 198 status == 0 ? "not " : ""); 199 } 200 mutex_exit(&sc->sc_mtx); 201 } 202 203 /* 204 * Clear status 205 */ 206 static void 207 acpiacad_clear_status(struct acpiacad_softc *sc) 208 { 209 sc->sc_sensor.state = ENVSYS_SINVALID; 210 AACAD_CLEAR(sc, AACAD_F_AVAILABLE); 211 AACAD_CLEAR(sc, AACAD_F_STCHANGED); 212 } 213 214 /* 215 * acpiacad_notify_handler: 216 * 217 * Callback from ACPI interrupt handler to notify us of an event. 218 */ 219 static void 220 acpiacad_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 221 { 222 device_t dv = context; 223 struct acpiacad_softc *sc = device_private(dv); 224 int rv; 225 226 switch (notify) { 227 /* 228 * XXX So, BusCheck is not exactly what I would expect, 229 * but at least my IBM T21 sends it on AC adapter status 230 * change. --thorpej@wasabisystems.com 231 */ 232 /* 233 * XXX My Acer TravelMate 291 sends DeviceCheck on AC 234 * adapter status change. 235 * --rpaulo@NetBSD.org 236 */ 237 /* 238 * XXX Sony VAIO VGN-N250E sends BatteryInformationChanged on AC 239 * adapter status change. 240 * --jmcneill@NetBSD.org 241 */ 242 case ACPI_NOTIFY_BusCheck: 243 case ACPI_NOTIFY_DeviceCheck: 244 case ACPI_NOTIFY_PowerSourceStatusChanged: 245 case ACPI_NOTIFY_BatteryInformationChanged: 246 mutex_enter(&sc->sc_mtx); 247 acpiacad_clear_status(sc); 248 mutex_exit(&sc->sc_mtx); 249 if (sc->sc_status == -1 || !sc->sc_notifysent) { 250 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 251 acpiacad_get_status, dv); 252 if (ACPI_FAILURE(rv)) 253 aprint_error_dev(dv, 254 "unable to queue status check: %s\n", 255 AcpiFormatException(rv)); 256 sc->sc_notifysent = 1; 257 #ifdef ACPI_ACAD_DEBUG 258 aprint_debug_dev(dv, "received notify message: 0x%x\n", 259 notify); 260 #endif 261 } 262 break; 263 264 default: 265 aprint_error_dev(dv, "received unknown notify message: 0x%x\n", 266 notify); 267 } 268 } 269 270 static void 271 acpiacad_init_envsys(device_t dv) 272 { 273 struct acpiacad_softc *sc = device_private(dv); 274 275 sc->sc_sme = sysmon_envsys_create(); 276 sc->sc_sensor.state = ENVSYS_SVALID; 277 sc->sc_sensor.units = ENVSYS_INDICATOR; 278 strlcpy(sc->sc_sensor.desc, "connected", sizeof(sc->sc_sensor.desc)); 279 280 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 281 aprint_error_dev(dv, "unable to add sensor\n"); 282 sysmon_envsys_destroy(sc->sc_sme); 283 return; 284 } 285 286 sc->sc_sme->sme_name = device_xname(dv); 287 sc->sc_sme->sme_cookie = dv; 288 sc->sc_sme->sme_refresh = acpiacad_refresh; 289 sc->sc_sme->sme_class = SME_CLASS_ACADAPTER; 290 291 if (sysmon_envsys_register(sc->sc_sme)) { 292 aprint_error_dev(dv, "unable to register with sysmon\n"); 293 sysmon_envsys_destroy(sc->sc_sme); 294 } 295 } 296 297 static void 298 acpiacad_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 299 { 300 device_t dv = sme->sme_cookie; 301 struct acpiacad_softc *sc = device_private(dv); 302 303 if (!AACAD_ISSET(sc, AACAD_F_AVAILABLE)) 304 acpiacad_get_status(dv); 305 } 306