/*
 * Copyright 1988, 1989, 1990, 1991 Massachusetts Institute of Technology
 */

/* necpcvcr_driver.c : routines for NEC PC-VCR videotape players/recorders */

#include "models.h"
#include "necpcvcr.h"
#include "structs.h"
#include "hash.h"

/* 
  after the NEC PC-VCR finishes a search operation, it still
  takes a significant portion of a second for its video
  output to stabilize; if you don't wait, it may be unstable
  enough to mess up a digitization 
  */

#define NECPCVCR_SETTLE_TIME 100

static	UBYTE	inbytes[32];
static	UBYTE	outbytes[32];
static int player_stat;
static HashTablePtr	necpcvcr_htp = NULL;	/* contains all commands */

/* ------------------------------------------------------------------------ */

static int necpcvcr_FillTable()	/* must be called first! */
{
  int	i;
  
  if( necpcvcr_htp )
    return -1;
  necpcvcr_htp = hash_create( "NEC PC-VCR commands", NUM_NECPCVCR_CMDS );
  for( i=0; i<NUM_NECPCVCR_CMDS; i++ )
    hash_install( necpcvcr_htp, necpcvcr_table[i].cmd_name, NULL, i );
  return 0;
}

/* ------------------------------------------------------------------------ */

int necpcvcr_lookup( cmdstr )/* convert command name into necpcvcr_table index */
     char *cmdstr;
{
  HashNodePtr np;
  
  if( !cmdstr )
    return -1;
  np = hash_lookup( necpcvcr_htp, cmdstr );
  if( !np )
    return -1;
  return np->def;
}

/* ------------------------------------------------------------------------ */

