#!/bin/sh
# Copyright (c) 2005, 2006 Dieter Jurzitza, Germany.
#
# updated to handle arbitrary numbers of graphics cards in the PC: August 2006
#
# Author: Dr. Ing. Dieter Jurzitza <dieter.jurzitza@t-online.de>
#
# /etc/init.d/framebufferset
#
#   and symbolic its link
#
# /usr/sbin/rcframebufferset
#
### BEGIN INIT INFO
# Provides:       framebuffer
# Required-Stop:
# Default-Start:  2 3 5
# Default-Stop:   0 1 6
# Description:    framebuffer
### END INIT INFO

# source rc functions
. /etc/rc.status

# some variables we need in the script
AWK=/bin/awk
CAT=/bin/cat

LSPCI="/sbin/lspci"
LSPCIOPTS="-v"
MTRR="/proc/mtrr"
CONFIGFILE=/etc/sysconfig/framebuffer
DOCPATH=/usr/share/doc/packages/framebuffer

declare -a RETVAL
declare -a RESULT

# First reset status of this service
rc_reset


###################################### START FUNCTION DECLARATIONS ######################################
function checkmtrr(){
    if [ ! -f ${MTRR} ]; then
	    echo -n " ${MTRR} not found "
         echo "$rc_failed"
         return 1
    fi
}

function makeconfigfile (){
    TMPNAME=`mktemp /tmp/RETVALBUF.XXXXXXXXXXXXXXXX`
    for ((i=1; i<${RETVAL[0]}; i+=2)); do
        if [ "1" != "$i" ]; then
        echo -n " " >> ${TMPNAME}
        fi
        echo -n "${RETVAL[$i]}" >> ${TMPNAME}
    done
    echo "## Path:        Hardware/Framebuffer/Memory" > ${CONFIGFILE}
    echo "## Description: Framebuffer-sizes" >> ${CONFIGFILE}
    echo "## Type:        string" >> ${CONFIGFILE}
    echo "##" >> ${CONFIGFILE}
    echo "## DO NEVER CHANGE THE NEXT ENTRY MANUALLY" >> ${CONFIGFILE}
    echo "## INITSTRING=${INITSTRING}" >> ${CONFIGFILE}
    echo "## DO NEVER CHANGE THE PREVIOUS ENTRY MANUALLY" >> ${CONFIGFILE}
    echo "##" >> ${CONFIGFILE}
    echo "# The size of the framebuffer memory in Megabyte we want to allocate in /proc/mtrr" >> ${CONFIGFILE}
    echo "# This can vary between 16 and 16384 (MB) in factors of 2 (16,32,64 ... 16384) for value a, b, c ..." >> ${CONFIGFILE}
    echo "# Prior to changing anything you SHOULD take a look into" >> ${CONFIGFILE}
    echo "# /usr/share/doc/packages/framebuffer/README.framebuffer" >> ${CONFIGFILE}
    echo "# Entries of "-1" mean that there are unused indexes of memory ranges in /proc/mtrr" >> ${CONFIGFILE}
    echo "# Entries of "NOVGA" refer to blocks that are not related to a VGA card within /proc/mtrr." >> ${CONFIGFILE}
    echo "# Only entries that belong to a VGA card may be changed." >> ${CONFIGFILE}
    echo "#" >> ${CONFIGFILE}
    # This AWK-script generates the FRAMEBUFFERSIZE-Entry in our configfile
    ${AWK} -v infile=${TMPNAME} '{                                                  \
        getline line < infile;                                                      \
        printf("FRAMEBUFFERSIZE=\"");                                               \
        max=0;                                                                      \
        do {                                                                        \
            baseaddr=$2;                                                            \
            gsub(/[^0-9 ]/, "", $0);                                                \
            i=$1+0;                                                                 \
            num[i]=i;                                                               \
            if (match(line, baseaddr)){                                             \
                memsize[i]=$4;                                                      \
            }                                                                       \
            else {                                                                  \
                memsize[i]="NOVGA";                                                 \
            }                                                                       \
        if( i > max){max=i;}                                                        \
        } while (getline >0 );                                                      \
        for (i=0; i<=max; i++){                                                     \
            if (""==num[i]){                                                        \
                num[i]=i;                                                           \
                memsize[i]="-1";                                                    \
            }                                                                       \
            if (max != i){                                                          \
                printf("%s ", memsize[i]);                                          \
            }                                                                       \
            else {                                                                  \
                printf("%s\"\n", memsize[i]);                                       \
            }                                                                       \
        }                                                                           \
    }' ${MTRR} >> ${CONFIGFILE}
    rm -f ${TMPNAME}
}

