// Compile with: gcc -Wall -otone -lrt tone.c gpiolib.c

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#include "gpiolib.h"

#define PORT 25
 
void set_max_priority(void) 
  {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
  sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
  sched_setscheduler(0, SCHED_FIFO, &sched);
  }


void udelay (unsigned long mikros)
  {
  /* busy wait */
  long int start_time;
  long int time_difference;
  struct timespec gettime_now;

  mikros = mikros * 1000L;
  time_difference = 0;
  clock_gettime(CLOCK_REALTIME, &gettime_now);
  start_time = gettime_now.tv_nsec;         // get ns value
  while (time_difference <= mikros)
    {
    clock_gettime(CLOCK_REALTIME, &gettime_now);
    time_difference = gettime_now.tv_nsec - start_time;
    if (time_difference < 0)
      time_difference += 1000000000;       // rolls over every second
    }
  }

int main(int argc, char **argv)
  {
  long i;
  long long t;
  struct timeval t1, t2;

  if (argc != 3)
    {
    printf("Aufruf %s Frequenz[Hz] Dauer[ms]\n",argv[0]); 
    return (3);
    }
  long frequency = atol(argv[1]);
  long duration = atol(argv[2]);
  if ((frequency < 1) || (frequency > 5000)) return(4);

  if (gpio_export(PORT) < 0)  return(1);
  if (gpio_direction(PORT, OUT) < 0) return(2);
  set_max_priority();
  
  // calculate the delay value between transitions:
  // 1 million microseconds, 
  // split in half since there are two phases to each cycle,
  // divided by the frequency
  long delayvalue = 500000/frequency; 
     
  // calculate the number of cycles for proper timing:
  // multiply frequency (cycles per second) by the number of durationeconds
  // to get the total number of cycles to produce
  long cycles = frequency * duration / 1000; 
    
  gettimeofday(&t1, NULL);
  for (i = 0; i < cycles; i++)
    { // for the calculated length of time...
    gpio_write(PORT,HIGH);
    udelay(delayvalue);
    gpio_write(PORT,LOW);
    udelay(delayvalue);
    }
  gettimeofday(&t2, NULL);
  t = ((t2.tv_sec * 1000000) + t2.tv_usec) - ((t1.tv_sec * 1000000) + t1.tv_usec);
 
  printf("Delay: %ld, Cycles: %ld\n", delayvalue, cycles);
  printf("Aufruf dauerte  %lld us (%lld us pro Cycle)\n", t, t/cycles);

  if (gpio_unexport(PORT) < 0)  return(1);
  return(0);
  }