necpcvcr_cmd(rpd,num)
     
     RPD_ptr	rpd;
     int 	num;
{
  int	success = 0, forked = 0;
  int	r1, r2;
  int   readnum;
  char switchchar;
  
  /* note that with the audio, must set the bits corresponding
     to the current state of the software */
  
  switch(num)
    {
    case INDEX_ON:
    case INDEX_OFF:
      return 1;
#ifdef OBSOLETE
      if( !necpcvcr_ttyio( rpd->fd, necpcvcr_lookup("Index-On"), 
			  0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.indexon = 1;
	  success = 1;
	}
      else {
	bsdsyslog(LOG_NOTICE, "%s: %s: index on failed",rpd->devname,
		  rpd->port);
	success = 0;
      }
      break;
    case INDEX_OFF:
      if( !necpcvcr_ttyio( rpd->fd, necpcvcr_lookup("Index-Off"),
			  0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.indexon = 0;
	  success = 1;
	}
      else {
	bsdsyslog(LOG_NOTICE, "%s: %s: index off failed",rpd->devname,
		  rpd->port);
	success = 0;
      }
      break;
#endif /* OBSOLETE */
    case A1_ON:
      rpd->state.audio1_on = 1;
      if (necpcvcr_audioctrl(rpd) == -1)
	success = 0;
      else success = 1;
      break;
    case A1_OFF:
      rpd->state.audio1_on = 0;
      if (necpcvcr_audioctrl(rpd) == -1)
	success = 0;
      else success = 1;
      break;
    case A2_ON:
      rpd->state.audio2_on = 1;
      if (necpcvcr_audioctrl(rpd) == -1)
	success = 0;
      else success = 1;
      break;
    case A2_OFF:
      rpd->state.audio2_on = 0;
      if (necpcvcr_audioctrl(rpd) == -1)
	success = 0;
      else success = 1;
      break;
    case SQUELCH_ON:
    case SQUELCH_OFF:
      return (1);
      break;
    case VIDEO_ON:
      if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Video-On"),
			 0, 0, &r1, &r2, 1)) {
	bsdsyslog(LOG_NOTICE, 
		  "%s: %s: necpcvcr_audioctrl: video on failed",
		  rpd->devname, rpd->port);
	rpd_error(rpd);
	  return -1;
      }
      else success = 1;
      break;
    case VIDEO_OFF:
      if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Video-Off"),
			 0, 0, &r1, &r2, 1)) {
	bsdsyslog(LOG_NOTICE, 
		  "%s: %s: necpcvcr_audioctrl: video off failed",
		  rpd->devname, rpd->port);
	rpd_error(rpd);
	  return -1;
      }
      else success = 1;
      break;
    case FORKLOAD:
      if (necpcvcr_getstatus(rpd) == -1) return -1;
      if (player_stat == NECPCVCR_NOTAPE) return -1;
      if (rpd->state.volume_loaded == 1) return(-1);
      forked = 1;
      if (fork() > 0) return(0);
    case LOAD:
      rpd_setframe(rpd,-1);  /* Could be anywhere */
      rpd_setspeed(rpd,0);
      if (necpcvcr_varspeed(rpd, 0) == -1) {
	bsdsyslog(LOG_NOTICE,"%s: %s: load failure",rpd->devname,
		  rpd->port);
	success = 0;
      }
      else {
	success = 1;
	rpd->state.volume_loaded = 1;
	rpd->state.ejected = 0;
      }
      break;
    case STARTLOAD:
      return 0;
    case ENDLOAD:
      return 0;
    case UNLOAD:
      if (necpcvcr_getstatus(rpd) == -1) return -1;
      if (rpd->state.volume_loaded == 0) return(0);
      if( !necpcvcr_ttyio( rpd->fd, necpcvcr_lookup("Motor-Off"),
			  0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.volume_loaded = 0;
	  rpd->volume[0] = 0;
	  success = 1;
	}
      else
	{
	  bsdsyslog(LOG_NOTICE,"%s: %s: unload failure",rpd->devname,
		    rpd->port);
	  success = 0;
	}
      rpd_setframe(rpd, -1);
      rpd_setspeed(rpd, 0);
      break;
    case EJECT:
      if (necpcvcr_getstatus(rpd) == -1) return -1;
      if (player_stat == NECPCVCR_NOTAPE) return(0);
      if( !necpcvcr_ttyio( rpd->fd, necpcvcr_lookup("Eject"),
			  0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.volume_loaded = 0;
	  rpd->volume[0] = 0;
	  success = 1;
	}
      else
	{
	  bsdsyslog(LOG_NOTICE,"%s: %s: eject failure\n",rpd->devname,
		    rpd->port);
	  success = 0;
	}
      rpd_setframe(rpd, -1);
      rpd_setspeed(rpd, 0);
      break;
    case RECORD_MODE_ON:
      if (necpcvcr_getstatus(rpd) == -1) return -1;
      if (rpd->state.volume_loaded == 0) return(0);
      if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Record-Setup"),
			 0,0,&r1, &r2, 1)) {
	bsdsyslog(LOG_NOTICE,"%s: %s: record setup failure",
		  rpd->devname, rpd->port);
	success = 0;
      }
      else success = 1;
      break;
    case RECORD_MODE_OFF:
      if (necpcvcr_getstatus(rpd) == -1) return -1;
      if (rpd->state.volume_loaded == 0) return(0);
      if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Record-Clear"),
			 0,0,&r1, &r2, 1)) {
	bsdsyslog(LOG_NOTICE,"%s: %s: manual record mode off failure",
		  rpd->devname, rpd->port);
	success = 0;
      }
      else success = 1;
      break;
    default:
      bsdsyslog(LOG_NOTICE,"%s: %s: ILLEGAL REQUEST %d\n",
		rpd->devname,rpd->port,num);
      success = 0;
      break;
    }
  if (forked) _exit(0);
  if( success ) {
    rpd_clear_error(rpd);
    return(0);
  }
  else {
    rpd_error(rpd);
    return -1;
  }
}
necpcvcr_audioctrl(rpd)
     RPD_ptr rpd;
     
