1 /* $OpenBSD: apm.c,v 1.24 2021/03/26 23:34:50 kn 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 #if NAPM > 1 37 #error only one APM emulation device may be configured 38 #endif 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/proc.h> 44 #include <sys/device.h> 45 #include <sys/fcntl.h> 46 #include <sys/ioctl.h> 47 #include <sys/event.h> 48 49 #include <machine/conf.h> 50 #include <machine/cpu.h> 51 #include <machine/apmvar.h> 52 #include <machine/autoconf.h> 53 54 #include <macppc/dev/pm_direct.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 88 const struct filterops apmread_filtops = { 89 .f_flags = FILTEROP_ISFD, 90 .f_attach = NULL, 91 .f_detach = filt_apmrdetach, 92 .f_event = filt_apmread, 93 }; 94 95 /* 96 * Flags to control kernel display 97 * SCFLAG_NOPRINT: do not output APM power messages due to 98 * a power change event. 99 * 100 * SCFLAG_PCTPRINT: do not output APM power messages due to 101 * to a power change event unless the battery 102 * percentage changes. 103 */ 104 105 #define SCFLAG_NOPRINT 0x0008000 106 #define SCFLAG_PCTPRINT 0x0004000 107 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT) 108 109 #define SCFLAG_OREAD (1 << 0) 110 #define SCFLAG_OWRITE (1 << 1) 111 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 112 113 114 int 115 apmmatch(struct device *parent, void *match, void *aux) 116 { 117 struct confargs *ca = aux; 118 119 if (strcmp(ca->ca_name, "apm") != 0) 120 return (0); 121 122 return (1); 123 } 124 125 void 126 apmattach(struct device *parent, struct device *self, void *aux) 127 { 128 struct pmu_battery_info info; 129 130 pm_battery_info(0, &info); 131 132 printf(": battery flags 0x%X, ", info.flags); 133 printf("%d%% charged\n", ((info.cur_charge * 100) / info.max_charge)); 134 } 135 136 int 137 apmopen(dev_t dev, int flag, int mode, struct proc *p) 138 { 139 struct apm_softc *sc; 140 int error = 0; 141 142 /* apm0 only */ 143 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 144 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 145 return ENXIO; 146 147 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n", 148 APMDEV(dev), p->p_p->ps_pid, flag, mode)); 149 150 switch (APMDEV(dev)) { 151 case APMDEV_CTL: 152 if (!(flag & FWRITE)) { 153 error = EINVAL; 154 break; 155 } 156 if (sc->sc_flags & SCFLAG_OWRITE) { 157 error = EBUSY; 158 break; 159 } 160 sc->sc_flags |= SCFLAG_OWRITE; 161 break; 162 case APMDEV_NORMAL: 163 if (!(flag & FREAD) || (flag & FWRITE)) { 164 error = EINVAL; 165 break; 166 } 167 sc->sc_flags |= SCFLAG_OREAD; 168 break; 169 default: 170 error = ENXIO; 171 break; 172 } 173 return error; 174 } 175 176 int 177 apmclose(dev_t dev, int flag, int mode, struct proc *p) 178 { 179 struct apm_softc *sc; 180 181 /* apm0 only */ 182 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 183 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 184 return ENXIO; 185 186 DPRINTF(("apmclose: pid %d flag %x mode %x\n", 187 p->p_p->ps_pid, flag, mode)); 188 189 switch (APMDEV(dev)) { 190 case APMDEV_CTL: 191 sc->sc_flags &= ~SCFLAG_OWRITE; 192 break; 193 case APMDEV_NORMAL: 194 sc->sc_flags &= ~SCFLAG_OREAD; 195 break; 196 } 197 return 0; 198 } 199 200 int 201 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 202 { 203 struct apm_softc *sc; 204 struct pmu_battery_info batt; 205 struct apm_power_info *power; 206 int error = 0; 207 208 /* apm0 only */ 209 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 210 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 211 return ENXIO; 212 213 switch (cmd) { 214 /* some ioctl names from linux */ 215 case APM_IOC_STANDBY: 216 case APM_IOC_STANDBY_REQ: 217 case APM_IOC_SUSPEND: 218 case APM_IOC_SUSPEND_REQ: 219 case APM_IOC_DEV_CTL: 220 if ((flag & FWRITE) == 0) 221 error = EBADF; 222 else 223 error = EOPNOTSUPP; 224 break; 225 case APM_IOC_PRN_CTL: 226 if ((flag & FWRITE) == 0) 227 error = EBADF; 228 else { 229 int flag = *(int *)data; 230 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); 231 switch (flag) { 232 case APM_PRINT_ON: /* enable printing */ 233 sc->sc_flags &= ~SCFLAG_PRINT; 234 break; 235 case APM_PRINT_OFF: /* disable printing */ 236 sc->sc_flags &= ~SCFLAG_PRINT; 237 sc->sc_flags |= SCFLAG_NOPRINT; 238 break; 239 case APM_PRINT_PCT: /* disable some printing */ 240 sc->sc_flags &= ~SCFLAG_PRINT; 241 sc->sc_flags |= SCFLAG_PCTPRINT; 242 break; 243 default: 244 error = EINVAL; 245 break; 246 } 247 } 248 break; 249 case APM_IOC_GETPOWER: 250 power = (struct apm_power_info *)data; 251 252 pm_battery_info(0, &batt); 253 254 power->ac_state = ((batt.flags & PMU_PWR_AC_PRESENT) ? 255 APM_AC_ON : APM_AC_OFF); 256 power->battery_life = 257 ((batt.cur_charge * 100) / batt.max_charge); 258 259 /* 260 * If the battery is charging, return the minutes left until 261 * charging is complete. apmd knows this. 262 */ 263 264 if (!(batt.flags & PMU_PWR_BATT_PRESENT)) { 265 power->battery_state = APM_BATT_UNKNOWN; 266 power->minutes_left = 0; 267 power->battery_life = 0; 268 } else if ((power->ac_state == APM_AC_ON) && 269 (batt.draw > 0)) { 270 power->minutes_left = 271 (((batt.max_charge - batt.cur_charge) * 3600) / 272 batt.draw) / 60; 273 power->battery_state = APM_BATT_CHARGING; 274 } else { 275 power->minutes_left = 276 ((batt.cur_charge * 3600) / (-batt.draw)) / 60; 277 278 if (power->battery_life > 50) 279 power->battery_state = APM_BATT_HIGH; 280 else if (power->battery_life > 25) 281 power->battery_state = APM_BATT_LOW; 282 else 283 power->battery_state = APM_BATT_CRITICAL; 284 } 285 break; 286 default: 287 error = ENOTTY; 288 } 289 290 return error; 291 } 292 293 void 294 filt_apmrdetach(struct knote *kn) 295 { 296 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook; 297 298 klist_remove_locked(&sc->sc_note, kn); 299 } 300 301 int 302 filt_apmread(struct knote *kn, long hint) 303 { 304 /* XXX weird kqueue_scan() semantics */ 305 if (hint && !kn->kn_data) 306 kn->kn_data = (int)hint; 307 308 return (1); 309 } 310 311 int 312 apmkqfilter(dev_t dev, struct knote *kn) 313 { 314 struct apm_softc *sc; 315 316 /* apm0 only */ 317 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 318 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 319 return ENXIO; 320 321 switch (kn->kn_filter) { 322 case EVFILT_READ: 323 kn->kn_fop = &apmread_filtops; 324 break; 325 default: 326 return (EINVAL); 327 } 328 329 kn->kn_hook = (caddr_t)sc; 330 klist_insert_locked(&sc->sc_note, kn); 331 332 return (0); 333 } 334