#!/bin/sh
# Copyright (C) 2011 One Laptop per Child
# Power logging script
# Released under GPLv3

VERSION="2.1.0"
HOST=`hostname -s`
B_INFO=/sys/class/power_supply/olpc-battery

# The delay between readings.
DELAY=20
FAST_SAMPLE_V=5200000

# Make sure we have a correct system date.  As of now
# Jan 1 2012 is in the past so use that as a marker.
if [[ `date +%s` -lt 1325376000 ]]; then
	echo "`date` is in the past."
	echo "Fix your system date please"
	exit 1
fi

FDATE=`date "+%y%m%d-%H%M%S"`

if [ -e /usr/share/olpc-utils/olpc-utils-functions ]; then
	source /usr/share/olpc-utils/olpc-utils-functions
else
	function get_xo_version
	{

		if [ -e /sys/class/dmi/id/product_version ]
		then
			XO_VERSION=$(< /sys/class/dmi/id/product_version )
		else
			XO_VERSION="1"
		fi

	}

	function get_ofw_file()
	{
		fpath=$1
		if [ -e "/proc/device-tree/$fpath" ]; then
			cat "/proc/device-tree/$fpath"  2>/dev/null
		else
			cat "/ofw/$fpath"  2>/dev/null
		fi
	}
fi

XO_VERSION=$(get_xo_version)

# we have to handle 2.6.dd which yield 2600 and 3.0.0 which yields 300
# all of this will fail on anything earlier than 2.6.9
KERNVER=`uname -r | cut -c 1-6 | sed 's/[\._-]//g'`

KERNAPI=1
if [[ $KERNVER -gt 2600 ]]; then
	# of the 2.6 series, 25 or later
	if [[ $KERNVER -gt 2625 ]]; then
		KERNAPI=2
	fi
else
	# 3.0.0
	KERNAPI=2
fi

echo "Checking/waiting for a battery"

until [ $(< $B_INFO/present ) = 1 ] 
do 
	sleep 1
done

# If we just inserted a battery the EC needs time to
# read all the info.

echo "Found one."

# XO-1's with older ec-code need some time to load in
# the battery values.
if [[ $XO_VERSION = 1 ]]; then

	echo "Pausing to let the EC think"
	sleep 1
fi

# Now that we have a battery we can use the battery serial number in
# the filename.
DS_SERNUM=$(< $B_INFO/serial_number )
LOGFILE="pwr-$FDATE-$DS_SERNUM.csv"

ACR_PROP="charge_counter"

if [ ! -e $B_INFO/$ACR_PROP ]
then
	ACR_PROP="accum_current"
fi

if [ -e /boot/olpc_build ]
then
	BUILD=$(< /boot/olpc_build )
fi

if [ -e /bootpart/boot/olpc_build ]
then
	BUILD=$(< /bootpart/boot/olpc_build )
fi

if [ -e $B_INFO/eeprom ]
then
	# Skip 64 bytes and read 5 bytes; display without an address
	# and in single byte mode.  I don't use od directly since the
	# -j skip does not do a real fseek.
	echo "Reading eeprom data."

	MFGFAC=`dd if=$B_INFO/eeprom bs=1 skip=06 count=2 2> /dev/null| od -A n -t x1 `
	MFG_SER=`dd if=$B_INFO/eeprom bs=1 skip=64 count=5 2> /dev/null | od -A n -t x1`
	CHGCNT=`dd if=$B_INFO/eeprom bs=1 skip=74 count=2 2> /dev/null| od -A n -t x1 `
	CHGSOC=`dd if=$B_INFO/eeprom bs=1 skip=76 count=1 2> /dev/null| od -A n -t x1 `
	DISCNT=`dd if=$B_INFO/eeprom bs=1 skip=77 count=2 2> /dev/null| od -A n -t x1 `
	DISSOC=`dd if=$B_INFO/eeprom bs=1 skip=79 count=1 2> /dev/null| od -A n -t x1 `
else
	echo "Can't read the eeprom data because your kernel dosen't support eeprom dump"
	MFGFAC="NA"
	MFG_SER="NA"
	CHGCNT="NA"
	CHGSOC="NA"
	DISCNT="NA"
	DISSOC="NA"
fi

echo "Starting log $LOGFILE"
echo

