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