diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2005-03-29 16:26:46 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2005-03-29 16:26:46 +0000 |
commit | f4ddf48fe34dc16fd30f0f844f03d285d0f3386d (patch) | |
tree | 86f0d8b496021105346b74c0c20cab54254773d8 /sys/arch/sparc/dev | |
parent | eef7068a6de10b8f1c78351ad85efa001012c7a2 (diff) |
Add an apm interface to the tctrl(4) device found on SPARCbooks; currently
limited to power change events and battery status retrieval.
ok deraadt@ mickey@
Diffstat (limited to 'sys/arch/sparc/dev')
-rw-r--r-- | sys/arch/sparc/dev/tctrl.c | 340 | ||||
-rw-r--r-- | sys/arch/sparc/dev/ts102reg.h | 3 |
2 files changed, 336 insertions, 7 deletions
diff --git a/sys/arch/sparc/dev/tctrl.c b/sys/arch/sparc/dev/tctrl.c index 6409aaca8c7..3f76cf2660c 100644 --- a/sys/arch/sparc/dev/tctrl.c +++ b/sys/arch/sparc/dev/tctrl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tctrl.c,v 1.9 2005/03/29 12:55:55 miod Exp $ */ +/* $OpenBSD: tctrl.c,v 1.10 2005/03/29 16:26:44 miod Exp $ */ /* $NetBSD: tctrl.c,v 1.2 1999/08/11 00:46:06 matt Exp $ */ /*- @@ -36,20 +36,75 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +/* + * The /dev/apm{,ctl} interface code falls under the following license + * terms: + * + * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved. + * Copyright (c) 1995 John T. Kohl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ #include <sys/param.h> #include <sys/systm.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/device.h> +#include <sys/event.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/proc.h> #include <sys/timeout.h> +#include <machine/apmvar.h> #include <machine/autoconf.h> +#include <machine/conf.h> #include <machine/cpu.h> #include <sparc/dev/ts102reg.h> #include <sparc/dev/tctrlvar.h> +/* + * Flags to control kernel display + * SCFLAG_NOPRINT: do not output APM power messages due to + * a power change event. + * + * SCFLAG_PCTPRINT: do not output APM power messages due to + * to a power change event unless the battery + * percentage changes. + */ + +#define SCFLAG_NOPRINT 0x0008000 +#define SCFLAG_PCTPRINT 0x0004000 +#define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT) + const char *tctrl_ext_statuses[16] = { "main power available", "internal battery attached", @@ -96,6 +151,10 @@ struct tctrl_softc { u_int sc_bellvol; struct timeout sc_tmo; + + /* /dev/apm{,ctl} fields */ + struct klist sc_note; + u_int sc_apmflags; }; int tctrl_match(struct device *, void *, void *); @@ -113,6 +172,8 @@ void tctrl_request(struct tctrl_softc *, struct tctrl_req *); void tctrl_tft(struct tctrl_softc *); void tctrl_write_data(struct tctrl_softc *, u_int8_t); +int apm_record_event(struct tctrl_softc *, u_int); + struct cfattach tctrl_ca = { sizeof(struct tctrl_softc), tctrl_match, tctrl_attach }; @@ -425,17 +486,23 @@ tctrl_read_event_status(void *arg) #ifdef TCTRLDEBUG /* Obviously status change */ if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) { - printf("%s: Battery level change\n", sc->sc_dev.dv_xname); + if (sc->sc_apmflags & SCFLAG_PCTPRINT) + printf("%s: Battery level change\n", + sc->sc_dev.dv_xname); } #endif if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) { - printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname); + if ((sc->sc_apmflags & SCFLAG_NOPRINT) == 0) + printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname); + apm_record_event(sc, APM_BATTERY_LOW); } if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) { tctrl_read_ext_status(sc); - printf("%s: main power %s\n", sc->sc_dev.dv_xname, - (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ? - "restored" : "removed"); + if ((sc->sc_apmflags & SCFLAG_NOPRINT) == 0) + printf("%s: main power %s\n", sc->sc_dev.dv_xname, + (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ? + "restored" : "removed"); + apm_record_event(sc, APM_POWER_CHANGE); #if 0 /* automatically done for us */ tctrl_lcd(sc, ~TS102_LCD_DC_OK, sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE ? @@ -788,3 +855,264 @@ tadpole_bell(u_int duration, u_int freq, u_int volume) return (1); } + +/* + * /dev/apm{,ctl} interface code + */ + +#define APMUNIT(dev) (minor(dev)&0xf0) +#define APMDEV(dev) (minor(dev)&0x0f) +#define APMDEV_NORMAL 0 +#define APMDEV_CTL 8 + +int apmkqfilter(dev_t dev, struct knote *kn); +void filt_apmrdetach(struct knote *kn); +int filt_apmread(struct knote *kn, long hint); + +struct filterops apmread_filtops = + { 1, NULL, filt_apmrdetach, filt_apmread}; + +#define SCFLAG_OREAD (1 << 0) +#define SCFLAG_OWRITE (1 << 1) +#define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) + +int +apmopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tctrl_softc *sc; + int error = 0; + + if (tctrl_cd.cd_devs == NULL + || tctrl_cd.cd_ndevs == 0 + || tctrl_cd.cd_devs[0] == NULL) { + return (ENXIO); + } + + /* apm0 only */ + if (APMUNIT(dev) != 0) + return (ENODEV); + + sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0]; + + switch (APMDEV(dev)) { + case APMDEV_CTL: + if (!(flag & FWRITE)) { + error = EINVAL; + break; + } + if (sc->sc_apmflags & SCFLAG_OWRITE) { + error = EBUSY; + break; + } + sc->sc_apmflags |= SCFLAG_OWRITE; + break; + case APMDEV_NORMAL: + if (!(flag & FREAD) || (flag & FWRITE)) { + error = EINVAL; + break; + } + sc->sc_apmflags |= SCFLAG_OREAD; + break; + default: + error = ENXIO; + break; + } + return (error); +} + +int +apmclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct tctrl_softc *sc; + + if (tctrl_cd.cd_devs == NULL + || tctrl_cd.cd_ndevs == 0 + || tctrl_cd.cd_devs[0] == NULL) { + return (ENXIO); + } + + /* apm0 only */ + if (APMUNIT(dev) != 0) + return (ENODEV); + + sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0]; + + switch (APMDEV(dev)) { + case APMDEV_CTL: + sc->sc_apmflags &= ~SCFLAG_OWRITE; + break; + case APMDEV_NORMAL: + sc->sc_apmflags &= ~SCFLAG_OREAD; + break; + } + return (0); +} + +int +apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct tctrl_softc *sc; + struct tctrl_req req; + struct apm_power_info *power; + u_int8_t c; + int error = 0; + + if (tctrl_cd.cd_devs == NULL + || tctrl_cd.cd_ndevs == 0 + || tctrl_cd.cd_devs[0] == NULL) { + return (ENXIO); + } + + /* apm0 only */ + if (APMUNIT(dev) != 0) + return (ENODEV); + + sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0]; + + switch (cmd) { + /* some ioctl names from linux */ + case APM_IOC_STANDBY: + if ((flag & FWRITE) == 0) + error = EBADF; + else + error = EOPNOTSUPP; /* XXX */ + break; + case APM_IOC_SUSPEND: + if ((flag & FWRITE) == 0) + error = EBADF; + else + error = EOPNOTSUPP; /* XXX */ + break; + case APM_IOC_PRN_CTL: + if ((flag & FWRITE) == 0) + error = EBADF; + else { + int flag = *(int *)data; + switch (flag) { + case APM_PRINT_ON: /* enable printing */ + sc->sc_apmflags &= ~SCFLAG_PRINT; + break; + case APM_PRINT_OFF: /* disable printing */ + sc->sc_apmflags &= ~SCFLAG_PRINT; + sc->sc_apmflags |= SCFLAG_NOPRINT; + break; + case APM_PRINT_PCT: /* disable some printing */ + sc->sc_apmflags &= ~SCFLAG_PRINT; + sc->sc_apmflags |= SCFLAG_PCTPRINT; + break; + default: + error = EINVAL; + break; + } + } + break; + case APM_IOC_GETPOWER: + power = (struct apm_power_info *)data; + + req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_RATE; + req.cmdlen = 1; + req.rsplen = 2; + tctrl_request(sc, &req); + if (req.rspbuf[0] != 0) + power->battery_state = APM_BATT_CHARGING; + else + power->battery_state = APM_BATT_UNKNOWN; + + req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_LEVEL; + req.cmdlen = 1; + req.rsplen = 3; + tctrl_request(sc, &req); + + c = req.rspbuf[0]; + if (c == TS102_CHARGE_UNKNOWN) + c = 0; + power->battery_life = c; + power->minutes_left = (u_int)-1; /* unknown */ + if (power->battery_state != APM_BATT_CHARGING) { + if (c < 0x20) + power->battery_state = APM_BATT_CRITICAL; + else if (c < 0x40) + power->battery_state = APM_BATT_HIGH; + else if (c < 0x66) + power->battery_state = APM_BATT_HIGH; + } + +#if 0 + tctrl_read_ext_status(sc); +#endif + if (sc->sc_ext_status & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) + power->ac_state = APM_AC_ON; + else + power->ac_state = APM_AC_OFF; + break; + + default: + error = ENOTTY; + } + + return (error); +} + +int +apm_record_event(struct tctrl_softc *sc, u_int type) +{ + static int apm_evindex; + + /* skip if no user waiting */ + if ((sc->sc_apmflags & SCFLAG_OPEN) == 0) + return (1); + + apm_evindex++; + KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, apm_evindex)); + + return (0); +} + +void +filt_apmrdetach(struct knote *kn) +{ + struct tctrl_softc *sc = (struct tctrl_softc *)kn->kn_hook; + + SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext); +} + +int +filt_apmread(struct knote *kn, long hint) +{ + /* XXX weird kqueue_scan() semantics */ + if (hint && !kn->kn_data) + kn->kn_data = (int)hint; + + return (1); +} + +int +apmkqfilter(dev_t dev, struct knote *kn) +{ + struct tctrl_softc *sc; + + if (tctrl_cd.cd_devs == NULL + || tctrl_cd.cd_ndevs == 0 + || tctrl_cd.cd_devs[0] == NULL) { + return (ENXIO); + } + + /* apm0 only */ + if (APMUNIT(dev) != 0) + return (ENODEV); + + sc = (struct tctrl_softc *)tctrl_cd.cd_devs[0]; + + switch (kn->kn_filter) { + case EVFILT_READ: + kn->kn_fop = &apmread_filtops; + break; + default: + return (1); + } + + kn->kn_hook = (caddr_t)sc; + SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext); + + return (0); +} diff --git a/sys/arch/sparc/dev/ts102reg.h b/sys/arch/sparc/dev/ts102reg.h index e03b6954214..e42a2ecf4e0 100644 --- a/sys/arch/sparc/dev/ts102reg.h +++ b/sys/arch/sparc/dev/ts102reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ts102reg.h,v 1.3 2003/06/18 17:50:23 miod Exp $ */ +/* $OpenBSD: ts102reg.h,v 1.4 2005/03/29 16:26:44 miod Exp $ */ /* $NetBSD: ts102reg.h,v 1.7 2002/09/29 23:23:58 wiz Exp $ */ /*- @@ -348,6 +348,7 @@ enum ts102_opcode { /* Argument Returned */ TS102_OP_ADMIN_VRFY_SYSTEM_PASS=0x73, /* len <pass> ack + status */ TS102_OP_RD_INT_CHARGE_LEVEL=0x7a, /* ack + 2 byte */ TS102_OP_RD_EXT_CHARGE_LEVEL=0x7b, /* ack + 2 byte */ +#define TS102_CHARGE_UNKNOWN 0xfa TS102_OP_SLEEP=0x80, /* supposedly sleeps, not sure */ TS102_OP_ADMIN_POWER_OFF=0x82, /* len <pass> none */ TS102_OP_ADMIN_POWER_RESTART=0x83, /* msb,xx,lsb none */ |