/*
 *   NVRAM WakeUp
 *   Copyright (C) 2001-2004, Sergei Haller.
 *
 *   $Id: nvramops.c 682 2004-05-03 22:40:13Z bistr-o-math $
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


#include <stdio.h>
#include <stdlib.h>

#include <sys/io.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>

#include "nvram-wakeup.h"

int  fd_rtc, fd_nvram;

void open_nvram(int _directisa, int mode) {

     if (_directisa == OFF) {
          char mode_str[10];
          switch (mode) {
               case O_RDONLY: sprintf(mode_str, "O_RDONLY"); break;
               case O_WRONLY: sprintf(mode_str, "O_WRONLY"); break;
               default:       sprintf(mode_str, "%d", mode); break;
          }
          /* open the nvram device */
          nvprintf(LOG_DEBUG, "Opening %s in %s mode...\n", NVRAM_DEV, mode_str);
          fd_nvram = open(NVRAM_DEV, mode);
          if (fd_nvram == -1) {
               nvprintf(LOG_ERR, "%s: %m\n", NVRAM_DEV);
               exit(2);
          }
     }
     else {
          /* get permission */
          nvprintf(LOG_DEBUG, "Requesting permissions for io ports 0x70...0x75\n");
          if (ioperm(0x70, 6, 1) == -1) { 
               nvprintf(LOG_ERR, "An error occurred while requesting permissions: %m\n");
               exit(2);
          }
     }

}

void close_nvram(int _directisa) {

     if (_directisa == OFF) {
          /* close the nvram device again */
          close(fd_nvram);
     }
     else {
          /* release ports */
	     ioperm( 0x70, 4, 0 );
     }

}

int read_whole_nvram(int _directisa, int upper_method, unsigned char * bytes) {
     int addr;
     char reg_A;
     int nvramSize = 0;
     
     if (_directisa == OFF) {         
          /* Now we read whole nvram ... */
          lseek(fd_nvram, 0, SEEK_SET); /* just to be sure we are at the beginning */
          nvramSize = read(fd_nvram, bytes, MAXNVRAMSIZE);
          if (nvramSize < 0) {
               nvprintf(LOG_ERR, "An error occurred while reading nvram: %m\n");
               exit(2);
          }

     }
     else /* if (_directisa == ON) */ {
          for(addr=0;addr<=13;addr++) {
               outb(addr, 0x70);
               bytes[addr+114] = inb(0x71);
          }
          for(addr=14;addr<=127;addr++) {
               outb(addr, 0x70);
               bytes[addr-14] = inb(0x71);
          }

          switch (upper_method) {
          case  OFF:
               nvramSize = 128;
               break;
          case  INTEL:
               for(addr=0;addr<=127;addr++) {
                    outb(addr, 0x72);
                    bytes[addr+128] = inb(0x73);
               }
               nvramSize = 256;
               break;
          case  DS1685:
               reg_A = bytes[0xA+114];

               outb(0xA,   0x70);
               outb(reg_A | 0x10, 0x71); /* set bit 4 of register A and enable upper nvram access */

               for(addr=0;addr<=127;addr++) {
                    outb(addr, 0x70);
                    bytes[addr+128] = inb(0x71);
               }
               
               outb(0xA,   0x70);
               outb(reg_A, 0x71); /* restore contents of register A */

               nvramSize = 256;
               break;
          case  VT82Cxxx:
               for(addr=128;addr<=255;addr++) {
                    outb(addr, 0x72);
                    bytes[addr] = inb(0x73);
               }
               nvramSize = 256;
               break;
          case  VT8235_37:
               for(addr=128;addr<=255;addr++) {
                    outb(addr, 0x74);
                    bytes[addr] = inb(0x75);
               }
               nvramSize = 256;
               break;
          default:
               nvramSize = 128;
               break;
          }
     }
     
     nvprintf(LOG_DEBUG, "The size of NVRAM is %u bytes.\n", nvramSize);

     return nvramSize;
}

void write_back(int _directisa, int upper_method, int addr, unsigned char * bytes) {
     int retval;
     char reg_A = bytes[0xA+114];

     nvprintf(LOG_DEBUG, "Writing value 0x%02X into byte 0x%02X of NVRAM\n", bytes[addr], addr);

     if (_directisa == OFF) {
          lseek(fd_nvram, addr, SEEK_SET);
          retval = write(fd_nvram, &bytes[addr], 1);
          if (retval != 1) {
               nvprintf(LOG_CRIT, "Error occurred while writing byte 0x%02X of %s: %m\n", addr, NVRAM_DEV);
               exit(2);
          }
     }
     else /* _directisa == ON */ {

          if (addr<114) {
               outb(addr+14,       0x70);
               outb(bytes[addr],   0x71);
          }

          else if (addr<128) {
               outb(addr-114,      0x70);
               outb(bytes[addr],   0x71);
          }
          /* upper nvram needs special treatment */
          else switch (upper_method) {
               case  INTEL:
                    outb(addr-128,      0x72);
                    outb(bytes[addr],   0x73);
                    break;
               case  DS1685:
                    outb(0xA,           0x70);
                    outb(reg_A | 0x10,  0x71); /* set bit 4 of register A and enable upper nvram access */

                    outb(addr-128,      0x70);
                    outb(bytes[addr],   0x71);

                    outb(0xA,           0x70);
                    outb(reg_A,         0x71); /* restore contents of register A */
                    break;
               case  VT82Cxxx:
                    outb(addr,          0x72);
                    outb(bytes[addr],   0x73);
                    break;
               case  VT8235_37:
                    outb(addr,          0x74);
                    outb(bytes[addr],   0x75);
                    break;
               }
     }

}