# check whether the configfile exists, if so read it and check whether there was a change in configuration - then we have to recreate it
function checkconfigfile () {
    # This is for the initial setup of the configfile
    if [ ! -f ${CONFIGFILE} ]; then
        echo -n " ${CONFIGFILE} does not exist - create new one .."
        makeconfigfile
        echo -n ".. done "
        echo "$rc_failed"
        return 1
    fi

    # ok - we have it - source it!
    . ${CONFIGFILE}

    # check whether the current configuration as returned by () and the configuration in the configfile are identical.
    CURRSTRING=`cat ${CONFIGFILE} |                                                 \
    ${AWK} '{                                                                       \
        if (sub("## INITSTRING=", "", $0)){                                         \
	       printf("%s", $0);                                                       \
        }                                                                           \
    }'`

    if [ "${INITSTRING}" != "${CURRSTRING}" ]; then
        echo -n " PCI-bus config changed .."
        FILENAME=`basename ${CONFIGFILE}`
        FILENAME=${FILENAME}-`date +%H.%M-%d.%m.%Y`.save
        echo -n "... saving ${CONFIGFILE} to ${DOCPATH}/${FILENAME} .."
        cp -p ${CONFIGFILE} ${DOCPATH}/${FILENAME}
        echo -n ".. ready .."
        echo -n ".. creating new configfile .."
        makeconfigfile
        echo -n ".. done "
        echo "$rc_failed"
        return 1
    fi
}

function fillarray (){
    # Analyze the output of lspci with regard to the graphics card's attributes. We search for " VGA ". Having found this
    # we search for the String "Memory at ", what in turn gives the address value. K-Byte values (/K/) are ignored.
    # For everything valid we get a printout 0xBASEADDR MEMSZ, what gets stored into RETVAL. num is our eventcounter: each
    # time we find a valid Memory entry num is increased twice. At the very end we assign data[0] the value of num. So our 
    # array contains pairs of addresses and memory sizes starting off 1, whereas arrayindex 0 tells us how many entries to
    # expect within the array.
    RETVAL=(`${LSPCI} ${LSPCIOPTS} | ${AWK} '{                                      \
        num=0;                                                                      \
        A=$0;                                                                       \
        # read until the end of the input file
        do {                                                                        \
            if (match(A, " VGA ")){                                                 \
                # if we found " VGA " things get interesting
                if ((getline A) <= 0){exit};                                        \
                # a blank line is signalizing the end of a description block of lspci!
                while(A != ""){                                                     \
                    if (match(A, "Memory at ")){                                    \
                        split(A,B, " ");                                            \
                        if ((sub(/K/, "", B[6]))||(match(B[6], "disabled"))) {      \
                            break;                                                  \
                        };                                                          \
                        # here we assume MByte to be defined.
                        if (match (B[6], "M]")){                                    \
                            gsub(/[^0-9]/, "", B[6]);                               \
                            num++;                                                  \
                            data[num]="base=0x"B[3];                                \
                            num++;                                                  \
                            data[num]=B[6];                                         \
                        };                                                          \
                        # This one cares for GByte.
                        if (match (B[6], "G]")){                                    \
                            gsub(/[^0-9]/, "", B[6]);                               \
                            num++;                                                  \
                            data[num]="base=0x"B[3];                                \
                            num++;                                                  \
                            data[num]=B[6]*1024;                                    \
                        };                                                          \
                    };                                                              \
                    if ((getline A) <= 0){exit;};                                   \
                };                                                                  \
            };                                                                      \
        } while ((getline A) > 0);                                                  \
        # data[0] contains information how many entries we have found!
        data[0]=num;                                                                \
        # print the entire array so it is available in the script "above"
        for (i=0; i<=num; i++){                                                     \
            printf("%s ", data[i]);                                                 \
        }	                                                                          \
        printf("\n");                                                               \
    }'`)
    # Here we store what can be seen within lspci into INITSTRING. As long as this entry remains constant, we can be sure
    # that nothing in hardware has changed and the script is working properly.
    INITSTRING=""
    for ((i=1; i<=${RETVAL[0]}; i++)); do
       INITSTRING=${INITSTRING}"${RETVAL[i]} "
       i=`expr $i + 1`
       INITSTRING=${INITSTRING}"${RETVAL[$i]} "
    done
}

