1 /* $OpenBSD: apm.c,v 1.5 2020/05/29 04:42:23 deraadt 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 49 #include <machine/conf.h> 50 #include <machine/cpu.h> 51 #include <machine/acpiapm.h> 52 #include <machine/apmvar.h> 53 54 #include <dev/wscons/wsdisplayvar.h> 55 56 #if defined(APMDEBUG) 57 #define DPRINTF(x) printf x 58 #else 59 #define DPRINTF(x) /**/ 60 #endif 61 62 struct apm_softc { 63 struct device sc_dev; 64 struct klist sc_note; 65 int sc_flags; 66 }; 67 68 int apmmatch(struct device *, void *, void *); 69 void apmattach(struct device *, struct device *, void *); 70 71 struct cfattach apm_ca = { 72 sizeof(struct apm_softc), apmmatch, apmattach 73 }; 74 75 struct cfdriver apm_cd = { 76 NULL, "apm", DV_DULL 77 }; 78 79 #define APMUNIT(dev) (minor(dev)&0xf0) 80 #define APMDEV(dev) (minor(dev)&0x0f) 81 #define APMDEV_NORMAL 0 82 #define APMDEV_CTL 8 83 84 void filt_apmrdetach(struct knote *kn); 85 int filt_apmread(struct knote *kn, long hint); 86 int apmkqfilter(dev_t dev, struct knote *kn); 87 int apm_getdefaultinfo(struct apm_power_info *); 88 89 const struct filterops apmread_filtops = { 90 .f_flags = FILTEROP_ISFD, 91 .f_attach = NULL, 92 .f_detach = filt_apmrdetach, 93 .f_event = filt_apmread, 94 }; 95 96 int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo; 97 98 /* 99 * Flags to control kernel display 100 * SCFLAG_NOPRINT: do not output APM power messages due to 101 * a power change event. 102 * 103 * SCFLAG_PCTPRINT: do not output APM power messages due to 104 * to a power change event unless the battery 105 * percentage changes. 106 */ 107 108 #define SCFLAG_NOPRINT 0x0008000 109 #define SCFLAG_PCTPRINT 0x0004000 110 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT) 111 112 #define SCFLAG_OREAD (1 << 0) 113 #define SCFLAG_OWRITE (1 << 1) 114 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 115 116 117 int 118 apmmatch(struct device *parent, void *match, void *aux) 119 { 120 return (1); 121 } 122 123 void 124 apmattach(struct device *parent, struct device *self, void *aux) 125 { 126 acpiapm_open = apmopen; 127 acpiapm_close = apmclose; 128 acpiapm_ioctl = apmioctl; 129 acpiapm_kqfilter = apmkqfilter; 130 131 printf("\n"); 132 } 133 134 int 135 apmopen(dev_t dev, int flag, int mode, struct proc *p) 136 { 137 struct apm_softc *sc; 138 int error = 0; 139 140 /* apm0 only */ 141 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 142 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 143 return ENXIO; 144 145 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n", 146 APMDEV(dev), p->p_p->ps_pid, flag, mode)); 147 148 switch (APMDEV(dev)) { 149 case APMDEV_CTL: 150 if (!(flag & FWRITE)) { 151 error = EINVAL; 152 break; 153 } 154 if (sc->sc_flags & SCFLAG_OWRITE) { 155 error = EBUSY; 156 break; 157 } 158 sc->sc_flags |= SCFLAG_OWRITE; 159 break; 160 case APMDEV_NORMAL: 161 if (!(flag & FREAD) || (flag & FWRITE)) { 162 error = EINVAL; 163 break; 164 } 165 sc->sc_flags |= SCFLAG_OREAD; 166 break; 167 default: 168 error = ENXIO; 169 break; 170 } 171 return error; 172 } 173 174 int 175 apmclose(dev_t dev, int flag, int mode, struct proc *p) 176 { 177 struct apm_softc *sc; 178 179 /* apm0 only */ 180 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 181 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 182 return ENXIO; 183 184 DPRINTF(("apmclose: pid %d flag %x mode %x\n", 185 p->p_p->ps_pid, flag, mode)); 186 187 switch (APMDEV(dev)) { 188 case APMDEV_CTL: 189 sc->sc_flags &= ~SCFLAG_OWRITE; 190 break; 191 case APMDEV_NORMAL: 192 sc->sc_flags &= ~SCFLAG_OREAD; 193 break; 194 } 195 return 0; 196 } 197 198 int 199 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 200 { 201 struct apm_softc *sc; 202 struct apm_power_info *power; 203 int error = 0; 204 205 /* apm0 only */ 206 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 207 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 208 return ENXIO; 209 210 switch (cmd) { 211 /* some ioctl names from linux */ 212 case APM_IOC_STANDBY: 213 case APM_IOC_STANDBY_REQ: 214 case APM_IOC_SUSPEND: 215 case APM_IOC_SUSPEND_REQ: 216 if ((flag & FWRITE) == 0) 217 error = EBADF; 218 error = EOPNOTSUPP; 219 break; 220 #ifdef HIBERNATE 221 case APM_IOC_HIBERNATE: 222 if ((flag & FWRITE) == 0) 223 error = EBADF; 224 error = EOPNOTSUPP; 225 break; 226 #endif 227 case APM_IOC_PRN_CTL: 228 if ((flag & FWRITE) == 0) 229 error = EBADF; 230 else { 231 int flag = *(int *)data; 232 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); 233 switch (flag) { 234 case APM_PRINT_ON: /* enable printing */ 235 sc->sc_flags &= ~SCFLAG_PRINT; 236 break; 237 case APM_PRINT_OFF: /* disable printing */ 238 sc->sc_flags &= ~SCFLAG_PRINT; 239 sc->sc_flags |= SCFLAG_NOPRINT; 240 break; 241 case APM_PRINT_PCT: /* disable some printing */ 242 sc->sc_flags &= ~SCFLAG_PRINT; 243 sc->sc_flags |= SCFLAG_PCTPRINT; 244 break; 245 default: 246 error = EINVAL; 247 break; 248 } 249 } 250 break; 251 case APM_IOC_DEV_CTL: 252 if ((flag & FWRITE) == 0) 253 error = EBADF; 254 else 255 error = EOPNOTSUPP; /* XXX */ 256 break; 257 case APM_IOC_GETPOWER: 258 power = (struct apm_power_info *)data; 259 error = (*get_apminfo)(power); 260 break; 261 default: 262 error = ENOTTY; 263 } 264 265 return error; 266 } 267 268 void 269 filt_apmrdetach(struct knote *kn) 270 { 271 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook; 272 273 klist_remove(&sc->sc_note, kn); 274 } 275 276 int 277 filt_apmread(struct knote *kn, long hint) 278 { 279 /* XXX weird kqueue_scan() semantics */ 280 if (hint && !kn->kn_data) 281 kn->kn_data = (int)hint; 282 283 return (1); 284 } 285 286 int 287 apmkqfilter(dev_t dev, struct knote *kn) 288 { 289 struct apm_softc *sc; 290 291 /* apm0 only */ 292 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 293 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 294 return ENXIO; 295 296 switch (kn->kn_filter) { 297 case EVFILT_READ: 298 kn->kn_fop = &apmread_filtops; 299 break; 300 default: 301 return (EINVAL); 302 } 303 304 kn->kn_hook = (caddr_t)sc; 305 klist_insert(&sc->sc_note, kn); 306 307 return (0); 308 } 309 310 int 311 apm_getdefaultinfo(struct apm_power_info *info) 312 { 313 info->battery_state = APM_BATT_UNKNOWN; 314 info->ac_state = APM_AC_UNKNOWN; 315 info->battery_life = 0; 316 info->minutes_left = -1; 317 return (0); 318 } 319 320 void 321 apm_setinfohook(int (*hook)(struct apm_power_info *)) 322 { 323 get_apminfo = hook; 324 } 325 326 int 327 apm_record_event(u_int event, const char *src, const char *msg) 328 { 329 static int apm_evindex; 330 struct apm_softc *sc; 331 332 /* apm0 only */ 333 if (apm_cd.cd_ndevs == 0 || (sc = apm_cd.cd_devs[0]) == NULL) 334 return ENXIO; 335 336 if ((sc->sc_flags & SCFLAG_NOPRINT) == 0) 337 printf("%s: %s %s\n", sc->sc_dev.dv_xname, src, msg); 338 339 /* skip if no user waiting */ 340 if ((sc->sc_flags & SCFLAG_OPEN) == 0) 341 return (1); 342 343 apm_evindex++; 344 KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex)); 345 346 return (0); 347 } 348