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