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