/*
command to compile:
gcc -O3 adc_dac_server.c -o adc_dac_server
*/

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <math.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define TCP_PORT 5000
//Variables for generating testing signal
#define DAC_DATA_LEN    4096
#define STEP            2*M_PI/DAC_DATA_LEN

int interrupted = 0;

void signal_handler(int sig)
{
  interrupted = 1;
}

int main ()
{
  pid_t pid;
  int pipefd_adc[2], pipefd_dac[2], mmapfd, sockServer, sockClient;
  int position, limit, offset = 0;
  volatile uint32_t *slcr, *axi_hp0;
  volatile void *cfg, *sts, *adc, *dac, *buf_adc, *buf_dac;
  struct sockaddr_in addr;
  int yes = 1, buffer_dac = 0, buffer_adc = 0;
  uint32_t idx_sin;
  int16_t buffer[DAC_DATA_LEN];
  float arg_sin = 0;

  //Create testing sinus signal for DAC
  for(idx_sin = 0; idx_sin < DAC_DATA_LEN; idx_sin += 2)
  {
    buffer[idx_sin] = (int16_t)((8192)*sin(idx_sin*STEP));
    buffer[idx_sin+1] = 0;
  }

  //Open memory file
  if((mmapfd = open("/dev/mem", O_RDWR)) < 0)
  {
    perror("open");
    return 1;
  }

  //Memory mapping
  slcr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0xF8000000);
  axi_hp0 = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0xF8008000);
  cfg = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x40000000);
  sts = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x40001000);
  adc = mmap(NULL, 2048*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x1E000000);
  dac = mmap(NULL, 16*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED, mmapfd, 0x40010000);
  buf_adc = mmap(NULL, 2048*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
  buf_dac = mmap(NULL, 16*sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

  //Set HP0 bus width to 64 bits
  slcr[2] = 0xDF0D;
  slcr[144] = 0;
  axi_hp0[0] &= ~1;
  axi_hp0[5] &= ~1;

  limit = 512*1024;

  //Reset RAM writer
  *((uint16_t *)(cfg + 0)) &= ~2;
  *((uint16_t *)(cfg + 0)) |= 2;

  //Enter normal mode (start recording ADC samples)
  *((uint16_t *)(cfg + 0)) |= 1;

  //Wait for RAM writer
  while(*((uint32_t *)(sts + 0)) < 32)
  {
    usleep(500);
  }

  //Create pipes
  pipe(pipefd_dac);
  pipe(pipefd_adc);

  //Create parent and child process
  pid = fork();
  if(pid == 0)
  {
    /* child process */
    //Close DAC pipe for write
    close(pipefd_dac[1]);
    //Close ADC pipe for read
    close(pipefd_adc[0]);
    while(1)
    {
      
      read(pipefd_dac[0], &buffer_dac, sizeof(buffer_dac));
      memcpy(dac, buf_dac, 16*4096);
      printf("Data copied from buf_dac_to DAC\n");

      memcpy(buf_adc + offset, adc + offset, 2048*4096);
      write(pipefd_adc[1], &buffer_adc, sizeof(buffer_adc));
      printf("Data copied from ADC to buf_adc\n");


      /* read ram writer position */
      position = *((uint32_t *)(sts + 0));
    }
  }
  else if(pid > 0)
  {
    /* parent process */

    //Close DAC pipe for read
    close(pipefd_dac[0]);
    //Close ADC pipe for write
    close(pipefd_adc[1]);

    //Create socket
    if((sockServer = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
      perror("socket");
      return 1;
    }

    //Set socket optins
    setsockopt(sockServer, SOL_SOCKET, SO_REUSEADDR, (void *)&yes , sizeof(yes));

    //Setup listening address
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(TCP_PORT);

    //Bind address to socket
    if(bind(sockServer, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
      perror("bind");
      return 1;
    }


    listen(sockServer, 1024);

    while(!interrupted)
    {
      /* enter reset mode */
      *((uint16_t *)(cfg + 0)) &= ~1;
      /* set default sample rate */
      *((uint16_t *)(cfg + 2)) = 8192;

      //Wait for TCP coonection
      if((sockClient = accept(sockServer, NULL, NULL)) < 0)
      {
        perror("accept");
        return 1;
      }

      signal(SIGINT, signal_handler);

      printf("New connection\n");

      /* enter normal operating mode */
      *((uint16_t *)(cfg + 0)) |= 1;

      while(!interrupted)
      {

        if(recv(sockClient, buf_dac, 16*4096, 0) < 0) break;
        write(pipefd_dac[1], &buffer_dac, sizeof(buffer_dac));
        printf("Data from Matlab received\n");
        
        read(pipefd_adc[0], &buffer_adc, sizeof(buffer_adc));
        if(send(sockClient, buf_adc, 2048*4096, 0) < 0) break;
        printf("Data 1 to matlab send\n");
        
      }

      signal(SIGINT, SIG_DFL);
      close(sockClient);
      printf("Client socket closed\n");
    }

    //Enter reset mode
    *((uint16_t *)(cfg + 0)) &= ~1;

    close(sockServer);
    printf("Server socket closed\n");

    kill(pid, SIGTERM);

    return 0;
  }
}