{
  int loc, r1, r2;
  if (rpd->state.audio1_on && rpd->state.audio2_on) 
    loc = necpcvcr_lookup("Audio-Both");
  else if (!(rpd->state.audio1_on) && rpd->state.audio2_on) 
    loc = necpcvcr_lookup("Audio-Right");
  else if (rpd->state.audio1_on && !(rpd->state.audio2_on)) 
    loc = necpcvcr_lookup("Audio-Left");
  if (!(rpd->state.audio1_on) && !(rpd->state.audio2_on)) {
    if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Audio-Mute"),
		       1, 0, &r1, &r2, 1)) {
      bsdsyslog(LOG_NOTICE, 
		"%s: %s: necpcvcr_audioctrl: Audio mute failed",
		rpd->devname, rpd->port);
      rpd_error(rpd);
      return -1;
    }
    else {
      rpd_clear_error(rpd);
      return 0;
    }
  }
  if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Audio-Mute"),
		     0, 0, &r1, &r2, 1)) {
    bsdsyslog(LOG_NOTICE, 
	      "%s: %s: necpcvcr_audioctrl: Audio un-mute failed",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  if( necpcvcr_ttyio( rpd->fd, loc, 0, 0, &r1, &r2, 4000 ) ) {
    bsdsyslog(LOG_NOTICE, 
	      "%s: %s: necpcvcr_audioctrl: Audio set failed",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  rpd_clear_error(rpd);
  return 0;
}

/* ------------------------------------------------------------------------ */
necpcvcr_varspeed(rpd,num)
     RPD_ptr	rpd;
     int	num;
{
  int	 ret;
  int	loc;	/* index in necpcvcr_table */
  int	r1, r2;
  char *playfunc;
  
  if (necpcvcr_getstatus(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, 
	      "%s: %s: necpcvcr_varspeed: unable to get status",
	      rpd->devname, rpd->port);
    return -1;
  }
  
  if (num == 0 && player_stat == NECPCVCR_PAUSE)
    return 0;
  else if (num == 30 && player_stat == NECPCVCR_PLAY)
    return 30;
  else if ((abs(num) < 30 && (player_stat == NECPCVCR_FASTFORWARD ||
			      player_stat == NECPCVCR_JETSEARCH ||
			      player_stat == NECPCVCR_PICTURESEARCH ||
			      player_stat == NECPCVCR_REVPICTURESEARCH ||
			      player_stat == NECPCVCR_REVJETSEARCH ||
			      player_stat == NECPCVCR_REWIND)) ||
	   (num != 30 && player_stat == NECPCVCR_STOPPED)) {
    if (necpcvcr_varspeed(rpd, 30) == -1) {
      bsdsyslog(LOG_NOTICE, "%s: %s: varspeed failure: recursion failed",
		rpd->devname, rpd->port);
      return -1;
    }
  }
  
  rpd_setframe(rpd, -1);
  
  if (num == 0) {
    playfunc = "Pause";
    ret = 0;
  }
  
  if (num < 0 && num > -30) {
    return -1;
  }
  if (num < 0) {
    if (num < -60) {
      playfunc = "Rev-Jet";
      ret = -60;
    }
    else {
      playfunc = "Rev-Search";
      ret = -40;
    }
  }
  else if (num < 2 && num > 0) {
    playfunc = "Fwd-30th";
    ret = 1;
  }
  else if (num >= 2 && num < 6) {
    playfunc = "Fwd-10th";
    ret = 3;
  }
  else if (num < 30 && num >= 6) {
    playfunc = "Fwd-5th";
    ret = 6;
  }
  else if (num == 30) {
    playfunc = "Fwd-Play";
    ret = 30;
  }
  else if (num > 30) {
    if (num > 60) {
      playfunc = "Fwd-Jet";
      ret = 90;
    }
    else {
      playfunc = "Fwd-Search";
      ret = 60;
    }
  }
  
  loc = necpcvcr_lookup(playfunc);
  if( !necpcvcr_ttyio(rpd->fd, loc, 0,0, &r1, &r2, 1 ) )
    {
      rpd_setspeed(rpd, ret);
      if(ret != 0)
	rpd_setframe(rpd, -1);
      rpd_clear_error(rpd);
      return ret;
    }
  else {
    bsdsyslog(LOG_NOTICE, "%s: %s: varspeed failure: %d frames/sec",
	      rpd->devname, rpd->port, num);
    rpd_error(rpd);
    return -1;
  }
}

/* ------------------------------------------------------------------- */

necpcvcr_jog(rpd,num)
     
     RPD_ptr	rpd;
     int	num;
{
  int	r1,r2;
  int	loc;
  int	i;
  
  rpd_setframe(rpd, -1);
  rpd_setspeed(rpd, 0);
  
  if (necpcvcr_varspeed(rpd, 0) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: jog (varspeed) failed", rpd->devname,
	      rpd->port);
    return -1;
  }
  
  if( num==0 )
    return 0;
  if( num<0 ) 
    {
      num = (-num);
      loc = necpcvcr_lookup("Step-Rev");
      bsdsyslog(LOG_NOTICE, "%s: %s: can't jog backwards", rpd->devname,
		rpd->port);
      return -1;
    }
  else
    loc = necpcvcr_lookup("Step-Fwd");
  
  for( i=0; i<abs(num); i++ )
    {
      if( necpcvcr_ttyio(rpd->fd, loc, 0,0, &r1, &r2, 1) ) {
	bsdsyslog(LOG_NOTICE, "%s: %s: jog failure: %d frames",
		  rpd->devname, rpd->port, num);
	rpd_error(rpd);
	return -1;
      }
    }
  rpd_clear_error(rpd);
  rpd_setspeed(rpd, 0);
  return 0;
}

/* ------------------------------------------------------------------------ */
necpcvcr_getframe(rpd)
     
     RPD_ptr	rpd;
{
  int addr;
  int	frame;
  int	r2;
  
  /*	if (rpd_getframe(rpd) != -1) return(rpd_getframe(rpd));*/
  /*	if (necpcvcr_getstatus(rpd) == -1) {
	bsdsyslog(LOG_NOTICE, "%s: %s: necpcvcr_getframe: unable to get status",
	rpd->devname, rpd->port);
	return -1;
	} */
  if( necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("GetAddr"), 0,0,&addr,&r2,1) )
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: can't get address",
		rpd->devname,rpd->port);
      rpd_error(rpd);
      return -1;
    }
  if( necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("GetFrame"),0,0,&frame,&r2,1))
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: can't get frame address",
		rpd->devname,rpd->port);
      rpd_error(rpd);
      return -1;
    }
  
  frame += addr*30;
  frame -= 89;
  rpd_clear_error(rpd);
  rpd_setframe(rpd, frame);
  return frame;
}