echo "pwr_log Ver: $VERSION" > $LOGFILE
echo -n "HOST: "	>> $LOGFILE 
echo $HOST  		>> $LOGFILE 
echo -n "DATE: " 	>> $LOGFILE 
echo `date -R` 		>> $LOGFILE
echo -n "ECVER: " 	>> $LOGFILE 
echo `get_ofw_file /ec-name` >> $LOGFILE
echo -n "OFWVER: " 	>> $LOGFILE 
echo `get_ofw_file /openprom/model` 	>> $LOGFILE
echo -n "MODEL: " 	>> $LOGFILE 
echo `get_ofw_file /model` 	>> $LOGFILE
echo -n "SERNUM: " 	>> $LOGFILE
echo `get_ofw_file /serial-number` 	>> $LOGFILE
echo -n "BATTECH: " 	>> $LOGFILE 
echo `cat $B_INFO/technology` 	>> $LOGFILE
echo -n "BATMFG: " 	>> $LOGFILE
echo `cat $B_INFO/manufacturer` >> $LOGFILE
echo -n "BATSER: " 	>> $LOGFILE
echo $DS_SERNUM 	>> $LOGFILE
echo -n "BUILD: " 	>> $LOGFILE
echo $BUILD 		>> $LOGFILE
echo -n "MFGSER: " 	>> $LOGFILE
echo $MFG_SER 		>> $LOGFILE
echo -n "CHGCNT: " 	>> $LOGFILE
echo $CHGCNT 		>> $LOGFILE
echo -n "CHGSOC: " 	>> $LOGFILE
echo $CHGSOC 		>> $LOGFILE
echo -n "DISCNT: " 	>> $LOGFILE
echo $DISCNT 		>> $LOGFILE
echo -n "DISSOC: " 	>> $LOGFILE
echo $DISSOC 		>> $LOGFILE
echo -n "DELAY: " 	>> $LOGFILE
echo $DELAY: 		>> $LOGFILE
echo -n "XOVER: " 	>> $LOGFILE
echo $XO_VERSION 	>> $LOGFILE
echo -n "KERNVER: " 	>> $LOGFILE
echo $KERNVER 		>> $LOGFILE
echo -n "KERNAPI: " 	>> $LOGFILE
echo $KERNAPI 		>> $LOGFILE
echo -n "MFGFAC: " 	>> $LOGFILE
echo $MFGFAC 		>> $LOGFILE

# Allow the addition of some descriptive text from the cmd line
echo -n "COMMENT: " >> $LOGFILE
echo $1 >> $LOGFILE
echo "<StartData>" >> $LOGFILE

# convert a number into 2's complement
function conv_2s_comp {

	# This has since been changed in the kernel so that it returns
	# a signed value rather than unsigned, which fixes the math.
	# So if its already negative then bail.

	if [ $1 -lt 0 ]
	then
		echo $1
		return
	fi

	if [ $1 -gt 32767 ]
	then
		echo $(( 0 - (65535-$1+1) ))
		return
	fi

	echo $1
}

CAPACITY=capacity
if [ ! -f $B_INFO/$CAPACITY ]
then
	CAPACITY=capacity_level
fi

function get_acr {

    local acr_temp

    acr_temp=$(< $B_INFO/$ACR_PROP )
    test $KERNAPI -eq 1 && acr_temp=$(conv_2s_comp ${acr_temp:-0})
    echo ${acr_temp:-0}
}

function get_seconds {
        echo `date +%s`
}

function take_reading {
	CAPLEVEL=$(< $B_INFO/$CAPACITY )
	VOLT=$(< $B_INFO/voltage_avg)
	CURR=$(< $B_INFO/current_avg)
	TEMP=$(< $B_INFO/temp)
	STAT=$(< $B_INFO/status)

	ACR=$(get_acr)
	ACR_DIFF=$(( ${ACR-0}-${START_ACR-0} ))
	if [ $KERNAPI -eq 1 ]
	then
		MAh=$(( $ACR_DIFF * 625 / 1500 ))
	else
		MAh=$ACR_DIFF
	fi
	THIS_SEC=$(get_seconds)
	NET_MINUTES=$(( ( ${THIS_SEC} - ${START_TIME} ) / 60 ))	
	echo `date +%s`",$CAPLEVEL,$VOLT,$CURR,$TEMP,$ACR,$STAT,$MAh,$NET_MINUTES" | tee -a $LOGFILE
	sync
}

START_ACR=$(get_acr)
START_TIME=$(get_seconds)
FULL_EDGE=0

take_reading

while true
do
        if [[ $STAT != "Full" ]]; then
                FULL_EDGE=0
        fi

        if [[ $FULL_EDGE = 0 ]]; then
                take_reading

		# During battery capacity testing we need to try and grab a sample
		# close to 5V so speed up the sample frequency when we get close
		if [[ $VOLT -lt $FAST_SAMPLE_V ]]; then
			sleep 1
		else
			sleep $DELAY
		fi
        else
                STAT=$(< $B_INFO/status)
		# Don't spin
		sleep .1
        fi

        if [[ $FULL_EDGE = 0 && $STAT = "Full" ]]; then
                FULL_EDGE=1
        fi
done
