/****************************************************************************/
/* Copyright 1992 to 1996 MBARI                                             */
/****************************************************************************/
/* Summary  : IBC Power Switching Interface Module for VxWorks              */
/* Filename : switch.c                                                      */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon                                                       */
/* Version  : Version 1.0                                                   */
/* Created  : 10/01/92                                                      */
/* Modified : 02/27/96                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: switch.c,v 1.1 93/07/01 14:34:34 pean Exp $
 * $Log:        switch.c,v $
 * Revision 1.1  93/07/01  14:34:34  14:34:34  pean (Andrew Pearce)
 * Initial revision
 *
 */
/****************************************************************************/

#include <vxWorks.h>                /* vxWorks system declarations          */
#include <semLib.h>                 /* vxWorks semaphore functions          */
#include <msgQLib.h>                /* vxWorks Message Queue Library        */

#include <mbariTypes.h>             /* MBARI style guide type declarations  */
#include <mbariConst.h>             /* Miscellaneous constants              */
#include <usrTime.h>                /* MBARI time and date functions        */

#include <datamgr.h>                /* Data Manager declarations            */
#include <dm_errno.h>               /* Data Manager error declarations      */

#include "sio32.h"                  /* SIO32 hardware and driver information*/
#include "sio32Server.h"            /* Sio32 server communication services  */
#include "sio32Client.h"            /* Sio32 client communication services  */
#include "powerTypes.h"             /* Power management Data Types          */
#include "powerAlloc.h"             /* ROV Device Power Allocation          */

#include "microdef.h"               /* Microcontroller definitions          */
#include "applic.h"                 /* Microcontroller applications         */
#include "timer.h"                  /* Microcontroler Timer Definitions     */
#include "microcmd.h"               /* Microcontroller commands             */
#include "microDm.h"                /* Micro Data Manager Items & Functions */

#include "ibc_card.h"               /* IBC Card Types                       */
#include "ibc_cmd.h"                /* IBC Card Serial Commands             */
#include "filter.h"                 /* General Filter Routines              */
#include "vicor.h"                  /* Dual Vicor Board Definitions         */
#include "low_pwr.h"                /* Low Power Switch Board Definitions   */
#include "switch.h"                 /* Application Specific Definitions     */

#include "ibc.h"                    /* IBC Function Library Definitions     */
#include "lps.h"                    /* Low Power Switch Command functions   */
#include "lpsDm.h"                  /* IBC LPS Board Interface Functions    */
#include "hps.h"                    /* High Power Switch Command functions  */
#include "vicorBoard.h"             /* Dual Vicor Board Command functions   */
#include "iso_io.h"                 /* Isolated IO Board Command functions  */
#include "hydroPsu.h"               /* Hudrophone PSU Board Command Function*/

#define POWER_REQUEST_TIMEOUT 2000  /* Timeout in ticks 2 second            */

MLocal STATUS ibcSwitchDmInit( Reg switchEntry *deviceSwitch,
   char* switchReqName, char* switchStatusName, char* powerReqName,
   char* powerOkName,   SEM_ID switchReqSem,    SEM_ID powerReqSem );

MLocal STATUS ibcWriteSwitchDmState( Reg switchEntry *sw,
   switchStatus switchState );

MLocal switchStatus ibcSwitchChainState( switchEntry *switchData );
MLocal STATUS ibcPowerMgrRequest( Reg switchEntry *device, Reg MBool state );
MLocal STATUS ibcChainPowerMgrRequest( Reg switchEntry *sw, Reg MBool state );
MLocal STATUS ibcChainSwitchControl( Reg switchEntry *sw, Reg MBool state );


/****************************************************************************/
/* Function    : initIbcSwitchEntry                                         */
/* Purpose     : Initializes switchEntry data structure                     */
/* Inputs      : Pointer to switch entry, series switch structure, sio32    */
/*               serial channel, power and bus for power request,           */
/*               physical device Type, address, channel, data manager item  */
/*               name, switch request semaphore and power request semaphore */
/* Outputs     : Returns OK or ERROR and updates switchEntry items          */
/****************************************************************************/
    STATUS
