$NetBSD: patch-af,v 1.5 2004/07/26 23:25:19 wiz Exp $

--- apm_read.c.orig	2001-11-23 23:07:19.000000000 +0200
+++ apm_read.c
@@ -31,6 +31,7 @@
 
 /* file -> APM device */
 extern char apm_device_file[];
+extern char sysmon_device_file[];
 
 #include "state.h"
 extern struct apm_state state;
@@ -136,18 +137,185 @@
 	state.time_left = info.ai_batt_time / 60;
 }
 #elif defined(__NetBSD__)||defined(__OpenBSD__)
+
+#include <sys/envsys.h>
+
+/* We cannot use ifdef ENVSYS_SWATTHOUR etc as it is enum. The
+   ENVSYS_FFRACVALID was added at the same time as WATTHOUR and INDICATOR, so
+   we use it to see if we have the support for those. */
+#ifdef ENVSYS_FFRACVALID
+#define HAVE_ENVSYS_SWATTHOUR
+#define HAVE_ENVSYS_INDICATOR
+#endif /* ENVSYS_FFRACVALID */
+
+int match_end(const char *str, const char *end_part)
+{
+	int len1, len2;
+	len1 = strlen(str);
+	len2 = strlen(end_part);
+	if (len1 < len2)
+		return 0;
+	return strcmp(str + len1 - len2, end_part) == 0;
+}
+
+void TrySysmonDevice( )
+{
+	int fd;
+	int count = 0;
+        envsys_basic_info_t ebis;
+	envsys_tre_data_t etds;
+	int32_t design, charge, warn_cap, low_cap, discharge_rate, charge_rate;
+	int connected, charging, percent, time_units, battery_status;
+
+	if ((fd = open(sysmon_device_file, O_RDONLY)) == -1) {
+		error_handle(1, "");
+		return;
+	}
+
+	design = 0;
+	charge = 0;
+	warn_cap = 0;
+	low_cap = 0;
+	charge_rate = 0;
+	discharge_rate = 0;
+	connected = 0;
+	charging = 0;
+	percent = 0;
+	time_units = 0;
+
+	for(etds.sensor = 0; ; etds.sensor++) {
+		if (ioctl(fd, ENVSYS_GTREDATA, &etds) == -1) {
+			error_handle(4, "");
+			close(fd);
+			return;
+		}
+		if (!(etds.validflags & ENVSYS_FVALID))
+			break;
+
+		ebis.sensor = etds.sensor;
+		if (ioctl(fd, ENVSYS_GTREINFO, &ebis) == -1) {
+			error_handle(4, "");
+			close(fd);
+			return;
+		}
+
+		if (etds.units != ebis.units) {
+			error_handle(6, "units does not match");
+		}
+		  
+		if (!(etds.validflags & ENVSYS_FCURVALID))
+			continue;
+
+#ifdef HAVE_ENVSYS_SWATTHOUR
+		if (etds.units == ENVSYS_SWATTHOUR ||
+		    etds.units == ENVSYS_SAMPHOUR) {
+			/* Watt or amp hours, this must be battery cap info. */
+			if (match_end(ebis.desc, " design cap")) {
+				design += etds.cur.data_s;
+			} else if (match_end(ebis.desc, " charge")) {
+				charge += etds.cur.data_s;
+			} else if (match_end(ebis.desc, " warn cap")) {
+				warn_cap += etds.cur.data_s;
+			} else if (match_end(ebis.desc, " low cap")) {
+				low_cap += etds.cur.data_s;
+			}
+		}
+#endif /* HAVE_ENVSYS_SWATTHOUR */
+#ifdef HAVE_ENVSYS_INDICATOR
+		if (etds.units == ENVSYS_INDICATOR) {
+			/* Indicator of something, check for connected. */
+			if (match_end(ebis.desc, " connected")) {
+				connected = etds.cur.data_us;
+			} else if (match_end(ebis.desc, " charging")) {
+				charging = etds.cur.data_us;
+			}
+
+		}
+#endif /* HAVE_ENVSYS_INDICATOR */
+		if (etds.units == ENVSYS_SWATTS ||
+		    etds.units == ENVSYS_SAMPS) {
+			/* Watts or apms, this must discharge rate. */
+			if (match_end(ebis.desc, " discharge rate")) {
+				discharge_rate += etds.cur.data_s;
+			} else if (match_end(ebis.desc, " charge rate")) {
+				charge_rate += etds.cur.data_s;
+			}
+
+		}
+	}
+
+	if (state.ac_line_status != connected) {
+		state.ac_line_status = connected ? AC_ONLINE : AC_BATTERY;
+		++state.update;
+		if ( state.ac_line_status == AC_ONLINE ) 
+			state.flags |= CHANGE_AC_ON;
+		else
+			state.flags |= CHANGE_AC_OFF;
+	}
+	if (charging) {
+		battery_status = BATTERY_CHARGING;
+	} else {
+		if (charge > warn_cap) {
+			battery_status = BATTERY_HIGH;
+		} else if (charge > low_cap) {
+			battery_status = BATTERY_LOW;
+		} else {
+			battery_status = BATTERY_CRITICAL;
+		}
+	}
+
+	if (state.battery_status != battery_status) {
+		state.battery_status = battery_status;
+		++state.update;
+	}
+
+	if (design != 0) {
+		percent = charge / (design / 100);
+		if (percent > 100) {
+			percent = 100;
+		}
+		else if (percent < 0) {
+			percent = 0;
+		}
+	}
+	if (state.percent != percent) {
+		if ( state.percent < percent )
+			state.flags |= CHANGE_POWER_UP;
+		else
+			state.flags |= CHANGE_POWER_DOWN;
+		state.percent = percent;
+		++state.update;
+	}
+
+	if (discharge_rate > 0) {
+		time_units = charge / (discharge_rate / 60);
+	} else if (charge_rate > 0 && design > 0 && charging) {
+		time_units = (design - charge) / (charge_rate / 60);
+	}
+
+	/* we can display maximum 99:59 */
+	if ( time_units > 5999 )
+		time_units = 5999;
+	if (state.time_left != time_units) {
+		state.time_left = time_units;
+		++state.update;
+	}
+	close(fd);
+	return;
+}
+
 void ReadAPMDevice( )		/* NetBSD version */
 {
 	int fd;
 	struct apm_power_info info;
 	memset(&info, 0, sizeof(info));
 	if ((fd = open(apm_device_file, O_RDONLY)) == -1) {
-		error_handle(1, "");
+		TrySysmonDevice();
 		return;
 	}
 	if (ioctl(fd, APM_IOC_GETPOWER, &info) == -1) {
-		error_handle(4, "");
 		close(fd);
+		TrySysmonDevice();
 		return;
 	}
 	close(fd);
