/****************************************************************************/
/*                                                                          */
/* MODULE NAME:     REBOOT (REBOOT.C)                                       */
/*                                                                          */
/* LAST UPDATE:     JULY 9, 1992 (13:56:05)                                 */
/*                                                                          */
/* AUTHOR:          N. SCOTT PEARSON, CABLESHARE INC.                       */
/*                                                                          */
/* DESCRIPTION:     This utility forces a reboot of the current processor   */
/*                  board. Prior to performing this operation, all devices  */
/*                  attached by the board are detached. In Multibus II      */
/*                  systems, if the current board is the server board, the  */
/*                  reboot affects the whold chassis.                       */
/*                                                                          */
/* COMPATIBILITY:   REBOOT obtains the names of the devices to be detached  */
/*                  by searching through the iRMX ROOT JOB's object         */
/*                  catalog. This technique makes REBOOT iRMX release-      */
/*                  specific (only iRMX II and iRMX III are supported). A   */
/*                  review of the structure of the iRMX JOB object will be  */
/*                  required to verify its compatibility with future        */
/*                  releases of iRMX.                                       */
/*                                                                          */
/****************************************************************************/

#include <i86.h>
#include <rmx.h>
#include <udi.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

#include "ic.h"

struct _directory_entry
    {
    char            name[14];
    RMXOBJ          object;
    } far *directory_entry;

USER_ID_LIST            id_list;
RMXRET                  status, cmd_status;
int                     index;
RMXOBJ                  ci_tkn, co_tkn, cmd_tkn, root_job;
U16                     entries_left, slot, curr_reg, conn_type;
char                    buff[100];
U8                      name[15], rec_type;
EXCP_HANDLER_ATTRIBS    hattribs;
DEVICE_ATTRIBS          dattribs;

int main( int argc, char *argv[] )
    {
    hattribs.handler = NULL;
    hattribs.mode = NO_EXCEPTIONS;
    RQSETEXCEPTIONHANDLER( &hattribs, &status );

    ci_tkn = RQCGETINPUTCONNECTION( "\004:CI:", &status );
    co_tkn = RQCGETOUTPUTCONNECTION( "\004:CO:", OVER_PREPOSITION, &status );
    cmd_tkn = RQCCREATECOMMANDCONNECTION( ci_tkn, co_tkn, 0, &status );

    root_job = RQGETTASKTOKENS( ROOT_JOB, &status );
    entries_left = *((U16 *)buildptr( root_job, (void near *)40 ));
    
    /************************************************************************/
    /* Only continue if invoked by super user                               */
    /************************************************************************/

    id_list.list_length = 3;
    RQINSPECTUSER( RQGETDEFAULTUSER( THIS_JOB, &status ), &id_list, &status );

    for ( index = 0; index < 3; index++ )
        if ( id_list.id[index] == SYSTEM_MANAGER )
            break;

    if ( index >= 3 )
        {
        puts( "\nYou must be the System Manager to use this command\n" );
        return( E_CONTEXT );
        }

    /************************************************************************/
    /* Ascertain whether iRMX II or iRMX III. Offset to object directory    */
    /* within job structure is different: 2 bytes farther for iRMX III due  */
    /* to 32 - rather than 16 - bit offset in pointer to exception handler. */
    /************************************************************************/

    dqgetsystemid( (BYTE *)buff, (WORD *)(&status) );
    cstr( buff, buff );

    if ( strcmp( buff, "iRMX II" ) == 0 )
        directory_entry = buildptr( root_job, (void near *)96 );
    else
        directory_entry = buildptr( root_job, (void near *)98 );

    /************************************************************************/
    /* Find all device connections in the root job's object directory and   */
    /* detach them. Notice that we always do the system disk last as we use */
    /* cusp DETACHDEVICE to perform detach.                                 */
    /************************************************************************/

    for ( index = 0; entries_left; index++ )
        {
        if ( directory_entry[index].name[0] )
            {
            --entries_left;
            cstr( name, directory_entry[index].name );

            if ( strcmp( name, "SD" ) != 0 )
                {
                conn_type = RQGETTYPE( directory_entry[index].object, &status );

                if ( !status && ( conn_type == DEVICE_CONNECTION_TOKEN ) )
                    {
                    sprintf( buff, "detachdevice :%s: force", name );
                    udistr( buff, buff );
                    RQCSENDCOMMAND( cmd_tkn, buff, &cmd_status, &status );
                    }
                }
            }
        }

    /************************************************************************/
    /* Prior to detaching system disk, ascertain whether it is attached via */
    /* named file driver. If so, know board is server for chassis.          */
    /************************************************************************/

    RQGETLOGICALDEVICESTATUS( "\004:SD:", &dattribs, &status );

    if ( status )
        {
        printf( "Status %04hX returned by RQGETLOGICALDEVICESTATUS\n", status );
        exit( status );
        }

    udistr( buff, "detachdevice :sd: force" );
    RQCSENDCOMMAND( cmd_tkn, buff, &cmd_status, &status );
    RQSLEEP( 300, &status );

    /************************************************************************/
    /* Reset the processor. If MBII and board is server board, reset by     */
    /* sending reset command to CSM. If MBII and board is not server board, */
    /* reset by sending reset command to board's Interconnect uController.  */
    /* If PC, reset by toggling processor's reset pin via Keyboard          */
    /* uController.                                                         */
    /************************************************************************/

    slot = RQGETHOSTID( &status );
    RQGETINTERCONNECT( 0, 0, &status );

    if ( status )
        {
        /* Not MBII...must be PC */

        for ( ; ; )
            outbyte( 0x64, 0xfe );
        }
    else
        if ( dattribs.file_driver == NAMED_FILE_DRIVER )
            {
            /* server board */

            rec_type = RQGETINTERCONNECT( 0, curr_reg = IC_FIRST_RECORD_OFFSET, &status );

            while ( rec_type != IC_CSM_RECORD_TYPE )
                {
                curr_reg += 2 + RQGETINTERCONNECT( 0, curr_reg + IC_RECORD_LENGTH_OFFSET, &status );
                rec_type = RQGETINTERCONNECT( 0, curr_reg, &status );
                }
    
            for ( ; ; )
                RQSETINTERCONNECT( IC_CSM_PERFORM_COLD_RESET, 0, curr_reg + IC_CSM_COMMAND_OFFSET, &status );
            }
        else
            {
            rec_type = RQGETINTERCONNECT( slot, curr_reg = IC_FIRST_RECORD_OFFSET, &status );

            while ( rec_type != IC_LOCAL_CPU_RECORD_TYPE )
                {
                curr_reg += 2 + RQGETINTERCONNECT( slot, curr_reg + IC_RECORD_LENGTH_OFFSET, &status );
                rec_type = RQGETINTERCONNECT( slot, curr_reg, &status );
                }

            for ( ; ; )
                RQSETINTERCONNECT( IC_RESET_LOCAL_CPU, slot, curr_reg + IC_LOCAL_CPU_CONTROL_OFFSET, &status );
            }
    }