initIbcSwitchEntry( Reg switchEntry *devSwitch, sio32Chan *ibcSerialChan,
    Nat16 power,               systemBus bus,     loadDivision division,
    IBC_BoardType boardType,   Word boardAddr,    Nat16 channel,
    char* switchReqName, char* switchStatusName,  SEM_ID switchReqSem,
    char* powerReqName,  char* powerOkName,       SEM_ID powerReqSem,
    const char *dataConName,   const char* itemName)
{
                                   /* Copy Switch Name                      */
    strncpy(devSwitch->name, itemName,
        min(strlen(itemName) + 1, MAX_SWITCH_NAME));

                                   /* Copy IBC Physical Device Mapping params*/
    devSwitch->ibcSerialChan = ibcSerialChan;
    devSwitch->boardType     = boardType;
    devSwitch->boardAddr     = boardAddr;
    devSwitch->channel       = channel;

                                   /* Copy power request params for switch   */
    devSwitch->power.power    = power;
    devSwitch->power.bus      = bus;
    devSwitch->power.division = division;

                                   /* Copy switch and power request sems     */
    devSwitch->switchReqSem  = switchReqSem;
    devSwitch->powerReqSem   = powerReqSem;

                                   /* Initialize switch interconnect list   */
    devSwitch->busHead      = NO_SWITCH;
    devSwitch->busNext      = NO_SWITCH;
    devSwitch->seriesSwitch = NO_SWITCH;

    devSwitch->currentDm = NO_ITEM;
    devSwitch->currentWarnThreshDm = NO_ITEM;
    devSwitch->currentAlarmThreshDm = NO_ITEM;
    devSwitch->currentAlarmDm = NO_ITEM;

    devSwitch->temperatureDm = NO_ITEM;
    devSwitch->tempWarnThreshDm = NO_ITEM;
    devSwitch->tempAlarmThreshDm = NO_ITEM;
    devSwitch->tempAlarmDm = NO_ITEM;

    devSwitch->tripAlarmDm = 0;
    devSwitch->gfBoardAddr = 0;

                                   /* Create Data Manager Items              */
    if (ibcSwitchDmInit( devSwitch, switchReqName, switchStatusName,
         powerReqName, powerOkName, switchReqSem,  powerReqSem ) == ERROR)
    {
        logMsg(" %s: Error initializing %s Entry\n", dataConName, itemName );
        return(ERROR);
    } /* if */

    return(OK);                    /* Switch Entry initialized correctly    */
} /* initIbcSwitchEntry() */

/****************************************************************************/
/* Function    : ibcAddSwitchInSeries                                       */
/* Purpose     : Connect two IBC switches in series                         */
/* Inputs      : Pointers to this switch and prev switch to be connected to */
/* Outputs     : Returns OK or ERROR and updates switchEntry items          */
/****************************************************************************/
    Void
ibcAddSwitchInSeries( switchEntry *prevSwitch, switchEntry *thisSwitch )
{
    Reg switchEntry *sw;

    if (prevSwitch->busHead == NO_SWITCH) /* If bus switch list is empty     */
        prevSwitch->busHead = thisSwitch; /* Connect switch before to this sw*/
    else                                  /* otherwise connect to end of list*/
    {                                     /* Find tail of bus switch list    */
        for ( sw = prevSwitch->busHead; sw->busNext != NO_SWITCH;
            sw = sw->busNext );

        sw->busNext = thisSwitch;         /* Add device to end of bus sw list*/
    } /* else */

    thisSwitch->seriesSwitch = prevSwitch;/* Connect this switch to one before*/
} /* ibcAddSwitchInSeries() */

/****************************************************************************/
/* Function    : ibcSetSwitchState                                          */
/* Purpose     : Set device switch data manager item state to ON/OFF        */
/* Inputs      : IBC device switch, desired state                           */
/* Outputs     : None, but data manager items change                        */
/****************************************************************************/
    Void
ibcSetSwitchState( Reg switchEntry *sw, switchStatus switchState )
{
                                        /* Set switch state                 */
    urgentWriteDmItem(sw->switchRequestDm, &switchState, sizeof(switchState));
    urgentWriteDmItem(sw->switchStatusDm,  &switchState, sizeof(switchState));

    ibcProcessSwitchRequest( sw );      /* Set physical switch desired state*/
} /* ibcSetSwitchState */

/****************************************************************************/
/* Function    : ibcWriteSwitchDmState                                      */
/* Purpose     : Write switch status Data Manager item for switch specified */
/* Inputs      : Pointer to switch entry, new switch state                  */
/* Outputs     : Returns switchStatus                                       */
/****************************************************************************/
    MLocal STATUS
ibcWriteSwitchDmState( Reg switchEntry *sw, switchStatus switchState )
{
    DM_Time switchTime;                 /* Switch update time for Data Mgr  */

                                        /* Write Switch State Data Item     */
    gettimeofday(&switchTime, (struct timezone *) NULL);
    return( dm_write( sw->switchStatusDm, (char *) &switchState,
                    sizeof(switchState), &switchTime) );
} /* ibcWriteSwitchDmState() */

/****************************************************************************/
/* Function    : ibcSwitchPowerError                                        */
/* Purpose     : Set device switch data manager item state to POWER_ERROR   */
/* Inputs      : IBC device switch                                          */
/* Outputs     : None, but data manager items change                        */
/****************************************************************************/
    Void
