/*  digifan.c - Fan control program for the ASUS Digimatrix.
 *
 *  (C) Copyright 2006 Andrew Calkin <calkina@geexbox.org>
 *  (C) Copyright 2004 Richard Taylor <richard@artaylor.co.uk>
 *
 *  based on code for accessing/initializing it8712/it8705 written by
 *
 *  (C) Copyright 2004 Wojtek Kaniewski <wojtekka@toxygen.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License Version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sys/io.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include "it8705.h"
#include "digifan.h"
#include "digitools.h"

static double min = FAN_DEFAULT_MIN; /* minimum fan speed default (%) */
static double gradient = FAN_DEFAULT_GRADIENT; /* speed increase rate */
static double offset = FAN_DEFAULT_OFFSET; /* temp for minimum speed */

static struct option digifan_longopts[] = {
  { "min",         1, 0, 'm' },
  { "gradient",    1, 0, 'g' },
  { "offset",      1, 0, 'o' },
  { "auto",        0, 0, 'a' },
  { "intelligent", 0, 0, 'i' },
  { "set",         1, 0, 's' },
  { "read",        0, 0, 'r' },
  { "kill",        0, 0, 'k' },
  { "version",     0, 0, 'v' },
  { "help",        0, 0, 'h' },
  { NULL,          0, 0,  0  }
};

static int set_fan (unsigned char state)
{
  unsigned char speed, temp1, temp2;
  double percentage = 0;

  it87_ec_port_open();

  daemon_kill();

  temp1 = it87_read_ec_byte(IT8705_TEMP_REG1);
  temp2 = it87_read_ec_byte(IT8705_TEMP_REG2);

  switch (state)
  {
    case FAN_AUTO:
#ifndef SILENT
      printf("Setting Fan to AUTO\n");
#endif
      it87_write_ec_byte(FAN1, AUTO);
      it87_write_ec_byte(FAN2, AUTO);
      it87_write_ec_byte(FAN3, AUTO);
      break;

    case FAN_OFF:
      it87_write_ec_byte(FAN1, OFF);
      it87_write_ec_byte(FAN2, OFF);
      it87_write_ec_byte(FAN3, OFF);
      break;

    case FAN_INTELLIGENT:
#ifndef SILENT
      printf("Running in intelligent mode (update = 5s)\n");
#endif
      daemon_init();

      while (1)
      {
        temp1 = it87_read_ec_byte(IT8705_TEMP_REG1);
        temp2 = it87_read_ec_byte(IT8705_TEMP_REG2);

        if (temp2 > temp1)
        {
          /* Go with the average of the two temperatures.
             (we need to cool down the mainboard too)*/
          temp1 = (temp1 + temp2) / 2;
        }

        percentage = min + ( (temp1 - offset) * gradient);

        /* We can't go more than 100%! */
        if (percentage > 100)
          percentage = 100;

        /* No slower than min */
        if (percentage < min)
          percentage = min;

        speed = (unsigned char) ((percentage * (double)FAN_MAX_PWM) / 100);

        it87_write_ec_byte(FAN1, (speed&0x7f));
        it87_write_ec_byte(FAN2, (speed&0x7f));

        sleep(5);
      } /* end while*/
      break;

    default:
#ifndef SILENT
      printf("Setting fan speed to: %d%%\n",
         (100 *(state & 0x7f)) / 0x7f);
#endif
      it87_write_ec_byte(FAN1, state);
      it87_write_ec_byte(FAN2, state);
      break;
  } /* end switch*/

  it87_ec_port_close();

  return 0;
}

static void digifan_help(const char *argv0)
{
#ifndef SILENT
  printf(
"Usage: %s [OPTIONS]\n"
"\n"
"  Program to control the fan speeds in the Asus Digimatrix.\n"
"  -v, --version          Print version information\n"
"  -h, --help             Display this help message\n"
"\n"
"  Fan control is initiated via one of the following options:\n"
"  -r, --read             Display the current fan speed (%%)\n"
"                           Note: does not alter the fan setting\n"
"  -s, --set=VALUE        Set the current fan speed (%%)\n"
"  -a, --auto             Set to H/W monitoring (as in BIOS)\n"
"  -i, --intelligent      Set the fan speed based on current temperature\n"
"  -k, --kill             Stop the daemon (if running), and exit\n"
"\n"
"  The -i option may also be used with any of the following options to\n"
"    override program defaults.\n"
"\n"
"  -m, --min=VALUE        Set the minimum Fan speed (Default: 30)\n"
"  -o, --offset=VALUE     The temperature (deg C) for minimum fan speed \n"
"                           (Default: 30)\n"
"  -g, --gradient=VALUE   Set the rate at which speed increases with\n"
"                           temperature (Default: 4)\n"
"                         e.g. gradient =     (100 - min)    \n"
"                                         -------------------\n"
"                                         (max_temp - offset)\n"
"\n", argv0);
#endif
}

