#include "common.h"

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#include "log.h"

// Use mmap to obtain virtual address, given physical
void *map_periph(MEM_MAP *mp, void *phys, int size) {
  mp->phys = phys;
  mp->size = PAGE_ROUNDUP(size);
  mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
  mp->virt = map_segment(phys, mp->size);
  return (mp->virt);
}

// Free mapped peripheral or memory
void unmap_periph_mem(MEM_MAP *mp) {
  if (mp) {
    unmap_segment(mp->virt, mp->size);
  }
}

// ----- VIRTUAL MEMORY -----

// Get virtual memory segment for peripheral regs or physical mem
void *map_segment(void *addr, int size) {
  int fd;
  void *mem;

  size = PAGE_ROUNDUP(size);
  if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) {
    log_fatal("can't open /dev/mem, run using sudo");
    exit(-EXIT_FAILURE);
  }

  mem = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, (uint32_t)addr);
  close(fd);
  log_info("Map %p -> %p", (void *)addr, mem);
  if (mem == MAP_FAILED) {
    log_fatal("can't map memory");
    exit(-EXIT_FAILURE);
  }
  return (mem);
}

// Free mapped memory
void unmap_segment(void *mem, int size) {
  if (mem) {
    munmap(mem, PAGE_ROUNDUP(size));
  }
}