ibcSwitchPowerError( Reg switchEntry *sw )
{
    DM_Time switchTime;                 /* Switch update time for Data Mgr  */
    switchStatus switchState = SWITCH_POWER_ERROR;

    if (sw == (switchEntry *) NULL)
        return;

    urgentWriteDmItem(sw->switchStatusDm, &switchState, sizeof(switchState));

                                        /* Switch Tripped so return power   */
    ibcPowerMgrRequest(sw, OFF);        /* to power manager                 */
} /* ibcSwitchPowerError */

/****************************************************************************/
/* Function    : ibcProcessSwitchRequest                                    */
/* Purpose     : Application Layer Interaface to Switch On/Off IBC Switch   */
/* Inputs      : IBC switch entry                                           */
/* Outputs     : None, but data manager items change                        */
/****************************************************************************/
    switchStatus
ibcProcessSwitchRequest( Reg switchEntry *sw )
{
    switchStatus switchRequest;         /* Desired State of Switch          */
    switchStatus switchState;           /* Actual State of Switch           */
    STATUS       switchStatus;          /* Status of switch on request      */

    switchState = ibcSwitchDmState(sw); /* Read present switch status       */

                                        /* Read desired switch status       */
    if (dm_read( sw->switchRequestDm, (char *) &switchRequest,
                 sizeof(switchRequest), (DM_Time *) NULL) == SUCCESS )
    {                                   /* Check desired switch status read */

      if (switchRequest == SWITCH_ON)   /* Is request to turn switch ON ?   */
      {                                 /* Request is to set switch ON      */
                                        /* Check state of switches in series*/
          if (ibcSwitchChainState(sw) == SWITCH_ON)
          {                             /* All series switches are ON       */
                                        /* so attempt to turn this switch ON*/

                /* Make power requests for this switch, all device switches */
                /* down stream that are in the SWITCH_NO_POWER state, all   */
                /* series switches down stream in the SWITCH_NO_POWER state */
                /* and all device switches attached to those switch that are*/
                /* in the SWITCH_NO_POWER state                             */

              if (ibcChainPowerMgrRequest(sw, ON) == OK)
              {                         /* At this point, all power requests*/
                                        /* have been granted                */

                  taskLock();           /* Prevent task pre-emption         */
                                        /* Restart GF Board Scan            */
                  IBC_gf5vRestartScan( sw->ibcSerialChan, sw->gfBoardAddr );
                                        /* so now we can turn switches ON   */
                  switchStatus = ibcChainSwitchControl(sw, ON);

                  taskUnlock();         /* Allow task pre-emption           */

                  if (switchStatus == OK)   /* Switch value for dm_write    */
                      switchState = SWITCH_ON;

                  else
                  {                     /* Switch failed to close so return */
                                        /* power for this switch and all    */
                                        /* switches down stream             */
                      ibcChainPowerMgrRequest(sw, OFF);
                  } /* else */
              } /* if */

          } /* if */

          else                          /* All series switches are not ON so*/
              switchState = SWITCH_NO_POWER;
                                        /* set Switch State to NO POWER     */
      } /* if  */

      else                              /* Request is to turn switch OFF    */
      {                                 /* so switch OFF this switch and set*/
                                        /* the state of all switches down   */
                                        /* stream to SWITCH_NO_POWER and    */
                                        /* return power to power            */
          if (ibcChainSwitchControl( sw, OFF ) == OK)
              switchState = SWITCH_OFF; /* Set switch state OFF for dm_write*/
      } /* else */
    } /* if */
                                        /* Write Device Switch State to     */
                                        /* unblock task requesting switch   */
    ibcWriteSwitchDmState(sw, switchState);

    return (switchState);
} /* ibcProcessSwitchRequest() */

/****************************************************************************/
/* Function    : ibcSetSwitchStateOff                                       */
/* Purpose     : Set IBC Switch to Off                                      */
/* Inputs      : IBC switch entry                                           */
/* Outputs     : None, but data manager items change                        */
/****************************************************************************/
    STATUS
ibcSetSwitchStateOff( Reg switchEntry *sw )
{
                                        /* Request is to turn switch OFF    */
                                        /* so switch OFF this switch and set*/
                                        /* the state of all switches down   */
                                        /* stream to SWITCH_NO_POWER and    */
                                        /* return power to power            */
    if (ibcChainSwitchControl( sw, OFF ) != OK)
      return (ERROR);                   /* Trying to Switch off failed      */

                                        /* Write Device Switch State to     */
                                        /* unblock task requesting switch   */
    ibcWriteSwitchDmState(sw, SWITCH_OFF);
    return (OK);
} /* ibcSetSwitchStateOff() */

/****************************************************************************/
/* Function    : ibcSwitchDmInit                                            */
/* Purpose     : Initializes Data Manager fields in switchEntry data struct */
/* Inputs      : Pointer to switch entry, the four Data Manager item names  */
/*               switch request semaphore and power request semaphore       */
/* Outputs     : Returns OK or ERROR and updates switchEntry items          */
/****************************************************************************/
    MLocal STATUS
