1 /* $NetBSD: acpi_acad.c,v 1.19 2006/04/30 20:44:35 rpaulo 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.19 2006/04/30 20:44:35 rpaulo Exp $"); 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/device.h> 52 53 #include <dev/acpi/acpica.h> 54 #include <dev/acpi/acpireg.h> 55 #include <dev/acpi/acpivar.h> 56 57 #include <dev/sysmon/sysmonvar.h> 58 59 #define ACPIACAD_NSENSORS 2 60 61 /* sensor indexes */ 62 #define ACPIACAD_CONNECTED 0 63 #define ACPIACAD_DISCONNECTED 1 64 65 struct acpiacad_softc { 66 struct device sc_dev; /* base device glue */ 67 struct acpi_devnode *sc_node; /* our ACPI devnode */ 68 int sc_flags; /* see below */ 69 70 struct sysmon_envsys sc_sysmon; 71 struct sysmon_pswitch sc_smpsw; /* our sysmon glue */ 72 struct envsys_basic_info sc_info[ACPIACAD_NSENSORS]; 73 struct envsys_tre_data sc_data[ACPIACAD_NSENSORS]; 74 75 struct simplelock sc_lock; 76 }; 77 78 static const struct envsys_range acpiacad_range[] = { 79 { 0, 2, ENVSYS_INDICATOR }, 80 { 1, 0, -1}, 81 }; 82 83 static const char * const acad_hid[] = { 84 "ACPI0003", 85 NULL 86 }; 87 88 #define AACAD_F_VERBOSE 0x01 /* verbose events */ 89 #define AACAD_F_AVAILABLE 0x02 /* information is available */ 90 #define AACAD_F_LOCKED 0x04 /* is locked? */ 91 92 #define AACAD_SET(sc, f) (void)((sc)->sc_flags |= (f)) 93 #define AACAD_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f)) 94 #define AACAD_ISSET(sc, f) ((sc)->sc_flags & (f)) 95 96 #define AACAD_ASSERT_LOCKED(sc) \ 97 do { \ 98 if (!((sc)->sc_flags & AACAD_F_LOCKED)) \ 99 panic("acpi_bat (expected to be locked)"); \ 100 } while(/*CONSTCOND*/0) 101 #define AACAD_ASSERT_UNLOCKED(sc) \ 102 do { \ 103 if (((sc)->sc_flags & AACAD_F_LOCKED)) \ 104 panic("acpi_bat (expected to be unlocked)"); \ 105 } while(/*CONSTCOND*/0) 106 #define AACAD_LOCK(sc, s) \ 107 do { \ 108 AACAD_ASSERT_UNLOCKED(sc); \ 109 (s) = splhigh(); \ 110 simple_lock(&(sc)->sc_lock); \ 111 AACAD_SET((sc), AACAD_F_LOCKED); \ 112 } while(/*CONSTCOND*/0) 113 #define AACAD_UNLOCK(sc, s) \ 114 do { \ 115 AACAD_ASSERT_LOCKED(sc); \ 116 AACAD_CLEAR((sc), AACAD_F_LOCKED); \ 117 simple_unlock(&(sc)->sc_lock); \ 118 splx((s)); \ 119 } while(/*CONSTCOND*/0) 120 121 static int acpiacad_match(struct device *, struct cfdata *, void *); 122 static void acpiacad_attach(struct device *, struct device *, void *); 123 124 CFATTACH_DECL(acpiacad, sizeof(struct acpiacad_softc), 125 acpiacad_match, acpiacad_attach, NULL, NULL); 126 127 static void acpiacad_get_status(void *); 128 static void acpiacad_clear_status(struct acpiacad_softc *); 129 static void acpiacad_notify_handler(ACPI_HANDLE, UINT32, void *); 130 static void acpiacad_init_envsys(struct acpiacad_softc *); 131 static int acpiacad_gtredata(struct sysmon_envsys *, struct envsys_tre_data *); 132 static int acpiacad_streinfo(struct sysmon_envsys *, struct envsys_basic_info *); 133 134 /* 135 * acpiacad_match: 136 * 137 * Autoconfiguration `match' routine. 138 */ 139 static int 140 acpiacad_match(struct device *parent, struct cfdata *match, void *aux) 141 { 142 struct acpi_attach_args *aa = aux; 143 144 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 145 return 0; 146 147 return acpi_match_hid(aa->aa_node->ad_devinfo, acad_hid); 148 } 149 150 /* 151 * acpiacad_attach: 152 * 153 * Autoconfiguration `attach' routine. 154 */ 155 static void 156 acpiacad_attach(struct device *parent, struct device *self, void *aux) 157 { 158 struct acpiacad_softc *sc = (void *) self; 159 struct acpi_attach_args *aa = aux; 160 ACPI_STATUS rv; 161 162 aprint_naive(": ACPI AC Adapter\n"); 163 aprint_normal(": ACPI AC Adapter\n"); 164 165 sc->sc_node = aa->aa_node; 166 simple_lock_init(&sc->sc_lock); 167 168 sc->sc_smpsw.smpsw_name = sc->sc_dev.dv_xname; 169 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER; 170 if (sysmon_pswitch_register(&sc->sc_smpsw) != 0) { 171 aprint_error("%s: unable to register with sysmon\n", 172 sc->sc_dev.dv_xname); 173 return; 174 } 175 176 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 177 ACPI_DEVICE_NOTIFY, acpiacad_notify_handler, sc); 178 if (ACPI_FAILURE(rv)) { 179 aprint_error("%s: unable to register DEVICE NOTIFY handler: %s\n", 180 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 181 return; 182 } 183 184 /* XXX See acpiacad_notify_handler() */ 185 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 186 ACPI_SYSTEM_NOTIFY, acpiacad_notify_handler, sc); 187 if (ACPI_FAILURE(rv)) { 188 aprint_error("%s: unable to register SYSTEM NOTIFY handler: %s\n", 189 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 190 return; 191 } 192 193 #ifdef ACPI_ACAD_DEBUG 194 /* Display the current state. */ 195 sc->sc_flags = AACAD_F_VERBOSE; 196 #endif 197 198 acpiacad_init_envsys(sc); 199 } 200 201 /* 202 * acpiacad_get_status: 203 * 204 * Get, and possibly display, the current AC line status. 205 */ 206 static void 207 acpiacad_get_status(void *arg) 208 { 209 struct acpiacad_softc *sc = arg; 210 ACPI_INTEGER status; 211 int s; 212 ACPI_STATUS rv; 213 214 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 215 if (ACPI_FAILURE(rv)) 216 return; 217 218 AACAD_LOCK(sc, s); 219 sc->sc_data[ACPIACAD_CONNECTED].cur.data_s = !!(status); 220 sc->sc_data[ACPIACAD_DISCONNECTED].cur.data_s = !(status); 221 AACAD_SET(sc, AACAD_F_AVAILABLE); 222 AACAD_UNLOCK(sc, s); 223 224 /* 225 * PSWITCH_EVENT_RELEASED : AC offline 226 * PSWITCH_EVENT_PRESSED : AC online 227 */ 228 229 sysmon_pswitch_event(&sc->sc_smpsw, status == 0 ? 230 PSWITCH_EVENT_RELEASED : PSWITCH_EVENT_PRESSED); 231 232 if (AACAD_ISSET(sc, AACAD_F_VERBOSE)) 233 printf("%s: AC adapter %sconnected\n", 234 sc->sc_dev.dv_xname, status == 0 ? "not " : ""); 235 } 236 237 /* 238 * Clear status 239 */ 240 static void 241 acpiacad_clear_status(struct acpiacad_softc *sc) 242 { 243 244 AACAD_ASSERT_LOCKED(sc); 245 246 sc->sc_data[ACPIACAD_CONNECTED].cur.data_s = 0; 247 sc->sc_data[ACPIACAD_DISCONNECTED].cur.data_s = 0; 248 AACAD_CLEAR(sc, AACAD_F_AVAILABLE); 249 } 250 251 /* 252 * acpiacad_notify_handler: 253 * 254 * Callback from ACPI interrupt handler to notify us of an event. 255 */ 256 static void 257 acpiacad_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 258 { 259 struct acpiacad_softc *sc = context; 260 int rv, s; 261 262 switch (notify) { 263 /* 264 * XXX So, BusCheck is not exactly what I would expect, 265 * but at least my IBM T21 sends it on AC adapter status 266 * change. --thorpej@wasabisystems.com 267 */ 268 /* 269 * XXX My Acer TravelMate 291 sends DeviceCheck on AC 270 * adapter status change. 271 * --rpaulo@NetBSD.org 272 */ 273 case ACPI_NOTIFY_BusCheck: 274 case ACPI_NOTIFY_DeviceCheck: 275 case ACPI_NOTIFY_PowerSourceStatusChanged: 276 #ifdef ACPI_ACAD_DEBUG 277 printf("%s: received notify message: 0x%x\n", 278 sc->sc_dev.dv_xname, notify); 279 #endif 280 AACAD_LOCK(sc, s); 281 acpiacad_clear_status(sc); 282 AACAD_UNLOCK(sc, s); 283 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 284 acpiacad_get_status, sc); 285 if (ACPI_FAILURE(rv)) 286 printf("%s: unable to queue status check: %s\n", 287 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 288 break; 289 290 default: 291 printf("%s: received unknown notify message: 0x%x\n", 292 sc->sc_dev.dv_xname, notify); 293 } 294 } 295 296 static void 297 acpiacad_init_envsys(struct acpiacad_softc *sc) 298 { 299 int i; 300 301 sc->sc_sysmon.sme_ranges = acpiacad_range; 302 303 for (i=0; i<ACPIACAD_NSENSORS; i++) { 304 sc->sc_data[i].sensor = sc->sc_info[i].sensor = i; 305 sc->sc_data[i].validflags |= (ENVSYS_FVALID | ENVSYS_FCURVALID); 306 sc->sc_info[i].validflags = ENVSYS_FVALID; 307 sc->sc_data[i].warnflags = 0; 308 } 309 310 #define INITDATA(index, unit, string) \ 311 sc->sc_data[index].units = unit; \ 312 sc->sc_info[index].units = unit; \ 313 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \ 314 "%s %s", sc->sc_dev.dv_xname, string); \ 315 316 INITDATA(ACPIACAD_CONNECTED, ENVSYS_INDICATOR, "connected"); 317 INITDATA(ACPIACAD_DISCONNECTED, ENVSYS_INDICATOR, "disconnected"); 318 319 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 320 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 321 sc->sc_sysmon.sme_cookie = sc; 322 sc->sc_sysmon.sme_gtredata = acpiacad_gtredata; 323 sc->sc_sysmon.sme_streinfo = acpiacad_streinfo; 324 sc->sc_sysmon.sme_nsensors = ACPIACAD_NSENSORS; 325 sc->sc_sysmon.sme_envsys_version = 1000; 326 327 if (sysmon_envsys_register(&sc->sc_sysmon)) 328 aprint_error("%s: unable to register with sysmon\n", 329 sc->sc_dev.dv_xname); 330 } 331 332 static int 333 acpiacad_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 334 { 335 struct acpiacad_softc *sc = sme->sme_cookie; 336 337 if (!AACAD_ISSET(sc, AACAD_F_AVAILABLE)) 338 acpiacad_get_status(sc); 339 340 /* XXX locking */ 341 *tred = sc->sc_data[tred->sensor]; 342 /* XXX locking */ 343 344 return 0; 345 } 346 347 348 static int 349 acpiacad_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 350 { 351 352 /* XXX Not implemented */ 353 binfo->validflags = 0; 354 355 return 0; 356 } 357