/*
 * Copyright (C) 2020 CERN (www.cern.ch)
 *
 * Released according to the GNU GPL, version 2 or any later version.
 *
 */

#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "libvmebus.h"
#include "libwr2rf/io.h"
#include "libwr2rf/board.h"

static const unsigned trace = 0;

#define MAX(A, B) ((A) > (B) ? (A) : (B))

struct libwr2rf_dev
{
  unsigned char *ptr8;
};

/* For little endian hosts (like x86).  */
static uint16_t
swaph (uint16_t v)
{
  return ((v & 0xff) << 8) | ((v >> 8) & 0xff);
}

void
libwr2rf_write16 (struct libwr2rf_dev *dev, unsigned off, uint16_t v)
{
  if (trace)
    printf("writeh %04x @ %08x\n", v, off);
  *(volatile uint16_t *)(dev->ptr8 + off) = swaph (v);
}

uint16_t
libwr2rf_read16 (struct libwr2rf_dev *dev, unsigned off)
{
  uint16_t res;
  res = swaph (*(volatile uint16_t *)(dev->ptr8 + off));
  if (trace)
    printf("readh %04x @ %08x\n", res, off);
  return res;
}

/* Map vme space defined by AM, BASE, MASK to user memory.  */

static struct libwr2rf_dev *
setup_map (uint8_t am, uint32_t base, uint32_t mask, int verbose)
{
  struct vme_mapping data_map;
  void *ptr;
  struct libwr2rf_dev *dev;

  memset(&data_map, 0, sizeof(data_map));
  data_map.am = am;
  data_map.data_width = 32;
  data_map.sizel = MAX (0x80000, ~mask + 1);
  data_map.vme_addrl = base & mask;

  if (verbose)
    printf ("INFO: Map VME 0x%08x AM 0x%02x (base=%08x mask=%08x)\n",
            data_map.vme_addrl, data_map.am, base, mask);

  /* Do mmap */
  ptr = vme_map(&data_map, 1);
  if (ptr == NULL)
    {
      fprintf (stderr, "cannot map vme space\n");
      return NULL;
    }

  dev = malloc(sizeof (*dev));
  if (dev == NULL)
    return NULL;
  dev->ptr8 = ptr;

  return dev;
}

struct libwr2rf_dev *
libwr2rf_host_init_by_slot_verbose (unsigned int slot, int verbose)
{
  if (slot == 0 || slot > 0x1f)
    {
      fprintf (stderr, "incorrect or missing slot value (1 - 0x1f)\n");
      return NULL;
    }

  /* MAP A24.
     By convention, the base address is SLOT * 1MB.  */
  return setup_map (0x39, slot << 19, ~((512 << 10) - 1), verbose);
}

struct libwr2rf_dev *
libwr2rf_host_init_by_slot (unsigned int slot)
{
  return libwr2rf_host_init_by_slot_verbose (slot, 1);
}

struct libwr2rf_dev *
libwr2rf_host_init (int argc, char **argv)
{
  int c;
  unsigned slot = 0;

  while ((c = getopt (argc, argv, "s:h")) != -1)
    switch (c)
      {
      case 's':
	slot = strtol (optarg, NULL, 0);
	break;
      case 'h':
	printf ("usage: %s [-h] -s SLOT\n", argv[0]);
	return NULL;
      case '?':
	fprintf (stderr, "incorrect option, try %s -h\n", argv[0]);
	return NULL;
      }

  return libwr2rf_host_init_by_slot_verbose (slot, 0);
}
