apm.c revision 1.22
1/* $OpenBSD: apm.c,v 1.22 2023/02/10 14:34:16 visa 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 60struct taskq *suspend_taskq; 61struct task suspend_task; 62void do_suspend(void *); 63#endif 64 65struct apm_softc { 66 struct device sc_dev; 67 struct klist sc_note; 68 int sc_flags; 69}; 70 71int apmmatch(struct device *, void *, void *); 72void apmattach(struct device *, struct device *, void *); 73 74const struct cfattach apm_ca = { 75 sizeof(struct apm_softc), apmmatch, apmattach 76}; 77 78struct cfdriver apm_cd = { 79 NULL, "apm", DV_DULL 80}; 81 82#define APMUNIT(dev) (minor(dev)&0xf0) 83#define APMDEV(dev) (minor(dev)&0x0f) 84#define APMDEV_NORMAL 0 85#define APMDEV_CTL 8 86 87void filt_apmrdetach(struct knote *kn); 88int filt_apmread(struct knote *kn, long hint); 89int apmkqfilter(dev_t dev, struct knote *kn); 90int apm_getdefaultinfo(struct apm_power_info *); 91 92const struct filterops apmread_filtops = { 93 .f_flags = FILTEROP_ISFD, 94 .f_attach = NULL, 95 .f_detach = filt_apmrdetach, 96 .f_event = filt_apmread, 97}; 98 99int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo; 100 101/* 102 * Flags to control kernel display 103 * SCFLAG_NOPRINT: do not output APM power messages due to 104 * a power change event. 105 * 106 * SCFLAG_PCTPRINT: do not output APM power messages due to 107 * to a power change event unless the battery 108 * percentage changes. 109 */ 110 111#define SCFLAG_NOPRINT 0x0008000 112#define SCFLAG_PCTPRINT 0x0004000 113#define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT) 114 115#define SCFLAG_OREAD (1 << 0) 116#define SCFLAG_OWRITE (1 << 1) 117#define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 118 119 120int 121apmmatch(struct device *parent, void *match, void *aux) 122{ 123 return (1); 124} 125 126void 127apmattach(struct device *parent, struct device *self, void *aux) 128{ 129#ifdef SUSPEND 130 suspend_taskq = taskq_create("suspend", 1, IPL_NONE, 0); 131 task_set(&suspend_task, do_suspend, NULL); 132#endif 133 134 acpiapm_open = apmopen; 135 acpiapm_close = apmclose; 136 acpiapm_ioctl = apmioctl; 137 acpiapm_kqfilter = apmkqfilter; 138 139 printf("\n"); 140} 141 142int 143apmopen(dev_t dev, int flag, int mode, struct proc *p) 144{ 145 struct apm_softc *sc; 146 int error = 0; 147 148 /* apm0 only */ 149 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 150 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 151 return ENXIO; 152 153 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n", 154 APMDEV(dev), p->p_p->ps_pid, flag, mode)); 155 156 switch (APMDEV(dev)) { 157 case APMDEV_CTL: 158 if (!(flag & FWRITE)) { 159 error = EINVAL; 160 break; 161 } 162 if (sc->sc_flags & SCFLAG_OWRITE) { 163 error = EBUSY; 164 break; 165 } 166 sc->sc_flags |= SCFLAG_OWRITE; 167 break; 168 case APMDEV_NORMAL: 169 if (!(flag & FREAD) || (flag & FWRITE)) { 170 error = EINVAL; 171 break; 172 } 173 sc->sc_flags |= SCFLAG_OREAD; 174 break; 175 default: 176 error = ENXIO; 177 break; 178 } 179 return error; 180} 181 182int 183apmclose(dev_t dev, int flag, int mode, struct proc *p) 184{ 185 struct apm_softc *sc; 186 187 /* apm0 only */ 188 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 189 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 190 return ENXIO; 191 192 DPRINTF(("apmclose: pid %d flag %x mode %x\n", 193 p->p_p->ps_pid, flag, mode)); 194 195 switch (APMDEV(dev)) { 196 case APMDEV_CTL: 197 sc->sc_flags &= ~SCFLAG_OWRITE; 198 break; 199 case APMDEV_NORMAL: 200 sc->sc_flags &= ~SCFLAG_OREAD; 201 break; 202 } 203 return 0; 204} 205 206int 207apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 208{ 209 struct apm_softc *sc; 210 struct apm_power_info *power; 211 int error = 0; 212 213 /* apm0 only */ 214 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 215 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 216 return ENXIO; 217 218 switch (cmd) { 219#ifdef SUSPEND 220 case APM_IOC_STANDBY: 221 case APM_IOC_SUSPEND: 222 if ((flag & FWRITE) == 0) { 223 error = EBADF; 224 break; 225 } 226 sleep_state(NULL, SLEEP_SUSPEND); 227 break; 228#ifdef HIBERNATE 229 case APM_IOC_HIBERNATE: 230 if ((error = suser(p)) != 0) 231 break; 232 if ((flag & FWRITE) == 0) { 233 error = EBADF; 234 break; 235 } 236 if (get_hibernate_io_function(swdevt[0].sw_dev) == NULL) { 237 error = EOPNOTSUPP; 238 break; 239 } 240 sleep_state(NULL, SLEEP_HIBERNATE); 241 break; 242#endif 243#endif 244 case APM_IOC_PRN_CTL: 245 if ((flag & FWRITE) == 0) 246 error = EBADF; 247 else { 248 int flag = *(int *)data; 249 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); 250 switch (flag) { 251 case APM_PRINT_ON: /* enable printing */ 252 sc->sc_flags &= ~SCFLAG_PRINT; 253 break; 254 case APM_PRINT_OFF: /* disable printing */ 255 sc->sc_flags &= ~SCFLAG_PRINT; 256 sc->sc_flags |= SCFLAG_NOPRINT; 257 break; 258 case APM_PRINT_PCT: /* disable some printing */ 259 sc->sc_flags &= ~SCFLAG_PRINT; 260 sc->sc_flags |= SCFLAG_PCTPRINT; 261 break; 262 default: 263 error = EINVAL; 264 break; 265 } 266 } 267 break; 268 case APM_IOC_GETPOWER: 269 power = (struct apm_power_info *)data; 270 error = (*get_apminfo)(power); 271 break; 272 default: 273 error = ENOTTY; 274 } 275 276 return error; 277} 278 279void 280filt_apmrdetach(struct knote *kn) 281{ 282 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook; 283 284 klist_remove_locked(&sc->sc_note, kn); 285} 286 287int 288filt_apmread(struct knote *kn, long hint) 289{ 290 /* XXX weird kqueue_scan() semantics */ 291 if (hint && !kn->kn_data) 292 kn->kn_data = (int)hint; 293 294 return (1); 295} 296 297int 298apmkqfilter(dev_t dev, struct knote *kn) 299{ 300 struct apm_softc *sc; 301 302 /* apm0 only */ 303 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 304 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 305 return ENXIO; 306 307 switch (kn->kn_filter) { 308 case EVFILT_READ: 309 kn->kn_fop = &apmread_filtops; 310 break; 311 default: 312 return (EINVAL); 313 } 314 315 kn->kn_hook = (caddr_t)sc; 316 klist_insert_locked(&sc->sc_note, kn); 317 318 return (0); 319} 320 321int 322apm_getdefaultinfo(struct apm_power_info *info) 323{ 324 info->battery_state = APM_BATT_UNKNOWN; 325 info->ac_state = APM_AC_UNKNOWN; 326 info->battery_life = 0; 327 info->minutes_left = -1; 328 return (0); 329} 330 331void 332apm_setinfohook(int (*hook)(struct apm_power_info *)) 333{ 334 get_apminfo = hook; 335} 336 337int 338apm_record_event(u_int event) 339{ 340 struct apm_softc *sc = apm_cd.cd_devs[0]; 341 static int apm_evindex; 342 343 /* skip if no user waiting */ 344 if (sc == NULL || (sc->sc_flags & SCFLAG_OPEN) == 0) 345 return 1; 346 347 apm_evindex++; 348 knote_locked(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex)); 349 return 0; 350} 351 352#ifdef SUSPEND 353 354void 355do_suspend(void *v) 356{ 357 sleep_state(v, SLEEP_SUSPEND); 358} 359 360void 361suspend(void) 362{ 363 if (suspend_taskq) 364 task_add(suspend_taskq, &suspend_task); 365} 366 367#ifdef MULTIPROCESSOR 368 369void 370sleep_mp(void) 371{ 372 CPU_INFO_ITERATOR cii; 373 struct cpu_info *ci; 374 375 CPU_INFO_FOREACH(cii, ci) { 376 if (CPU_IS_PRIMARY(ci)) 377 continue; 378 arm_send_ipi(ci, ARM_IPI_HALT); 379 while (ci->ci_flags & CPUF_RUNNING) 380 CPU_BUSY_CYCLE(); 381 } 382} 383 384void 385resume_mp(void) 386{ 387 CPU_INFO_ITERATOR cii; 388 struct cpu_info *ci; 389 390 CPU_INFO_FOREACH(cii, ci) { 391 if (CPU_IS_PRIMARY(ci)) 392 continue; 393 cpu_resume_secondary(ci); 394 } 395 cpu_boot_secondary_processors(); 396} 397 398#endif /* MULTIPROCESSOR */ 399 400int 401sleep_showstate(void *v, int sleepmode) 402{ 403 if (sleepmode == SLEEP_SUSPEND) 404 return 0; 405 406 return EOPNOTSUPP; 407} 408 409int 410sleep_setstate(void *v) 411{ 412 return 0; 413} 414 415int 416gosleep(void *v) 417{ 418 return cpu_suspend_primary(); 419} 420 421void 422sleep_abort(void *v) 423{ 424} 425 426int 427sleep_resume(void *v) 428{ 429 return 0; 430} 431 432int 433suspend_finish(void *v) 434{ 435 apm_record_event(APM_NORMAL_RESUME); 436 return 0; 437} 438 439#endif /* SUSPEND */ 440