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