1 /* $OpenBSD: apm.c,v 1.24 2023/07/08 14:44:43 tobhe Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 Alexander Guy. All rights reserved. 5 * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved. 6 * Copyright (c) 1995 John T. Kohl. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the names of the authors nor the names of contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 34 #include "apm.h" 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/proc.h> 40 #include <sys/device.h> 41 #include <sys/fcntl.h> 42 #include <sys/ioctl.h> 43 #include <sys/event.h> 44 #include <sys/reboot.h> 45 #include <sys/hibernate.h> 46 #include <sys/task.h> 47 48 #include <machine/conf.h> 49 #include <machine/cpu.h> 50 #include <machine/acpiapm.h> 51 #include <machine/apmvar.h> 52 53 #if defined(APMDEBUG) 54 #define DPRINTF(x) printf x 55 #else 56 #define DPRINTF(x) /**/ 57 #endif 58 59 #ifdef SUSPEND 60 struct taskq *sleep_taskq; 61 struct task suspend_task; 62 void do_suspend(void *); 63 #ifdef HIBERNATE 64 struct task hibernate_task; 65 void do_hibernate(void *); 66 #endif 67 #endif 68 69 struct apm_softc { 70 struct device sc_dev; 71 struct klist sc_note; 72 int sc_flags; 73 }; 74 75 int apmmatch(struct device *, void *, void *); 76 void apmattach(struct device *, struct device *, void *); 77 78 const struct cfattach apm_ca = { 79 sizeof(struct apm_softc), apmmatch, apmattach 80 }; 81 82 struct cfdriver apm_cd = { 83 NULL, "apm", DV_DULL 84 }; 85 86 #define APMUNIT(dev) (minor(dev)&0xf0) 87 #define APMDEV(dev) (minor(dev)&0x0f) 88 #define APMDEV_NORMAL 0 89 #define APMDEV_CTL 8 90 91 void filt_apmrdetach(struct knote *kn); 92 int filt_apmread(struct knote *kn, long hint); 93 int apmkqfilter(dev_t dev, struct knote *kn); 94 int apm_getdefaultinfo(struct apm_power_info *); 95 96 const struct filterops apmread_filtops = { 97 .f_flags = FILTEROP_ISFD, 98 .f_attach = NULL, 99 .f_detach = filt_apmrdetach, 100 .f_event = filt_apmread, 101 }; 102 103 int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo; 104 105 /* 106 * Flags to control kernel display 107 * SCFLAG_NOPRINT: do not output APM power messages due to 108 * a power change event. 109 * 110 * SCFLAG_PCTPRINT: do not output APM power messages due to 111 * to a power change event unless the battery 112 * percentage changes. 113 */ 114 115 #define SCFLAG_NOPRINT 0x0008000 116 #define SCFLAG_PCTPRINT 0x0004000 117 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT) 118 119 #define SCFLAG_OREAD (1 << 0) 120 #define SCFLAG_OWRITE (1 << 1) 121 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 122 123 124 int 125 apmmatch(struct device *parent, void *match, void *aux) 126 { 127 return (1); 128 } 129 130 void 131 apmattach(struct device *parent, struct device *self, void *aux) 132 { 133 #ifdef SUSPEND 134 sleep_taskq = taskq_create("sleep", 1, IPL_NONE, 0); 135 task_set(&suspend_task, do_suspend, NULL); 136 #ifdef HIBERNATE 137 task_set(&hibernate_task, do_hibernate, NULL); 138 #endif 139 #endif 140 141 acpiapm_open = apmopen; 142 acpiapm_close = apmclose; 143 acpiapm_ioctl = apmioctl; 144 acpiapm_kqfilter = apmkqfilter; 145 146 printf("\n"); 147 } 148 149 int 150 apmopen(dev_t dev, int flag, int mode, struct proc *p) 151 { 152 struct apm_softc *sc; 153 int error = 0; 154 155 /* apm0 only */ 156 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 157 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 158 return ENXIO; 159 160 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n", 161 APMDEV(dev), p->p_p->ps_pid, flag, mode)); 162 163 switch (APMDEV(dev)) { 164 case APMDEV_CTL: 165 if (!(flag & FWRITE)) { 166 error = EINVAL; 167 break; 168 } 169 if (sc->sc_flags & SCFLAG_OWRITE) { 170 error = EBUSY; 171 break; 172 } 173 sc->sc_flags |= SCFLAG_OWRITE; 174 break; 175 case APMDEV_NORMAL: 176 if (!(flag & FREAD) || (flag & FWRITE)) { 177 error = EINVAL; 178 break; 179 } 180 sc->sc_flags |= SCFLAG_OREAD; 181 break; 182 default: 183 error = ENXIO; 184 break; 185 } 186 return error; 187 } 188 189 int 190 apmclose(dev_t dev, int flag, int mode, struct proc *p) 191 { 192 struct apm_softc *sc; 193 194 /* apm0 only */ 195 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 196 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 197 return ENXIO; 198 199 DPRINTF(("apmclose: pid %d flag %x mode %x\n", 200 p->p_p->ps_pid, flag, mode)); 201 202 switch (APMDEV(dev)) { 203 case APMDEV_CTL: 204 sc->sc_flags &= ~SCFLAG_OWRITE; 205 break; 206 case APMDEV_NORMAL: 207 sc->sc_flags &= ~SCFLAG_OREAD; 208 break; 209 } 210 return 0; 211 } 212 213 int 214 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 215 { 216 struct apm_softc *sc; 217 struct apm_power_info *power; 218 int error = 0; 219 220 /* apm0 only */ 221 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 222 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 223 return ENXIO; 224 225 switch (cmd) { 226 #ifdef SUSPEND 227 case APM_IOC_STANDBY: 228 case APM_IOC_SUSPEND: 229 if ((flag & FWRITE) == 0) { 230 error = EBADF; 231 break; 232 } 233 error = request_sleep(SLEEP_SUSPEND); 234 break; 235 #ifdef HIBERNATE 236 case APM_IOC_HIBERNATE: 237 if ((error = suser(p)) != 0) 238 break; 239 if ((flag & FWRITE) == 0) { 240 error = EBADF; 241 break; 242 } 243 error = request_sleep(SLEEP_HIBERNATE); 244 break; 245 #endif 246 #endif 247 case APM_IOC_PRN_CTL: 248 if ((flag & FWRITE) == 0) 249 error = EBADF; 250 else { 251 int flag = *(int *)data; 252 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); 253 switch (flag) { 254 case APM_PRINT_ON: /* enable printing */ 255 sc->sc_flags &= ~SCFLAG_PRINT; 256 break; 257 case APM_PRINT_OFF: /* disable printing */ 258 sc->sc_flags &= ~SCFLAG_PRINT; 259 sc->sc_flags |= SCFLAG_NOPRINT; 260 break; 261 case APM_PRINT_PCT: /* disable some printing */ 262 sc->sc_flags &= ~SCFLAG_PRINT; 263 sc->sc_flags |= SCFLAG_PCTPRINT; 264 break; 265 default: 266 error = EINVAL; 267 break; 268 } 269 } 270 break; 271 case APM_IOC_GETPOWER: 272 power = (struct apm_power_info *)data; 273 error = (*get_apminfo)(power); 274 break; 275 default: 276 error = ENOTTY; 277 } 278 279 return error; 280 } 281 282 void 283 filt_apmrdetach(struct knote *kn) 284 { 285 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook; 286 287 klist_remove_locked(&sc->sc_note, kn); 288 } 289 290 int 291 filt_apmread(struct knote *kn, long hint) 292 { 293 /* XXX weird kqueue_scan() semantics */ 294 if (hint && !kn->kn_data) 295 kn->kn_data = (int)hint; 296 297 return (1); 298 } 299 300 int 301 apmkqfilter(dev_t dev, struct knote *kn) 302 { 303 struct apm_softc *sc; 304 305 /* apm0 only */ 306 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 307 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 308 return ENXIO; 309 310 switch (kn->kn_filter) { 311 case EVFILT_READ: 312 kn->kn_fop = &apmread_filtops; 313 break; 314 default: 315 return (EINVAL); 316 } 317 318 kn->kn_hook = (caddr_t)sc; 319 klist_insert_locked(&sc->sc_note, kn); 320 321 return (0); 322 } 323 324 int 325 apm_getdefaultinfo(struct apm_power_info *info) 326 { 327 info->battery_state = APM_BATT_UNKNOWN; 328 info->ac_state = APM_AC_UNKNOWN; 329 info->battery_life = 0; 330 info->minutes_left = -1; 331 return (0); 332 } 333 334 void 335 apm_setinfohook(int (*hook)(struct apm_power_info *)) 336 { 337 get_apminfo = hook; 338 } 339 340 int 341 apm_record_event(u_int event) 342 { 343 struct apm_softc *sc = apm_cd.cd_devs[0]; 344 static int apm_evindex; 345 346 /* skip if no user waiting */ 347 if (sc == NULL || (sc->sc_flags & SCFLAG_OPEN) == 0) 348 return 1; 349 350 apm_evindex++; 351 knote_locked(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex)); 352 return 0; 353 } 354 355 #ifdef SUSPEND 356 357 void 358 do_suspend(void *v) 359 { 360 sleep_state(v, SLEEP_SUSPEND); 361 } 362 363 #ifdef HIBERNATE 364 void 365 do_hibernate(void *v) 366 { 367 sleep_state(v, SLEEP_HIBERNATE); 368 } 369 #endif 370 371 int 372 request_sleep(int sleepmode) 373 { 374 if (sleep_taskq == NULL) 375 return EINVAL; 376 377 switch (sleepmode) { 378 case SLEEP_SUSPEND: 379 task_add(sleep_taskq, &suspend_task); 380 break; 381 #ifdef HIBERNATE 382 case SLEEP_HIBERNATE: 383 if (get_hibernate_io_function(swdevt[0].sw_dev) == NULL) 384 return EOPNOTSUPP; 385 task_add(sleep_taskq, &hibernate_task); 386 break; 387 #endif 388 } 389 390 return 0; 391 } 392 393 #ifdef MULTIPROCESSOR 394 395 void 396 sleep_mp(void) 397 { 398 CPU_INFO_ITERATOR cii; 399 struct cpu_info *ci; 400 401 CPU_INFO_FOREACH(cii, ci) { 402 if (CPU_IS_PRIMARY(ci)) 403 continue; 404 arm_send_ipi(ci, ARM_IPI_HALT); 405 while (ci->ci_flags & CPUF_RUNNING) 406 CPU_BUSY_CYCLE(); 407 } 408 } 409 410 void 411 resume_mp(void) 412 { 413 CPU_INFO_ITERATOR cii; 414 struct cpu_info *ci; 415 416 CPU_INFO_FOREACH(cii, ci) { 417 if (CPU_IS_PRIMARY(ci)) 418 continue; 419 cpu_resume_secondary(ci); 420 } 421 cpu_boot_secondary_processors(); 422 } 423 424 #endif /* MULTIPROCESSOR */ 425 426 int 427 sleep_showstate(void *v, int sleepmode) 428 { 429 if (sleepmode == SLEEP_SUSPEND) 430 return 0; 431 432 return EOPNOTSUPP; 433 } 434 435 int 436 sleep_setstate(void *v) 437 { 438 return 0; 439 } 440 441 int 442 gosleep(void *v) 443 { 444 return cpu_suspend_primary(); 445 } 446 447 void 448 sleep_abort(void *v) 449 { 450 } 451 452 int 453 sleep_resume(void *v) 454 { 455 return 0; 456 } 457 458 int 459 suspend_finish(void *v) 460 { 461 apm_record_event(APM_NORMAL_RESUME); 462 return 0; 463 } 464 465 #endif /* SUSPEND */ 466