ibcSwitchDmInit( Reg switchEntry *deviceSwitch,
   char* switchReqName, char* switchStatusName,  char* powerReqName,
   char* powerOkName,   SEM_ID switchReqSem,     SEM_ID powerReqSem )
{
    Errno    status;               /* Result of dm_create                   */

                                   /* Create Switch Request Dmgr Item       */
    if ( (deviceSwitch->switchRequestDm =
              initMicroDmItem("", switchReqName, DM_ENUM, 1)) == ERROR)
        return(ERROR);             /* An error occurred so return ERROR     */

                                   /* Create Switch Status Data Manager Item*/
    if ( (deviceSwitch->switchStatusDm =
              initMicroDmItem("", switchStatusName, DM_ENUM, 1)) == ERROR)
        return(ERROR);             /* An error occurred so return ERROR     */

                                   /* Create Power Request Data Manager Item*/
    status = dm_create(powerReqName, 1, &deviceSwitch->powerRequestDm,
       DM_NAT16, 1, DM_ENUM, 1, DM_ENUM, 1, DM_ENDT );

                                   /* Treat item exists the same as success */
    if ((status != SUCCESS) && (status != EDM_NAME_EXISTS))
        return(ERROR);             /* An error occurred so return ERROR     */

                                   /* Create Power Ok Data Manager Item     */
    if ( (deviceSwitch->powerOkDm =
              initMicroDmItem("", powerOkName, DM_MBOOL, 1)) == ERROR)
        return(ERROR);             /* An error occurred so return ERROR     */

                                   /* Register us as a provider & consumer  */
    dm_start_consumer( deviceSwitch->switchRequestDm, DM_STATIC, switchReqSem );
    dm_start_consumer( deviceSwitch->switchStatusDm,  DM_STATIC, SEM_NULL);

    dm_start_provider( deviceSwitch->switchStatusDm, DM_STATIC );
    dm_start_provider( deviceSwitch->powerRequestDm, DM_STATIC );

    dm_start_consumer( deviceSwitch->powerOkDm, DM_STATIC, powerReqSem );

    return(OK);
} /* ibcSwitchDmInit() */

/****************************************************************************/
/* Function    : ibcSwitchDmState                                           */
/* Purpose     : Reads switch status Data Manager item for switch specified */
/* Inputs      : Pointer to switch entry                                    */
/* Outputs     : Returns switchStatus                                       */
/****************************************************************************/
    switchStatus
ibcSwitchDmState( Reg switchEntry *sw )
{
    switchStatus switchState = SWITCH_OFF;
                                        /* Read present switch status       */
    dm_read( sw->switchStatusDm, (char *) &switchState, sizeof(switchState),
      (DM_Time *) NULL);

    return (switchState);               /* Return switch state item value   */
} /* ibcSwitchDmState() */

/****************************************************************************/
/* Function    : ibcSwitchRequestDmState                                    */
/* Purpose     : Reads switch request Data Manager item for switch specified*/
/* Inputs      : Pointer to switch entry                                    */
/* Outputs     : Returns switchStatus                                       */
/****************************************************************************/
    switchStatus
ibcSwitchRequestDmState( Reg switchEntry *sw )
{
    switchStatus switchState = SWITCH_OFF;
                                        /* Read switch requst status        */
    dm_read( sw->switchRequestDm, (char *) &switchState, sizeof(switchState),
                 (DM_Time *) NULL);

    return (switchState);               /* Return switch state item value   */
} /* ibcSwitchRequestDmState() */

/****************************************************************************/
/* Function    : ibcSwitchControl                                           */
/* Purpose     : Process a switch request for IBC board switch entries      */
/* Inputs      : Sio32 channel, IBC Board Address, PSU, Desired state       */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    STATUS
ibcSwitchControl( sio32Chan *ibcSerialChan, IBC_BoardType boardType,
    Word boardAddr, Nat16 channel, MBool desiredState )
{
    switch(boardType)               /* Function call depends on board type */
    {
        case LOW_POWER_SWITCH:
            if (channel == LPS_VICOR_ENABLE)
                return(IBC_LPS_VicorControl(ibcSerialChan, boardAddr,
                    desiredState));
            else
                return( IBC_LPS_SwitchControl(ibcSerialChan, boardAddr,
                    channel, desiredState) );
            break;

        case HIGH_POWER_SWITCH:
            return( IBC_HPS_SwitchControl(ibcSerialChan, boardAddr,
                  desiredState) );
            break;

        case DUAL_VICOR:
            return( IBC_DualVicorSwitchControl(ibcSerialChan, boardAddr,
                 channel, desiredState) );
            break;

        case ISOLATED_IO:
            return ( IBC_ISO_IO_OutputCtrl(ibcSerialChan, boardAddr,
                 channel, (desiredState ? FALSE : TRUE)) );
            break;

        case ISOLATED_IO_POS_TRUE:
            return ( IBC_ISO_IO_OutputCtrl(ibcSerialChan, boardAddr,
                 channel, (desiredState ? TRUE : FALSE)) );
            break;

        case HYDROPHONE_PSU:
            return ( IBC_hydroPSUOutputCtrl(ibcSerialChan, boardAddr,
                 (desiredState ? FALSE : TRUE)) );
            break;

    } /* switch */

    return(ERROR);
} /* ibcSwitchControl() */

