#!/bin/bash

# Copyright 2003 MBARI
# Author: Kent Headley

# dpaView
# Description: This script wraps a bash shell with a set of commands
# used to provide a complete interface to the DPA hardware

# Designed to be used on the sidearm processesor board, running
# linux. It also requires that the sa1100spi and sa1100gpio kernel
# modules be loaded.

# Constants
VERSION="1.8"
## SPI device
SPI_DEV="/dev/spi"
GPIO_DEV="/dev/gpio"
#SPI_DEV="/dev/null"
#GPIO_DEV="/dev/null"

## Commands
W_DPOT=( 0x0000 0x1000 )
W_RELAY=( 0x2000 0x3000 )
let START_ADC=0x4000
let W_INTERRUPT_REG=0x5000
W_CONTROL=( 0x6000 0x7000 )
let UNUSED=0x8000
R_RELAY=( 0x9000 0xA000 )
let R_ADC=0xB000
let R_INTERRUPT_REG=0xC000
R_CONTROL=( 0xD000 0xE000 )
let BUSY=0xF000

## Configuration Masks
let RELAY_485_CON=0x0004
let RELAY_COMM_ISO=0x0002
let RELAY_IPOWER_ISO=0x0001
let CONTROL_SDIR_ON=0x0020
let CONTROL_SLEW_FAST=0x0004
let CONTROL_MODE_485=0x0008
let CONTROL_DUP_HALF=0x0010
let CONTROL_CPOWER_ON=0x0002
let CONTROL_IPOWER_ON=0x0001
IFLAGS_OCE[0]=0x0020
IFLAGS_OCE[1]=0x0010
let IFLAGS_GLOBAL_EN=0x0008
let DPOT_SAVE=0x0080
let DPOT_UP=0x0100
let DPOT_FULL_COUNTS=0x0063
let ADC_PD_FULL=0x0000
let ADC_PD_STBY=0x0040
let ADC_PD_INT=0x0080
let ADC_PD_EXT=0x00C0
let ADC_ACQMOD_EXT=0x0020
let ADC_SGL=0x0010
let ADC_UNI=0x0008
let ADC_CH_VBAT=0x0007
let ADC_CH_VINSTR1=0x0006
let ADC_CH_VTRIP1=0x0005
let ADC_CH_ISENSE1=0x0004
let ADC_CH_HEATSINK=0x0003
let ADC_CH_VINSTR0=0x0002
let ADC_CH_VTRIP0=0x0001
let ADC_CH_ISENSE0=0x0000

## DPA busy response
let DPA_BUSY=0x7FFF

## Variables

## Functions place DPA return 
# Global DEBUG info mask
let DEBUG=0x0000

# Max Number of DPAs
let MAX_DPA=6
let MAX_CHAN=2

# current SPI chip Select
let SPI_CS=0x00

## value in this variable
let DPA_RTN=0x0000

# Register write values
let DPOT_REG=0x0000
let RELAY_REG=0x0000
let CONTROL_REG=0x0000
let INTERRUPT_REG=0x0000
let ADC_REG=0x0000

# Register read values
let DPOT_STAT=0x0000
RELAY_STAT[0]=0x0000
RELAY_STAT[1]=0x0000
CONTROL_STAT[0]=0x0000
CONTROL_STAT[1]=0x0000
let INTERRUPT_STAT=0x0000
let ADC_STAT=0x0000

# DPOT value
let DPOT_VALUE=0x0000

# DPA detected
DPA_FOUND=( true true true true true true )

# ADC Channel Names
adcChannels=( "Ch 1 Current" "Ch 1 Trip" "Ch 1 Voltage"  "Heatsink Temp" "Ch 2 Current" "Ch 2 Trip" "Ch 2 Voltage" "Battery Supply Voltage" )

# state
probeDone=false
interruptsDone=false

## Functions

## printDebug(msg)
printDebug()
{
  if [ $DEBUG -gt 0 ]
  then
    echo $@
  fi
}

# initSpi(initString)
# Initialize SPI bus

initSpi(){
  printDebug "initializing SPI using $1"
  echo $1 > $SPI_DEV
  setSpiCs 7
}

# busyWait()
# Send busy query to DPA and wait
# until return value indicates that
# the DPA is ready

busyWait(){
  # send BUSY command
  echo `printf "%04X=" $BUSY` > $SPI_DEV

  # read response, exit when not BUSY
  read DPA_RTN <$SPI_DEV
  DPA_RTN="0x$DPA_RTN"
  while (( DPA_RTN == $DPA_BUSY  ))
  do
    printDebug "BUSY"
    echo `printf "%04X=" $BUSY` > $SPI_DEV
    read DPA_RTN <$SPI_DEV    
    DPA_RTN="0x$DPA_RTN"
  done
}

# writeReadSpi(16BitValue)
# Perform a single write/read cycle
# on the SPI bus

writeReadSpi()
{
  # wait till not busy
  busyWait

  # write command
  printDebug "WRSPI writing `printf "%04X=" $1` to $SPI_DEV"
  echo `printf "%04X=" $1` > $SPI_DEV

  # read response
  read DPA_RTN <$SPI_DEV    
  DPA_RTN="0x$DPA_RTN"
  printDebug "WRSPI read `printf "0x%04x" $DPA_RTN`"
}

# setSpiCs(dpa)
# Set Chip Select to address of a
# desired DPA

