1 /* $OpenBSD: apm.c,v 1.34 2020/02/16 23:37:23 jca 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 #include "wsdisplay.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/proc.h> 41 #include <sys/device.h> 42 #include <sys/fcntl.h> 43 #include <sys/ioctl.h> 44 #include <sys/buf.h> 45 #include <sys/event.h> 46 #include <sys/reboot.h> 47 #include <sys/hibernate.h> 48 #include <dev/rndvar.h> 49 50 #include <machine/autoconf.h> 51 #include <machine/conf.h> 52 #include <machine/cpu.h> 53 #include <machine/apmvar.h> 54 55 #include <dev/pci/pcivar.h> /* pci_dopm */ 56 57 #include <dev/wscons/wsdisplayvar.h> 58 59 #include <loongson/dev/kb3310var.h> 60 61 #if defined(APMDEBUG) 62 #define DPRINTF(x) printf x 63 #else 64 #define DPRINTF(x) /**/ 65 #endif 66 67 struct apm_softc { 68 struct device sc_dev; 69 struct klist sc_note; 70 int sc_flags; 71 }; 72 73 int apmmatch(struct device *, void *, void *); 74 void apmattach(struct device *, struct device *, void *); 75 76 struct cfattach apm_ca = { 77 sizeof(struct apm_softc), apmmatch, apmattach 78 }; 79 80 struct cfdriver apm_cd = { 81 NULL, "apm", DV_DULL 82 }; 83 84 #define APMUNIT(dev) (minor(dev)&0xf0) 85 #define APMDEV(dev) (minor(dev)&0x0f) 86 #define APMDEV_NORMAL 0 87 #define APMDEV_CTL 8 88 89 void filt_apmrdetach(struct knote *kn); 90 int filt_apmread(struct knote *kn, long hint); 91 int apmkqfilter(dev_t dev, struct knote *kn); 92 int apm_getdefaultinfo(struct apm_power_info *); 93 94 int apm_suspend(int state); 95 96 const struct filterops apmread_filtops = { 97 .f_isfd = 1, 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 struct mainbus_attach_args *maa = aux; 128 129 /* 130 * It only makes sense to attach on a 2F system, since 2E do not 131 * feature speed throttling, and we do not support 2E-based 132 * notebooks yet (assuming there are any). 133 */ 134 if (strcmp(maa->maa_name, apm_cd.cd_name) == 0 && loongson_ver == 0x2f) 135 return (1); 136 return (0); 137 } 138 139 void 140 apmattach(struct device *parent, struct device *self, void *aux) 141 { 142 /* Enable PCI Power Management. */ 143 pci_dopm = 1; 144 145 printf("\n"); 146 } 147 148 int 149 apmopen(dev_t dev, int flag, int mode, struct proc *p) 150 { 151 struct apm_softc *sc; 152 int error = 0; 153 154 /* apm0 only */ 155 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 156 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 157 return ENXIO; 158 159 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n", 160 APMDEV(dev), p->p_p->ps_pid, flag, mode)); 161 162 switch (APMDEV(dev)) { 163 case APMDEV_CTL: 164 if (!(flag & FWRITE)) { 165 error = EINVAL; 166 break; 167 } 168 if (sc->sc_flags & SCFLAG_OWRITE) { 169 error = EBUSY; 170 break; 171 } 172 sc->sc_flags |= SCFLAG_OWRITE; 173 break; 174 case APMDEV_NORMAL: 175 if (!(flag & FREAD) || (flag & FWRITE)) { 176 error = EINVAL; 177 break; 178 } 179 sc->sc_flags |= SCFLAG_OREAD; 180 break; 181 default: 182 error = ENXIO; 183 break; 184 } 185 return error; 186 } 187 188 int 189 apmclose(dev_t dev, int flag, int mode, struct proc *p) 190 { 191 struct apm_softc *sc; 192 193 /* apm0 only */ 194 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 195 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 196 return ENXIO; 197 198 DPRINTF(("apmclose: pid %d flag %x mode %x\n", 199 p->p_p->ps_pid, flag, mode)); 200 201 switch (APMDEV(dev)) { 202 case APMDEV_CTL: 203 sc->sc_flags &= ~SCFLAG_OWRITE; 204 break; 205 case APMDEV_NORMAL: 206 sc->sc_flags &= ~SCFLAG_OREAD; 207 break; 208 } 209 return 0; 210 } 211 212 int 213 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 214 { 215 struct apm_softc *sc; 216 struct apm_power_info *power; 217 int error = 0; 218 219 /* apm0 only */ 220 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 221 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 222 return ENXIO; 223 224 switch (cmd) { 225 /* some ioctl names from linux */ 226 case APM_IOC_STANDBY: 227 case APM_IOC_STANDBY_REQ: 228 case APM_IOC_SUSPEND: 229 case APM_IOC_SUSPEND_REQ: 230 if ((flag & FWRITE) == 0) 231 error = EBADF; 232 else if (sys_platform->suspend == NULL || 233 sys_platform->resume == NULL) 234 error = EOPNOTSUPP; 235 else 236 error = apm_suspend(APM_IOC_SUSPEND); 237 break; 238 #ifdef HIBERNATE 239 case APM_IOC_HIBERNATE: 240 if ((flag & FWRITE) == 0) 241 error = EBADF; 242 else if (sys_platform->suspend == NULL || 243 sys_platform->resume == NULL) 244 error = EOPNOTSUPP; 245 else 246 error = apm_suspend(APM_IOC_HIBERNATE); 247 break; 248 #endif 249 case APM_IOC_PRN_CTL: 250 if ((flag & FWRITE) == 0) 251 error = EBADF; 252 else { 253 int flag = *(int *)data; 254 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); 255 switch (flag) { 256 case APM_PRINT_ON: /* enable printing */ 257 sc->sc_flags &= ~SCFLAG_PRINT; 258 break; 259 case APM_PRINT_OFF: /* disable printing */ 260 sc->sc_flags &= ~SCFLAG_PRINT; 261 sc->sc_flags |= SCFLAG_NOPRINT; 262 break; 263 case APM_PRINT_PCT: /* disable some printing */ 264 sc->sc_flags &= ~SCFLAG_PRINT; 265 sc->sc_flags |= SCFLAG_PCTPRINT; 266 break; 267 default: 268 error = EINVAL; 269 break; 270 } 271 } 272 break; 273 case APM_IOC_DEV_CTL: 274 if ((flag & FWRITE) == 0) 275 error = EBADF; 276 else 277 error = EOPNOTSUPP; /* XXX */ 278 break; 279 case APM_IOC_GETPOWER: 280 power = (struct apm_power_info *)data; 281 error = (*get_apminfo)(power); 282 break; 283 default: 284 error = ENOTTY; 285 } 286 287 return error; 288 } 289 290 void 291 filt_apmrdetach(struct knote *kn) 292 { 293 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook; 294 295 SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext); 296 } 297 298 int 299 filt_apmread(struct knote *kn, long hint) 300 { 301 /* XXX weird kqueue_scan() semantics */ 302 if (hint && !kn->kn_data) 303 kn->kn_data = (int)hint; 304 305 return (1); 306 } 307 308 int 309 apmkqfilter(dev_t dev, struct knote *kn) 310 { 311 struct apm_softc *sc; 312 313 /* apm0 only */ 314 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 315 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 316 return ENXIO; 317 318 switch (kn->kn_filter) { 319 case EVFILT_READ: 320 kn->kn_fop = &apmread_filtops; 321 break; 322 default: 323 return (EINVAL); 324 } 325 326 kn->kn_hook = (caddr_t)sc; 327 SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext); 328 329 return (0); 330 } 331 332 int 333 apm_getdefaultinfo(struct apm_power_info *info) 334 { 335 info->battery_state = APM_BATT_UNKNOWN; 336 info->ac_state = APM_AC_UNKNOWN; 337 info->battery_life = 0; 338 info->minutes_left = -1; 339 return (0); 340 } 341 342 void 343 apm_setinfohook(int (*hook)(struct apm_power_info *)) 344 { 345 get_apminfo = hook; 346 } 347 348 int 349 apm_record_event(u_int event, const char *src, const char *msg) 350 { 351 static int apm_evindex; 352 struct apm_softc *sc; 353 354 /* apm0 only */ 355 if (apm_cd.cd_ndevs == 0 || (sc = apm_cd.cd_devs[0]) == NULL) 356 return ENXIO; 357 358 if ((sc->sc_flags & SCFLAG_NOPRINT) == 0) 359 printf("%s: %s %s\n", sc->sc_dev.dv_xname, src, msg); 360 361 /* skip if no user waiting */ 362 if ((sc->sc_flags & SCFLAG_OPEN) == 0) 363 return (1); 364 365 apm_evindex++; 366 KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex)); 367 368 return (0); 369 } 370 371 int 372 apm_suspend(int state) 373 { 374 int rv; 375 int s; 376 377 #if NWSDISPLAY > 0 378 wsdisplay_suspend(); 379 #endif 380 381 stop_periodic_resettodr(); 382 resettodr(); 383 384 config_suspend_all(DVACT_QUIESCE); 385 bufq_quiesce(); 386 387 s = splhigh(); 388 (void)disableintr(); 389 cold = 2; 390 391 rv = config_suspend_all(DVACT_SUSPEND); 392 393 suspend_randomness(); 394 395 #ifdef HIBERNATE 396 if (state == APM_IOC_HIBERNATE) { 397 uvm_pmr_zero_everything(); 398 if (hibernate_suspend()) { 399 printf("apm: hibernate_suspend failed"); 400 uvm_pmr_dirty_everything(); 401 return (ECANCELED); 402 } 403 } 404 #endif 405 406 /* XXX 407 * Flag to disk drivers that they should "power down" the disk 408 * when we get to DVACT_POWERDOWN. 409 */ 410 boothowto |= RB_POWERDOWN; 411 config_suspend_all(DVACT_POWERDOWN); 412 boothowto &= ~RB_POWERDOWN; 413 414 if (rv == 0) { 415 rv = sys_platform->suspend(); 416 if (rv == 0) 417 rv = sys_platform->resume(); 418 } 419 inittodr(time_second); /* Move the clock forward */ 420 config_suspend_all(DVACT_RESUME); 421 422 cold = 0; 423 (void)enableintr(); 424 splx(s); 425 426 resume_randomness(NULL, 0); /* force RNG upper level reseed */ 427 bufq_restart(); 428 429 config_suspend_all(DVACT_WAKEUP); 430 431 start_periodic_resettodr(); 432 433 #if NWSDISPLAY > 0 434 wsdisplay_resume(); 435 #endif 436 437 apm_record_event(APM_NORMAL_RESUME, "System", "resumed from sleep"); 438 439 return rv; 440 } 441