/****************************************************************************/
/* Function    : ibcPowerMgrRequest                                         */
/* Purpose     : Send IBC power request to power manager and await response */
/* Inputs      : IBC Switch entry and power required                        */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    MLocal STATUS
ibcPowerMgrRequest( Reg switchEntry *device, Reg MBool state )
{
    STATUS status;
    powerRequest request;               /* Power request data structure     */
    MBool   requestOk;                  /* Result of request from power mgr */
    DM_Time requestTime;                /* Time of request for Data Manager */

                                        /* Fill in time of request for DMgr */
    gettimeofday(&requestTime, (struct timezone *) NULL);

    if (state == ON)                    /* Switch Device ON                 */
    {                                   /* Send power request to power mgr  */

                                        /* ensure that semaphore is empty    */
        semTake(device->powerReqSem, NO_WAIT);

        dm_write( device->powerRequestDm, (char *) &device->power,
                sizeof(device->power), &requestTime );
                                        /* Wait for power manager to respond*/
                                        /* or timeout                       */
        if ( semTake(device->powerReqSem, POWER_REQUEST_TIMEOUT) != OK )
        {
            logMsg("Power Mgr Request Timeout for dmi 0x%x\n",
                device->powerRequestDm );
            return(ERROR);              /* Timeout occurred so return ERROR */
        }
                                        /* Read power manager response      */
        if ((status = dm_read( device->powerOkDm, (char *) &requestOk,
                 sizeof(requestOk), (DM_Time *) NULL)) != SUCCESS )
        {
            logMsg("Power Manager Request DM Read failed. Returned 0x%x\n",
                   status);
            return(ERROR);              /* dm_read failed so return ERROR   */
        } /* if */
                                        /* Map power reponse to OK or ERROR */
        return (requestOk == TRUE ? OK : ERROR);
    } /* if */

    else                                /* Switch Device Off                */
    {
        request.power = POWER_OFF;      /* Return Power to Power Pool       */
        request.bus = device->power.bus;/* Bus power is to be drawn from    */
                                        /* Load division catagory           */
        request.division = device->power.division;

        dm_write( device->powerRequestDm, (char *) &request, sizeof(request),
                 &requestTime );

        semTake(device->powerReqSem, POWER_REQUEST_TIMEOUT);
        return(OK);                     /* Always return OK                 */
    } /* else */

} /* ibcPowerMgrRequest() */

/****************************************************************************/
/* Function    : ibcSwitchChainState                                        */
/* Purpose     : Check that all switches in series are in SWITCH_ON state   */
/* Inputs      : IBC Switch entry pointer                                   */
/* Outputs     : Returns switch state                                       */
/****************************************************************************/
    MLocal switchStatus
ibcSwitchChainState( switchEntry *switchData )
{
    Reg switchEntry *sw;                /* pointer for walking switch list  */
    Reg switchStatus switchState;       /* Actual State of Switch           */

    switchState = SWITCH_ON;            /* Assume all switches are ON       */
                                        /* Find head of series switch list  */
    for ( sw = switchData->seriesSwitch; sw != NO_SWITCH;
             sw = sw->seriesSwitch )
    {                                   /* Read present switch status       */
        switchState = ibcSwitchDmState(sw);

        if (switchState != SWITCH_ON)   /* If any series switch is not ON   */
            return (switchState);       /* then return switch statu         */
    } /* for */

    return (switchState);               /* All series switches are ON       */
} /* ibcSwitchChainState() */

/****************************************************************************/
/* Function    : ibcChainPowerMgrRequest                                    */
/* Purpose     : Make Power Manager requests for a chain of switches        */
/* Inputs      : IBC device switch at head of chain, desired switch state   */
/* Outputs     : Returns power request status. Data Manager items change    */
/****************************************************************************/
    MLocal STATUS