setSpiCs()
{
  if [ $# -lt 1 ]
  then
    echo " Use: setSpiCs <dpa>"
  fi
  if [ $SPI_CS != $1 ]
  then
    printDebug "setting spi CS `printf "0x%02X -> %s" $1 $GPIO_DEV`"
    let newVal="$1&0x7"
    printDebug "setSpiCs: newVal=$newVal"
    let newVal="$newVal<<4"
    printDebug "setSpiCs: newVal=$newVal"
    # for some reason, the following didn't work...
    # Whenever $1=0, the CS would be the set to the
    # same value it last had; hence, channel 0 probe
    # would always return the result of the probe done
    # previously
    # set barf=`printf "%02X" $newVal`
    let barf="`printf "%02X" $newVal`"
    echo "$barf""=" > $GPIO_DEV
    printDebug "setSpiCs: barf=$barf"
    let "SPI_CS=$1"
  fi
}

## Many functions affecting one or more DPAs/Channels
## take dpa and channel arguments, which may be set to
## some number or all. Functions are broken into
## two parts, so that they may share a looping 
## routine, looper. The first part is the routine
## that is called from the command line. It checks
## the arguments and calls looper, passing the name
## of the working function (e.g., somenameFunc()) as
## well as the arguments. the working function,
## invoked by looper with different values of dpa/channel,
## does the operation for a specific dpa/channel
#
# Example:
#
# blarg(dpa,chan,arg1,arg2)
# calls looper, a shared routine that
# loops through the dpas and channels,
# invoking a function and its args.
# note that the function's args are 
# bundled together in quotes. 
# May be invoked using all for dpa and/or chan.
blarg(){
  if [ $# -lt 4 ]
  then
    echo;echo "  Use: blarg <dpa> <channel> <arg1> <arg2>"
    return
  fi
  eval "looper $1 $2 blargFunc \"$3 $4\""
  return
}
# blargFunc(dpa,chan,arg1,arg2)
# does the work for blarg, for a specific
# dpa and channel; Invoked by looper, but may be 
# invoked directly.
blargFunc(){
  echo blargFunc $1 $2 $4 $3
}


# probe(dpa|a)
# Detect installed DPAs

probe(){
  if [ $# -lt 1 ]
  then
    echo;echo "  Use: probe <dpa|a]>"
   return
  fi
  if [ "$1" = "a" ]
  then
    eval "looper $1 0 probeFunc"
  else
    probeFunc $1
  fi
}

# probeFunc(dpa)
# Implementation of probe.
# Detects DPA by writing a pattern to
# the interrupt register. Original
# value is preserved.

probeFunc(){
  let "slot=$1"
  let "found=1"

  printDebug "probing $slot"

  # Select DPA
  setSpiCs $slot

  # Save current value 
  printDebug "Saving INT_EN register..."
  getInterrupts $slot q
  let "saveReg=$INTERRUPT_STAT"

  # Clear interrupt reg
  printDebug "disabling interrupts..."
  clearInterruptReg 0xFFFF
  writeInterruptReg $slot

  # write pattern
  printDebug "writing 0x10 to INTERRUPT_REG..."
  clearInterruptReg 0xFFFF
  setInterruptReg 0x10
  writeInterruptReg $slot

  # read and  test
  printDebug "reading INTERRUPT_REG..."
  getInterrupts $slot q
  let "pattern=$INTERRUPT_STAT&0x10"
  let "test=0x10"
  if [ $pattern -ne $test ]
  then
   let "found=0"
  fi

  # if first test OK, write another pattern,
  # then do readback and test.
  if [ $found -ne 0 ]
  then 
   printDebug "writing 0x20 to INTERRUPT_REG..."
   clearInterruptReg 0xFFFF
   setInterruptReg 0x20
   writeInterruptReg $slot

   printDebug "reading INTERRUPT_REG..."
    getInterrupts $slot q
    let "pattern=$INTERRUPT_STAT&0x20"
    let "test=0x20"
    if [ $pattern -ne $test ]
    then
      let "found=0"
    fi
  fi

  # display results  
  if [ $found -eq 1 ]
  then
    echo;echo "Probe: DPA detected at slot $slot";echo
    DPA_FOUND[$slot]=true
  else
    echo;echo "Probe: DPA not found at slot $slot";echo
    DPA_FOUND[$slot]=false
  fi

  # restore results
  printDebug "Restoring InterruptRegister..."
  clearInterruptReg 0xFFFF
  setInterruptReg $saveReg
  writeInterruptReg $slot
  getInterrupts $slot q
  printDebug "InterruptRegister restored to `printf "0x%04X" $INTERRUPT_STAT`..."

}

# init(dpa,chan,CurrentLimitCounts[0-99],ChannelControlMask,RelayMask,IntEnMask)
# Initialize one or all DPAs. Will use defaults indicated, or
# specified values.
init()
{
  if [ $# -lt 2 ]
  then
    echo;echo "  Use: init <dpa[0|1|a]> <chan[0|1|a]> <iLimit counts> <chControlMask> <relayMask> <intEnableMask>"
         echo "       Defaults:"
         echo "         iLimit        - 0x0004 (480 mA)"
         echo "         chControl     - 0x0020 (SDIR=LO,SLEW=FAST,MODE=RS232,DUP=FULL,CPWR=OFF,IPWR=OFF)"
         echo "         relayMask     - 0x0003 (485=COMM=POWER=ISOLATED)"
         echo "         interruptMask - 0x0038 (OCE0=OCE1=OCGLOBAL=ENABLED)"
   return
  fi
  eval "looper $1 $2 initFunc \"$3 $4 $5 $6\""
}

# initFunc(dpa,chan(unused),CurrentLimitCounts[0-99],ChannelControlMask,RelayMask,IntEnMask)
# Implementation of init.
initFunc()
{
  printDebug "initFunc $1 $2 ${3-4} ${4-0x20} ${5-0x3} ${6-0x38}"

  let "iLimit=${3-4}"
  let "chControl=${4-0x20}"
  let "relayMask=${5-0x3}"
  let "intMask=${6-0x38}"
  # set SPI chip select
  setSpiCs $1
 
  #clear overcurrents, disable interrupts
  printDebug "clearing interrupts..."
  clearInterruptReg 0xFFFF
  writeInterruptReg $1

  # put ADC in Standby mode
  printDebug "setting ADC standby..."
  clearAdcReg 0xFFFF
  let "PARAM=$ADC_PD_STBY|$ADC_SGL|$ADC_UNI"
  setAdcReg $PARAM
  printDebug "AdcReg is `printf "0x%04X" $ADC_REG` param is `printf "0x%04X" $PARAM`"
  writeAdcReg $1

  # toggle relays closed, open
  printDebug "clearing relays..."
  clearRelayReg 0xFFFF
  setRelayReg $RELAY_485_CON
  setRelayReg $RELAY_COMM_ISO
  setRelayReg $RELAY_IPOWER_ISO
  writeRelayReg $1 $2

  clearRelayReg 0xFFFF
  setRelayReg $RELAY_COMM_ISO
  setRelayReg $RELAY_IPOWER_ISO
  writeRelayReg $1 $2

  # set relays in specified state
  printDebug "setting relays...$relayMask"
  clearRelayReg 0xFFFF
  setRelayReg $relayMask
  writeRelayReg $1 $2

  # Set current limit to specified value
  printDebug "setting current limit...$iLimit"
  setCurrentLimit $1 $2 $iLimit

  # Set channel control parameters
  # mode, slew, duplex, ipower, cpower
  printDebug "setting channel controls...$chControl"
  clearControlReg 0xFFFF
  setControlReg $chControl
  writeControlReg $1 $2

  # Set interrupts
  printDebug "setting interrupts...$intMask"
  clearInterruptReg 0xFFFF
  writeInterruptReg $1
  setInterruptReg $intMask
  writeInterruptReg $1

  echo;echo initialization complete;echo

}

# setCurrentLimit(dpa,channel,counts)
# Set DPA overcurrent limit. The value is specified
# as counts, 0-99; Each count is approximately 0.12 A.

setCurrentLimit()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: setCurrentLimit <dpa|a> <channel[0|1|a]> <counts>"
   return
  fi
  eval "looper $1 $2 setCurrentLimitFunc \"$3\""
}

#setCurrentLimitFunc(dpa,channel,counts)
# Implementation of setCurrentLimit

setCurrentLimitFunc()
{
  printDebug "setting $1.$2 current limit to `printf "0x%04X" $3`..."

  # Select DPA
  setSpiCs $1

  # set DPOT full off
  clearDpotReg 0xFFFF
  setDpotReg $DPOT_FULL_COUNTS
  writeDpotReg $1 $2

  # turn up by amount and save
  clearDpotReg 0xFFFF
  setDpotReg $3
  setDpotReg $DPOT_UP
  setDpotReg $DPOT_SAVE
  writeDpotReg $1 $2 
  let "DPOT_VALUE=$3"
  printDebug "set current limit $1:$2 set to `printf "0x%04X" $DPOT_VALUE`"
}

# readAdcChannel(dpa,AdcChan[01234567])
# Read one or more ADC channels on one or all
# DPAs. The ADC channels monitor various parameters
# of the DPA, indicated below.

readAdcChannel()
{
  if [ $# -lt 2 ]
  then
    echo;echo "  Use: readAdcChannel <dpa|a> <AdcChannels[01234567a]>"
         echo "       AdcChannels:"
         echo "         0: Ch 1 Current" 
         echo "         1: Ch 1 Trip" 
         echo "         2: Ch 1 Voltage" 
         echo "         3: Heatsink Temp"
         echo "         4: Ch 2 Current" 
         echo "         5: Ch 2 Trip" 
         echo "         6: Ch 2 Voltage" 
         echo "         7: Battery Supply Voltage"
         echo "         a: all"
   return
  fi
  eval "looper $1 0 readAdcChannelFunc \"$2\""
}

# readAdcChannelFunc(dpa,AdcChan[01234567a])
# Implementation of readAdcChannel

readAdcChannelFunc()
{
  # init vars
  doList=( 0 0 0 0 0 0 0 0 )

  # get options
  opts=( ${3:0:1} ${3:1:1} ${3:2:1} ${3:3:1} ${3:4:1} ${3:5:1} ${3:6:1} ${3:7:1} )

  # select which operations to do
  # based on given options
  for arg in ${opts[@]}
  do 
    case $arg in
    [Aa] )
      doList[0]=1
      doList[1]=1
      doList[2]=1
      doList[3]=1
      doList[4]=1
      doList[5]=1
      doList[6]=1
      doList[7]=1
    ;;
    0 )
      doList[0]=1
    ;;
    1 )
      doList[1]=1
    ;;
    2 )
      doList[2]=1
    ;;
    3 )
      doList[3]=1
    ;;
    4 )
      doList[4]=1
    ;;
    5 )
      doList[5]=1
    ;;
    6 )
      doList[6]=1
    ;;
    7 )
      doList[7]=1
    ;;
    esac  
  done 

  let "chan=0"

  # Now do the selected operations
  for flag in ${doList[@]}
  do
    if [ $flag -eq 1 ]
    then
      setSpiCs $1
      clearAdcReg 0xFFFF
      setAdcReg $ADC_PD_EXT
      setAdcReg $ADC_SGL
      setAdcReg $ADC_UNI
      setAdcReg $chan
      writeAdcReg $1
      clearAdcReg $ADC_PD_EXT
      setAdcReg $ADC_PD_INT
      writeAdcReg $1
      getAdc $1
      echo; echo "DPA $1 ADC channel $chan (${adcChannels[$chan]}) is $ADC_STAT"
    fi
    let "chan=$chan+1"
  done
}

