
#include "stdio.h"
#include "sys/time.h"
#include "errno.h"
#include "pthread.h"
#include "timer.h"


#define BASE_ADDR 0x40001000
#define SIZE 0x4


void milliseconds_sleep(unsigned long mSec){
  struct timeval tv;
  tv.tv_sec=mSec/1000;
  tv.tv_usec=(mSec%1000)*1000;
  int err;
  do{
    err=select(0,NULL,NULL,NULL,&tv);
  }while(err<0 && errno==EINTR);
}

#define dev_wr(addr) g_map[(addr-BASE_ADDR) >> 2]
#define dev_wrb(addr) ((uint8_t *)g_map)[(addr-BASE_ADDR)]
#define dev_wrh(addr) ((uint16_t *)g_map)[(addr-BASE_ADDR) >> 1]



static uint32_t g_map[1];


static uint32_t read(uint32_t addr) {
  if (!(addr & 0x3)) {
    return dev_wr(addr);
  } else if (!(addr & 0x1)) {
    return dev_wrh(addr);
  } else {
    return dev_wrb(addr);
  }
}

static void write(uint32_t addr, uint32_t data) {
  // printf("print: write data=%x\n", data);
  if (!(addr & 0x3)) {
    dev_wr(addr) = data;
  } else if (!(addr & 0x1)) {
    dev_wrh(addr) = data;
  } else {
    dev_wrb(addr) = data;
  }
}

typedef struct {
  uint32_t ins_count;
  pthread_mutex_t lock; /* 互斥锁定义 */
} mtimer_t;

static mtimer_t g_self;

static void run(riscv_t *riscv, const struct device_t* device) {
  int irq = 0;
  // printf("timer_run...\n");
  pthread_mutex_lock(&g_self.lock);
  dev_wr(BASE_ADDR) += g_self.ins_count;
  if (g_self.ins_count) {
    irq = 1;
  }
  g_self.ins_count = 0;
  pthread_mutex_unlock(&g_self.lock);
  if (irq) {
    riscv->mip |= MIP_MTIP;
  }
}


static void *timer_thread(void *arg) {
  while (1) {
    milliseconds_sleep(1);
    pthread_mutex_lock(&g_self.lock);
    g_self.ins_count++;
    pthread_mutex_unlock(&g_self.lock);
  }
  return NULL;
}

static void init(const struct device_t* device) {
  pthread_t tid;
  pthread_mutex_init(&g_self.lock, NULL);
  dev_wr(BASE_ADDR) = 0x0;
  pthread_create(&tid, NULL, timer_thread, NULL);
}

const static device_t g_timer = {
    .addr = BASE_ADDR,
    .size = SIZE,
    .read = read,
    .write = write,
    .run = run,
    .init = init,
};


const device_t* timer_dev() {
  return &g_timer;
}