/* ------------------------------------------------------------------------ */
necpcvcr_getstatus(rpd)
     RPD_ptr rpd;
{
  int	r1, r2;
  int loc;
  
  player_stat = -1;
  loc = necpcvcr_lookup("Status");
  if (necpcvcr_ttyio(rpd->fd, loc, 0,0, &r1, &r2, 1) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: can't get status",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  else {
    if (r1 == NECPCVCR_NOTAPE || r1 == NECPCVCR_STOPPED) {
      rpd->state.volume_loaded = 0;
      rpd->state.ejected = 1;
    }
    else {
      rpd->state.volume_loaded = 1;
      rpd->state.ejected = 0;
    }
  }
  player_stat = r1;
  return (0);
}

/* ------------------------------------------------------------------------ */

necpcvcr_search(rpd,frame,wait)
     RPD_ptr	rpd;
     int	frame;
     int	wait;		/* if true, hangs until it gets there 
				   or times out */
{
  int	r1, r2, destframe, curframe, success = 0;;
  
  if (necpcvcr_varspeed(rpd, 0) == -1)
    return (-1);
  
  curframe = necpcvcr_getframe(rpd);
  
  /* If already on the goal frame, don't do anything */
  /* Can only use a special search, if we're going to wait, otherwise
     use a real search, cause it can work async */
  /* If within 50 frames, just let the compensation code take care of
     the search */
  /* If between 50 and 300 frames, just do a segplay up to the right
     place. */
  /* If goal is before current, or beyond 300 frames, use a real
     search.  */
  /* 300 is a kinda arbitrary number.  A higher one might be 
     appropriate. */
  /* If any of the other kind of tries fails, use a real search */
  
  if (curframe != -1 && curframe == frame)
    return 0;
  else if (wait && curframe != -1 && 
	   curframe < frame && 
	   curframe > frame - 50) {
    if (DEBUG && 0) printf("necpcvcr_search: using jog search\n");
    success = 1;
  }
  else if (curframe != -1 &&
	   curframe <= frame - 50 && 
	   curframe > frame - 300) {
    if (DEBUG && 0) printf("necpcvcr_search: using segplay search\n");
    if (necpcvcr_segplay(rpd, 0, frame, 30, wait) == -1) {
      bsdsyslog(LOG_NOTICE, "%s: %s: segplay search failed: frame %d", 
		rpd->devname, rpd->port, frame);
      success = 0;
    }
    else success = 1;
  }
  if (success == 0) {
    /* This wait might take a really long time, like a really, 
       really long time. 999999 msec is 16 minutes */
    if( necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Search"), frame + 89, 0, 
		       &r1, &r2, wait ? 999999 : 0) ) {
      bsdsyslog(LOG_NOTICE, "%s: %s: search failed: frame %d", 
		rpd->devname, rpd->port, frame);
      rpd_error(rpd);
      return -1;
    }
  }
  rpd_setspeed(rpd, 0);
  rpd_setframe(rpd, -1);
  rpd_clear_error(rpd);
  if (!wait) return 0;
  destframe = necpcvcr_getframe(rpd);
  if (DEBUG && 0) printf("destframe = %d, goal frame = %d\n", destframe, frame);
  if (destframe > frame) {
    bsdsyslog(LOG_NOTICE, 
	      "%s: %s: can't compensate for overshoot: frame = %d, destframe = %d", 
	      rpd->devname, rpd->port, frame, destframe);
    return -1;
  }
  if (destframe != -1 && destframe > frame - 50 && destframe < frame) {
    while (destframe < frame) {
      if (necpcvcr_jog(rpd, 1) == -1) {
	bsdsyslog(LOG_NOTICE, 
		  "%s: %s: search compensation fail: frame = %d, destframe = %d", 
		  rpd->devname, rpd->port, frame, destframe);
	return -1;
      }
      destframe = necpcvcr_getframe(rpd);
      if (destframe == -1) break;
    }
  }
  rpd_setframe(rpd, necpcvcr_getframe(rpd));
  mpause(NECPCVCR_SETTLE_TIME);
  return(0);
}
/* ------------------------------------------------------------------------ */
int necpcvcr_proddev(rpd)
     RPD_ptr rpd;
{
  int r1, r2;
  
  if (necpcvcr_getstatus(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "necpcvcr_proddev: necpcvcr_getstatus failed");
    return -1;
  }
  
  if (player_stat == NECPCVCR_NOTAPE) {
    bsdsyslog(LOG_NOTICE,"%s, %s: no tape\n",
	      rpd->devname,rpd->port);
    return -1;
  }
  return 0;
}

