/*      -*- linux-c -*-
 * hpifan.cpp
 *
 * Copyright (c) 2003,2004 by FORCE Computers
 * (C) Copyright Nokia Siemens Networks 2010
 * (C) Copyright Ulrich Kleber 2011
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  This
 * file and program are licensed under a BSD style license.  See
 * the Copying file included with the OpenHPI distribution for
 * full licensing terms.
 *
 * Authors:
 *     Thomas Kanngieser <thomas.kanngieser@fci.com>
 *     Ulrich Kleber <ulikleber@users.sourceforge.net>
 *
 * Changes:
 *     10/13/2004  kouzmich   porting to HPI B
 *     09/06/2010  ulikleber  New option -D to select domain
 *     01/02/2011  ulikleber  Refactoring to use glib for option parsing and
 *                            introduce common options for all clients
 *
 */

#include "oh_clients.h"

#define OH_SVN_REV "$Revision: 7291 $"

static SaHpiBoolT set_new = SAHPI_FALSE;
static SaHpiCtrlModeT new_mode = SAHPI_CTRL_MODE_MANUAL;
static int new_speed = -1;
static gchar *f_speed = NULL;
static oHpiCommonOptionsT copt;

static GOptionEntry my_options[] =
{
  { "speed", 's', 0, G_OPTION_ARG_STRING, &f_speed, "Set fan speed for ALL fans in domain\n"
    "                      speed is a number or \"auto\" for setting fan in auto mode", "auto|nn" },
  { NULL }
};

static SaErrorT
get_fan_speed( SaHpiSessionIdT session_id,
               SaHpiResourceIdT resource_id,
               SaHpiCtrlNumT ctrl_num,
               SaHpiCtrlStateAnalogT *speed,
	       SaHpiCtrlModeT *mode )
{
        SaHpiCtrlStateT state;

	if (copt.debug) DBG("get fan speed for resource %u, control %u", resource_id, ctrl_num);

        SaErrorT rv = saHpiControlGet( session_id, resource_id, ctrl_num, mode, &state );

        if ( rv != SA_OK ) {
                fprintf( stderr, "cannot get fan state: %s!\n", oh_lookup_error( rv ) );
                return rv;
        }

        if ( state.Type != SAHPI_CTRL_TYPE_ANALOG ) {
                fprintf( stderr, "cannot handle non analog fan state !\n" );
                return SA_ERR_HPI_ERROR;
        }

        *speed = state.StateUnion.Analog;

        return SA_OK;
}


static SaErrorT
set_fan_speed( SaHpiSessionIdT session_id,
               SaHpiResourceIdT resource_id,
               SaHpiCtrlNumT ctrl_num,
               SaHpiCtrlStateAnalogT speed,
	       SaHpiCtrlModeT mode )
{
        SaErrorT rv;
        SaHpiCtrlStateT state;
        state.Type = SAHPI_CTRL_TYPE_ANALOG;
        state.StateUnion.Analog = speed;

	if (copt.debug) DBG("set fan speed for resource %u, control %u", resource_id, ctrl_num);
        rv = saHpiControlSet( session_id, resource_id, ctrl_num, mode, &state );

        if ( rv != SA_OK ) {
                fprintf( stderr, "cannot set fan state: %s!\n", oh_lookup_error( rv ) );
                return rv;
        }

        return SA_OK;
}


static int
do_fan( SaHpiSessionIdT session_id, SaHpiResourceIdT resource_id,
        SaHpiRdrT *rdr )
{
        SaErrorT rv;
        SaHpiCtrlRecT *ctrl_rec = &rdr->RdrTypeUnion.CtrlRec;
	SaHpiCtrlModeT ctrl_mode;

        printf( "\tfan: num %u, id ", ctrl_rec->Num );
        oh_print_text ( &rdr->IdString );
        printf( "\n" );

        if ( ctrl_rec->Type != SAHPI_CTRL_TYPE_ANALOG ) {
                fprintf( stderr, "cannot handle non analog fan controls !\n" );
                return 0;
        }

        printf( "\t\tmin       %d\n", ctrl_rec->TypeUnion.Analog.Min );
        printf( "\t\tmax       %d\n", ctrl_rec->TypeUnion.Analog.Max );
        printf( "\t\tdefault   %d\n", ctrl_rec->TypeUnion.Analog.Default );

        SaHpiCtrlStateAnalogT speed;

        rv = get_fan_speed( session_id, resource_id, ctrl_rec->Num, &speed, &ctrl_mode);

        if ( rv != SA_OK )
                return 0;

        printf( "\t\tmode      %s\n", ctrl_mode == SAHPI_CTRL_MODE_AUTO ? "auto" : "manual" );
        printf( "\t\tcurrent   %d\n", speed );

        if ( set_new == SAHPI_FALSE ) {
                return 0;
        }
        if ( new_mode == SAHPI_CTRL_MODE_AUTO ) {
                new_speed = speed;
        }
  
        if (   new_speed < ctrl_rec->TypeUnion.Analog.Min 
                || new_speed > ctrl_rec->TypeUnion.Analog.Max ) {
                fprintf( stderr, "fan speed %d out of range [%d,%d] !\n",
                        new_speed, ctrl_rec->TypeUnion.Analog.Min,
                         ctrl_rec->TypeUnion.Analog.Max );
                return 0;
        }

        rv = set_fan_speed( session_id, resource_id, ctrl_rec->Num, new_speed, new_mode);

        if ( rv != SA_OK )
                return 0;
  
        rv = get_fan_speed( session_id, resource_id, ctrl_rec->Num, &speed, &ctrl_mode);

        if ( rv != SA_OK )
                return 0;
  
        printf( "\t\tnew mode  %s\n", ctrl_mode == SAHPI_CTRL_MODE_AUTO ? "auto" : "manual" );
        printf( "\t\tnew speed %d\n", speed );

        return 0;
}


