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