1 /* $OpenBSD: apm.c,v 1.9 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 #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 47 #include <machine/autoconf.h> 48 #include <machine/conf.h> 49 #include <machine/cpu.h> 50 #include <machine/apmvar.h> 51 52 #include <dev/wscons/wsdisplayvar.h> 53 54 #include <loongson/dev/kb3310var.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 int apm_suspend(void); 90 91 struct filterops apmread_filtops = 92 { 1, NULL, filt_apmrdetach, filt_apmread}; 93 94 int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo; 95 96 /* 97 * Flags to control kernel display 98 * SCFLAG_NOPRINT: do not output APM power messages due to 99 * a power change event. 100 * 101 * SCFLAG_PCTPRINT: do not output APM power messages due to 102 * to a power change event unless the battery 103 * percentage changes. 104 */ 105 106 #define SCFLAG_NOPRINT 0x0008000 107 #define SCFLAG_PCTPRINT 0x0004000 108 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT) 109 110 #define SCFLAG_OREAD (1 << 0) 111 #define SCFLAG_OWRITE (1 << 1) 112 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 113 114 115 int 116 apmmatch(struct device *parent, void *match, void *aux) 117 { 118 struct mainbus_attach_args *maa = aux; 119 120 /* 121 * It only makes sense to attach on a 2F system, since 2E do not 122 * feature speed throttling, and we do not support 2E-based 123 * notebooks yet (assuming there are any). 124 */ 125 if (strcmp(maa->maa_name, apm_cd.cd_name) == 0 && loongson_ver == 0x2f) 126 return (1); 127 return (0); 128 } 129 130 void 131 apmattach(struct device *parent, struct device *self, void *aux) 132 { 133 printf("\n"); 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_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", p->p_pid, flag, mode)); 187 188 switch (APMDEV(dev)) { 189 case APMDEV_CTL: 190 sc->sc_flags &= ~SCFLAG_OWRITE; 191 break; 192 case APMDEV_NORMAL: 193 sc->sc_flags &= ~SCFLAG_OREAD; 194 break; 195 } 196 return 0; 197 } 198 199 int 200 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 201 { 202 struct apm_softc *sc; 203 struct apm_power_info *power; 204 int error = 0; 205 206 /* apm0 only */ 207 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 208 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 209 return ENXIO; 210 211 switch (cmd) { 212 /* some ioctl names from linux */ 213 case APM_IOC_STANDBY: 214 case APM_IOC_STANDBY_REQ: 215 if ((flag & FWRITE) == 0) 216 error = EBADF; 217 else 218 error = EOPNOTSUPP; /* XXX */ 219 break; 220 case APM_IOC_SUSPEND: 221 case APM_IOC_SUSPEND_REQ: 222 if ((flag & FWRITE) == 0) 223 error = EBADF; 224 else if (sys_platform->suspend == NULL || 225 sys_platform->resume == NULL) 226 error = EOPNOTSUPP; 227 else 228 error = apm_suspend(); 229 break; 230 case APM_IOC_PRN_CTL: 231 if ((flag & FWRITE) == 0) 232 error = EBADF; 233 else { 234 int flag = *(int *)data; 235 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); 236 switch (flag) { 237 case APM_PRINT_ON: /* enable printing */ 238 sc->sc_flags &= ~SCFLAG_PRINT; 239 break; 240 case APM_PRINT_OFF: /* disable printing */ 241 sc->sc_flags &= ~SCFLAG_PRINT; 242 sc->sc_flags |= SCFLAG_NOPRINT; 243 break; 244 case APM_PRINT_PCT: /* disable some printing */ 245 sc->sc_flags &= ~SCFLAG_PRINT; 246 sc->sc_flags |= SCFLAG_PCTPRINT; 247 break; 248 default: 249 error = EINVAL; 250 break; 251 } 252 } 253 break; 254 case APM_IOC_DEV_CTL: 255 if ((flag & FWRITE) == 0) 256 error = EBADF; 257 else 258 error = EOPNOTSUPP; /* XXX */ 259 break; 260 case APM_IOC_GETPOWER: 261 power = (struct apm_power_info *)data; 262 error = (*get_apminfo)(power); 263 break; 264 default: 265 error = ENOTTY; 266 } 267 268 return error; 269 } 270 271 void 272 filt_apmrdetach(struct knote *kn) 273 { 274 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook; 275 276 SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext); 277 } 278 279 int 280 filt_apmread(struct knote *kn, long hint) 281 { 282 /* XXX weird kqueue_scan() semantics */ 283 if (hint && !kn->kn_data) 284 kn->kn_data = (int)hint; 285 286 return (1); 287 } 288 289 int 290 apmkqfilter(dev_t dev, struct knote *kn) 291 { 292 struct apm_softc *sc; 293 294 /* apm0 only */ 295 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || 296 !(sc = apm_cd.cd_devs[APMUNIT(dev)])) 297 return ENXIO; 298 299 switch (kn->kn_filter) { 300 case EVFILT_READ: 301 kn->kn_fop = &apmread_filtops; 302 break; 303 default: 304 return (EINVAL); 305 } 306 307 kn->kn_hook = (caddr_t)sc; 308 SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext); 309 310 return (0); 311 } 312 313 int 314 apm_getdefaultinfo(struct apm_power_info *info) 315 { 316 info->battery_state = APM_BATT_UNKNOWN; 317 info->ac_state = APM_AC_UNKNOWN; 318 info->battery_life = 0; 319 info->minutes_left = -1; 320 return (0); 321 } 322 323 void 324 apm_setinfohook(int (*hook)(struct apm_power_info *)) 325 { 326 get_apminfo = hook; 327 } 328 329 int 330 apm_record_event(u_int event, const char *src, const char *msg) 331 { 332 static int apm_evindex; 333 struct apm_softc *sc; 334 335 /* apm0 only */ 336 if (apm_cd.cd_ndevs == 0 || (sc = apm_cd.cd_devs[0]) == NULL) 337 return ENXIO; 338 339 if ((sc->sc_flags & SCFLAG_NOPRINT) == 0) 340 printf("%s: %s %s\n", sc->sc_dev.dv_xname, src, msg); 341 342 /* skip if no user waiting */ 343 if ((sc->sc_flags & SCFLAG_OPEN) == 0) 344 return (1); 345 346 apm_evindex++; 347 KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex)); 348 349 return (0); 350 } 351 352 int 353 apm_suspend() 354 { 355 int rv; 356 int s; 357 358 #if NSWDISPLAY > 0 359 wsdisplay_suspend(); 360 #endif 361 bufq_quiesce(); 362 config_suspend(TAILQ_FIRST(&alldevs), DVACT_QUIESCE); 363 364 s = splhigh(); 365 (void)disableintr(); 366 cold = 1; 367 368 rv = config_suspend(TAILQ_FIRST(&alldevs), DVACT_SUSPEND); 369 if (rv == 0) { 370 rv = sys_platform->suspend(); 371 if (rv == 0) 372 rv = sys_platform->resume(); 373 } 374 config_suspend(TAILQ_FIRST(&alldevs), DVACT_RESUME); 375 376 cold = 0; 377 (void)enableintr(); 378 splx(s); 379 380 bufq_restart(); 381 #if NWSDISPLAY > 0 382 wsdisplay_resume(); 383 #endif 384 385 return rv; 386 } 387