# power(dpa,chan,on|off)
# Turn instrument power on|off on
# one or more channels on one or
# all DPAs

power()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: power <dpa|a> <channel[0|1|a]> <on|off>"
    return
  fi
  eval "looper $1 $2 powerFunc \"$3\""
  return
}

# powerFunc(dpa,chan,on|off)
# implementation of power

powerFunc()
{
  printDebug powerFunc: $1 $2 $3

  # select DPA
  setSpiCs $1

  # Read relay reg
  getRelays $1 $2
  let "RELAY_REG=${RELAY_STAT[$2]}"

  # Read control reg
  getControls $1 $2 q
  let "CONTROL_REG=${CONTROL_STAT[$2]}"

  # Do indicated operation
  case "$3" in 
  [Oo][Ff][Ff] )
    # Disconnect instrument power
    clearControlReg $CONTROL_IPOWER_ON
    writeControlReg $1 $2

    # Open relays
    setRelayReg $RELAY_IPOWER_ISO
    writeRelayReg $1 $2
  ;;

  [Oo][Nn] )
    # Close relays
    clearRelayReg $RELAY_IPOWER_ISO
    writeRelayReg $1 $2

    # Connect instrument power
    setControlReg $CONTROL_IPOWER_ON
    writeControlReg $1 $2
  ;;
  esac
}