ibcChainPowerMgrRequest( Reg switchEntry *sw, Reg MBool state )
{
    Reg switchEntry *devSwitch;         /* Device switch entry pointer      */
    Reg MBool   powerReqOk;             /* Switch Power Request Status      */

    if (state == ON)                    /* Request is to turn switches ON   */
    {                                   /* Send power request to power mgr  */
                                        /* Power request for first switch   */
        powerReqOk = ibcPowerMgrRequest(sw, ON);
                                        /* Power request for device switches*/
                                        /* attached to this switch          */
        for ( devSwitch = sw->busHead;
              (powerReqOk == OK) && (devSwitch != NO_SWITCH);
              devSwitch = devSwitch->busNext )
        {                               /* If dev switch is set to NO_POWER */
            if (ibcSwitchDmState(devSwitch) == SWITCH_NO_POWER)
                                        /* Make power request for dev switch*/
                powerReqOk = ibcPowerMgrRequest(devSwitch, ON);

                                         /* Repeat for all connected switches*/
            if ( (powerReqOk == OK) && (devSwitch->busHead != NO_SWITCH) )
                powerReqOk = ibcChainPowerMgrRequest( devSwitch->busHead, ON );
        } /* for */

        if (powerReqOk != OK)           /* Request failed so clean up power */
            ibcChainPowerMgrRequest(sw, OFF);
    } /* if */

    else                                /* Request is to turn switches OFF  */
    {                                   /* so return power to power manager */
        ibcPowerMgrRequest(sw, OFF);
                                        /* Return power for device switches */
                                        /* to this switch                   */
        for ( devSwitch = sw->busHead; devSwitch != NO_SWITCH;
               devSwitch = devSwitch->busNext )
        {                               /* Return power for device switches */
            ibcPowerMgrRequest(devSwitch, OFF);

                                        /* Repeat for connected switches    */
            if ( devSwitch->busHead != NO_SWITCH)
                ibcChainPowerMgrRequest( devSwitch->busHead, OFF );

        } /* for */

        powerReqOk = OK;                /* Returning power is guaranteed OK */
    } /* else */

    return (powerReqOk);                /* Return result of power request   */
} /* ibcChainPowerMgrRequest() */

/****************************************************************************/
/* Function    : ibcChainSwitchControl                                      */
/* Purpose     : Set state of switch and update other switches in the chain */
/* Inputs      : IBC device switch at head of chain, desired switch state   */
/* Outputs     : Returns power request status. Data Manager items change    */
/****************************************************************************/
    MLocal STATUS
ibcChainSwitchControl( Reg switchEntry *sw, Reg MBool state )
{
    Reg switchEntry *devSwitch;         /* Device switch entry pointer      */

    if (state == ON)
    {                                   /* Request is to turn switches ON   */
                                        /* Switch ON physical switch        */
        if (ibcSwitchControl(sw->ibcSerialChan, sw->boardType,
                sw->boardAddr, sw->channel, ON) != OK)
            return(ERROR);
                                        /* Switch ON all device switches    */
                                        /* attached to this switch          */
        for ( devSwitch = sw->busHead; devSwitch != NO_SWITCH;
              devSwitch = devSwitch->busNext )
        {
                                        /* If dev switch is set to NO_POWER */
            if (ibcSwitchDmState(devSwitch) == SWITCH_NO_POWER)
            {                          /* Switch ON physical device switches*/
                if (ibcSwitchControl(devSwitch->ibcSerialChan,
                        devSwitch->boardType, devSwitch->boardAddr,
                        devSwitch->channel, ON) == OK)
                {                       /* Device switched ON so update     */
                                        /* Data Manager Switch Status       */
                    ibcWriteSwitchDmState(devSwitch, SWITCH_ON);

                                        /* Check state of connected switches*/
                    if (( devSwitch->busHead != NO_SWITCH ) &&
                     (ibcSwitchDmState(devSwitch->busHead) == SWITCH_NO_POWER))
                        ibcChainSwitchControl( devSwitch->busHead, ON );
                } /* if */

                else                    /* Switch failed so return power to */
                                        /* Power Manager                    */
                    ibcPowerMgrRequest(devSwitch, OFF);
            } /* if */

        } /* for */
    } /* if */

    else                                /* Request is to turn switches OFF  */
    {                                   /* Switch OFF physical switch       */
        if (ibcSwitchControl(sw->ibcSerialChan, sw->boardType,
                sw->boardAddr, sw->channel, OFF) != OK)
            return (ERROR);

        ibcPowerMgrRequest(sw, OFF);    /* Return switch's power to pool    */

                                        /* Switch OFF all device switches   */
                                        /* attached to this switch          */
        for ( devSwitch = sw->busHead; devSwitch != NO_SWITCH;
              devSwitch = devSwitch->busNext )
        {                               /* For each device that is not OFF  */
            if (ibcSwitchDmState(devSwitch) != SWITCH_OFF)
            {                           /* Switch off device                */
                ibcSwitchControl(devSwitch->ibcSerialChan,
                    devSwitch->boardType, devSwitch->boardAddr,
                    devSwitch->channel, OFF);

                                         /* Return power to Power Manager   */
                ibcPowerMgrRequest(devSwitch, OFF);

                                        /* Update Data Manager Switch Status*/
                ibcWriteSwitchDmState(devSwitch, SWITCH_NO_POWER);
            } /* if */
                                        /* Repeat for all connected switches*/
            if ( devSwitch->busHead != NO_SWITCH )
                ibcChainSwitchControl( devSwitch->busHead, OFF );

        } /* for */
    } /* else */

    return (OK);                         /* Switch closed successfully       */
} /* ibcChainSwitchControl() */

