1 /* $NetBSD: acpi_acad.c,v 1.20 2006/10/12 01:30:54 christos 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.20 2006/10/12 01:30:54 christos 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 __unused, struct cfdata *match __unused, 141 void *aux) 142 { 143 struct acpi_attach_args *aa = aux; 144 145 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 146 return 0; 147 148 return acpi_match_hid(aa->aa_node->ad_devinfo, acad_hid); 149 } 150 151 /* 152 * acpiacad_attach: 153 * 154 * Autoconfiguration `attach' routine. 155 */ 156 static void 157 acpiacad_attach(struct device *parent __unused, struct device *self, void *aux) 158 { 159 struct acpiacad_softc *sc = (void *) self; 160 struct acpi_attach_args *aa = aux; 161 ACPI_STATUS rv; 162 163 aprint_naive(": ACPI AC Adapter\n"); 164 aprint_normal(": ACPI AC Adapter\n"); 165 166 sc->sc_node = aa->aa_node; 167 simple_lock_init(&sc->sc_lock); 168 169 sc->sc_smpsw.smpsw_name = sc->sc_dev.dv_xname; 170 sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_ACADAPTER; 171 if (sysmon_pswitch_register(&sc->sc_smpsw) != 0) { 172 aprint_error("%s: unable to register with sysmon\n", 173 sc->sc_dev.dv_xname); 174 return; 175 } 176 177 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 178 ACPI_DEVICE_NOTIFY, acpiacad_notify_handler, sc); 179 if (ACPI_FAILURE(rv)) { 180 aprint_error("%s: unable to register DEVICE NOTIFY handler: %s\n", 181 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 182 return; 183 } 184 185 /* XXX See acpiacad_notify_handler() */ 186 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 187 ACPI_SYSTEM_NOTIFY, acpiacad_notify_handler, sc); 188 if (ACPI_FAILURE(rv)) { 189 aprint_error("%s: unable to register SYSTEM NOTIFY handler: %s\n", 190 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 191 return; 192 } 193 194 #ifdef ACPI_ACAD_DEBUG 195 /* Display the current state. */ 196 sc->sc_flags = AACAD_F_VERBOSE; 197 #endif 198 199 acpiacad_init_envsys(sc); 200 } 201 202 /* 203 * acpiacad_get_status: 204 * 205 * Get, and possibly display, the current AC line status. 206 */ 207 static void 208 acpiacad_get_status(void *arg) 209 { 210 struct acpiacad_softc *sc = arg; 211 ACPI_INTEGER status; 212 int s; 213 ACPI_STATUS rv; 214 215 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 216 if (ACPI_FAILURE(rv)) 217 return; 218 219 AACAD_LOCK(sc, s); 220 sc->sc_data[ACPIACAD_CONNECTED].cur.data_s = !!(status); 221 sc->sc_data[ACPIACAD_DISCONNECTED].cur.data_s = !(status); 222 AACAD_SET(sc, AACAD_F_AVAILABLE); 223 AACAD_UNLOCK(sc, s); 224 225 /* 226 * PSWITCH_EVENT_RELEASED : AC offline 227 * PSWITCH_EVENT_PRESSED : AC online 228 */ 229 230 sysmon_pswitch_event(&sc->sc_smpsw, status == 0 ? 231 PSWITCH_EVENT_RELEASED : PSWITCH_EVENT_PRESSED); 232 233 if (AACAD_ISSET(sc, AACAD_F_VERBOSE)) 234 printf("%s: AC adapter %sconnected\n", 235 sc->sc_dev.dv_xname, status == 0 ? "not " : ""); 236 } 237 238 /* 239 * Clear status 240 */ 241 static void 242 acpiacad_clear_status(struct acpiacad_softc *sc) 243 { 244 245 AACAD_ASSERT_LOCKED(sc); 246 247 sc->sc_data[ACPIACAD_CONNECTED].cur.data_s = 0; 248 sc->sc_data[ACPIACAD_DISCONNECTED].cur.data_s = 0; 249 AACAD_CLEAR(sc, AACAD_F_AVAILABLE); 250 } 251 252 /* 253 * acpiacad_notify_handler: 254 * 255 * Callback from ACPI interrupt handler to notify us of an event. 256 */ 257 static void 258 acpiacad_notify_handler(ACPI_HANDLE handle __unused, UINT32 notify, 259 void *context) 260 { 261 struct acpiacad_softc *sc = context; 262 int rv, s; 263 264 switch (notify) { 265 /* 266 * XXX So, BusCheck is not exactly what I would expect, 267 * but at least my IBM T21 sends it on AC adapter status 268 * change. --thorpej@wasabisystems.com 269 */ 270 /* 271 * XXX My Acer TravelMate 291 sends DeviceCheck on AC 272 * adapter status change. 273 * --rpaulo@NetBSD.org 274 */ 275 case ACPI_NOTIFY_BusCheck: 276 case ACPI_NOTIFY_DeviceCheck: 277 case ACPI_NOTIFY_PowerSourceStatusChanged: 278 #ifdef ACPI_ACAD_DEBUG 279 printf("%s: received notify message: 0x%x\n", 280 sc->sc_dev.dv_xname, notify); 281 #endif 282 AACAD_LOCK(sc, s); 283 acpiacad_clear_status(sc); 284 AACAD_UNLOCK(sc, s); 285 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 286 acpiacad_get_status, sc); 287 if (ACPI_FAILURE(rv)) 288 printf("%s: unable to queue status check: %s\n", 289 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 290 break; 291 292 default: 293 printf("%s: received unknown notify message: 0x%x\n", 294 sc->sc_dev.dv_xname, notify); 295 } 296 } 297 298 static void 299 acpiacad_init_envsys(struct acpiacad_softc *sc) 300 { 301 int i; 302 303 sc->sc_sysmon.sme_ranges = acpiacad_range; 304 305 for (i=0; i<ACPIACAD_NSENSORS; i++) { 306 sc->sc_data[i].sensor = sc->sc_info[i].sensor = i; 307 sc->sc_data[i].validflags |= (ENVSYS_FVALID | ENVSYS_FCURVALID); 308 sc->sc_info[i].validflags = ENVSYS_FVALID; 309 sc->sc_data[i].warnflags = 0; 310 } 311 312 #define INITDATA(index, unit, string) \ 313 sc->sc_data[index].units = unit; \ 314 sc->sc_info[index].units = unit; \ 315 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \ 316 "%s %s", sc->sc_dev.dv_xname, string); \ 317 318 INITDATA(ACPIACAD_CONNECTED, ENVSYS_INDICATOR, "connected"); 319 INITDATA(ACPIACAD_DISCONNECTED, ENVSYS_INDICATOR, "disconnected"); 320 321 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 322 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 323 sc->sc_sysmon.sme_cookie = sc; 324 sc->sc_sysmon.sme_gtredata = acpiacad_gtredata; 325 sc->sc_sysmon.sme_streinfo = acpiacad_streinfo; 326 sc->sc_sysmon.sme_nsensors = ACPIACAD_NSENSORS; 327 sc->sc_sysmon.sme_envsys_version = 1000; 328 329 if (sysmon_envsys_register(&sc->sc_sysmon)) 330 aprint_error("%s: unable to register with sysmon\n", 331 sc->sc_dev.dv_xname); 332 } 333 334 static int 335 acpiacad_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 336 { 337 struct acpiacad_softc *sc = sme->sme_cookie; 338 339 if (!AACAD_ISSET(sc, AACAD_F_AVAILABLE)) 340 acpiacad_get_status(sc); 341 342 /* XXX locking */ 343 *tred = sc->sc_data[tred->sensor]; 344 /* XXX locking */ 345 346 return 0; 347 } 348 349 350 static int 351 acpiacad_streinfo(struct sysmon_envsys *sme __unused, 352 struct envsys_basic_info *binfo) 353 { 354 355 /* XXX Not implemented */ 356 binfo->validflags = 0; 357 358 return 0; 359 } 360