# comms(dpa,chan,up|down)
# Enable|disable comms for one or more
# channels on one or all DPAs

comms()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: comms <dpa|a> <channel[0|1|a]> <up|down>"
    return
  fi
  eval "looper $1 $2 commsFunc \"$3\""
  return
}

# commsFunc(dpa,chan,up|down)
# Implementation of comms

commsFunc()
{
  printDebug "commsFunc: $1 $2 $3"

  # select DPA
  setSpiCs $1

  # Read relay reg
  getRelays $1 $2
  let "RELAY_REG=${RELAY_STAT[$2]}"

  # Read control reg
  getControls $1 $2 q
  let "CONTROL_REG=${CONTROL_STAT[$2]}"

  # do indicated operation
  case "$3" in 
  [Dd][Oo][Ww][Nn] )
    # Disconnect comm power
    clearControlReg $CONTROL_CPOWER_ON
    clearControlReg $CONTROL_SDIR_ON
    clearControlReg $CONTROL_SLEW_FAST
    writeControlReg $1 $2

    # Open relays
    setRelayReg $RELAY_COMM_ISO
    writeRelayReg $1 $2
  ;;

  [Uu][Pp] )
    # Close relays
    clearRelayReg $RELAY_COMM_ISO
    writeRelayReg $1 $2

    # Connect comm power
    setControlReg $CONTROL_SDIR_ON
    setControlReg $CONTROL_SLEW_FAST
    setControlReg $CONTROL_CPOWER_ON
    writeControlReg $1 $2
  ;;
  esac
}

# setComms(dpa,chan,[(2)32|(4)85],[f|s],[o|l])
# Set communications modes on one or more
# channels for one or all DPAs

setComms()
{
  if [ $# -lt 2 ]
  then
    echo;echo "  Use: setComms <dpa|a> <channel[0|1|a]> [<mode [(2)32|(4)85]>] [<slew[f|s]>] [<dir[o|l]>] "
    return
  fi
  eval "looper $1 $2 setCommsFunc \"${3-"2"} ${4-"f"} ${5-"o"}\""
  return
}

# setCommsFunc(dpa,chan,232|485,f|s,o|l)
# Implementation or setComm

setCommsFunc()
{
  printDebug "setCommsFunc: $1 $2 $3 $4 $5"

  # select DPA
  setSpiCs $1

  # Read control reg
  getControls $1 $2 q
  let "CONTROL_REG=${CONTROL_STAT[$2]}"

  # Modify control register

  # Set Mode bits
  case "$3" in 
  [2] )
    clearControlReg $CONTROL_MODE_485
  ;;
  [4] )
    setControlReg $CONTROL_MODE_485
  ;;
  esac

  # Set SlewRate bits
  case "$4" in 
  [Ff] )
    setControlReg $CONTROL_SLEW_FAST
  ;;
  [Ss] )
    clearControlReg $CONTROL_SLEW_FAST
  ;;
  esac

  # Set SerialDirection bits
  case "$5" in 
  [Oo] )
    setControlReg $CONTROL_SDIR_ON
  ;;
  [Ll] )
    clearControlReg $CONTROL_SDIR_ON
  ;;
  esac

  # Write Changes
  writeControlReg $1 $2

}

