1 1.34 thorpej /* $NetBSD: apmdev.c,v 1.34 2021/09/26 01:16:08 thorpej Exp $ */ 2 1.1 uch 3 1.1 uch /*- 4 1.1 uch * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. 5 1.1 uch * All rights reserved. 6 1.1 uch * 7 1.1 uch * This code is derived from software contributed to The NetBSD Foundation 8 1.1 uch * by John Kohl and Christopher G. Demetriou. 9 1.1 uch * 10 1.1 uch * Redistribution and use in source and binary forms, with or without 11 1.1 uch * modification, are permitted provided that the following conditions 12 1.1 uch * are met: 13 1.1 uch * 1. Redistributions of source code must retain the above copyright 14 1.1 uch * notice, this list of conditions and the following disclaimer. 15 1.1 uch * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 uch * notice, this list of conditions and the following disclaimer in the 17 1.1 uch * documentation and/or other materials provided with the distribution. 18 1.1 uch * 19 1.1 uch * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 uch * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 uch * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 uch * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 uch * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 uch * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 uch * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 uch * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 uch * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 uch * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 uch * POSSIBILITY OF SUCH DAMAGE. 30 1.1 uch */ 31 1.1 uch /* 32 1.1 uch * from: sys/arch/i386/i386/apm.c,v 1.49 2000/05/08 33 1.1 uch */ 34 1.1 uch 35 1.1 uch #include <sys/cdefs.h> 36 1.34 thorpej __KERNEL_RCSID(0, "$NetBSD: apmdev.c,v 1.34 2021/09/26 01:16:08 thorpej Exp $"); 37 1.1 uch 38 1.7 peter #ifdef _KERNEL_OPT 39 1.23 uwe #include "opt_apm.h" 40 1.7 peter #endif 41 1.1 uch 42 1.1 uch #if defined(DEBUG) && !defined(APMDEBUG) 43 1.1 uch #define APMDEBUG 44 1.1 uch #endif 45 1.1 uch 46 1.1 uch #include <sys/param.h> 47 1.1 uch #include <sys/systm.h> 48 1.1 uch #include <sys/signalvar.h> 49 1.1 uch #include <sys/kernel.h> 50 1.1 uch #include <sys/proc.h> 51 1.1 uch #include <sys/kthread.h> 52 1.1 uch #include <sys/malloc.h> 53 1.1 uch #include <sys/device.h> 54 1.1 uch #include <sys/fcntl.h> 55 1.1 uch #include <sys/ioctl.h> 56 1.1 uch #include <sys/select.h> 57 1.1 uch #include <sys/poll.h> 58 1.1 uch #include <sys/conf.h> 59 1.1 uch 60 1.1 uch #include <dev/hpc/apm/apmvar.h> 61 1.1 uch 62 1.20 uwe #ifdef APMDEBUG 63 1.20 uwe #define DPRINTF(f, x) do { if (apmdebug & (f)) printf x; } while (0) 64 1.1 uch 65 1.1 uch 66 1.1 uch #ifdef APMDEBUG_VALUE 67 1.1 uch int apmdebug = APMDEBUG_VALUE; 68 1.1 uch #else 69 1.1 uch int apmdebug = 0; 70 1.20 uwe #endif /* APMDEBUG_VALUE */ 71 1.20 uwe 72 1.1 uch #else 73 1.1 uch #define DPRINTF(f, x) /**/ 74 1.20 uwe #endif /* APMDEBUG */ 75 1.1 uch 76 1.1 uch #define SCFLAG_OREAD 0x0000001 77 1.1 uch #define SCFLAG_OWRITE 0x0000002 78 1.1 uch #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 79 1.1 uch 80 1.1 uch #define APMUNIT(dev) (minor(dev)&0xf0) 81 1.21 uwe #define APM(dev) (minor(dev)&0x0f) 82 1.21 uwe #define APM_NORMAL 0 83 1.21 uwe #define APM_CTL 8 84 1.1 uch 85 1.1 uch /* 86 1.1 uch * A brief note on the locking protocol: it's very simple; we 87 1.1 uch * assert an exclusive lock any time thread context enters the 88 1.1 uch * APM module. This is both the APM thread itself, as well as 89 1.1 uch * user context. 90 1.1 uch */ 91 1.20 uwe #define APM_LOCK(apmsc) \ 92 1.20 uwe (void) mutex_enter(&(apmsc)->sc_lock) 93 1.20 uwe #define APM_UNLOCK(apmsc) \ 94 1.20 uwe (void) mutex_exit(&(apmsc)->sc_lock) 95 1.1 uch 96 1.21 uwe static void apmdevattach(device_t, device_t, void *); 97 1.21 uwe static int apmdevmatch(device_t, cfdata_t, void *); 98 1.1 uch 99 1.1 uch static void apm_event_handle(struct apm_softc *, u_int, u_int); 100 1.1 uch static void apm_periodic_check(struct apm_softc *); 101 1.1 uch static void apm_thread(void *); 102 1.1 uch static void apm_perror(const char *, int, ...) 103 1.1 uch __attribute__((__format__(__printf__,1,3))); 104 1.1 uch #ifdef APM_POWER_PRINT 105 1.1 uch static void apm_power_print(struct apm_softc *, struct apm_power_info *); 106 1.1 uch #endif 107 1.1 uch static int apm_record_event(struct apm_softc *, u_int); 108 1.20 uwe static void apm_set_ver(struct apm_softc *); 109 1.1 uch static void apm_standby(struct apm_softc *); 110 1.1 uch static void apm_suspend(struct apm_softc *); 111 1.1 uch static void apm_resume(struct apm_softc *, u_int, u_int); 112 1.1 uch 113 1.20 uwe CFATTACH_DECL_NEW(apmdev, sizeof(struct apm_softc), 114 1.21 uwe apmdevmatch, apmdevattach, NULL, NULL); 115 1.1 uch 116 1.1 uch extern struct cfdriver apmdev_cd; 117 1.1 uch 118 1.1 uch dev_type_open(apmdevopen); 119 1.1 uch dev_type_close(apmdevclose); 120 1.1 uch dev_type_ioctl(apmdevioctl); 121 1.1 uch dev_type_poll(apmdevpoll); 122 1.1 uch dev_type_kqfilter(apmdevkqfilter); 123 1.1 uch 124 1.1 uch const struct cdevsw apmdev_cdevsw = { 125 1.29 dholland .d_open = apmdevopen, 126 1.29 dholland .d_close = apmdevclose, 127 1.29 dholland .d_read = noread, 128 1.29 dholland .d_write = nowrite, 129 1.29 dholland .d_ioctl = apmdevioctl, 130 1.29 dholland .d_stop = nostop, 131 1.29 dholland .d_tty = notty, 132 1.29 dholland .d_poll = apmdevpoll, 133 1.29 dholland .d_mmap = nommap, 134 1.29 dholland .d_kqfilter = apmdevkqfilter, 135 1.30 dholland .d_discard = nodiscard, 136 1.29 dholland .d_flag = D_OTHER 137 1.1 uch }; 138 1.1 uch 139 1.1 uch /* configurable variables */ 140 1.1 uch #ifdef APM_NO_STANDBY 141 1.1 uch int apm_do_standby = 0; 142 1.1 uch #else 143 1.1 uch int apm_do_standby = 1; 144 1.1 uch #endif 145 1.1 uch #ifdef APM_V10_ONLY 146 1.1 uch int apm_v11_enabled = 0; 147 1.1 uch #else 148 1.1 uch int apm_v11_enabled = 1; 149 1.1 uch #endif 150 1.1 uch #ifdef APM_NO_V12 151 1.1 uch int apm_v12_enabled = 0; 152 1.1 uch #else 153 1.1 uch int apm_v12_enabled = 1; 154 1.1 uch #endif 155 1.1 uch 156 1.1 uch /* variables used during operation (XXX cgd) */ 157 1.1 uch u_char apm_majver, apm_minver; 158 1.1 uch int apm_inited; 159 1.1 uch int apm_standbys, apm_userstandbys, apm_suspends, apm_battlow; 160 1.1 uch int apm_damn_fool_bios, apm_op_inprog; 161 1.1 uch int apm_evindex; 162 1.1 uch 163 1.1 uch static int apm_spl; /* saved spl while suspended */ 164 1.1 uch 165 1.21 uwe const char * 166 1.1 uch apm_strerror(int code) 167 1.1 uch { 168 1.1 uch switch (code) { 169 1.1 uch case APM_ERR_PM_DISABLED: 170 1.1 uch return ("power management disabled"); 171 1.1 uch case APM_ERR_REALALREADY: 172 1.1 uch return ("real mode interface already connected"); 173 1.1 uch case APM_ERR_NOTCONN: 174 1.1 uch return ("interface not connected"); 175 1.1 uch case APM_ERR_16ALREADY: 176 1.1 uch return ("16-bit interface already connected"); 177 1.1 uch case APM_ERR_16NOTSUPP: 178 1.1 uch return ("16-bit interface not supported"); 179 1.1 uch case APM_ERR_32ALREADY: 180 1.1 uch return ("32-bit interface already connected"); 181 1.1 uch case APM_ERR_32NOTSUPP: 182 1.1 uch return ("32-bit interface not supported"); 183 1.1 uch case APM_ERR_UNRECOG_DEV: 184 1.1 uch return ("unrecognized device ID"); 185 1.1 uch case APM_ERR_ERANGE: 186 1.1 uch return ("parameter out of range"); 187 1.1 uch case APM_ERR_NOTENGAGED: 188 1.1 uch return ("interface not engaged"); 189 1.1 uch case APM_ERR_UNABLE: 190 1.1 uch return ("unable to enter requested state"); 191 1.1 uch case APM_ERR_NOEVENTS: 192 1.1 uch return ("no pending events"); 193 1.1 uch case APM_ERR_NOT_PRESENT: 194 1.1 uch return ("no APM present"); 195 1.1 uch default: 196 1.1 uch return ("unknown error code"); 197 1.1 uch } 198 1.1 uch } 199 1.1 uch 200 1.1 uch static void 201 1.1 uch apm_perror(const char *str, int errinfo, ...) /* XXX cgd */ 202 1.1 uch { 203 1.1 uch va_list ap; 204 1.1 uch 205 1.1 uch printf("APM "); 206 1.1 uch 207 1.1 uch va_start(ap, errinfo); 208 1.1 uch vprintf(str, ap); /* XXX cgd */ 209 1.1 uch va_end(ap); 210 1.1 uch 211 1.1 uch printf(": %s\n", apm_strerror(errinfo)); 212 1.1 uch } 213 1.1 uch 214 1.1 uch #ifdef APM_POWER_PRINT 215 1.1 uch static void 216 1.1 uch apm_power_print(struct apm_softc *sc, struct apm_power_info *pi) 217 1.1 uch { 218 1.1 uch 219 1.1 uch if (pi->battery_life != APM_BATT_LIFE_UNKNOWN) { 220 1.20 uwe aprint_normal_dev(sc->sc_dev, 221 1.20 uwe "battery life expectancy: %d%%\n", 222 1.20 uwe pi->battery_life); 223 1.1 uch } 224 1.20 uwe aprint_normal_dev(sc->sc_dev, "A/C state: "); 225 1.1 uch switch (pi->ac_state) { 226 1.1 uch case APM_AC_OFF: 227 1.1 uch printf("off\n"); 228 1.1 uch break; 229 1.1 uch case APM_AC_ON: 230 1.1 uch printf("on\n"); 231 1.1 uch break; 232 1.1 uch case APM_AC_BACKUP: 233 1.1 uch printf("backup power\n"); 234 1.1 uch break; 235 1.1 uch default: 236 1.1 uch case APM_AC_UNKNOWN: 237 1.1 uch printf("unknown\n"); 238 1.1 uch break; 239 1.1 uch } 240 1.20 uwe aprint_normal_dev(sc->sc_dev, "battery charge state:"); 241 1.20 uwe if (apm_minver == 0) 242 1.1 uch switch (pi->battery_state) { 243 1.1 uch case APM_BATT_HIGH: 244 1.1 uch printf("high\n"); 245 1.1 uch break; 246 1.1 uch case APM_BATT_LOW: 247 1.1 uch printf("low\n"); 248 1.1 uch break; 249 1.1 uch case APM_BATT_CRITICAL: 250 1.1 uch printf("critical\n"); 251 1.1 uch break; 252 1.1 uch case APM_BATT_CHARGING: 253 1.1 uch printf("charging\n"); 254 1.1 uch break; 255 1.1 uch case APM_BATT_UNKNOWN: 256 1.1 uch printf("unknown\n"); 257 1.1 uch break; 258 1.1 uch default: 259 1.1 uch printf("undecoded state %x\n", pi->battery_state); 260 1.1 uch break; 261 1.1 uch } 262 1.20 uwe else if (apm_minver >= 1) { 263 1.20 uwe if (pi->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) 264 1.20 uwe printf(" no battery"); 265 1.20 uwe else { 266 1.20 uwe if (pi->battery_flags & APM_BATT_FLAG_HIGH) 267 1.20 uwe printf(" high"); 268 1.20 uwe if (pi->battery_flags & APM_BATT_FLAG_LOW) 269 1.20 uwe printf(" low"); 270 1.20 uwe if (pi->battery_flags & APM_BATT_FLAG_CRITICAL) 271 1.20 uwe printf(" critical"); 272 1.20 uwe if (pi->battery_flags & APM_BATT_FLAG_CHARGING) 273 1.20 uwe printf(" charging"); 274 1.20 uwe } 275 1.20 uwe printf("\n"); 276 1.20 uwe if (pi->minutes_valid) { 277 1.20 uwe aprint_normal_dev(sc->sc_dev, "estimated "); 278 1.20 uwe if (pi->minutes_left / 60) 279 1.20 uwe printf("%dh ", pi->minutes_left / 60); 280 1.20 uwe printf("%dm\n", pi->minutes_left % 60); 281 1.1 uch } 282 1.1 uch } 283 1.1 uch return; 284 1.1 uch } 285 1.1 uch #endif 286 1.1 uch 287 1.1 uch static void 288 1.1 uch apm_suspend(struct apm_softc *sc) 289 1.1 uch { 290 1.20 uwe int error; 291 1.1 uch 292 1.1 uch if (sc->sc_power_state == PWR_SUSPEND) { 293 1.1 uch #ifdef APMDEBUG 294 1.20 uwe aprint_debug_dev(sc->sc_dev, 295 1.20 uwe "apm_suspend: already suspended?\n"); 296 1.1 uch #endif 297 1.1 uch return; 298 1.1 uch } 299 1.1 uch sc->sc_power_state = PWR_SUSPEND; 300 1.1 uch 301 1.1 uch dopowerhooks(PWR_SOFTSUSPEND); 302 1.1 uch (void) tsleep(sc, PWAIT, "apmsuspend", hz/2); 303 1.1 uch 304 1.1 uch apm_spl = splhigh(); 305 1.1 uch 306 1.1 uch dopowerhooks(PWR_SUSPEND); 307 1.1 uch 308 1.20 uwe error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 309 1.20 uwe APM_SYS_SUSPEND); 310 1.21 uwe 311 1.21 uwe if (error) 312 1.21 uwe apm_resume(sc, 0, 0); 313 1.1 uch } 314 1.1 uch 315 1.1 uch static void 316 1.1 uch apm_standby(struct apm_softc *sc) 317 1.1 uch { 318 1.20 uwe int error; 319 1.1 uch 320 1.1 uch if (sc->sc_power_state == PWR_STANDBY) { 321 1.1 uch #ifdef APMDEBUG 322 1.20 uwe aprint_debug_dev(sc->sc_dev, 323 1.20 uwe "apm_standby: already standing by?\n"); 324 1.1 uch #endif 325 1.1 uch return; 326 1.1 uch } 327 1.1 uch sc->sc_power_state = PWR_STANDBY; 328 1.1 uch 329 1.1 uch dopowerhooks(PWR_SOFTSTANDBY); 330 1.1 uch (void) tsleep(sc, PWAIT, "apmstandby", hz/2); 331 1.1 uch 332 1.1 uch apm_spl = splhigh(); 333 1.1 uch 334 1.1 uch dopowerhooks(PWR_STANDBY); 335 1.20 uwe 336 1.20 uwe error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 337 1.20 uwe APM_SYS_STANDBY); 338 1.21 uwe if (error) 339 1.21 uwe apm_resume(sc, 0, 0); 340 1.1 uch } 341 1.1 uch 342 1.1 uch static void 343 1.9 christos apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info) 344 1.1 uch { 345 1.1 uch 346 1.1 uch if (sc->sc_power_state == PWR_RESUME) { 347 1.1 uch #ifdef APMDEBUG 348 1.20 uwe aprint_debug_dev(sc->sc_dev, "apm_resume: already running?\n"); 349 1.1 uch #endif 350 1.1 uch return; 351 1.1 uch } 352 1.1 uch sc->sc_power_state = PWR_RESUME; 353 1.1 uch 354 1.20 uwe #if 0 /* XXX: def TIME_FREQ */ 355 1.1 uch /* 356 1.1 uch * Some system requires its clock to be initialized after hybernation. 357 1.1 uch */ 358 1.20 uwe initrtclock(TIMER_FREQ); 359 1.20 uwe #endif 360 1.1 uch 361 1.6 gdamore inittodr(time_second); 362 1.1 uch dopowerhooks(PWR_RESUME); 363 1.1 uch 364 1.1 uch splx(apm_spl); 365 1.1 uch 366 1.1 uch dopowerhooks(PWR_SOFTRESUME); 367 1.1 uch 368 1.1 uch apm_record_event(sc, event_type); 369 1.1 uch } 370 1.1 uch 371 1.1 uch /* 372 1.1 uch * return 0 if the user will notice and handle the event, 373 1.1 uch * return 1 if the kernel driver should do so. 374 1.1 uch */ 375 1.1 uch static int 376 1.1 uch apm_record_event(struct apm_softc *sc, u_int event_type) 377 1.1 uch { 378 1.1 uch struct apm_event_info *evp; 379 1.1 uch 380 1.1 uch if ((sc->sc_flags & SCFLAG_OPEN) == 0) 381 1.1 uch return 1; /* no user waiting */ 382 1.20 uwe if (sc->sc_event_count == APM_NEVENTS) 383 1.1 uch return 1; /* overflow */ 384 1.20 uwe evp = &sc->sc_event_list[sc->sc_event_ptr]; 385 1.20 uwe sc->sc_event_count++; 386 1.20 uwe sc->sc_event_ptr++; 387 1.20 uwe sc->sc_event_ptr %= APM_NEVENTS; 388 1.1 uch evp->type = event_type; 389 1.1 uch evp->index = ++apm_evindex; 390 1.16 rmind selnotify(&sc->sc_rsel, 0, 0); 391 1.1 uch return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */ 392 1.1 uch } 393 1.1 uch 394 1.1 uch static void 395 1.1 uch apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info) 396 1.1 uch { 397 1.1 uch int error; 398 1.3 uwe const char *code; 399 1.1 uch struct apm_power_info pi; 400 1.1 uch 401 1.1 uch switch (event_code) { 402 1.1 uch case APM_USER_STANDBY_REQ: 403 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n")); 404 1.1 uch if (apm_do_standby) { 405 1.21 uwe if (apm_op_inprog == 0 && apm_record_event(sc, event_code)) 406 1.1 uch apm_userstandbys++; 407 1.1 uch apm_op_inprog++; 408 1.20 uwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 409 1.20 uwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 410 1.1 uch } else { 411 1.20 uwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 412 1.20 uwe APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED); 413 1.1 uch /* in case BIOS hates being spurned */ 414 1.20 uwe (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 415 1.1 uch } 416 1.1 uch break; 417 1.1 uch 418 1.1 uch case APM_STANDBY_REQ: 419 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n")); 420 1.1 uch if (apm_standbys || apm_suspends) { 421 1.1 uch DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM, 422 1.1 uch ("damn fool BIOS did not wait for answer\n")); 423 1.1 uch /* just give up the fight */ 424 1.1 uch apm_damn_fool_bios = 1; 425 1.1 uch } 426 1.1 uch if (apm_do_standby) { 427 1.21 uwe if (apm_op_inprog == 0 && 428 1.21 uwe apm_record_event(sc, event_code)) 429 1.1 uch apm_standbys++; 430 1.1 uch apm_op_inprog++; 431 1.20 uwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 432 1.20 uwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 433 1.1 uch } else { 434 1.20 uwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 435 1.20 uwe APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED); 436 1.1 uch /* in case BIOS hates being spurned */ 437 1.20 uwe (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 438 1.1 uch } 439 1.1 uch break; 440 1.1 uch 441 1.1 uch case APM_USER_SUSPEND_REQ: 442 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n")); 443 1.21 uwe if (apm_op_inprog == 0 && apm_record_event(sc, event_code)) 444 1.1 uch apm_suspends++; 445 1.1 uch apm_op_inprog++; 446 1.20 uwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 447 1.20 uwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 448 1.1 uch break; 449 1.1 uch 450 1.1 uch case APM_SUSPEND_REQ: 451 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n")); 452 1.1 uch if (apm_standbys || apm_suspends) { 453 1.1 uch DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM, 454 1.1 uch ("damn fool BIOS did not wait for answer\n")); 455 1.1 uch /* just give up the fight */ 456 1.1 uch apm_damn_fool_bios = 1; 457 1.1 uch } 458 1.21 uwe if (apm_op_inprog == 0 && apm_record_event(sc, event_code)) 459 1.1 uch apm_suspends++; 460 1.1 uch apm_op_inprog++; 461 1.20 uwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 462 1.20 uwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 463 1.1 uch break; 464 1.1 uch 465 1.1 uch case APM_POWER_CHANGE: 466 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n")); 467 1.22 uwe error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi); 468 1.1 uch #ifdef APM_POWER_PRINT 469 1.1 uch /* only print if nobody is catching events. */ 470 1.1 uch if (error == 0 && 471 1.1 uch (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0) 472 1.1 uch apm_power_print(sc, &pi); 473 1.28 christos #else 474 1.28 christos __USE(error); 475 1.1 uch #endif 476 1.1 uch apm_record_event(sc, event_code); 477 1.1 uch break; 478 1.1 uch 479 1.1 uch case APM_NORMAL_RESUME: 480 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n")); 481 1.1 uch apm_resume(sc, event_code, event_info); 482 1.1 uch break; 483 1.1 uch 484 1.1 uch case APM_CRIT_RESUME: 485 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system")); 486 1.1 uch apm_resume(sc, event_code, event_info); 487 1.1 uch break; 488 1.1 uch 489 1.1 uch case APM_SYS_STANDBY_RESUME: 490 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n")); 491 1.1 uch apm_resume(sc, event_code, event_info); 492 1.1 uch break; 493 1.1 uch 494 1.1 uch case APM_UPDATE_TIME: 495 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n")); 496 1.1 uch apm_resume(sc, event_code, event_info); 497 1.1 uch break; 498 1.1 uch 499 1.1 uch case APM_CRIT_SUSPEND_REQ: 500 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n")); 501 1.1 uch apm_record_event(sc, event_code); 502 1.1 uch apm_suspend(sc); 503 1.1 uch break; 504 1.1 uch 505 1.1 uch case APM_BATTERY_LOW: 506 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n")); 507 1.1 uch apm_battlow++; 508 1.1 uch apm_record_event(sc, event_code); 509 1.1 uch break; 510 1.1 uch 511 1.1 uch case APM_CAP_CHANGE: 512 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n")); 513 1.1 uch if (apm_minver < 2) { 514 1.1 uch DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n")); 515 1.1 uch } else { 516 1.1 uch u_int numbatts, capflags; 517 1.20 uwe (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, 518 1.20 uwe &numbatts, &capflags); 519 1.22 uwe (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi); 520 1.1 uch } 521 1.1 uch break; 522 1.1 uch 523 1.1 uch default: 524 1.1 uch switch (event_code >> 8) { 525 1.1 uch case 0: 526 1.1 uch code = "reserved system"; 527 1.1 uch break; 528 1.1 uch case 1: 529 1.1 uch code = "reserved device"; 530 1.1 uch break; 531 1.1 uch case 2: 532 1.1 uch code = "OEM defined"; 533 1.1 uch break; 534 1.1 uch default: 535 1.1 uch code = "reserved"; 536 1.1 uch break; 537 1.2 perry } 538 1.1 uch printf("APM: %s event code %x\n", code, event_code); 539 1.1 uch } 540 1.1 uch } 541 1.1 uch 542 1.1 uch static void 543 1.1 uch apm_periodic_check(struct apm_softc *sc) 544 1.1 uch { 545 1.1 uch int error; 546 1.1 uch u_int event_code, event_info; 547 1.2 perry 548 1.1 uch 549 1.1 uch /* 550 1.1 uch * tell the BIOS we're working on it, if asked to do a 551 1.1 uch * suspend/standby 552 1.1 uch */ 553 1.1 uch if (apm_op_inprog) 554 1.20 uwe (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 555 1.20 uwe APM_LASTREQ_INPROG); 556 1.1 uch 557 1.20 uwe while ((error = (*sc->sc_ops->aa_get_event)(sc->sc_cookie, &event_code, 558 1.20 uwe &event_info)) == 0 && !apm_damn_fool_bios) 559 1.1 uch apm_event_handle(sc, event_code, event_info); 560 1.1 uch 561 1.1 uch if (error != APM_ERR_NOEVENTS) 562 1.1 uch apm_perror("get event", error); 563 1.1 uch if (apm_suspends) { 564 1.1 uch apm_op_inprog = 0; 565 1.1 uch apm_suspend(sc); 566 1.1 uch } else if (apm_standbys || apm_userstandbys) { 567 1.1 uch apm_op_inprog = 0; 568 1.1 uch apm_standby(sc); 569 1.1 uch } 570 1.1 uch apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0; 571 1.1 uch apm_damn_fool_bios = 0; 572 1.1 uch } 573 1.1 uch 574 1.1 uch static void 575 1.20 uwe apm_set_ver(struct apm_softc *sc) 576 1.1 uch { 577 1.1 uch 578 1.1 uch if (apm_v12_enabled && 579 1.20 uwe APM_MAJOR_VERS(sc->sc_vers) == 1 && 580 1.20 uwe APM_MINOR_VERS(sc->sc_vers) == 2) { 581 1.1 uch apm_majver = 1; 582 1.1 uch apm_minver = 2; 583 1.1 uch goto ok; 584 1.1 uch } 585 1.1 uch 586 1.1 uch if (apm_v11_enabled && 587 1.20 uwe APM_MAJOR_VERS(sc->sc_vers) == 1 && 588 1.20 uwe APM_MINOR_VERS(sc->sc_vers) == 1) { 589 1.1 uch apm_majver = 1; 590 1.1 uch apm_minver = 1; 591 1.1 uch } else { 592 1.1 uch apm_majver = 1; 593 1.1 uch apm_minver = 0; 594 1.1 uch } 595 1.1 uch ok: 596 1.20 uwe aprint_normal("Power Management spec V%d.%d", apm_majver, apm_minver); 597 1.1 uch apm_inited = 1; 598 1.1 uch } 599 1.1 uch 600 1.1 uch static int 601 1.21 uwe apmdevmatch(device_t parent, cfdata_t match, void *aux) 602 1.1 uch { 603 1.21 uwe 604 1.21 uwe return apm_match(); 605 1.1 uch } 606 1.1 uch 607 1.1 uch static void 608 1.21 uwe apmdevattach(device_t parent, device_t self, void *aux) 609 1.1 uch { 610 1.20 uwe struct apm_softc *sc; 611 1.1 uch struct apmdev_attach_args *aaa = aux; 612 1.1 uch 613 1.20 uwe sc = device_private(self); 614 1.20 uwe sc->sc_dev = self; 615 1.20 uwe 616 1.20 uwe sc->sc_detail = aaa->apm_detail; 617 1.20 uwe sc->sc_vers = aaa->apm_detail & 0xffff; /* XXX: magic */ 618 1.20 uwe 619 1.20 uwe sc->sc_ops = aaa->accessops; 620 1.20 uwe sc->sc_cookie = aaa->accesscookie; 621 1.1 uch 622 1.21 uwe apm_attach(sc); 623 1.21 uwe } 624 1.21 uwe 625 1.21 uwe /* 626 1.21 uwe * Print function (for parent devices). 627 1.21 uwe */ 628 1.21 uwe int 629 1.21 uwe apmprint(void *aux, const char *pnp) 630 1.21 uwe { 631 1.21 uwe if (pnp) 632 1.21 uwe aprint_normal("apm at %s", pnp); 633 1.21 uwe 634 1.21 uwe return (UNCONF); 635 1.21 uwe } 636 1.21 uwe 637 1.21 uwe int 638 1.21 uwe apm_match(void) 639 1.21 uwe { 640 1.21 uwe static int got; 641 1.21 uwe return !got++; 642 1.21 uwe } 643 1.21 uwe 644 1.21 uwe void 645 1.21 uwe apm_attach(struct apm_softc *sc) 646 1.21 uwe { 647 1.21 uwe struct apm_power_info pinfo; 648 1.21 uwe u_int numbatts, capflags; 649 1.21 uwe int error; 650 1.21 uwe 651 1.20 uwe aprint_naive("\n"); 652 1.20 uwe aprint_normal(": "); 653 1.1 uch 654 1.20 uwe switch ((APM_MAJOR_VERS(sc->sc_vers) << 8) + APM_MINOR_VERS(sc->sc_vers)) { 655 1.1 uch case 0x0100: 656 1.1 uch apm_v11_enabled = 0; 657 1.1 uch apm_v12_enabled = 0; 658 1.1 uch break; 659 1.1 uch case 0x0101: 660 1.1 uch apm_v12_enabled = 0; 661 1.1 uch /* fall through */ 662 1.1 uch case 0x0102: 663 1.1 uch default: 664 1.1 uch break; 665 1.1 uch } 666 1.1 uch 667 1.20 uwe apm_set_ver(sc); /* prints version info */ 668 1.20 uwe aprint_normal("\n"); 669 1.1 uch if (apm_minver >= 2) 670 1.20 uwe (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, &numbatts, 671 1.20 uwe &capflags); 672 1.1 uch 673 1.1 uch /* 674 1.1 uch * enable power management 675 1.1 uch */ 676 1.20 uwe (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 677 1.1 uch 678 1.22 uwe error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pinfo); 679 1.1 uch if (error == 0) { 680 1.1 uch #ifdef APM_POWER_PRINT 681 1.20 uwe apm_power_print(sc, &pinfo); 682 1.1 uch #endif 683 1.1 uch } else 684 1.1 uch apm_perror("get power status", error); 685 1.1 uch 686 1.20 uwe if (sc->sc_ops->aa_cpu_busy) 687 1.20 uwe (*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie); 688 1.20 uwe 689 1.20 uwe mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 690 1.1 uch 691 1.1 uch /* Initial state is `resumed'. */ 692 1.1 uch sc->sc_power_state = PWR_RESUME; 693 1.20 uwe selinit(&sc->sc_rsel); 694 1.20 uwe selinit(&sc->sc_xsel); 695 1.1 uch 696 1.1 uch /* Do an initial check. */ 697 1.1 uch apm_periodic_check(sc); 698 1.1 uch 699 1.1 uch /* 700 1.1 uch * Create a kernel thread to periodically check for APM events, 701 1.1 uch * and notify other subsystems when they occur. 702 1.1 uch */ 703 1.11 ad if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc, 704 1.20 uwe &sc->sc_thread, "%s", device_xname(sc->sc_dev)) != 0) { 705 1.11 ad /* 706 1.11 ad * We were unable to create the APM thread; bail out. 707 1.11 ad */ 708 1.20 uwe if (sc->sc_ops->aa_disconnect) 709 1.20 uwe (*sc->sc_ops->aa_disconnect)(sc->sc_cookie); 710 1.20 uwe aprint_error_dev(sc->sc_dev, "unable to create thread, " 711 1.17 cegger "kernel APM support disabled\n"); 712 1.11 ad } 713 1.1 uch } 714 1.1 uch 715 1.1 uch void 716 1.1 uch apm_thread(void *arg) 717 1.1 uch { 718 1.1 uch struct apm_softc *apmsc = arg; 719 1.1 uch 720 1.1 uch /* 721 1.1 uch * Loop forever, doing a periodic check for APM events. 722 1.1 uch */ 723 1.1 uch for (;;) { 724 1.1 uch APM_LOCK(apmsc); 725 1.1 uch apm_periodic_check(apmsc); 726 1.1 uch APM_UNLOCK(apmsc); 727 1.1 uch (void) tsleep(apmsc, PWAIT, "apmev", (8 * hz) / 7); 728 1.1 uch } 729 1.1 uch } 730 1.1 uch 731 1.1 uch int 732 1.9 christos apmdevopen(dev_t dev, int flag, int mode, struct lwp *l) 733 1.1 uch { 734 1.21 uwe int ctl = APM(dev); 735 1.1 uch int error = 0; 736 1.1 uch struct apm_softc *sc; 737 1.1 uch 738 1.19 rafal sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 739 1.1 uch if (!sc) 740 1.1 uch return ENXIO; 741 1.1 uch 742 1.1 uch if (!apm_inited) 743 1.1 uch return ENXIO; 744 1.2 perry 745 1.1 uch DPRINTF(APMDEBUG_DEVICE, 746 1.4 christos ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode)); 747 1.1 uch 748 1.1 uch APM_LOCK(sc); 749 1.1 uch switch (ctl) { 750 1.21 uwe case APM_CTL: 751 1.1 uch if (!(flag & FWRITE)) { 752 1.1 uch error = EINVAL; 753 1.1 uch break; 754 1.1 uch } 755 1.1 uch if (sc->sc_flags & SCFLAG_OWRITE) { 756 1.1 uch error = EBUSY; 757 1.1 uch break; 758 1.1 uch } 759 1.1 uch sc->sc_flags |= SCFLAG_OWRITE; 760 1.1 uch break; 761 1.21 uwe case APM_NORMAL: 762 1.1 uch if (!(flag & FREAD) || (flag & FWRITE)) { 763 1.1 uch error = EINVAL; 764 1.1 uch break; 765 1.1 uch } 766 1.1 uch sc->sc_flags |= SCFLAG_OREAD; 767 1.1 uch break; 768 1.1 uch default: 769 1.1 uch error = ENXIO; 770 1.1 uch break; 771 1.1 uch } 772 1.1 uch APM_UNLOCK(sc); 773 1.1 uch 774 1.1 uch return (error); 775 1.1 uch } 776 1.1 uch 777 1.1 uch int 778 1.9 christos apmdevclose(dev_t dev, int flag, int mode, 779 1.9 christos struct lwp *l) 780 1.1 uch { 781 1.19 rafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 782 1.21 uwe int ctl = APM(dev); 783 1.1 uch 784 1.1 uch DPRINTF(APMDEBUG_DEVICE, 785 1.4 christos ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode)); 786 1.1 uch 787 1.1 uch APM_LOCK(sc); 788 1.1 uch switch (ctl) { 789 1.21 uwe case APM_CTL: 790 1.1 uch sc->sc_flags &= ~SCFLAG_OWRITE; 791 1.1 uch break; 792 1.21 uwe case APM_NORMAL: 793 1.1 uch sc->sc_flags &= ~SCFLAG_OREAD; 794 1.1 uch break; 795 1.1 uch } 796 1.1 uch if ((sc->sc_flags & SCFLAG_OPEN) == 0) { 797 1.20 uwe sc->sc_event_count = 0; 798 1.20 uwe sc->sc_event_ptr = 0; 799 1.1 uch } 800 1.1 uch APM_UNLOCK(sc); 801 1.1 uch return 0; 802 1.1 uch } 803 1.1 uch 804 1.1 uch int 805 1.10 christos apmdevioctl(dev_t dev, u_long cmd, void *data, int flag, 806 1.9 christos struct lwp *l) 807 1.1 uch { 808 1.19 rafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 809 1.1 uch struct apm_power_info *powerp; 810 1.1 uch struct apm_event_info *evp; 811 1.1 uch #if 0 812 1.1 uch struct apm_ctl *actl; 813 1.1 uch #endif 814 1.1 uch int i, error = 0; 815 1.1 uch int batt_flags; 816 1.1 uch 817 1.1 uch APM_LOCK(sc); 818 1.1 uch switch (cmd) { 819 1.1 uch case APM_IOC_STANDBY: 820 1.1 uch if (!apm_do_standby) { 821 1.1 uch error = EOPNOTSUPP; 822 1.1 uch break; 823 1.1 uch } 824 1.1 uch 825 1.1 uch if ((flag & FWRITE) == 0) { 826 1.1 uch error = EBADF; 827 1.1 uch break; 828 1.1 uch } 829 1.1 uch apm_userstandbys++; 830 1.1 uch break; 831 1.1 uch 832 1.1 uch case APM_IOC_SUSPEND: 833 1.1 uch if ((flag & FWRITE) == 0) { 834 1.1 uch error = EBADF; 835 1.1 uch break; 836 1.1 uch } 837 1.1 uch apm_suspends++; 838 1.1 uch break; 839 1.1 uch 840 1.1 uch case APM_IOC_NEXTEVENT: 841 1.20 uwe if (!sc->sc_event_count) 842 1.1 uch error = EAGAIN; 843 1.1 uch else { 844 1.1 uch evp = (struct apm_event_info *)data; 845 1.20 uwe i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count; 846 1.1 uch i %= APM_NEVENTS; 847 1.20 uwe *evp = sc->sc_event_list[i]; 848 1.20 uwe sc->sc_event_count--; 849 1.1 uch } 850 1.1 uch break; 851 1.1 uch 852 1.5 cube case OAPM_IOC_GETPOWER: 853 1.1 uch case APM_IOC_GETPOWER: 854 1.1 uch powerp = (struct apm_power_info *)data; 855 1.22 uwe if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, 856 1.20 uwe powerp)) != 0) { 857 1.1 uch apm_perror("ioctl get power status", error); 858 1.1 uch error = EIO; 859 1.1 uch break; 860 1.1 uch } 861 1.1 uch switch (apm_minver) { 862 1.1 uch case 0: 863 1.1 uch break; 864 1.1 uch case 1: 865 1.1 uch default: 866 1.20 uwe batt_flags = powerp->battery_flags; 867 1.1 uch powerp->battery_state = APM_BATT_UNKNOWN; 868 1.1 uch if (batt_flags & APM_BATT_FLAG_HIGH) 869 1.1 uch powerp->battery_state = APM_BATT_HIGH; 870 1.1 uch else if (batt_flags & APM_BATT_FLAG_LOW) 871 1.1 uch powerp->battery_state = APM_BATT_LOW; 872 1.1 uch else if (batt_flags & APM_BATT_FLAG_CRITICAL) 873 1.1 uch powerp->battery_state = APM_BATT_CRITICAL; 874 1.1 uch else if (batt_flags & APM_BATT_FLAG_CHARGING) 875 1.1 uch powerp->battery_state = APM_BATT_CHARGING; 876 1.1 uch else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) 877 1.1 uch powerp->battery_state = APM_BATT_ABSENT; 878 1.1 uch break; 879 1.1 uch } 880 1.1 uch break; 881 1.2 perry 882 1.1 uch default: 883 1.1 uch error = ENOTTY; 884 1.1 uch } 885 1.1 uch APM_UNLOCK(sc); 886 1.1 uch 887 1.1 uch return (error); 888 1.1 uch } 889 1.1 uch 890 1.1 uch int 891 1.4 christos apmdevpoll(dev_t dev, int events, struct lwp *l) 892 1.1 uch { 893 1.19 rafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 894 1.1 uch int revents = 0; 895 1.1 uch 896 1.1 uch APM_LOCK(sc); 897 1.1 uch if (events & (POLLIN | POLLRDNORM)) { 898 1.20 uwe if (sc->sc_event_count) 899 1.1 uch revents |= events & (POLLIN | POLLRDNORM); 900 1.1 uch else 901 1.4 christos selrecord(l, &sc->sc_rsel); 902 1.1 uch } 903 1.1 uch APM_UNLOCK(sc); 904 1.1 uch 905 1.1 uch return (revents); 906 1.1 uch } 907 1.1 uch 908 1.1 uch static void 909 1.1 uch filt_apmrdetach(struct knote *kn) 910 1.1 uch { 911 1.1 uch struct apm_softc *sc = kn->kn_hook; 912 1.1 uch 913 1.2 perry APM_LOCK(sc); 914 1.33 thorpej selremove_knote(&sc->sc_rsel, kn); 915 1.1 uch APM_UNLOCK(sc); 916 1.1 uch } 917 1.1 uch 918 1.1 uch static int 919 1.9 christos filt_apmread(struct knote *kn, long hint) 920 1.1 uch { 921 1.1 uch struct apm_softc *sc = kn->kn_hook; 922 1.1 uch 923 1.20 uwe kn->kn_data = sc->sc_event_count; 924 1.1 uch return (kn->kn_data > 0); 925 1.1 uch } 926 1.1 uch 927 1.31 maya static const struct filterops apmread_filtops = { 928 1.34 thorpej .f_flags = FILTEROP_ISFD, 929 1.31 maya .f_attach = NULL, 930 1.31 maya .f_detach = filt_apmrdetach, 931 1.31 maya .f_event = filt_apmread, 932 1.31 maya }; 933 1.1 uch 934 1.1 uch int 935 1.1 uch apmdevkqfilter(dev_t dev, struct knote *kn) 936 1.1 uch { 937 1.19 rafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 938 1.1 uch 939 1.1 uch switch (kn->kn_filter) { 940 1.1 uch case EVFILT_READ: 941 1.1 uch kn->kn_fop = &apmread_filtops; 942 1.1 uch break; 943 1.1 uch 944 1.1 uch default: 945 1.14 pooka return (EINVAL); 946 1.1 uch } 947 1.1 uch 948 1.1 uch kn->kn_hook = sc; 949 1.1 uch 950 1.1 uch APM_LOCK(sc); 951 1.33 thorpej selrecord_knote(&sc->sc_rsel, kn); 952 1.1 uch APM_UNLOCK(sc); 953 1.1 uch 954 1.1 uch return (0); 955 1.1 uch } 956