#include <stdio.h>
#include <sys/time.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

// #define TRANSFER_SIZE  (1024 * 1024)
#define TRANSFER_SIZE  153600
#define COUNT          30


unsigned char src_buf[TRANSFER_SIZE];
unsigned char dst_buf[TRANSFER_SIZE];

void mymemcpy(unsigned int* dst, unsigned int* src, int count){
  int i;
  count >>= 2;
  for(count --; count != 0;){
    dst[count--] = src[count];
  }
}


#define DEFAULT_DEVICE  "/dev/fb0"
char *fbdev  = NULL;
unsigned short            red[256],green[256],blue[256];
struct fb_cmap            cmap = { 0, 256, red, green, blue, NULL };
struct fb_fix_screeninfo  fix;
struct fb_var_screeninfo  var;

char *fb(){
  int fd;
  struct fb_fix_screeninfo* ffs = &fix;
  struct fb_var_screeninfo* fvs = &var;

  fbdev = DEFAULT_DEVICE;
  if (-1 == (fd = open(fbdev,O_RDWR))) {
    fprintf(stderr,"open %s: %s\n",fbdev,strerror(errno));
    exit(1);
  }
  if (-1 == ioctl(fd,FBIOGET_FSCREENINFO,&fix))
    perror("ioctl FBIOGET_FSCREENINFO");
  if (-1 == ioctl(fd,FBIOGET_VSCREENINFO,&var))
    perror("ioctl FBIOGET_VSCREENINFO");

  printf("SMEM   : %p L=%u(0x%x)\n",
	 ffs->smem_start, ffs->smem_len, ffs->smem_len);
  printf("MMIO   : %p L=%u(0x%x)\n",
	 ffs->mmio_start, ffs->mmio_len, ffs->mmio_len);
  printf("LLEN   : %u bytes (0x%x)\n",
	 ffs->line_length, ffs->line_length);
  printf("PSIZ   : %ux%u @ %ubpp (%s)\n",
                        fvs->xres, fvs->yres, fvs->bits_per_pixel,
	 fvs->grayscale ? "grayscale" : "color" );
  printf("VSIZ   : %ux%u (%ux%u+%u+%u)\n",
                        fvs->xres_virtual, fvs->yres_virtual,
                        fvs->xres, fvs->yres, fvs->xoffset, fvs->yoffset);



  return mmap(NULL, ffs->smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0);
}

int main(){
  struct timeval tv;
  struct timezone tz;
  struct timeval tv_s;
  struct timezone tz_s;
  struct timeval tv_e;
  struct timezone tz_e;
  struct timeval tv_f;
  struct timezone tz_f;
  struct timeval tv_g;
  struct timezone tz_g;

  int i, x, y;
  double rate;
  char* fp = fb();
  unsigned short *q, *q2, *r;
  unsigned int *p;
  int width = 240 * sizeof(short);

  gettimeofday(&tv, &tz);

  for(i = 0; i < COUNT; i++){
    // memcpy(dst_buf, src_buf,  TRANSFER_SIZE);
    // memset(dst_buf, 1,  TRANSFER_SIZE);
    // mymemcpy(dst_buf, src_buf,  TRANSFER_SIZE);

    memcpy(dst_buf, fp,  TRANSFER_SIZE);
  }
  gettimeofday(&tv_s, &tz_s);

#if 0
  for(i = 0; i < COUNT; i++){
    // memcpy(fp, src_buf, TRANSFER_SIZE);
    q = fp;
    q2 = q + 240;
    r = dst_buf;
    for(y = 320; y > 0; y--){
      p = r++; 
      for(x = 240; x > 0; x--){
	unsigned int tmp = *p;
	*q ++ = tmp;
	*q2 ++ = tmp >> 16;
	p += 160;
      }
      q += 240;
      q2 += 240;
    }
  }
#endif
  for(i = 0; i < COUNT; i++){
    memcpy(fp, dst_buf, TRANSFER_SIZE);
  }
  gettimeofday(&tv_e, &tz_e);

  for(i = 0; i < COUNT; i++){
    memset(dst_buf, 0, TRANSFER_SIZE);
  }
  gettimeofday(&tv_f, &tz_f);

  for(i = 0; i < COUNT; i++){
    memcpy(dst_buf, src_buf, TRANSFER_SIZE);
  }
  gettimeofday(&tv_g, &tz_g);


  printf("Read rate: %.2fMB/s\n",
	 (TRANSFER_SIZE * COUNT / (1024 * 1024.0)) /
	 ((tv_s.tv_sec - tv.tv_sec) + (tv_s.tv_usec - tv.tv_usec) / 1000000.0) );
#if 0
  rate = (TRANSFER_SIZE * COUNT) /
	 ((tv_e.tv_sec - tv_s.tv_sec) + (tv_e.tv_usec - tv_s.tv_usec) / 1000000.0);
  printf("Rate: %.2fMB/s (%.1ffps)\n", rate / (1024 * 1024.0), rate / 153600); 
#endif
  printf("Write rate: %.2fMB/s\n",
	 (TRANSFER_SIZE * COUNT / (1024 * 1024.0)) /
	 ((tv_e.tv_sec - tv_s.tv_sec) + (tv_e.tv_usec - tv_s.tv_usec) / 1000000.0) );

  printf("Memory write rate: %.2fMB/s\n",
	 (TRANSFER_SIZE * COUNT / (1024 * 1024.0)) /
	 ((tv_f.tv_sec - tv_e.tv_sec) + (tv_f.tv_usec - tv_e.tv_usec) / 1000000.0) );

  printf("Memory transfer rate: %.2fMB/s\n",
	 (TRANSFER_SIZE * COUNT / (1024 * 1024.0)) /
	 ((tv_g.tv_sec - tv_f.tv_sec) + (tv_g.tv_usec - tv_f.tv_usec) / 1000000.0) );

  return 0;
}