/* ------------------------------------------------------------------------ */
necpcvcr_reset(rpd)
     RPD_ptr	rpd;
{
  int	ret = 0, r1, r2;
  
  ret = necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Power-Ctrl"), 1, 0, &r1, &r2, 1);
  if (ret == -1) {
    bsdsyslog(LOG_NOTICE, "%s, %s: failed to turn on power", 
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  ret = necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("AC-Ctrl"), 1, 0, &r1, &r2, 1);
  if (ret == -1) {
    bsdsyslog(LOG_NOTICE, "%s, %s: failed to turn on ac outlet power",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  
  ret = necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Display-Reset"), 1,0,&r1, &r2,1);
  if (ret == -1) {
    bsdsyslog(LOG_NOTICE, "%s, %s: failed to reset char display",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  
  if (necpcvcr_getstatus(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s, %s: failed to get status", 
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  if (player_stat == NECPCVCR_NOTAPE) {
    bsdsyslog(LOG_NOTICE,"%s, %s: no tape\n",
	      rpd->devname,rpd->port);
    return -1;
  }
  if (necpcvcr_varspeed(rpd, 0) == -1) {
    bsdsyslog(LOG_NOTICE, "%s, %s: failed to pause", 
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  return 0;
}

/* ------------------------------------------------------------------------ */

necpcvcr_segplay(rpd,f1,f2,speed,wait)
     
     RPD_ptr	rpd;
     int	f1,f2;
     int     speed;
     int	wait;		/* if true, hangs until done */
{
  int	fail = 0;
  int	r1,r2;
  int	len;
  int	timeout = 0;
  int	readnum;
  int	res, ret;
  char    *playfunc;
  
  if (speed != 30) {
    bsdsyslog(LOG_NOTICE, "%s: %s: can't play segment at non-30fps",
	      rpd->devname, rpd->port);
    return -1;
  }
  
  /*	if (necpcvcr_cancel(rpd) == -1) return -1;  */
  
  if (necpcvcr_varspeed(rpd, 0) == -1)
    return -1;
  
  if (f1) {
    if( necpcvcr_search(rpd,f1,1) ) {  /* must wait for now */
      bsdsyslog(LOG_NOTICE,"%s: %s: couldn't search to %d for segplay\n",
		rpd->devname,rpd->port,f1);
      return -1;
    }
    len = abs(f2-f1);
    if(!len)
      return (0); /* already at target */
  }
  else {
    if((readnum = necpcvcr_getframe(rpd)) == -1)
      return -1;
    len = abs(f2 - readnum);
  }
  
  rpd_setspeed(rpd, 30);
  rpd_setframe(rpd, -1);
  
  if (wait)
    timeout = len * 33 + 2000;
  else
    timeout = 0;
  
  if( necpcvcr_ttyio(rpd->fd,necpcvcr_lookup("Fwd-Play-To"), 
		     (f2+89)/30, (f2+89)%30, &r1,&r2, timeout) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: segplay i/o failed",rpd->devname,
	      rpd->port);
    fail = 1;
  }
  if( fail ) {
    rpd_error(rpd);
    return -1;
  }
  else {
    if (wait) {
      rpd_setframe(rpd, f2);
      rpd_setspeed(rpd, 0);
    }
    rpd_clear_error(rpd);
    return 0;
  }
}

/* ------------------------------------------------------------------------ */

necpcvcr_cancel(rpd)
     RPD_ptr rpd;
{
  int r1, r2;
  
  if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Cancel"), 0, 0, &r1, &r2, 1))
    { 
      bsdsyslog(LOG_NOTICE,"%s: %s: can't cancel",rpd->devname, 
		rpd->port);
      rpd_error(rpd);
      return(-1);
    }
  else {
    rpd_clear_error(rpd);
    return 0;
  }
}

necpcvcr_clear(rpd)
     RPD_ptr rpd;
{
  int r1, r2;
  
  if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Clear"), 0, 0, &r1, &r2, 1))
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: can't clear",rpd->devname,
		rpd->port);
      rpd_error(rpd);
      return(-1);
    }
  else {
    rpd_clear_error(rpd);
    return(0);
  }
}

/* ------------------------------------------------------------------------ */

necpcvcr_ttyio( fd, loc, parm1, parm2, ip1, ip2, wait )
     
     int	fd;
     int	loc;
     int	parm1, parm2;
     int *	ip1;
     int *	ip2;
     int	wait;		/* if true, wait for the reply */
{
  static char *funcname = "necpcvcr_ttyio";
  int	writenum, readnum;
  int	timeout = 1500;
  int	num = 0;
  static char	outpacket[128];
  
  if( loc < 0 )
    {
      bsdsyslog(LOG_NOTICE,"%s: bad table index %d\n",funcname,loc);
      return -1;
    }
  
  sprintf(outbytes, necpcvcr_table[loc].cmd_fmt, parm1, parm2 );
  sprintf(outpacket, "%s%c", outbytes, NECPCVCR_END );	
  if (DEBUG && 0) printf("To NEC_PCVCR: %s\n", outbytes);
  
  writenum = strlen(outpacket);
  ttyutil_clear(fd);		/* flush input; maybe a mistake ... */
  inbytes[0] = 0;
  
  if( ttyutil_write( fd,outpacket,writenum,0 ) == -1 ) {
    bsdsyslog(LOG_NOTICE,"%s: i/o write failed",funcname); 
    return (-1);
  }
  
  if ((num = ttyutil_read(fd, inbytes, 2, timeout)) != 2) {
    if( num==-2 ) /* timeout condition */
      bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
    else if (num == -1)
      bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
    else 
      bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		funcname,2,num);
    return -1;
  }
  if (DEBUG && 0) printf("Got %c%c\n", inbytes[0], inbytes[1]);
  if (inbytes[0] == 'C' && inbytes[1] == 'P') {
    /* Swallow newline */
    if ((num = ttyutil_read(fd, inbytes, 1, timeout)) != 1) {
      if( num==-2 ) /* timeout condition */
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
      else if (num == -1)
	bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
      else 
	bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		  funcname,1,num);
      return (-1);
    }
  }
  
  else if (inbytes[0] == 'E' && inbytes[1] == 'E') {
    /* Get errnum & newline */
    int errnum = 0;
    if ((num = ttyutil_read(fd, inbytes, 4, timeout)) != 4) {
      if( num==-2 ) /* timeout condition */
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
      else if (num == -1)
	bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
      else 
	bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		  funcname,4,num);
      return -1;
    }
    sscanf(inbytes, "%d", &errnum);
    bsdsyslog(LOG_NOTICE, "%s: error %d = %s", funcname, errnum,
	      Necpcvcr_Errors[errnum]);
    return -1;
  }
  else {
    bsdsyslog(LOG_NOTICE, "%s, unexpected response: %c%c", funcname,
	      inbytes[0], inbytes[1]);
    return -1;
  }
  
  if( !wait )
    return (0);
  
  if (wait > 2000) timeout = wait;
  else timeout = 2000;
  
  readnum = necpcvcr_table[loc].nresp;	/* readbytes + NECPCVCR_END */
  if (DEBUG && 0) printf("readnum = %d\n", readnum);
  if (readnum) {
    /* Get first two bytes, to see if error */
    if ((num = ttyutil_read(fd, inbytes, 2, timeout)) != 2) {
      if( num==-2 ) /* timeout condition */
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
      else if (num == -1)
	bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
      else 
	bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		  funcname,2,num);
      return -1;
    }
    
    if (DEBUG && 0) printf("Got %c%c\n", inbytes[0], inbytes[1]);
    
    if (inbytes[0] == 'E' && inbytes[1] == 'E') {
      /* Get errnum & newline */
      int errnum = 0;
      if ((num = ttyutil_read(fd, inbytes, 4, timeout)) != 4) {
	if( num==-2 ) /* timeout condition */
	  bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
	else if (num == -1)
	  bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
	else 
	  bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		    funcname,4,num);
	return (-1);
      }
      else {
	sscanf(inbytes, "%d", &errnum);
	bsdsyslog(LOG_NOTICE, "%s: error %d = %s", funcname, errnum,
		  Necpcvcr_Errors[errnum]);
	return -1;
      }
    }
    
    /* Read return parameter and newline */
    if( (num=ttyutil_read(fd,inbytes+2,readnum-1,timeout)) != readnum-1 ) {
      if( num==-2 ) /* timeout condition */
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
      else if (num == -1)
	bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
      else 
	bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		  funcname,readnum-1,num);
      return (-1);
    }
    
    if(inbytes[0] == necpcvcr_table[loc].resp_fmt[0] &&
       inbytes[1] == necpcvcr_table[loc].resp_fmt[1]) {
      sscanf( inbytes, necpcvcr_table[loc].resp_fmt, 
	     ip1, ip2 );
    }
    else {
      bsdsyslog(LOG_NOTICE, "%s: unexpected response: %c%c\n",
		funcname, inbytes[0], inbytes[1]);
      return -1;
    }
  }
  /* Get next three bytes, to see if error */
  if ((num = ttyutil_read(fd, inbytes, 2, timeout)) != 2) {
    if( num==-2 ) /* timeout condition */
      bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
    else if (num == -1)
      bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
    else 
      bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		funcname,2,num);
    return (-1);
  }
  
  if (DEBUG && 0) printf("Got %c%c\n", inbytes[0], inbytes[1]);
  if (inbytes[0] == 'E' && inbytes[1] == 'E') {
    /* Get errnum & newline */
    int errnum = 0;
    if ((num = ttyutil_read(fd, inbytes, 4, timeout)) != 4) {
      if( num==-2 ) /* timeout condition */
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
      else if (num == -1)
	bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
      else 
	bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		  funcname,4,num);
      return -1;
    }
    else {
      sscanf(inbytes, "%d", &errnum);
      bsdsyslog(LOG_NOTICE, "%s: error %d = %s", funcname, errnum,
		Necpcvcr_Errors[errnum]);
      return -1;
    }
  }
  else if (inbytes[0] == 'A' && inbytes[1] == 'O') {
    /* Swallow newline */
    if ((num = ttyutil_read(fd, inbytes, 1, 1500)) != 1) {
      if( num==-2 ) /* timeout condition */
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
      else if (num == -1)
	bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
      else 
	bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		  funcname,1,num);
      return -1;
    }
    else 
      return 0;
  }
  else {
    bsdsyslog(LOG_NOTICE, "%s: unexpected response: %c%c\n",
	      funcname, inbytes[0], inbytes[1]);
    return -1;
  }
}