/****************************************************************************/
/* Function    : staticPowerMgrRequest                                      */
/* Purpose     : Send power request to power manager and await response     */
/* Inputs      : Load power request & confirm DM item names, power, bus     */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    STATUS
staticPowerMgrRequest( char* powerReqName, char *powerOkName,
    Nat16 power, systemBus bus, loadDivision division )
{
    Errno   status;                     /* Result of dm_create              */
    DM_Item powerRequestDm;
    DM_Item powerOkDm;
    DM_Time requestTime;                /* Time of request for Data Manager */

    SEM_ID  powerReqSem;                /* Power Request Update Semaphore   */
    powerRequest request;               /* Power request data structure     */
    MBool   requestOk;                  /* Result of request from power mgr */

    request.power = power;              /* Fill in power request structure  */
    request.bus   = bus;                /* Bus power is to be drawn from    */
    request.division = division;        /* Load division catagory           */

                                   /* Create Power Request Data Manager Item*/
    status = dm_create(powerReqName, 1, &powerRequestDm,
                DM_NAT16, 1, DM_ENUM, 1, DM_ENUM, 1, DM_ENDT );

                                   /* Treat item exists the same as success */
    if ((status != SUCCESS) && (status != EDM_NAME_EXISTS))
        return(ERROR);             /* An error occurred so return ERROR     */

                                   /* Create Power Ok Data Manager Item     */
    if ( (powerOkDm = initMicroDmItem("", powerOkName, DM_MBOOL, 1)) == ERROR)
        return(ERROR);             /* An error occurred so return ERROR     */

    if ( (powerReqSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) != NULL)
    {
                                   /* Register us as a provider & consumer  */
        dm_start_provider( powerRequestDm, DM_STATIC );
        dm_start_consumer( powerOkDm, DM_STATIC, powerReqSem );

                                   /* Fill in time of request for DMgr      */
        gettimeofday(&requestTime, (struct timezone *) NULL);

                                   /* Send power request to power mgr       */
        dm_write( powerRequestDm, &request, sizeof(request), &requestTime );

                                   /* Wait for power manager to respond     */
                                   /* or timeout & read response            */
        if ( ( semTake(powerReqSem, POWER_REQUEST_TIMEOUT) != OK ) ||
            (dm_read( powerOkDm, &requestOk, sizeof(requestOk),
                  (DM_Time *) NULL) != SUCCESS ) )
            requestOk = ERROR;

        dm_stop_provider( powerRequestDm );
        dm_stop_consumer( powerOkDm );
        semDelete (powerReqSem);
    } /* if */
                                   /* Map power reponse to OK or ERROR      */
    return (requestOk == TRUE ? OK : ERROR);
} /* staticPowerMgrRequest() */


/****************************************************************************/
/* Function    : duplicateIbcSwitchEntry                                    */
/* Purpose     : Duplicates switchEntry data structure                      */
/* Inputs      : Pointer to switch entry, series switch structure, sio32    */
/*               serial channel, power and bus for power request,           */
/*               physical device Type, address, channel, data manager item  */
/*               name, switch request semaphore and power request semaphore */
/* Outputs     : Returns OK or ERROR and updates switchEntry items          */
/****************************************************************************/
    STATUS
