summaryrefslogtreecommitdiff
path: root/sys/arch/sparc/dev
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2005-03-29 16:26:46 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2005-03-29 16:26:46 +0000
commitf4ddf48fe34dc16fd30f0f844f03d285d0f3386d (patch)
tree86f0d8b496021105346b74c0c20cab54254773d8 /sys/arch/sparc/dev
parenteef7068a6de10b8f1c78351ad85efa001012c7a2 (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.c340
-rw-r--r--sys/arch/sparc/dev/ts102reg.h3
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 */