# relays(dpa,chan,con|iso)
# Connect or isolate relays on one or
# both channels of one or all DPAs.

relays()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: relays <dpa|a> <channel[0|1|a]> <con|iso>"
    return
  fi
  eval "looper $1 $2 relaysFunc \"$3\""
  return
}

# relaysFunc(dpa,chan,con|iso)
# Implements relays

relaysFunc()
{
  printDebug relaysFunc: $1 $2 $3

  # select DPA
  setSpiCs $1

  # Read relay reg
  getRelays $1 $2
  let "RELAY_REG=${RELAY_STAT[$2]}"

  # perform indicated operation
  case "$3" in 
  [Ii][Ss][Oo] )
    # Open relays
    clearRelayReg $RELAY_485_CON
    setRelayReg $RELAY_COMM_ISO
    setRelayReg $RELAY_IPOWER_ISO
    writeRelayReg $1 $2
  ;;
  [Cc][Oo][Nn] )
    # Close relays
    setRelayReg $RELAY_485_CON
    clearRelayReg $RELAY_COMM_ISO
    clearRelayReg $RELAY_IPOWER_ISO
    writeRelayReg $1 $2
  ;;
  esac
}

# interrupts(dpa|a,0|1|g|a,set|clear|en|dis)
# Set, clear, enable or disable interrupts
# for one or both channels on one or all
# DPAs. Set/Clear operations do not 
# automatically turn any interrupts on, unless
# (g)lobal interrupts is specified, which 
# sets/clears only the global interrupt(s).

interrupts()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: interrupts <dpa|a> <interrupt[0|1|g|a]> <set|clr|en|dis>"
    return
  fi
  eval "looper $1 0 interruptsFunc \"$3 $4\""
  return
}

# interruptsFunc(dpa,chan,0|1|g|a,set|clr|en|dis)
# Implements interrupts.

interruptsFunc()
{
  printDebug interruptsFunc: $1 $2 $3 $4
  setSpiCs $1

  # Read interrupt reg
  getInterrupts $1 q
  let "INTERRUPT_REG=$INTERRUPT_STAT"
  case "$4" in 
  [Ee][Nn] )
    # Enable specified interrupts
    case "$3" in
    [Aa] )
      setInterruptReg ${IFLAGS_OCE[0]}
      setInterruptReg ${IFLAGS_OCE[1]}
      setInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    [01] )
      setInterruptReg ${IFLAGS_OCE[$3]}
      setInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    [g] )
      setInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    esac
  ;;
  [Dd][Ii][Ss] )
    # Disable specified interrupts
    case "$3" in
    [Aa] )
      clearInterruptReg ${IFLAGS_OCE[0]}
      clearInterruptReg ${IFLAGS_OCE[1]}
      clearInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    [01] )
      clearInterruptReg ${IFLAGS_OCE[$3]}
      writeInterruptReg $1
    ;;
    [g] )
      clearInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    esac
  ;;
  [Ss][Ee][Tt] )
    # Set specified interrupts
    case "$3" in
    [Aa] )
      setInterruptReg ${IFLAGS_OCE[0]}
      setInterruptReg ${IFLAGS_OCE[1]}
      writeInterruptReg $1
    ;;
    [01] )
      setInterruptReg ${IFLAGS_OCE[$3]}
      writeInterruptReg $1
    ;;
    [g] )
      setInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    esac
  ;;
  [Cc][Ll][Rr] )
    # Clear specified interrupts
    case "$3" in
    [Aa] )
      clearInterruptReg ${IFLAGS_OCE[0]}
      clearInterruptReg ${IFLAGS_OCE[1]}
      writeInterruptReg $1
    ;;
    [01] )
      clearInterruptReg ${IFLAGS_OCE[$3]}
      writeInterruptReg $1
    ;;
    [g] )
      clearInterruptReg $IFLAGS_GLOBAL_EN
      writeInterruptReg $1
    ;;
    esac
  ;;
  esac
}

# status(dpa|a,chan[0|1|a],[prcia])
# Returns the status or one or more
# parameters for one or more DPAs/channels.
# (p)robe: DPA installed; 
# (r)elays: relay status;
# (c)ontrol: control reg status; 
# (i)nterrupts: interrupt reg status;
# (a) all 

status()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: status <dpa|a> <channel[0|1|a]> <options[prcia]>"
         echo "    p - probe"
         echo "    r - relays"
         echo "    c - control"
         echo "    i - interrupts"
         echo "    a - all"
    return
  fi
  
  eval "looper $1 $2 statusFunc \"$3\""

  return
}

# statusFunc(dpa,chan,[prcia])
# Implements status