duplicateIbcSwitchEntry( Reg switchEntry *devSwitch,
    char* switchReqName,     char* switchStatusName,  SEM_ID switchSem,
    const char *dataConName, const char* itemName)
{
    Errno    status;               /* Result of dm_create                   */

                                   /* Copy Switch Name                      */
    strncpy(devSwitch->name, itemName,
            min(strlen(itemName) + 1, MAX_SWITCH_NAME));

                                   /* Copy IBC Physical Device Mapping params*/
    devSwitch->ibcSerialChan = NULL;
    devSwitch->boardType     = NULL_IBC_BOARD;
    devSwitch->boardAddr     = 0;
    devSwitch->channel       = 0;

                                   /* Copy power request params for switch   */
    devSwitch->power.power    = POWER_OFF;
    devSwitch->power.bus      = NO_BUS;
    devSwitch->power.division = NO_DIVISION;

                                   /* Copy switch and power request sems     */
    devSwitch->switchReqSem  = switchSem;
    devSwitch->powerReqSem   = SEM_NULL;

                                   /* Initialize switch interconnect list   */
    devSwitch->busHead      = NO_SWITCH;
    devSwitch->busNext      = NO_SWITCH;
    devSwitch->seriesSwitch = NO_SWITCH;

    devSwitch->powerRequestDm = NO_ITEM;
    devSwitch->powerOkDm = NO_ITEM;

    devSwitch->currentDm = NO_ITEM;
    devSwitch->currentWarnThreshDm = NO_ITEM;
    devSwitch->currentAlarmThreshDm = NO_ITEM;
    devSwitch->currentAlarmDm = NO_ITEM;

    devSwitch->temperatureDm = NO_ITEM;
    devSwitch->tempWarnThreshDm = NO_ITEM;
    devSwitch->tempAlarmThreshDm = NO_ITEM;
    devSwitch->tempAlarmDm = NO_ITEM;

                                   /* Create Switch Request Dmgr Item       */
    if ( ((devSwitch->switchRequestDm =
              initMicroDmItem("", switchReqName, DM_ENUM, 1)) == ERROR) ||

                                   /* Create Switch Status Data Manager Item*/
          ((devSwitch->switchStatusDm =
              initMicroDmItem("", switchStatusName, DM_ENUM, 1)) == ERROR) )
    {
        logMsg(" %s: Error initializing %s Entry\n", dataConName, itemName );
        return(ERROR);
    } /* if */
                                   /* Register us as a provider & consumer  */
    dm_start_consumer( devSwitch->switchRequestDm, DM_STATIC, SEM_NULL );
    dm_start_consumer( devSwitch->switchStatusDm,  DM_STATIC, switchSem );

    return(OK);                    /* Switch Entry initialized correctly    */
} /* duplicateIbcSwitchEntry() */

/****************************************************************************/
/* Function    : ibcSwitchGangOutputs                                       */
/* Purpose     : Enables Ganged output switching mode for LPS boards        */
/* Inputs      : IBC Switch Entry                                           */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    STATUS
ibcSwitchGangOutputs( Reg switchEntry *devSwitch )
{
    return (IBC_LPS_GangOutputs( devSwitch->ibcSerialChan,
        devSwitch->boardAddr, devSwitch->channel, TRUE));
} /* ibcSwitchGangOutputs() */

/****************************************************************************/
/* Function    : ibcSwitchSetCurrentThresh                                  */
/* Purpose     : Sets current wartning/alarm thresholds for IBC switch      */
/* Inputs      : IBC Switch Entry, warning thresh, alarm thresh             */
/* Outputs     : Returns OK or ERROR                                        */
/****************************************************************************/
    STATUS
ibcSwitchSetCurrentThresh( Reg switchEntry *devSwitch,
    Nat16 warnThresh, Nat16 alarmThresh )
{
    urgentWriteDmItem( devSwitch->currentWarnThreshDm,  &warnThresh,
       sizeof(warnThresh) );

    urgentWriteDmItem( devSwitch->currentAlarmThreshDm, &alarmThresh,
       sizeof(alarmThresh) );

    switch ( devSwitch->boardType )
    {
      case LOW_POWER_SWITCH:
        return (IBC_LPS_SetChanCurrentThreshold(devSwitch->ibcSerialChan,
            devSwitch->boardAddr, devSwitch->channel, warnThresh, alarmThresh));
        break;

      case HIGH_POWER_SWITCH:
        return (IBC_HPS_SetCurrentThresh(devSwitch->ibcSerialChan,
           devSwitch->boardAddr, warnThresh, alarmThresh));
        break;

      default:
        return (ERROR);
    } /* switch */
} /* ibcSwitchGangOutputs() */

/****************************************************************************/
/* Function    : ibcProcessDupSwitchRequest                                 */
/* Purpose     : Switches Off all switches connceted to a dup IBC Switch    */
/* Inputs      : IBC switch entry                                           */
/* Outputs     : None, but data manager items change                        */
/****************************************************************************/
    Void
ibcProcessDupSwitchRequest( Reg switchEntry *sw )
{
    switchStatus switchState;           /* Actual State of Switch           */
    Reg switchEntry *devSwitch;         /* Device switch entry pointer      */

                                        /* Switch OFF all device switches   */
                                        /* attached to this switch          */
    for ( devSwitch = sw->busHead; devSwitch != NO_SWITCH;
         devSwitch = devSwitch->busNext )
    {                                   /* For each device that is not OFF  */
        ibcPowerMgrRequest(devSwitch, OFF);
                                        /* Update Data Manager Switch Status*/
        ibcWriteSwitchDmState(devSwitch, SWITCH_OFF);
                                        /* Repeat for all connected switches*/
        if ( devSwitch->busHead != NO_SWITCH )
            ibcProcessDupSwitchRequest( devSwitch );

    } /* for */
} /* ibcProcessDupSwitchRequest() */