###################################### END FUNCTION DECLARATIONS ######################################

case $1 in
    start)
	   echo -n "Verify the apropriate mtrr setting"
        # watch the sequence of function calls!
	   checkmtrr || exit 1
	   fillarray
	   checkconfigfile || exit 1
	   . ${CONFIGFILE}
        # This is needed because SUSE cannot handle arrays in the /etc/sysconfig - files. The format of the
        # variable FRAMEBUFFERSIZE must be adapted to what it should be!
	   eval FRAMEBUFFERSIZE=(${FRAMEBUFFERSIZE})

        #
        # Now we have to look into $MTRR. There are settings for the graphics card we find by looking at the address values.
        # Those addresses that are identical to the ones from the graphics card are the ones we need.
        # However, one cannot rely on the return value for memory of lspci. Therefore we introduced an environment variable Array
        # defined in /etc/sysconfig/framebuffer that needs to be set by the user. It is called FRAMEBUFFERSIZE[i] and can have values
        # from 16 to 16384 [MB] for each index.
        #
        # If we find an entry in /proc/mtrr that corresponds to an entry we got out of lspci, we check whether the settings in
        # in out of lspci differ from those in /proc/mtrr. If that would be the case, we modify the mtrr settings in accordance
        # with what is defined in the setting for FRAMEBUFFERSIZE[i]
        #

        # initializations:
	   MEMERR="FALSE"
	   MEMVALID="FALSE"

	   for (( j=1 ; $j <= ${RETVAL[0]} ; j=`expr $j + 2` )) ; do
		  RESULT=(`$CAT $MTRR | ${AWK} -v cmp="${RETVAL[$j]}" '{if (cmp==$2){gsub(/[^0-9 ]/, "", $0); printf("%d %d", $1, $4)}}'`)
            # if RESULT is empty, put defaults into it.
		  if [ "" = "${RESULT[0]}" ]; then
			 RESULT[0]=-1
			 RESULT[1]=-1
		  fi
		  if [ "${RESULT[1]}" != "${RETVAL[`expr $j + 1`]}" -a "${RESULT[0]}" != "-1" ]; then
			 if [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "16" ]; then
				MEMSIZE=1000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "32" ]; then
				MEMSIZE=2000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "64" ]; then
				MEMSIZE=4000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "128" ]; then
				MEMSIZE=8000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "256" ]; then
				MEMSIZE=10000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "512" ]; then
				MEMSIZE=20000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "1024" ]; then
				MEMSIZE=40000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "2048" ]; then
				MEMSIZE=80000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "4096" ]; then
				MEMSIZE=100000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "8192" ]; then
				MEMSIZE=200000000
			 elif [ "${FRAMEBUFFERSIZE[${RESULT[0]}]]}" = "16384" ]; then
				MEMSIZE=400000000
			 else
				MEMSIZE=INVALID
				MEMERR=TRUE
			 fi
		  else
			 MEMSIZE=""
		  fi
		  
		  if [ "${MEMSIZE}" != "INVALID" -a "${MEMSIZE}" != "" ]; then
			 MEMVALID=TRUE
			 echo "disable=${RESULT[0]}" > ${MTRR}
			 echo "${RETVAL[$j]} size=0x${MEMSIZE} type=write-combining" > ${MTRR}
			 echo "$rc_done"
		  fi
	   done
	   
	   if [ "${MEMERR}" = "TRUE" ]; then
		  echo -n "  ${attn}found one wrong memsize at last!${norm}"
		  echo $rc_failed
	   elif [ "${MEMVALID}" = "FALSE" ]; then
		  echo -n "  ${attn}nothing to do, sizes are equal!${norm}"
		  echo "$rc_done"
	   else
		  echo "$rc_done"
	   fi
	   ;;
    stop)
	   echo -n "Shut down mtrr setting (do nothing)"
	   echo "$rc_done"
	   ;;
    init)
           fillarray
           makeconfigfile
           ;;
    *)
	   echo -n "Usage: $0 {start|stop}"
	   echo "$rc_done"
	   ;;
esac
