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