statusFunc()
{

  printDebug statusFunc: $1 $2 $3
  doProbe=false
  doRelays=false
  doControl=false
  doInterrupts=false

  # get options
  opts=( ${3:0:1} ${3:1:1} ${3:2:1} ${3:3:1} ${3:4:1} ${3:5:1} )

  printDebug opts= ${opts[@]}

  # select operations
  for arg in ${opts[@]}
  do 
    case $arg in
    a )
      doProbe=true
      doRelays=true
      doControl=true
      doInterrupts=true
    ;;
    c )
      doControl=true
    ;;
    i )
      doInterrupts=true
    ;;
    p )
      doProbe=true
    ;;
    r )
      doRelays=true
    ;;
    esac  
  done 

  printDebug doI=$doInterrupts doC=$doControl doP=$doProbe doR=$doRelays

  # Now do the selected operations

  ## Note the prescence of ...Done variables being checked
  ## These are set in looper, to make sure that 
  ## things that should only be done once per DPA
  ## are done only once
  ## Do things that only get done once per DPA first
  ## so that the 'done' state doesn't get trashed (if
  ## something calls getInterrupts,e.g., after the
  ## interrupts are 'done', the state could revert back
  ## to !'done'

  # probe
  if [ $doProbe = true ]
  then
    if [ $probeDone = false ]
    then
      probe $1
      probeDone=true
    fi
  fi

  # do only if DPA installed
  if [ ${DPA_FOUND[$1]} = true ]
  then
    # interrupts
    if [ $doInterrupts = true ]
    then
      if [ $interruptsDone = false ] 
      then 
        getInterrupts $1
        interruptsDone=true
      fi
    fi

    # relays
    if [ $doRelays = true ]
    then
      getRelays $1 $2 
    fi

    # controls
    if [ $doControl = true ]
    then
      getControls $1 $2 
    fi
  fi
}

# getRelays(dpa|a, chan[0|1|a]) 
# Read and display current value of
# relay register for one or both channels
# on one or all DPAs.

