1 /* $NetBSD: acpi_acad.c,v 1.13 2004/03/24 11:32:09 kanaoka 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.13 2004/03/24 11:32:09 kanaoka 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 ACPI_INTEGER status; 201 int s; 202 ACPI_STATUS rv; 203 204 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PSR", &status); 205 if (ACPI_FAILURE(rv)) 206 return; 207 208 AACAD_LOCK(sc, s); 209 sc->sc_data[ACPIACAD_CONNECTED].cur.data_s = !!(status); 210 sc->sc_data[ACPIACAD_DISCONNECTED].cur.data_s = !(status); 211 AACAD_SET(sc, AACAD_F_AVAILABLE); 212 AACAD_UNLOCK(sc, s); 213 214 if (AACAD_ISSET(sc, AACAD_F_VERBOSE)) 215 printf("%s: AC adapter %sconnected\n", 216 sc->sc_dev.dv_xname, status == 0 ? "not " : ""); 217 } 218 219 /* 220 * Clear status 221 */ 222 void 223 acpiacad_clear_status(struct acpiacad_softc *sc) 224 { 225 226 AACAD_ASSERT_LOCKED(sc); 227 228 sc->sc_data[ACPIACAD_CONNECTED].cur.data_s = 0; 229 sc->sc_data[ACPIACAD_DISCONNECTED].cur.data_s = 0; 230 AACAD_CLEAR(sc, AACAD_F_AVAILABLE); 231 } 232 233 /* 234 * acpiacad_notify_handler: 235 * 236 * Callback from ACPI interrupt handler to notify us of an event. 237 */ 238 void 239 acpiacad_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) 240 { 241 struct acpiacad_softc *sc = context; 242 int rv, s; 243 244 switch (notify) { 245 /* 246 * XXX So, BusCheck is not exactly what I would expect, 247 * but at least my IBM T21 sends it on AC adapter status 248 * change. --thorpej@wasabisystems.com 249 */ 250 case ACPI_NOTIFY_BusCheck: 251 case ACPI_NOTIFY_PowerSourceStatusChanged: 252 #ifdef ACPI_ACAD_DEBUG 253 printf("%s: received notify message: 0x%x\n", 254 sc->sc_dev.dv_xname, notify); 255 #endif 256 AACAD_LOCK(sc, s); 257 acpiacad_clear_status(sc); 258 AACAD_UNLOCK(sc, s); 259 rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, 260 acpiacad_get_status, sc); 261 if (ACPI_FAILURE(rv)) 262 printf("%s: unable to queue status check: %s\n", 263 sc->sc_dev.dv_xname, AcpiFormatException(rv)); 264 break; 265 266 default: 267 printf("%s: received unknown notify message: 0x%x\n", 268 sc->sc_dev.dv_xname, notify); 269 } 270 } 271 272 void 273 acpiacad_init_envsys(struct acpiacad_softc *sc) 274 { 275 int i; 276 277 sc->sc_sysmon.sme_ranges = acpiacad_range; 278 279 for (i=0; i<ACPIACAD_NSENSORS; i++) { 280 sc->sc_data[i].sensor = sc->sc_info[i].sensor = i; 281 sc->sc_data[i].validflags |= (ENVSYS_FVALID | ENVSYS_FCURVALID); 282 sc->sc_info[i].validflags = ENVSYS_FVALID; 283 sc->sc_data[i].warnflags = 0; 284 } 285 286 #define INITDATA(index, unit, string) \ 287 sc->sc_data[index].units = unit; \ 288 sc->sc_info[index].units = unit; \ 289 snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info->desc), \ 290 "%s %s", sc->sc_dev.dv_xname, string); \ 291 292 INITDATA(ACPIACAD_CONNECTED, ENVSYS_INDICATOR, "connected"); 293 INITDATA(ACPIACAD_DISCONNECTED, ENVSYS_INDICATOR, "disconnected"); 294 295 sc->sc_sysmon.sme_sensor_info = sc->sc_info; 296 sc->sc_sysmon.sme_sensor_data = sc->sc_data; 297 sc->sc_sysmon.sme_cookie = sc; 298 sc->sc_sysmon.sme_gtredata = acpiacad_gtredata; 299 sc->sc_sysmon.sme_streinfo = acpiacad_streinfo; 300 sc->sc_sysmon.sme_nsensors = ACPIACAD_NSENSORS; 301 sc->sc_sysmon.sme_envsys_version = 1000; 302 303 if (sysmon_envsys_register(&sc->sc_sysmon)) 304 printf("%s: unable to register with sysmon\n", 305 sc->sc_dev.dv_xname); 306 } 307 308 int 309 acpiacad_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred) 310 { 311 struct acpiacad_softc *sc = sme->sme_cookie; 312 313 if (!AACAD_ISSET(sc, AACAD_F_AVAILABLE)) 314 acpiacad_get_status(sc); 315 316 /* XXX locking */ 317 *tred = sc->sc_data[tred->sensor]; 318 /* XXX locking */ 319 320 return (0); 321 } 322 323 324 int 325 acpiacad_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo) 326 { 327 328 /* XXX Not implemented */ 329 binfo->validflags = 0; 330 331 return (0); 332 } 333