#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

#include "pci17x4.h"

int
main(int argc, char **argv)
{
  int fd = (argc > 1) ? open(argv[1], O_RDWR) : (-1);
  pci17x4_dmastatus_t dma;
  void *mapaddr = NULL;
  size_t mapsize = 0;
  char line[2048];
  memset(&dma, 0, sizeof(dma));
  unsigned index, value;
  if (fd < 0) {
    fprintf(stderr, "can not open device\n");
    return(1);
  }
  while (1) {
    uint32_t value;
    int err;
    printf("cmd> ");
    fflush(stdout);
    if (fgets(line, sizeof(line), stdin) == NULL) {
      break;
    }
    if (sscanf(line, " wb %u %x", &index, &value) == 2) {
      pci17x4_register_t regval = { index, 8, PCI17X4_IOCTL_REGISTER_WRITE, value };
      if ((err = ioctl(fd, IOCTL_PCI17X4_REGISTER_IO, &regval))) {
        error(0, errno, "ioctl() register write error");
      }
      continue;
    }
    if (sscanf(line, " ww %u %x", &index, &value) == 2) {
      pci17x4_register_t regval = { index, 16, PCI17X4_IOCTL_REGISTER_WRITE, value };
      if ((err = ioctl(fd, IOCTL_PCI17X4_REGISTER_IO, &regval))) {
        error(0, errno, "ioctl() register write error");
      }
      continue;
    }
    if (sscanf(line, " wl %u %x", &index, &value) == 2) {
      pci17x4_register_t regval = { index, 32, PCI17X4_IOCTL_REGISTER_WRITE, value };
      if ((err = ioctl(fd, IOCTL_PCI17X4_REGISTER_IO, &regval))) {
        error(0, errno, "ioctl() register write error");
      }
      continue;
    }
    if (sscanf(line, " rb %u", &index) == 1) {
      pci17x4_register_t regval = { index, 8, PCI17X4_IOCTL_REGISTER_READ, 0 };
      if ((err = ioctl(fd, IOCTL_PCI17X4_REGISTER_IO, &regval))) {
        error(0, errno, "ioctl() register read error");
        continue;
      }
      printf("value of register %u is 0x%02x\n", regval.index, regval.value);
      continue;
    }
    if (sscanf(line, " rw %u", &index) == 1) {
      pci17x4_register_t regval = { index, 16, PCI17X4_IOCTL_REGISTER_READ, 0 };
      if ((err = ioctl(fd, IOCTL_PCI17X4_REGISTER_IO, &regval))) {
        error(0, errno, "ioctl() register read error");
        continue;
      }
      printf("value of register %u is 0x%04x\n", regval.index, regval.value);
      continue;
    }
    if (sscanf(line, " rl %u", &index) == 1) {
      pci17x4_register_t regval = { index, 32, PCI17X4_IOCTL_REGISTER_READ, 0 };
      if ((err = ioctl(fd, IOCTL_PCI17X4_REGISTER_IO, &regval))) {
        error(0, errno, "ioctl() register read error");
        continue;
      }
      printf("value of register %u is 0x%08x\n", regval.index, regval.value);
      continue;
    }
    if (sscanf(line, " b %u %u", &index, &value) == 2) {
      pci17x4_dmasetup_t setup = { value, index, 0, 0 };
      if (mapaddr) {
        munmap(mapaddr, mapsize);
        mapaddr = NULL;
      }
      if ((err = ioctl(fd, IOCTL_PCI17X4_DMA_SETUP, &setup))) {
        error(0, errno, "ioctl() buffer setup error");
        continue;
      }
      if ((err = ioctl(fd, IOCTL_PCI17X4_DMA_STATUS, &dma))) {
        error(0, errno, "ioctl() buffer status error");
        continue;
      }
      printf("buffer set up: partsize=%u, nbuffers=%u\n", (1 << (dma.pageshift + dma.setup.blockshift)), dma.setup.numblocks);
      continue;
    }
    if (sscanf(line, " m %u", &value) == 1) {
      if (mapaddr) {
        munmap(mapaddr, mapsize);
      }
      if (((mapaddr = mmap(NULL, value, PROT_READ, MAP_SHARED, fd, 0)) == NULL) || (mapaddr == MAP_FAILED)) {
        error(0, errno, "mmap() error");
        continue;
      }
      if ((err = ioctl(fd, IOCTL_PCI17X4_DMA_STATUS, &dma))) {
        error(0, errno, "ioctl() buffer status error");
        continue;
      }
      mapsize = value;
      printf("mapped %u bytes at %p\n", mapsize, mapaddr);
    }
  }
  close(fd);
  return(0);
}