static necpcvcr_buildfreelist(rpd)
     RPD_ptr rpd;
{
  vfree( rpd, 1, 999998 );
  return(0);
}

/*ARGSUSER*/
necpcvcr_record(rpd,f1,f2,speed,wait)
     RPD_ptr	rpd;
     int	f1,f2;
     int     speed;
     int	wait;		/* if true, hangs until done */
{
  int	len;
  
  if (necpcvcr_recordsetup(rpd, f1, f2) == -1) {
    bsdsyslog(LOG_NOTICE,
	      "%s: %s: necpcvcr_record: error in necpcvcr_recordsetup",
	      rpd->devname, rpd->port);
    return -1;
  }
  
  len = abs(f2-f1) + 1;  /* ie. if f1 = 1 & f2 = 1, then thats a 1 frame
			    recording. */
  if (necpcvcr_recordlen(rpd, len, speed, wait) == -1) {
    bsdsyslog(LOG_NOTICE,"%s: %s: necpcvcr_record: error in necpcvcr_recordlen",
	      rpd->devname, rpd->port);
    return -1;
  }
  else
    return 0;
}

/*ARGSUSED*/
necpcvcr_recordsetup(rpd,f1,f2)
     RPD_ptr	rpd;
     int	f1,f2;
{
  int	r1,r2;
  
  if(f1 < 1 || (f2 < f1) || f2 > rpd->lastframe)
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: necpcvcr_recordsetup: bad parameters\n",
		rpd->devname,rpd->port );
      return(-1);
    }
  
  if (DEBUG && 0)
    printf("necpcvcr_recordsetup: setup: frame %d to %d\n", f1,f2);
  
  rpd_setframe(rpd, -1);
  rpd_setspeed(rpd, 0);
  
  if (necpcvcr_search(rpd, f2, 1) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: necpcvcr_recordsetup: search to %d failed",
	      rpd->devname, rpd->port, f2);
    rpd_error(rpd);
    return(-1);
  }
  
  /* In Insert Record mode, the PC-VCR will stop recording when
     the real-time-counter reaches zero */
  if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Counter-Reset"), 0, 0, 
		     &r1, &r2, 1)) {
    bsdsyslog(LOG_NOTICE, "%s: %s: necpcvcr_recordsetup: counter reset failed",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  
  if (necpcvcr_search(rpd, f1-17, 1) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: necpcvcr_recordsetup: search to %d failed",
	      rpd->devname, rpd->port, f1-27);
    rpd_error(rpd);
    return(-1);
  }
  
  if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Record-Setup"), 0, 0, 
		     &r1, &r2, 1)) {
    bsdsyslog(LOG_NOTICE, "%s: %s: necpcvcr_recordsetup: record setup failed",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
}

necpcvcr_recordlen(rpd, speed, len, wait)
     RPD_ptr rpd;
     int speed, len, wait;
{
  int pauselen, r1, r2;
  
  if (speed != 30) {
    bsdsyslog(LOG_NOTICE, "%s: %s: can't record at non-30fps",
	      rpd->devname, rpd->port);
    return -1;
  }
  
  if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Record-Start"), 0, 0,
		     &r1, &r2, 1)) {
    bsdsyslog(LOG_NOTICE, "%s: %s: necpcvcr_recordlen: record start failed",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return -1;
  }
  
  /* Use the WN command to wait for change to PLAY-PS mode */
  pauselen = len * 33 + 2000;
  
  if (wait) {
    if (necpcvcr_ttyio(rpd->fd, necpcvcr_lookup("Wait-For-Pause"), 0, 0,
		       &r1, &r2, pauselen)) {
      bsdsyslog(LOG_NOTICE, 
		"%s: %s: necpcvcr_recordlen: Wait-For-Pause failed",
		rpd->devname, rpd->port);
      rpd_error(rpd);
      return -1;
    }
  }
  rpd_clear_error(rpd);
  return 0;
}

/* ------------------------------------------------------------------------ */

necpcvcr_setfuncs( disc )
     RPD_ptr	disc;
{
  disc->reset = necpcvcr_reset;
  disc->proddev = necpcvcr_proddev;
  disc->cmd = necpcvcr_cmd;
  disc->search = necpcvcr_search;
  disc->segplay = necpcvcr_segplay;
  disc->getspeed = rpd_getspeed_f;
  disc->getframe = necpcvcr_getframe;
  disc->varspeed = necpcvcr_varspeed;
  disc->jog = necpcvcr_jog;
  disc->record = necpcvcr_record;
  disc->recordsetup = necpcvcr_recordsetup;
  disc->recordlen = necpcvcr_recordlen;
  disc->buildfreelist = necpcvcr_buildfreelist;
  disc->lastframe = 999999;
  necpcvcr_FillTable();
}

