#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>

void write_data(unsigned int location, unsigned char *buf, unsigned int size, int fd);
void print_buf(unsigned char *buf, unsigned int size, unsigned int offset);
void erase_data(unsigned int location, unsigned int erase_size, int fd);

unsigned int Diff(struct timeval *t1, struct timeval * t2)
{

    if(t1->tv_sec == t2->tv_sec)
        return (t2->tv_usec - t1->tv_usec);
    else if (t1->tv_usec < t2->tv_usec)
        return (((t2->tv_sec - t1->tv_sec)*1000000) + (t2->tv_usec - t1->tv_usec));
    else
        return (((t2->tv_sec - t1->tv_sec - 1)*1000000) + (1000000 - t1->tv_usec + t2->tv_usec));
}

int main(int argc, char *argv[])
{
  unsigned char *buf;
  mtd_info_t mtd_info;
  int fd;
  int i,j,k;
  char *operation;
  unsigned int address = 0;
  unsigned int size = 0;
  static struct timeval tv1 = {0,0};
  static struct timeval tv2 = {0,0};
  unsigned int timeDiff = 0;
  double rate;
 
    printf("argc = %d\n", argc);
  for (i=0; i<argc; i++)
    printf("arg = %s\n", argv[i]);
  if(argc < 3)
  {
    printf("usage: spi_mem_test {r,w,e} /dev/mtdx start_address size\n");
    return 1;
  } 
  fd = open(argv[1], O_RDWR);
  ioctl(fd, MEMGETINFO, &mtd_info);

  operation = argv[2];
  
  address = atoi(argv[3]);
  size = atoi(argv[4]);
  
  printf("operation = %s address = %x size = %x\n", operation, address, size);
  printf("MTD type: %u\n", mtd_info.type);
  printf("MTD total size: %u bytes\n", mtd_info.size);
  printf("MTD erase size: %u bytes\n", mtd_info.erasesize);
  if(size == 0)
  {
    size = mtd_info.size;
  }
  buf = malloc(size);

  k=0;
  for(i=0; i < size; i++)
  {
    buf[i] = k;
  }

  lseek(fd, address, SEEK_SET);
 
  switch (*operation)
  {
    case 'r':
      gettimeofday(&tv1, NULL);
      read(fd, buf, size);
      gettimeofday(&tv2, NULL);
      print_buf(buf, size, address);
      break;
    case 'w':
      gettimeofday(&tv1, NULL);
      write_data(address, buf, size, fd);
      gettimeofday(&tv2, NULL);
      break;
    case 'e':
      gettimeofday(&tv1, NULL);
      erase_data(address , size, fd);
      gettimeofday(&tv2, NULL);
      break;
    default: 
      printf("%s is not a valid operation\n",operation);
  }
  timeDiff = Diff(&tv1, &tv2);

  rate = ((double)size/(1024*1024)) / ((double)timeDiff / 1000000);
  printf("read rate = %f MB/s\n", rate);

}

void print_buf(unsigned char *buf, unsigned int size, unsigned int offset)
{
  int i;
  for(i=0;i<size;i+=4)
  {
    printf("%4x: %02x%02x%02x%02x\n",i+offset,buf[i],buf[i+1],buf[i+2],buf[i+3]);
  }
}

void write_data(unsigned int location, unsigned char *buf, unsigned int size, int fd)
{
  int i;
  lseek(fd, location, SEEK_SET);
  write(fd, buf, size);
}

void erase_data(unsigned int location, unsigned int erase_size, int fd)
{
  erase_info_t ei;
  ei.length = erase_size;
  ei.start = location;
  ioctl(fd, MEMUNLOCK, &ei);
  ioctl(fd, MEMERASE, &ei);
}