getRelays()
{
  if [ $# -lt 2 ]
  then
    echo;echo "  Use: getRelays <dpa|a> <channel[0|1|a]>"
   return
  fi
  eval "looper $1 $2 getRelaysFunc"
}

# getRelaysFunc(dpa|a, chan[0|1|a]) 
# Implements getRelays.

getRelaysFunc()
{
  # select DPA
  setSpiCs $1

  printDebug "reading relay positions..."

  # read ch 0 relay reg 
  if  [ $2 == 0 ] || [ $2 == "a" ]
  then
    let "CMD = ${R_RELAY[0]}"
    writeReadSpi $CMD
    let "foo=$DPA_RTN&0x7"
    RELAY_STAT[0]=$foo
    echo;echo "getRelays: DPA $1 Channel 0 relay status is `printf "0x%04X" ${RELAY_STAT[0]}`"
  fi
  
  # read ch 1 relay reg 
  if [ $2 == 1 ] || [ "$2" == "a" ]
  then
    let "CMD = ${R_RELAY[1]}"
    writeReadSpi $CMD
    let "foo=$DPA_RTN&0x7"
    RELAY_STAT[1]=$foo
    echo;echo "getRelays: DPA $1 Channel 1 relay status is `printf "0x%04X" ${RELAY_STAT[1]}`"
  fi
}

# getControls(dpa|a,chan[0|1|a],[q])
# Read and display current value of
# channel control register for one 
# or both channels on one or all DPAs.
# Supports a (q)uiet option, allowing
# it to be called without displaying
# results.

getControls()
{
  if [ $# -lt 2 ]
  then
    echo;echo "  Use: getControls <dpa|a> <chan[0|1|a]> [q]"
   return
  fi

  quiet=${3-"loud"}

  eval "looper $1 $2 getControlsFunc \"$quiet\" "
}

# getControlsFunc(dpa,chan)
# Implements getControls.

getControlsFunc(){

  # select DPA
  setSpiCs $1

  printDebug "reading control registers..."
  
  # read channel 0 control reg
  if [ $2 == 0 ] || [ "$2" == "a" ]
  then
    let "CMD = ${R_CONTROL[0]}"
    writeReadSpi $CMD
    let "foo=$DPA_RTN&0x3F"
    CONTROL_STAT[0]=$foo
    if [ "$3" = "loud" ]
    then
      echo;echo "getControls: DPA $1 Channel 0 control status is `printf "0x%04X" ${CONTROL_STAT[0]}`"
    fi
  fi
 
  # read channel 1 control reg
  if [ $2 == 1 ] || [ "$2" == "a" ]
  then
    let "CMD = ${R_CONTROL[1]}"
    writeReadSpi $CMD
    let "foo=$DPA_RTN&0x3F"
    CONTROL_STAT[1]=$foo
    if [ "$3" = "loud" ]
    then
      echo;echo "getControls: DPA $1 Channel 1 control status is `printf "0x%04X" ${CONTROL_STAT[1]}`"
    fi
  fi
}

# getInterrupts(dpa|a,[q])
# Read and display current value of
# interrupt register for one 
# or both channels on one or all DPAs.
# Supports a (q)uiet option, allowing
# it to be called without displaying
# results.

getInterrupts()
{
  if [ $# -lt 1 ]
  then
    echo;echo "  Use: getInterrupts <dpa|a> [q]"
   return
  fi

  quiet=${2-"loud"}

  if [ "$1" = "a" ]
  then
    eval "looper $1 0 getInterruptsFunc \"$quiet\" "
  else
    getInterruptsFunc $1 $quiet
  fi
}

# getInterruptsFunc(dpa,q)
# Implements getInterrupts.

getInterruptsFunc()
{
  # select DPA
  setSpiCs $1

  printDebug "reading interrupt flags..."

  # read interrupt reg
  let "CMD=$R_INTERRUPT_REG"
  writeReadSpi $CMD
  let "INTERRUPT_STAT=$DPA_RTN&0x3F"
  if [ "$2" = "loud" ]
  then
    echo;echo "getInterrupts: DPA $1 interrupt flag status is `printf "0x%04X" $INTERRUPT_STAT`"
  fi
}

# getAdc(dpa)
# Read ADC register, store the value
# in ADC_STAT
getAdc(){
  let "CMD=$R_ADC"
  writeReadSpi $CMD
  let "ADC_STAT=$DPA_RTN"
}

# The following functions implement
# set,clear and write operations for
# the relay, control, ADC, DPOT
# and interrupt registers.

# Relay Reg
# set (mask)
setRelayReg(){
  let "RELAY_REG|=$1"
}
# clear(mask)
clearRelayReg(){
  let "RELAY_REG&=~$1"
}
# write(dpa,chan)
writeRelayReg(){
  setSpiCs $1
  let "CMD=${W_RELAY[$2]}|$RELAY_REG"
  writeReadSpi $CMD
}

# Control Reg
# set (mask)
setControlReg(){
  let "CONTROL_REG|=$1"
}
# clear(mask)
clearControlReg(){
  let "CONTROL_REG&=~$1"
}
# write(dpa,chan)
writeControlReg(){
  setSpiCs $1
  let "CMD=${W_CONTROL[$2]}|$CONTROL_REG"
  writeReadSpi $CMD
}

# Interrupt Reg
# set (mask)
setInterruptReg(){
  let "INTERRUPT_REG|=$1"
}
# clear(mask)
clearInterruptReg(){
  let "INTERRUPT_REG&=~$1"
}
# write(dpa)
writeInterruptReg(){
  setSpiCs $1
  let "CMD=$W_INTERRUPT_REG|$INTERRUPT_REG"
  writeReadSpi $CMD
}

# ADC Reg
# set (mask)
setAdcReg(){
  let "ADC_REG|=$1"
}
# clear(mask)
clearAdcReg(){
  let "ADC_REG&=~$1"
}
# write(dpa)
writeAdcReg(){
  setSpiCs $1
  let "CMD=$START_ADC|$ADC_REG"
  writeReadSpi $CMD
}

# DPOT Reg
# set (mask)
setDpotReg(){
  let "DPOT_REG|=$1"
}
# clear(mask)
clearDpotReg(){
  let "DPOT_REG&=~$1"
}
# write(dpa,chan)
writeDpotReg(){
  setSpiCs $1
  let "CMD=${W_DPOT[$2]}|$DPOT_REG"
  writeReadSpi $CMD
}

# help([cmd])
# Display help info

help()
{

  if [ $# -ge 1 ]
  then
   case $1 in
   [Rr][Ee][Gg] )
     echo "  ## Control Reg Map   ##"
     echo "     <11:6> 0"
     echo "     <5> SerialDirection 1=On 0=LowPower"
     echo "     <4> CommSlewRate 1=Fast 0=SRL"
     echo "     <3> CommMode 1=485 0=232"
     echo "     <2> CommDuplex 1=On 0=Off"
     echo "     <1> CommPower 1=On 0=Off"
     echo "     <0> Instrument Power 1=On 0=Off"
     echo
     echo "  ## Relays Reg Map    ##"
     echo "     <11:3> unused"
     echo "     <2> 485 Terminators 1=Connected 0=Isolated"
     echo "     <1> CommPowerRelay 1=Isolated 0=Connected"
     echo "     <0> InstrumentPowerRelay 1=Isolated 0=Connected"
     echo
     echo "  ## DPOT Reg Map      ##"
     echo "     <11:9> unused"
     echo "     <8> Direction 1=Up 0=Down"
     echo "     <7> SavePosition 1=Save"
     echo "     <6:0> Counts (range 0 to 100d)"
     echo
     echo "  ## Interrupt Reg Map ##"
     echo "     <11:6> 0"
     echo "     <5> Ch1OverCurrentIntEn 1=Enabled 0=Disabled"
     echo "     <4> Ch2OverCurrentIntEn 1=Enabled 0=Disabled"
     echo "     <3> GlobalEnable 1=Enabled 0=Disabled"
     echo "     <2> Ch1OverCurrentFlag 1=Overcurrent 0=OK"
     echo "     <1> Ch2OverCurrentFlag 1=Overcurrent 0=OK"
     echo "     <0> GlobalFlag 1=Overcurrent 0=OK"
     echo
     echo "  ## ADC Reg Map       ##"
     echo "     <11:8> 0"
     echo "     <7> PowerDownMode1 (see notes)"
     echo "     <6> PowerDownMode0 (see notes)"
     echo "     <5> AcquisitionMode 0=Ext 1=Int"
     echo "     <4> MeasurementType 0=Single 0=Differential"
     echo "     <3> Polarity 0=Unipolar 1=Bipolar"
     echo "     <2> Channel Address 2"
     echo "     <1> Channel Address 1"
     echo "     <0> Channel Address 0"
     echo
     echo "     Notes:"
     echo "     PowerDownModes:" 
     echo "       00=Full"
     echo "       01=Stby"
     echo "       10=Normal,IntClock"
     echo "       11=Normal,ExtClock"
     echo
     echo "     ADC Channels:"
     echo "       0: VBat=5.8*Vin"
     echo "       1: Ch 2 Vsense=6*Vin"
     echo "       2: Ch 2 Trip Level Sense"
     echo "       3: Ch 2 Current Sense"
     echo "       4: Heat Sink Temp"
     echo "       5: Ch 1 Vsense=6*Vin"
     echo "       6: Ch 1 Trip Level Sense"
     echo "       7: Ch 1 Current Sense"
     echo
    ;;
    esac
  else  
  echo
  echo "  Commands:"
  echo
  echo "    ## Control Commands ##"
  echo
  echo "    comms               enable/disable comms"
  echo "    setComms            set comm parameters"
  echo "    power               turn instrument power on/off"
  echo "    relays              connect/isolate relays"
  echo "    interrupts          enable/disable interrupts"
  echo "    init                initialize DPAs"
  echo "    setCurrentLimit     set DPA current limit"
  echo
  echo "    ## Status Commands ##"
  echo
  echo "    probe               detect installed DPAs"
  echo "    status              get current state of hardware"
  echo "    readAdcChannel      read ADC channels"
  echo "    getRelays           get relay state"
  echo "    getControls         get channel control state"
  echo "    getInterrupts       get interrupt state"
  echo
  echo
  echo "    ## Information Commands ##"
  echo
  echo "    help                print this message"
  echo "    show                show variable value"
  echo
  echo "    ## Program Commands ##"
  echo
  echo "    exit                quits dpaView"
  echo
  echo "  Type command with no options for info"
  echo "  Type help reg to see register map"
  echo "  Bash shell commands are also available"
  fi
}

# show(var)
# Echo dpaView variables
# User may also use echo from command line

show()
{
  if [ $# -ge 1 ]
  then
   echo $1
  else
   echo Use: show '$<variable>'
   echo Variables:
   echo "  ADC_REG"
   echo "  ADC_STAT"
   echo "  CONTROL_REG"
   echo "  CONTROL_STAT"
   echo "  DEBUG"
   echo "  DPA_RTN"
   echo "  DPOT_REG"
   echo "  DPOT_STAT"
   echo "  INTERRUPT_REG"
   echo "  INTERRUPT_STAT"
   echo "  RELAY_REG"
   echo "  RELAY_STAT"
   echo "  SPI_CS"
  fi
}

# looper( dpa chan "func" args)
# Shared routine used by all functions that
# take dpa and chan arguments. Iterates through
# dpas and channels, executing "func dpa chan args"

## Note the prescence of ...Done variables being set
## These are checked in status, to make sure that 
## things that should only be done once per DPA
## are done only once

looper()
{
  if [ $# -lt 3 ]
  then
    echo;echo "  Use: looper <dpa|a> <channel[0|1|a]> \"<func>\""
    return
  fi
  func=$3
  printDebug "looper executing $func $1 $2 $4"

  # if all DPAs
  if [ "$1" == "a" ]
  then
    let "dpa=0"
    while [ $dpa -lt $MAX_DPA ]
    do
      probeDone=false
      interruptsDone=false

      # if all channels
      if [ "$2" == "a" ]
      then
        let "chan=0"        
        while [ $chan -lt $MAX_CHAN ]
        do
          printDebug "  ## DPA $dpa Channel $chan"
          eval "$3 $dpa $chan $4"
          let "chan+=1"
          probeDone=true
          interruptsDone=true
        done
      else

        # if specified channel
        let "chan=$2"
        printDebug "  ## DPA $dpa Channel $chan"
        eval "$3 $dpa $chan $4"
        probeDone=true
        interruptsDone=true
      fi
    let "dpa+=1"
    echo
    done
  else

  # If Specified DPA
    let "dpa=$1"

    # If all channels
    if [ "$2" == "a" ]
    then
      let "chan=0"
      probeDone=false
      interruptsDone=false
      while [ $chan -lt $MAX_CHAN ]
      do
        printDebug "  ## DPA $dpa Channel $chan"
        eval "$3 $dpa $chan $4"
        let "chan+=1"
        probeDone=true
        interruptsDone=true
      done
      echo
    else

      # if specified channel
      probeDone=false
      interruptsDone=false
      # if specified channel
      let "chan=$2"
      printDebug "  ## DPA $dpa Channel $chan"
      eval "$3 $dpa $chan $4"
      probeDone=true
      interruptsDone=true
    fi
  fi

  # return ...Done variables to false
  # or probe,interrupt funcs won't work
  # after this
  probeDone=false
  interruptsDone=false
}

# main
# main entry point

main()
{
  # echo startup blather
  echo;echo `basename $0` version $VERSION
  echo MBARI 2003;echo
  echo "Type help for assistance"
  echo "Type exit to quit"
  echo

  # save tty settings
  old_stty=`stty -g`

  # setup terminal (bkspc, history)
  # history doesn't work yet
  stty sane
  stty erase "^H"
  set -o history

  # initialize the SPI bus
  initSpi "10W0G100S0L0I0P"
  
  # set restricted mode (can't redirect output
  # and so is too restrictive, so don't)
  #set -r

  line=""

  # read and process commands
  while [ 1 ]
  do
    echo
    read -p "dpaView> " line <&0
    eval "$line"
  done

      # restore tty settings
      # before exiting
      stty "$old_stty"

  exit 0
}

####################################
# Main entry point
####################################

# Yep, this is it. Jump into the main loop
# and don't come back.
main







