/*
  I-O DATA IEEE 1394 TV Tuner Box: GV-MVP/IDV controller for FreeBSD5
    written by Kazumaro Aoki (ka at flu.if0.org)
  NO WARRANTY: use this program at your own risk

  07/ 8/10: consider CATV channel
  06/ 5/ 4: consider firewire bus#
  04/10/ 2: try to receive ack code but failed
  04/ 7/11: coding start
  04/ 6/29: firewire device analysis start
*/

#include<ctype.h>
#include<fcntl.h>
#include<limits.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<dev/firewire/firewire.h>

/*
  GV-MVP/IDV control AV/C packet format

  vendor_id: 0xa0b0 ... I-O DATA
  model_id:  0x1100 ... GV-1394TV & GV-MVP/IDV
  command: 0x02 ... ch&audio change, 0x03,0x04,0x05 ... ?
  ch:
    0x00 ... 1ch, 0x01 ... 2ch, ..., 0xb ... 12ch
    0x40 ... c13ch, 0x41 ... c14ch, ...
    0x80 ... svideo input
    0x88 ... video input
    0xff ... no change
  audio:
    0x00 ... no change (?)
    0x01 ... main(default)
    0x02 ... sub
    0x03 ... main + sub
 */



/* use fwmem */
int main(int argc, char *argv[]){
  u_int64_t target;		/* for EUI64 */
  struct fw_eui64 eui;
  int unitNum = 0;		/* firewire bus# */
  char devName[32];		/* firewire device name */
  int fd;			/* for /dev/fwmem */
  unsigned char audio = 0x00;
  unsigned char channel = 0xff;
  extern int optind;		/* for getopt() */
  extern char *optarg;
  int ch;
  static unsigned char buf[] = {
    0x00, 0xff, 0x00, 0x00,	/* 0:4 ctype:4 subuint_type:5 subunit_ID:3 opcode:8 ?:8 */
    0xa0, 0xb0, 0x11, 0x00,	/* vendor_id:16 model_id:16 */
    0x02, 0x00, 0x80, 0x10,	/* command:8 channel:8 freq_adj:8 ?:8  */
    0x00			/* audio:8 */
  };

  /* argument analysis */
  while((ch = getopt(argc, argv, "c:msu:")) != -1){
    switch(ch){
    case 'c':
      switch(*optarg){
      case 's':
	channel = 0x80;
	break;
      case 'v':
	channel = 0x88;
	break;
      default:
	if(tolower(*optarg) == 'c'){ /* CATV */
	  channel = 0x40 + atoi(optarg + 1) - 12; /* no c1ch - c12ch */
	}
	else{
	  channel = atoi(optarg);
	}
	if((channel == 0) || (channel >= 0x80)){
	  fprintf(stderr, "ignored illeagal channel: %s\n", optarg);
	  channel = 0xff;
	  break;
	}
	channel --;
      }
      break;
    case 'm':
      audio |= 0x01;
      break;
    case 's':
      audio |= 0x02;
      break;
    case 'u':
      unitNum = atoi(optarg);
      break;
    }
  }
  argc -= optind;
  argv += optind;
  if(argc != 1){
    fprintf(stderr,
	    "Usage: %s [-m] [-s] [-c ch] [-u unitNum] target\n"
	    "\ttarget is specified by EUI64, ex. 0x00a0b0210001a733.  Try fwcontrol.\n"
	    "\tchannel s means svideo, and v means video input, c## menas CATV ## ch\n"
	    "\t-m: main audio\n"
	    "\t-s: sub audio\n",
	    argv[-optind]);
//    fprintf(stderr, "argc=%d\n", argc);
    return 1;
  }
  target = strtoull(argv[0], NULL, 0);
//  target = 0x00a0b0210001a733ull;
  eui.hi = target >> 32;
  eui.lo = target & (((u_int64_t)1 << 32) - 1);
//  printf("EUI: %08x  %08x:  %016qx\n", eui.hi, eui.lo, target);

  /* setup AV/C packet */
  buf[9] = channel;
  buf[12] = audio;

  /* output AV/C packet to firewire */
  sprintf(devName, "/dev/fwmem%d", unitNum);
  if((fd = open(devName, O_RDWR)) == -1){
    perror(devName);
    return 2;
  }
  if(ioctl(fd, FW_SDEUI64, &eui)){
    perror("ioctl FW_SDEUI64");
    return 3;
  }
  pwrite(fd, buf, sizeof(buf), (off_t)0xFFFFF0000B00ull);

  return 0;
}

/* end of file */
