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