static int
discover_domain( SaHpiSessionIdT session_id )
{
        /* walk the RPT list */
        SaErrorT rv;
        SaHpiEntryIdT next = SAHPI_FIRST_ENTRY;
        int found = 0;

        do {
                SaHpiRptEntryT entry;
                SaHpiEntryIdT  current = next;

                rv = saHpiRptEntryGet( session_id, current, &next, &entry );

                if ( rv != SA_OK ) {
                        printf( "saHpiRptEntryGet: %s\n", oh_lookup_error( rv ) );
                        return 1;
                }

                // check for control rdr
                if (    !(entry.ResourceCapabilities & SAHPI_CAPABILITY_RDR)
                        || !(entry.ResourceCapabilities & SAHPI_CAPABILITY_CONTROL) )
                        continue;

                /* walk the RDR list for this RPT entry */
                SaHpiEntryIdT next_rdr = SAHPI_FIRST_ENTRY;
                SaHpiResourceIdT resource_id = entry.ResourceId;
                int epath_out = 1;

                do {
                        SaHpiEntryIdT current_rdr = next_rdr;
                        SaHpiRdrT     rdr;

                        rv = saHpiRdrGet( session_id, resource_id, current_rdr, 
                                          &next_rdr, &rdr );
           
                        if ( rv != SA_OK ) {
                                printf( "saHpiRdrGet: %s\n", oh_lookup_error( rv ) );
                                return 1;
                        }

                        // check for control
                        if ( rdr.RdrType != SAHPI_CTRL_RDR )
                                continue;

                        // check for fan
                        if ( rdr.RdrTypeUnion.CtrlRec.OutputType != SAHPI_CTRL_FAN_SPEED )
                                continue;

                        if ( epath_out ) {
				oh_print_ep(&entry.ResourceEntity, 0);
                                epath_out = 0;
                        }

                        do_fan( session_id, resource_id, &rdr );
                        found++;
                } while( next_rdr != SAHPI_LAST_ENTRY );
        } while( next != SAHPI_LAST_ENTRY );

        if ( found == 0 )
                printf( "no fans found.\n" );

        return 0;
}


int
main( int argc, char *argv[] )
{
        SaErrorT rv;
        SaHpiSessionIdT sessionid;
        GOptionContext *context;

        /* Print version strings */
	oh_prog_version(argv[0]);

        /* Parsing options */
        static char usetext[]="- Show \"Fan Control\" management instruments\n  "
                              OH_SVN_REV; 
        OHC_PREPARE_REVISION(usetext);
        context = g_option_context_new (usetext);
        g_option_context_add_main_entries (context, my_options, NULL);

        if (!ohc_option_parse(&argc, argv, 
                context, &copt, 
                OHC_ALL_OPTIONS 
                    - OHC_ENTITY_PATH_OPTION  //TODO: Feature 880127
                    - OHC_VERBOSE_OPTION )) { // no verbose mode implemented
                g_option_context_free (context);
		return 1;
	}
        g_option_context_free (context);
 
        if (f_speed) {
           set_new = SAHPI_TRUE;
           if ( strcmp( f_speed, "auto" ) == 0 ) {
              new_mode = SAHPI_CTRL_MODE_AUTO;
           } else if ( strcmp( f_speed, "0" ) == 0 ) {
              new_speed = 0;
           } else {
              new_speed = atoi( f_speed );
              if (new_speed == 0) {
                 CRIT ("please enter a valid speed: \"auto\" or a number.\n");
                 g_free (f_speed);
                 return 1;
              }
           }
           g_free(f_speed);
        }

        rv = ohc_session_open_by_option ( &copt, &sessionid);
	if (rv != SA_OK) return rv;

	/*
	 * Resource discovery
	 */
	if (copt.debug) DBG("saHpiDiscover");
	rv = saHpiDiscover(sessionid);
        if ( rv != SA_OK ) {
                CRIT( "saHpiDiscover: %s", oh_lookup_error( rv ) );
                return rv;
        }
	if (copt.debug) DBG("Discovery done");

        int rc = discover_domain( sessionid );

        rv = saHpiSessionClose( sessionid );
        if ( rv != SA_OK )
                CRIT( "saHpiSessionClose: %s", oh_lookup_error( rv ) );

        return  rc;
}
