1 /* $NetBSD: acpi_acad.c,v 1.43 2010/04/15 07:02:24 jruoho 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 /* 39 * ACPI AC Adapter driver. 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: acpi_acad.c,v 1.43 2010/04/15 07:02:24 jruoho Exp $"); 44 45 #include <sys/param.h> 46 #include <sys/device.h> 47 #include <sys/module.h> 48 #include <sys/mutex.h> 49 #include <sys/systm.h> 50 51 #include <dev/acpi/acpireg.h> 52 #include <dev/acpi/acpivar.h> 53 54 #define _COMPONENT ACPI_ACAD_COMPONENT 55 ACPI_MODULE_NAME ("acpi_acad") 56 57 struct acpiacad_softc { 58 struct acpi_devnode *sc_node; 59 struct sysmon_envsys *sc_sme; 60 struct sysmon_pswitch sc_smpsw; 61 envsys_data_t sc_sensor; 62 kmutex_t sc_mutex; 63 int sc_status; 64 }; 65 66 static const char * const acad_hid[] = { 67 "ACPI0003", 68 NULL 69 }; 70 71 static int acpiacad_match(device_t, cfdata_t, void *); 72 static void acpiacad_attach(device_t, device_t, void *); 73 static int acpiacad_detach(device_t, int); 74 static bool acpiacad_resume(device_t, const pmf_qual_t *); 75 static void acpiacad_get_status(void *); 76 static void acpiacad_notify_handler(ACPI_HANDLE, uint32_t, void *); 77 static void acpiacad_init_envsys(device_t); 78 79 CFATTACH_DECL_NEW(acpiacad, sizeof(struct acpiacad_softc), 80 acpiacad_match, acpiacad_attach, acpiacad_detach, NULL); 81 82 /* 83 * acpiacad_match: 84 * 85 * Autoconfiguration `match' routine. 86 */ 87 static int 88 acpiacad_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 return acpi_match_hid(aa->aa_node->ad_devinfo, acad_hid); 96 } 97 98 /* 99 * acpiacad_attach: 100 * 101 * Autoconfiguration `attach' routine. 102 */ 103 static void 104 acpiacad_attach(device_t parent, device_t self, void *aux) 105 { 106 struct acpiacad_softc *sc = device_private(self); 107 struct acpi_attach_args *aa = aux; 108 109 aprint_naive(": ACPI AC Adapter\n"); 110 aprint_normal(": ACPI AC Adapter\n"); 111 112 sc->sc_sme = NULL; 113 sc->sc_status = -1; 114 sc->sc_node = aa->aa_node; 115 116 acpiacad_init_envsys(self); 117 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); 118 119 sc->sc_smpsw.smpsw_name = device_xname(self); 120 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER; 121 122 (void)sysmon_pswitch_register(&sc->sc_smpsw); 123 (void)pmf_device_register(self, NULL, acpiacad_resume); 124 (void)acpi_register_notify(sc->sc_node, acpiacad_notify_handler); 125 } 126 127 /* 128 * acpiacad_detach: 129 * 130 * Autoconfiguration `detach' routine. 131 */ 132 static int 133 acpiacad_detach(device_t self, int flags) 134 { 135 struct acpiacad_softc *sc = device_private(self); 136 137 acpi_deregister_notify(sc->sc_node); 138 139 mutex_destroy(&sc->sc_mutex); 140 141 if (sc->sc_sme != NULL) 142 sysmon_envsys_unregister(sc->sc_sme); 143 144 pmf_device_deregister(self); 145 sysmon_pswitch_unregister(&sc->sc_smpsw); 146 147 return 0; 148 } 149 150 /* 151 * acpiacad_resume: 152 * 153 * Queue a new status check. 154 */ 155 static bool 156 acpiacad_resume(device_t dv, const pmf_qual_t *qual) 157 { 158 159 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpiacad_get_status, dv); 160 161 return true; 162 } 163 164 /* 165 * acpiacad_get_status: 166 * 167 * Get, and possibly display, the current AC line status. 168 */ 169 static void 170 acpiacad_get_status(void *arg) 171 { 172 device_t dv = arg; 173 struct acpiacad_softc *sc = device_private(dv); 174 ACPI_INTEGER status; 175 ACPI_STATUS rv; 176 177 mutex_enter(&sc->sc_mutex); 178 179 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 180 181 if (ACPI_FAILURE(rv)) 182 goto fail; 183 184 if (status != 0 && status != 1) { 185 rv = AE_BAD_VALUE; 186 goto fail; 187 } 188 189 if (sc->sc_status != status) { 190 191 /* 192 * If status has changed, send the event: 193 * 194 * PSWITCH_EVENT_PRESSED : _PSR = 1 : AC online. 195 * PSWITCH_EVENT_RELEASED : _PSR = 0 : AC offline. 196 */ 197 sysmon_pswitch_event(&sc->sc_smpsw, (status != 0) ? 198 PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 199 200 aprint_debug_dev(dv, "AC adapter %sconnected\n", 201 status == 0 ? "not " : ""); 202 } 203 204 sc->sc_status = status; 205 sc->sc_sensor.state = ENVSYS_SVALID; 206 sc->sc_sensor.value_cur = sc->sc_status; 207 208 mutex_exit(&sc->sc_mutex); 209 210 return; 211 212 fail: 213 sc->sc_status = -1; 214 sc->sc_sensor.state = ENVSYS_SINVALID; 215 216 aprint_debug_dev(dv, "failed to evaluate _PSR: %s\n", 217 AcpiFormatException(rv)); 218 219 mutex_exit(&sc->sc_mutex); 220 } 221 222 /* 223 * acpiacad_notify_handler: 224 * 225 * Callback from ACPI interrupt handler to notify us of an event. 226 */ 227 static void 228 acpiacad_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context) 229 { 230 static const int handler = OSL_NOTIFY_HANDLER; 231 device_t dv = context; 232 233 switch (notify) { 234 /* 235 * XXX So, BusCheck is not exactly what I would expect, 236 * but at least my IBM T21 sends it on AC adapter status 237 * change. --thorpej@wasabisystems.com 238 */ 239 /* 240 * XXX My Acer TravelMate 291 sends DeviceCheck on AC 241 * adapter status change. 242 * --rpaulo@NetBSD.org 243 */ 244 /* 245 * XXX Sony VAIO VGN-N250E sends BatteryInformationChanged on AC 246 * adapter status change. 247 * --jmcneill@NetBSD.org 248 */ 249 case ACPI_NOTIFY_BusCheck: 250 case ACPI_NOTIFY_DeviceCheck: 251 case ACPI_NOTIFY_PowerSourceStatusChanged: 252 case ACPI_NOTIFY_BatteryInformationChanged: 253 (void)AcpiOsExecute(handler, acpiacad_get_status, dv); 254 break; 255 256 default: 257 aprint_error_dev(dv, "unknown notify 0x%02X\n", notify); 258 } 259 } 260 261 static void 262 acpiacad_init_envsys(device_t dv) 263 { 264 struct acpiacad_softc *sc = device_private(dv); 265 266 sc->sc_sme = sysmon_envsys_create(); 267 268 sc->sc_sensor.state = ENVSYS_SINVALID; 269 sc->sc_sensor.units = ENVSYS_INDICATOR; 270 271 (void)strlcpy(sc->sc_sensor.desc, "connected", ENVSYS_DESCLEN); 272 273 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0) 274 goto fail; 275 276 sc->sc_sme->sme_name = device_xname(dv); 277 sc->sc_sme->sme_class = SME_CLASS_ACADAPTER; 278 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 279 280 if (sysmon_envsys_register(sc->sc_sme) != 0) 281 goto fail; 282 283 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpiacad_get_status, dv); 284 285 return; 286 287 fail: 288 aprint_error_dev(dv, "failed to initialize sysmon\n"); 289 sysmon_envsys_destroy(sc->sc_sme); 290 sc->sc_sme = NULL; 291 } 292 293 #ifdef _MODULE 294 295 MODULE(MODULE_CLASS_DRIVER, acpiacad, NULL); 296 CFDRIVER_DECL(acpiacad, DV_DULL, NULL); 297 298 static int acpiacadloc[] = { -1 }; 299 extern struct cfattach acpiacad_ca; 300 301 static struct cfparent acpiparent = { 302 "acpinodebus", NULL, DVUNIT_ANY 303 }; 304 305 static struct cfdata acpiacad_cfdata[] = { 306 { 307 .cf_name = "acpiacad", 308 .cf_atname = "acpiacad", 309 .cf_unit = 0, 310 .cf_fstate = FSTATE_STAR, 311 .cf_loc = acpiacadloc, 312 .cf_flags = 0, 313 .cf_pspec = &acpiparent, 314 }, 315 316 { NULL } 317 }; 318 319 static int 320 acpiacad_modcmd(modcmd_t cmd, void *context) 321 { 322 int err; 323 324 switch (cmd) { 325 326 case MODULE_CMD_INIT: 327 328 err = config_cfdriver_attach(&acpiacad_cd); 329 330 if (err != 0) 331 return err; 332 333 err = config_cfattach_attach("acpiacad", &acpiacad_ca); 334 335 if (err != 0) { 336 config_cfdriver_detach(&acpiacad_cd); 337 return err; 338 } 339 340 err = config_cfdata_attach(acpiacad_cfdata, 1); 341 342 if (err != 0) { 343 config_cfattach_detach("acpiacad", &acpiacad_ca); 344 config_cfdriver_detach(&acpiacad_cd); 345 return err; 346 } 347 348 return 0; 349 350 case MODULE_CMD_FINI: 351 352 err = config_cfdata_detach(acpiacad_cfdata); 353 354 if (err != 0) 355 return err; 356 357 config_cfattach_detach("acpiacad", &acpiacad_ca); 358 config_cfdriver_detach(&acpiacad_cd); 359 360 return 0; 361 362 default: 363 return ENOTTY; 364 } 365 } 366 367 #endif /* _MODULE */ 368