int digifan_main(int argc, char **argv)
{
  unsigned char next_state = 0;

  if ( (int)getuid() != 0)
  {
#ifndef SILENT
    fprintf(stderr,"Must be root to run this program!\n");
#endif
    return -1;
  }

  int ch, longindex = 0;

  if (argc == 1)
  {
#ifndef SILENT
    printf("\nPlease supply some parameters.\n"
             "Use %s -h for list.\n\n", argv[0]);
#endif
    return 1;
  }

  it87_open();

  it87_ldn_set(IT8705_EC_LDN);

  it87_close();

  for (;;)
  {
    if ((ch = getopt_long(argc, argv, "m:g:o:ais:rkvh",
               digifan_longopts, &longindex)) == -1)
    {
      break;
    }

    switch (ch)
    {
      case 'm':
        if (atof(optarg)<5)
        {
#ifndef SILENT
          printf("Specified minimum too low! Using default.\n");
#endif
        }
        else
        {
          if (atof(optarg)>100)
          {
#ifndef SILENT
            printf("Specified minimum too high! Using default.\n");
#endif
          }
          else
            min=atof(optarg);
        }
#ifndef SILENT
        printf("Setting min to: %.02f%%\n", min);
#endif
        break;

      case 'g':
        if (atof(optarg)<2)
        {
#ifndef SILENT
          printf("Specified gradient too low! Using default.\n");
#endif
        }
        else
        {
          if (atof(optarg)>100)
          {
#ifndef SILENT
            printf("Specified gradient too high! Using default.\n");
#endif
          }
          else
            gradient=atof(optarg);
        }
#ifndef SILENT
        printf("Setting gradient to: %.02f\n", gradient);
#endif
        break;

      case 'o':
        if (atof(optarg)<18)
        {
#ifndef SILENT
          printf("Specified offset too low! Using default.\n");
#endif
        }
        else
        {
          if (atof(optarg)>55)
          {
#ifndef SILENT
            printf("Specified offset too high! Using default.\n");
#endif
          }
          else
            offset=atof(optarg);
        }
#ifndef SILENT
        printf("Setting offset to: %.02fDegC\n", offset);
#endif
        break;

      case 'r':
        it87_ec_port_open();
        printf("Current Fan setting is: %d%%\n",
                (100 *(it87_read_ec_byte(FAN1) & 0x7f)) / 0x7f);
        it87_ec_port_close();
        return 0;
        break;

      case 's':
        if (atoi(optarg) < OFF)
        {
#ifndef SILENT
          printf("Warning: Value provided (%d%%) too low!\n"
                    "Minimum fan speed: %d%%\n", atoi(optarg),OFF);
#endif
          next_state = ( OFF * 0x7f) / 100;
        }
        else if (atoi(optarg)>100)
        {
#ifndef SILENT
          printf("Warning: Value provided (%d%%) too high!\n"
                    "Maximum fan speed: 100%%\n", atoi(optarg));
#endif
          next_state = FAN_MAX_PWM;
        }
           else
        {
          next_state = ( atoi(optarg) * 0x7f) / 100;
        }
        break;

      case 'a':
        next_state = FAN_AUTO;
        break;

      case 'i':
        next_state = FAN_INTELLIGENT;
        break;

      case 'k':
#ifndef SILENT
        printf("Stopping the %s daemon\n",argv[0]);
#endif
        daemon_kill();
        exit(0);

      case 'v':
#ifndef SILENT
        printf("ASUS DigiMatrix Fan Controller, from DigiTools Version "
                "%s\n",DIGI_VER);
#endif
        /* Exit program after giving version info */
        return 0;
        break;

      case 'h':
        digifan_help(argv[0]);
        /* Exit program after giving help info */
        return 0;
        break;

      default:
#ifndef SILENT
        printf("Undefined input parameter(s) provided\n");
#endif
        /* Impossible to get here (unless programming error?) */
        return 1;
        break;
    } /* end switch */
  } /* end for loop */

  if (next_state != 0)
    set_fan(next_state);
  else
  {
#ifndef SILENT
    printf("No fan mode selected!\n");
#endif
  }

  return 0;
}
