// Copyright (C) 2002 Jean-Marc Valin

#include "rtc.h"
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/ioctl.h>
#include <BaseException.h>

using namespace std;

namespace FD {

#if defined(HAVE_LINUX_RTC_H) || defined(HAVE_LINUX_MC146818RTC_H)
#define HAVE_RTC

#ifdef HAVE_LINUX_RTC_H
#include <linux/rtc.h>
#else
#include <linux/mc146818rtc.h>
#endif
#endif
//#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

RTCUser *RTCTimer::create(float delay)
{
   RTCUser *u = new RTCUser(delay);
   pthread_mutex_lock(&instance().lock);
   if (instance().users.empty())
   {
#ifdef HAVE_RTC
      int retval = ioctl(instance().fd, RTC_PIE_ON, 0);
      if (retval == -1) {
         perror("ioctl");
         //exit(errno);
      }
#endif
   }

   instance().users.push_front(u);
   pthread_mutex_unlock(&instance().lock);
   return u;
}

void RTCTimer::destroy(RTCUser *u)
{
   pthread_mutex_lock(&instance().lock);

   list<RTCUser*>::iterator it=instance().users.begin();
   while (it != instance().users.end())
   {
      if (*it==u)
      {
         instance().users.erase(it);
         break;
      }
      it++;
   }
   
   if (instance().users.empty())
   {
#ifdef HAVE_RTC
      int retval = ioctl(instance().fd, RTC_PIE_OFF, 0);
      if (retval == -1) {
         perror("ioctl");
         //exit(errno);
      }
#endif
   }
   
   pthread_mutex_unlock(&instance().lock);
   delete u;
}

static void start_rtc_thread(RTCTimer *t)
{
   t->runThread();
}

RTCTimer::RTCTimer()
   : exit_status(false)
{
#ifndef HAVE_RTC
   throw new GeneralException("Overflow not compiled with /dev/rtc support", __FILE__, __LINE__);
#endif
   pthread_mutex_init(&lock, NULL);
      pthread_mutex_lock(&lock);

   pthread_create(&thread, NULL, (void * (*)(void *)) start_rtc_thread, this);

}

RTCTimer::~RTCTimer()
{
   cerr << "Destroying timer..." << endl;
   exit_status=true;
#ifdef HAVE_RTC
   ioctl(fd, RTC_PIE_ON, 0);
#endif
   pthread_join(thread, NULL);
   cerr << "Done" << endl;
}

void RTCTimer::runThread()
{
   fd = open("/dev/rtc", O_RDONLY);
   if (fd<0)
   {
      perror("/dev/rtc");
      cerr << "Cannot open /dev/rtc" << endl;
   }
   /*int retval = ioctl(fd, RTC_PIE_ON, 0);
   if (retval == -1) {
      perror("ioctl");
      //exit(errno);
      }
   */
   int retval;
#ifdef HAVE_RTC
   freq=16;
   do{
      freq*=2;
      retval = ioctl(fd, RTC_IRQP_SET, freq*2);
      /*if (retval == -1) {
        perror("ioctl");
        //exit(errno);
        }*/
      
   } while (retval!=-1);
   //cerr << "freq = " << freq << endl;
   dt=1.0/freq;
#endif
   //cerr << "dt = " << dt << endl;
   pthread_mutex_unlock(&lock);

   while(1)
   {
      unsigned long ret;
      ret = read(fd,&ret,4);
      if (exit_status)
         pthread_exit(NULL);
      pthread_mutex_lock(&lock);
      
      //cerr << "+";
      //cerr.flush();
      list<RTCUser*>::iterator it=users.begin();
      while (it != users.end())
      {
         (*it)->interrupt(dt);
         it++;
      }

      pthread_mutex_unlock(&lock);
   }
}

RTCTimer &RTCTimer::instance()
{
   static RTCTimer ret;
   return ret;
}

}//namespace FD


syntax highlighted by Code2HTML, v. 0.9.1