/*
filedummy: A dummy user space video4linux driver.
(c) Harmen van der Wal, 2006.
$Id: filedummy.c,v 1.1 2006/08/16 22:24:41 harmen Exp $
License: GPLv2
See http://www.harmwal.nl/pccam880/
*/

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <unistd.h>
#include <linux/ioctl.h>
#include <linux/videodev.h>
#include <sys/types.h>
#include <sys/stat.h>

#define ENOIOCTLCMD 515

#define DBG(fmt,args...)
//#define DBG(fmt,args...) fprintf(stderr, "%s %s (%d): "fmt, __FILE__, __FUNCTION__, __LINE__,##args)
#define WARN(fmt,args...) fprintf(stderr, "%s %s (%d): "fmt, __FILE__, __FUNCTION__, __LINE__,##args)

struct buffer {
  void *buf;
  int write;
  int read;
  int size;
};

struct buffer data;

/* v4l */

struct video_capability cap = {
  .name = __FILE__,
  .channels = 1,
  .type = VID_TYPE_CAPTURE,
  .maxwidth = 320,
  .maxheight = 240,
  .minwidth = 320,
  .minheight = 240,      
};

struct video_picture pic = {
  .brightness = 0xffff,
  .hue = 0xffff,
  .colour = 0xffff,
  .contrast = 0xffff,
  .whiteness = 0xffff,
  .depth = 24,
  .palette = VIDEO_PALETTE_RGB24,
};

struct video_window window = {
  .width = 320,
  .height = 240,
};

struct video_channel channel = {
  .name = __FILE__,
  .channel = 0,
  .type = VIDEO_TYPE_CAMERA,
};

/* file io */

int shim_open()
{
  int fd, len, result = -1;
  char *str;

  DBG("\n");

  if (data.write > 0) // opened before.
    return 0;

  // Read image data from a file
  str = getenv("DATA");
  fd = open(str, O_RDONLY);
  if (fd < 0) {
    perror("open");
    return fd;
  }

  while (1) {
    if (data.size - data.write < 4096) {
      data.size += 4096 - data.size + data.write;
      data.buf = realloc(data.buf, data.size);
    }
    len = read(fd, data.buf + data.write, 4096);
    if (len < 1)
      break;
    data.write += len;
  }
  data.read = data.write;
  close(fd);
  if (len < 0) {
    WARN("error reading file: %s", strerror(errno));
    return len;
  }
  result = 0;

  DBG("data.write=%d\n", data.write);
  DBG("result=%d\n", result);

  return result;
}

int shim_ioctl(unsigned long int request, char *argp){

  int result = 0;

  DBG("request=%d nr %d\n", (int)request, (int)_IOC_NR(request));

  switch(request) {

  case VIDIOCGCAP:
    /* Get capabilities */
    {
      DBG("\n");
      memcpy(argp, &cap, sizeof(struct video_capability));
      break;
    }
  case VIDIOCGCHAN:
      /* Get channel info (sources) */
      DBG("\n");
      memcpy(argp, &channel, sizeof(struct video_channel));
      break;
  case VIDIOCSCHAN:
    /* Set channel  */
    if (((struct video_channel *)argp)->channel != 0) {
      errno = EINVAL;
      result = -1;
    }
    break;
  case VIDIOCGTUNER:
    /* Get tuner abilities */
  case VIDIOCSTUNER:
    /* Tune the tuner for the current channel */
  case VIDIOCGPICT:
    /* Get picture properties */
    {
      DBG("\n");
      memcpy(argp, &pic, sizeof(struct video_picture));
      break;
    }
  case VIDIOCSPICT:
    /* Set picture properties */
    DBG("\n");
    if (memcmp(argp, &pic, sizeof(struct video_picture))) {
      errno = EINVAL;
      result = -1;
    }
    break;
  case VIDIOCCAPTURE:
    /* Start, end capture */
  case VIDIOCGWIN:
    /* Get the video overlay window */
    DBG("\n");
    memcpy(argp, &window, sizeof(struct video_window));
    break;
  case VIDIOCSWIN:
    /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
    DBG("\n");
    if (memcmp(argp, &window, sizeof(struct video_window))) {
      errno = EINVAL;
      result = -1;
    }
    break;
  case VIDIOCGFBUF:
    /* Get frame buffer */
  case VIDIOCSFBUF:
     /* Set frame buffer - root only */
  case VIDIOCKEY:
    /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
  case VIDIOCGFREQ:
    /* Set tuner */
  case VIDIOCSFREQ:
    /* Set tuner */
  case VIDIOCGAUDIO:
    /* Get audio info */
  case VIDIOCSAUDIO:
    /* Audio source, mute etc */
  case VIDIOCSYNC:
      /* Sync with mmap grabbing */
  case VIDIOCMCAPTURE:
    /* Grab frames */
  case VIDIOCGMBUF:
    /* Memory map buffer info */
  case VIDIOCGUNIT:
    /* Get attached units */
  case VIDIOCGCAPTURE:
    /* Get subcapture */
  case VIDIOCSCAPTURE:
    /* Set subcapture */
  case VIDIOCSPLAYMODE:
    /* Set output video mode/feature */
  case VIDIOCSWRITEMODE:
    /* Set write mode */
  case VIDIOCGPLAYINFO:
    /* Get current playback info from hardware */
  case VIDIOCSMICROCODE:
    /* Load microcode into hardware */
  case VIDIOCGVBIFMT:
    /* Get VBI information */
  case VIDIOCSVBIFMT:
    /* Set VBI information */
    errno = EINVAL;
    result = -1;
    break;

  default:
    DBG("\n");
    errno = ENOIOCTLCMD;
    result = -1;
 }

  DBG("result=%d error=%d %s\n", result, errno, strerror(errno));

  return result;
}

ssize_t shim_read(void *buf, size_t count)
{
  if (count < data.write) {
    WARN("count to small: %d < %d\n", count, data.write);
    return -1;
  }
  memcpy(buf, data.buf, count);
  return count;
}

int shim_close(){
  return 0;
}
