/*
 * Copyright 1988, 1989, 1990, 1991 Massachusetts Institute of Technology
 */
/* pana_driver.c : routines for Panasonic videodisc players/recorders */
#include "models.h"
#include "pana.h"
#include "structs.h"
#include "hash.h"

#define MAXRETRYS 0    /* the number of times we try before giving up */

/* after the panasonic 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 PANA_SETTLE_TIME 100

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

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

static int pana_FillTable()	/* must be called first! */
{
  int	i;
  
  if( pana_htp )
    return -1;
  pana_htp = hash_create( "Panasonic-OMDR commands", 60 );
  for( i=0; i<NUM_PANA_CMDS; i++ )
    hash_install( pana_htp, pana_table[i].cmd_name, NULL, i );
  return 0;
}

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

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

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

pana_cmd(rpd,num)
     
     RPD_ptr	rpd;
     int 	num;
{
  int	success = 0, forked = 0;
  int	r1, r2;
  int   readnum;
  
  /* note that with the audio, must set the bits corresponding
     to the current state of the software */
  
  switch(num)
    {
    case INDEX_ON:
      if (rpd->state.defdisp) pana_disp(rpd, " ", 4);
      if( !pana_ttyio( rpd->fd, pana_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 (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if( !pana_ttyio( rpd->fd, pana_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;
    case A1_ON:
      if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if( !pana_ttyio( rpd->fd, pana_lookup("A1-On"),
		      0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.audio1_on = 1;
	  success = 1;
	}
      else {
	bsdsyslog(LOG_NOTICE, "%s: %s: audio 1 on failed",rpd->devname,
		  rpd->port);
	success = 0;
      }
      break;
    case A1_OFF:
      if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if( !pana_ttyio( rpd->fd, pana_lookup("A1-Off"),
		      0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.audio1_on = 0;
	  success = 1;
	}
      else {
	bsdsyslog(LOG_NOTICE, "%s: %s: audio 1 off failed",rpd->devname,
		  rpd->port);
	success = 0;
      }
      break;
    case A2_ON:
      if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if( !pana_ttyio( rpd->fd, pana_lookup("A2-On"),
		      0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.audio2_on = 1;
	  success = 1;
	}
      else {
	bsdsyslog(LOG_NOTICE, "%s: %s: audio 2 on failed",rpd->devname,
		  rpd->port);
	success = 0;
      }
      break;
    case A2_OFF:
      if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if( !pana_ttyio( rpd->fd, pana_lookup("A2-Off"),
		      0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.audio2_on = 0;
	  success = 1;
	}
      else {
	bsdsyslog(LOG_NOTICE, "%s: %s: audio 2 off failed",rpd->devname,
		  rpd->port);
	success = 0;
      }
      break;
    case SQUELCH_ON:
    case SQUELCH_OFF:
      return (1);
      break;
    case FORKLOAD:
      if (pana_getstatus(rpd) == -1) return -1;
      if (player_stat == PANA_EJECTED) return -1;
      if (rpd->state.volume_loaded == 1) return(-1);
      forked = 1;
      if (fork() > 0) return(0);
    case LOAD:
      if (!forked) {
	if (pana_getstatus(rpd) == -1) return -1;
	if (player_stat == PANA_EJECTED) return -1;
	if (rpd->state.volume_loaded == 1) 
	  return(0);
      }
/*      pana_showdef(rpd);*/
      if( !pana_ttyio( rpd->fd, pana_lookup("Load"),
		      0,0, &r1, &r2, 1 ) )
	{
	  rpd->state.volume_loaded = 1;
	  success = 1;
	}
      else
	{
	  bsdsyslog(LOG_NOTICE, "%s: %s: load failed",rpd->devname,
		    rpd->port);
	  success = 0;
	  rpd->state.volume_loaded = 0;
	}
      if (success) {
	if ( !pana_ttyio(rpd->fd, pana_lookup("Motor-On"),
			 0,0, &r1, &r2, 1 ) )
	  {
	    rpd->state.volume_loaded = 1;
	    success = 1;
	  }
	else	
	  {
	    bsdsyslog(LOG_NOTICE,"%s: %s: motor-on failure",
		      rpd->devname, rpd->port);
	    rpd->state.volume_loaded = 0;
	    success = 0;
	  }
      }
      if (success) {
	if ( !pana_ttyio(rpd->fd, pana_lookup("Motor-On-2"),
			 0,0, &r1, &r2, 1 ) )
	  {
	    rpd->state.volume_loaded = 1;
	    success = 1;
	  }
	else
	  {
	    bsdsyslog(LOG_NOTICE,"%s: %s: motor-on-2 failure",
		      rpd->devname, rpd->port);
	    rpd->state.volume_loaded = 0;
	    success = 0;
	  }
      }
      rpd_setframe(rpd,1);
      rpd_setspeed(rpd,0);
/*      pana_showdef(rpd);*/
      break;


    case STARTLOAD:
      pana_ttyio( rpd->fd, pana_lookup("Load"),
		      0,0, &r1, &r2, 0 );
      return(15000);  /* number of milliseconds */

    case ENDLOAD:
      readnum = pana_table[pana_lookup("Load")].nresp+2;
      r1 = ttyutil_read(rpd->fd,inbytes,readnum,500);
      if(r1 != readnum)
	{
	  bsdsyslog(LOG_NOTICE,"%s: %s: failure reading during ENDLOAD",
		   rpd->devname, rpd->port);
	  rpd->state.volume_loaded = 0;
	  success = 0;
	}
      else
	{
	  rpd->state.volume_loaded = 1;
	  success = 1;
	}

      if (success) {
	if ( !pana_ttyio(rpd->fd, pana_lookup("Motor-On"),
			 0,0, &r1, &r2, 1 ) )
	  {
	    rpd->state.volume_loaded = 1;
	    success = 1;
	  }
	else	
	  {
	    bsdsyslog(LOG_NOTICE,"%s: %s: motor-on failure",
		      rpd->devname, rpd->port);
	    rpd->state.volume_loaded = 0;
	    success = 0;
	  }
      }
      if (success) {
	if ( !pana_ttyio(rpd->fd, pana_lookup("Motor-On-2"),
			 0,0, &r1, &r2, 1 ) )
	  {
	    rpd->state.volume_loaded = 1;
	    success = 1;
	  }
	else
	  {
	    bsdsyslog(LOG_NOTICE,"%s: %s: motor-on-2 failure",
		      rpd->devname, rpd->port);
	    rpd->state.volume_loaded = 0;
	    success = 0;
	  }
      }
      rpd_setframe (rpd,1);
      rpd_setspeed (rpd,0);
      return(15);  /* number of seconds */

    case UNLOAD:
      if (pana_getstatus(rpd) == -1) return -1;
      if (rpd->state.volume_loaded == 0) return(0);
      if( !pana_ttyio( rpd->fd, pana_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 (pana_getstatus(rpd) == -1) return -1;
      if (player_stat == PANA_EJECTED) return(0);
      if( !pana_ttyio( rpd->fd, pana_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 (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if (pana_getstatus(rpd) == -1) return -1;
      if (rpd->state.volume_loaded == 0) return(0);
      if (pana_ttyio(rpd->fd, pana_lookup("Manual-Record-Mode"),
		     0,0,&r1, &r2, 1)) {
	bsdsyslog(LOG_NOTICE,"%s: %s: manual record mode on failure",
		  rpd->devname, rpd->port);
	success = 0;
      }
      else success = 1;
      break;
    case RECORD_MODE_OFF:
      if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);
      if (pana_getstatus(rpd) == -1) return -1;
      if (rpd->state.volume_loaded == 0) return(0);
      if (pana_ttyio(rpd->fd, pana_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;
  }
}

/* ------------------------------------------------------------------------ */
/* inspeed is the raw speed from the caller
 * play_cmd is the Panasonic play command to use
 * play_type determines whether we are doing a segment or unterminated
 */
int pana_find_speed( inspeed, play_cmd, play_speed, play_type)
     int inspeed, play_type, *play_speed;
     char **play_cmd;
{
  
  int abs_inspeed;
  abs_inspeed = abs(inspeed);
  
  if (inspeed < 30 && inspeed > 0) {
    if (play_type) 
      *play_cmd = "Fwd-Slow-To";
    else *play_cmd = "Fwd-Slow";
    *play_speed = 30/abs_inspeed;
    return(30 / (*play_speed));
  }
  else if (inspeed == 30) {
    if (play_type)
      *play_cmd = "Fwd-Play-To";
    else *play_cmd = "Fwd-Play";
    *play_speed = 0;
    return(30);
  }
  else if (inspeed > 30) {
    if (play_type)
      *play_cmd = "Fwd-Fast-To";
    else *play_cmd = "Fwd-Fast";
    *play_speed = abs_inspeed/30;
    if (*play_speed > 10) *play_speed = 10;
    return((*play_speed) * 30);
  }
  if (inspeed > -30 && inspeed < 0) {
    if (play_type)
      *play_cmd = "Rev-Slow-To";
    else *play_cmd = "Rev-Slow";
    *play_speed = 30/abs_inspeed;
    return(-30 / (*play_speed));
  }
  else if (inspeed == -30) {
    if (play_type)
      *play_cmd = "Rev-Play-To";
    else *play_cmd = "Rev-Play";
    *play_speed = 0;
    return(-30);
  }
  else if (inspeed < -30) {
    if (play_type)
      *play_cmd = "Rev-Fast-To";
    else *play_cmd = "Rev-Fast";
    *play_speed = abs_inspeed/30;
    if (*play_speed > 10) *play_speed = 10;
    return((*play_speed) * -30);
  }
  *play_cmd = "Clear";
  *play_speed = 0; /* doesn't really matter */
  return(0);
}

/* ------------------------------------------------------------------------ */
pana_varspeed(rpd,num)
     
     RPD_ptr	rpd;
     int	num;
{
  int	 ret, factor;
  int	loc;	/* index in pana_table */
  int	r1, r2;
  char *playfunc;
  
  if (pana_cancel(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: varspeed (cancel) failed", rpd->devname,
	      rpd->port);
    return -1;
  }

  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  ret = pana_find_speed(num, &playfunc, &factor, 0);
  loc = pana_lookup(playfunc);
  if( !pana_ttyio(rpd->fd, loc, factor,0, &r1, &r2, 1 ) )
    {
      rpd_setspeed(rpd, ret);
      if(ret != 0)
	rpd_setframe(rpd, -1);
      else
	rpd_setframe(rpd, pana_getframe(rpd));
      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;
  }
}

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

pana_jog(rpd,num)
     
     RPD_ptr	rpd;
     int	num;
{
  int	r1,r2;
  int	loc;
  int	i;
  
  if (pana_cancel(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: jog (cancel) failed", rpd->devname,
	      rpd->port);
    return -1;
  }

  if(rpd->speed)
    pana_varspeed(rpd, 0);

  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  if( num==0 )
    return 0;
  if( num<0 ) 
    {
      num = (-num);
      loc = pana_lookup("Step-Rev");
    }
  else
    loc = pana_lookup("Step-Fwd");
  
  for( i=0; i<abs(num); i++ )
    {
      if( pana_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);
  rpd_setframe(rpd, pana_getframe(rpd));
  return 0;
}

/* ------------------------------------------------------------------------ */
pana_getframe(rpd)
     
     RPD_ptr	rpd;
{
  int	frame;
  int	r2;
  
  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  if( pana_ttyio(rpd->fd, pana_lookup("GetFrame"), 0,0, &frame, &r2, 1) )
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: can't get frame number",
		rpd->devname,rpd->port);
      rpd_error(rpd);
      return -1;
    }
  else {
    rpd_clear_error(rpd);
    rpd_setframe(rpd, frame);
    return frame;
  }
}

/* ------------------------------------------------------------------------ */
pana_getstatus(rpd)
     RPD_ptr rpd;
{
  int	r1, r2;
  int loc, readnum;
  char resp_buf[30];
  int off = 0;
  
  /* Normal pana_ttyio expects only integers back, so do custom stuff
     for strings */
  
  loc = pana_lookup("Status");
  pana_ttyio(rpd->fd, loc, 0,0, &r1, &r2, 0);
  readnum = pana_table[loc].nresp+2;	/* STX + readbytes + ETX */

  if (ttyutil_read(rpd->fd, inbytes, readnum, 500) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: getstatus failed", rpd->devname,
	      rpd->port);
    rpd_error(rpd);
    return -1;
  }
  if( inbytes[0] == 0xa )
	off = 1;
  sscanf( inbytes+off+1, pana_table[loc].resp_fmt, resp_buf);
#ifdef FULLDEBUG
  if (DEBUG) printf("%s: %s: player status is %2s\n",rpd->devname, rpd->port,
		    resp_buf);
#endif /* FULLDEBUG */
  player_stat = 0;
  if (!strcmp(resp_buf, "EJ")) {
    player_stat = PANA_EJECTED;
    rpd->state.volume_loaded = 0;
    rpd_setframe(rpd, -1);
    rpd_setspeed(rpd, 0);
  }
  else {
    if (!strcmp(resp_buf, "LD")) {
      player_stat = PANA_UNLOADED;
      rpd->state.volume_loaded = 0;
      rpd_setframe(rpd, -1);
      rpd_setspeed(rpd, 0);
    }
    else {
      if (!strcmp(resp_buf,"SP")) {
	player_stat = PANA_UNLOADED;
	rpd->state.volume_loaded = 0;
	rpd_setframe(rpd, -1);
	rpd_setspeed(rpd, 0);
      }
      else {
	if (!strcmp(resp_buf, "SD")) {
	  player_stat = PANA_DOWN;
	  rpd->state.volume_loaded = 0;
	  rpd_setframe(rpd, -1);
	  rpd_setspeed(rpd, 0);
	}
	else {
	  player_stat = PANA_LOADED;
	  if (!strcmp(resp_buf, "RM"))
	    player_stat |= PANA_RM;
	  rpd->state.volume_loaded = 1;
	}
      }
    }
  }
  rpd_clear_error(rpd);
  return (0);
}
  
/* ------------------------------------------------------------------------ */

pana_search(rpd,frame,wait)
     RPD_ptr	rpd;
     int	frame;
     int	wait;		/* if true, hangs until it gets there 
				   or times out */
{
  int	r1, r2;
  static int finding_last = 0;
  
  if (pana_cancel(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: search (cancel) failed: frame %d", 
	      rpd->devname, rpd->port, frame);
    return -1;
  }

  if (finding_last == 0 && rpd->lastframe == 999999) {
    finding_last = 1;
    pana_findlastframe(rpd);
    finding_last = 0;
  }

  if (frame > rpd->lastframe) return(-1);

  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  if( !pana_ttyio(rpd->fd, pana_lookup("Search"), frame, 0, &r1, &r2, wait) ) {
    rpd_setspeed(rpd, 0);
    rpd_setframe(rpd, pana_getframe(rpd));
    rpd_clear_error(rpd);
    if(wait)
	mpause(PANA_SETTLE_TIME);
    return(0);
  }
  else {
    bsdsyslog(LOG_NOTICE, "%s: %s: search failed: frame %d", rpd->devname,
	      rpd->port, frame);
    rpd_error(rpd);
    return -1;
  }
}
/* ------------------------------------------------------------------------ */
int pana_proddev(rpd)
     RPD_ptr rpd;
{
  int r1, r2;
  if( pana_ttyio(rpd->fd, pana_lookup("On-Line-Mode"), 0,0, &r1,&r2, 1) ) {
    if( pana_ttyio(rpd->fd, pana_lookup("On-Line-Mode"), 0,0, &r1,&r2, 1) ) {
      bsdsyslog(LOG_NOTICE, "%s: %s: pana_proddev: on-line-mode failed: mode 12",
		rpd->devname, rpd->port);
      rpd_error(rpd);
      return -1;
    }
  }
  if (pana_getstatus(rpd) == -1) return -1;
  if (player_stat == PANA_EJECTED) {
    bsdsyslog(LOG_NOTICE,"%s, %s: lid open\n",
	      rpd->devname,rpd->port);
    return -1;
  }
  return 0;
}

/* ------------------------------------------------------------------------ */
pana_reset(rpd)
     RPD_ptr	rpd;
{
  int	ret = 0, r1, r2;
  /* First attempt usually fails when Panasonic is just powered on, so
     don't log it.  Let's give the illusion of perfection. */
  if( pana_ttyio(rpd->fd, pana_lookup("On-Line-Mode"), 0,0, &r1,&r2, 1) ) {
    if( pana_ttyio(rpd->fd, pana_lookup("On-Line-Mode"), 0,0, &r1,&r2, 1) ) {
      bsdsyslog(LOG_NOTICE, "%s: %s: on-line-mode failed: mode 12",
		rpd->devname, rpd->port);
      rpd_error(rpd);
      return -1;
    }
  }
  if (rpd->devtype == PANA_3031F || rpd->devtype == PANA_3032F)
    if (pana_ttyio(rpd->fd, pana_lookup("On-Line-Lock"), 0, 0, &r1, &r2, 1))
      bsdsyslog(LOG_INFO, "%s: %s: on-line-lock failed",rpd->devname,
		rpd->port);

  if (pana_getstatus(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: reset (getstatus) failed", rpd->devname,
	      rpd->port);
    return -1;
  }
  if (player_stat == PANA_DOWN) {
    bsdsyslog(LOG_NOTICE, "%s: %s: player is down",rpd->devname,
	      rpd->port);
    return(-1);
  }

  if (pana_clear(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: reset (clear) failed", rpd->devname,
	      rpd->port);
    return -1;
  }
  if (pana_cancel(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: reset (cancel) failed", rpd->devname,
	      rpd->port);
    return -1;
  }

  /* Get the status again, to see if it's in record on mode */
  if (pana_getstatus(rpd) == -1) {
    bsdsyslog(LOG_NOTICE, "%s: %s: reset (getstatus) failed", rpd->devname,
	      rpd->port);
    return -1;
  }
  if (player_stat == PANA_DOWN) {
    bsdsyslog(LOG_NOTICE, "%s: %s: player is down",rpd->devname,
	      rpd->port);
    return(-1);
  }

  if (player_stat & PANA_RM)
    pana_cmd(rpd, RECORD_MODE_OFF);

  if (pana_varspeed(rpd,STOP) == -1) ret = -1;

  if (rpd->state.volume_loaded == 1) {
    if( pana_cmd(rpd,A1_ON) )
      ret = -1;
    if( pana_cmd(rpd,A2_ON) )
      ret = -1;
  }
  if( pana_cmd(rpd,INDEX_OFF) )
    ret = -1;

  if( ret < 0 )
    {
      rpd->state.not_responding = 1;
      bsdsyslog(LOG_NOTICE,"%s: %s: not responding to reset\n",
		rpd->devname, rpd->port);
    }
  else
    rpd->state.not_responding = 0;
/*
  if (rpd->record && ret == 0) pana_buildfreelist(rpd);
*/
  return ret;
}


pana_showdef(rpd)
     RPD_ptr rpd;
{
  int r1;
  char string_buf[100];
  for (r1 = 0; r1 <= 9; r1++)
    pana_disp(rpd, NULL, r1);
  sprintf(string_buf,"GALATEA %.1f",(float)SERVER_REVISION / 10.0);
  pana_disp(rpd, string_buf, 4);
  rpd->state.defdisp = 1;
}

pana_disp(rpd, str_buf, lin_num)
     RPD_ptr rpd;
     char *str_buf;
     int lin_num;
{
  int r1, r2, writenum, loc, readnum, num;
  char scratch_buf[100], outpacket[128];
  if (!str_buf) {
    if (!pana_ttyio(rpd->fd, pana_lookup("Display-Off"), lin_num, 0,
		    &r1, &r2, 1)) {
      if (lin_num == 4) rpd->state.defdisp = 0;
      rpd_clear_error(rpd);
      return(0);
    }
    else {
      bsdsyslog(LOG_NOTICE, "%s: %s: display off of line %d failed",
		rpd->devname, rpd->port, lin_num);
      rpd_error(rpd);
      return(-1);
    }
  }
  /* pana_ttyio can't deal with strings */
  loc = pana_lookup("Display-Write");
  sprintf(scratch_buf, pana_table[loc].cmd_fmt,
	  lin_num, str_buf);
  sprintf(outpacket, "%c%s%c", PANA_BEGIN, scratch_buf, PANA_END );	
  
  writenum = strlen(outpacket);
  ttyutil_clear(rpd->fd);	/* flush input; maybe a mistake ... */
  rpd->state.defdisp = 1;
  if( ttyutil_write( rpd->fd,outpacket,writenum,0 ) == -1 ) {
    bsdsyslog(LOG_NOTICE,"%s: %s: i/o write failed",rpd->devname,rpd->port);
    rpd_error(rpd);
    /* need_to_rebuild_list = 1; */
    return (-1);
  }
  readnum = pana_table[loc].nresp+2;
  if( (num=ttyutil_read(rpd->fd,inbytes,readnum,500)) == -1 ) {
    bsdsyslog(LOG_NOTICE,"%s: %s: i/o read failed",rpd->devname, rpd->port);
    rpd_error(rpd);
    /* need_to_rebuild_list = 1; */
    return (-1);
  }
  if( num != readnum )
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: expected %d bytes, got %d\n",
		rpd->devname,rpd->port,readnum,num);
      rpd_error(rpd);
      /* need_to_rebuild_list = 1; */
      return -1;
    }
    if( readnum > 2 )	/* have a response */
    {
      int off = 0;

      if( inbytes[0] == 0xa )
	off = 1;

      if( inbytes[off+0] != PANA_BEGIN || 
	 (inbytes[off+1] == 'E' && inbytes[off+2] != 'J')) {
	   if (DEBUG) {
	     sscanf(inbytes+off+1,"E%d",&loc);
	     bsdsyslog(LOG_NOTICE,
		       "%s: %s: display-write: error reponse: %d",
		       rpd->devname, rpd->port,loc);
	   }
	   rpd_error(rpd);
	   return -1;
	 }
    }
  if (lin_num == 4) rpd->state.defdisp = 0;
  rpd_clear_error(rpd);
  return(0);
}

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

pana_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;
  int	readnum;
  int   factor;
  int	res, ret;
  char    *playfunc;
  
  if (pana_cancel(rpd) == -1) return -1; 

  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  if (rpd->lastframe == 999999) pana_findlastframe(rpd);
  if (f1 > rpd->lastframe || f2 > rpd->lastframe) return(-1);

  if (f1) {
    if( pana_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 = pana_getframe(rpd)) == -1)
	return -1;
    len = abs(f2 - readnum);
  }

  /* if disc is in motion, and CURRENT_FRAME is specified, 
     may have to stop it momentarily to determine the
     proper direction to go in (by getting an exact current frame) */

  if(f1 == CURRENT_FRAME && rpd->speed)
    {      
      /* disc is already moving; see if we're inside the 'danger zone'; 
	 if so, stop disc and get current frame before changing rate;
	 otherwise, we might have passed the target and be going in
	 the wrong direction; note that rpd->frame has just been set
	 by DoPlaySeg() in dofuncs.c, if CURRENT_FRAME is specified */

      if( abs(f2 - rpd->frame) <= MAX_GETFRAME_ERROR(abs(rpd->speed)) )
	{
	  pana_varspeed(rpd, 0);
	  rpd_setframe(rpd, rpd_getframe(rpd));
	  /* reverse direction if need to */
	  if(f2 < rpd->frame && speed > 0)
	    speed = -1 * speed;
	  else if(f2 > rpd->frame && speed < 0)
	    speed = abs(speed);
	  else if(f2 == rpd->frame)
	    return (0);
	}
    }
  else if(f1 == CURRENT_FRAME && !rpd->speed && f2 == rpd->frame)
    {
      /* we're not moving and the target is where we already are */
      return 0;
    }

  ret = pana_find_speed(speed, &playfunc, &factor, 1);
  
  if (factor == 0) factor = f2;  /* coerce arguments for regular play */

  if( !pana_ttyio(rpd->fd,pana_lookup(playfunc), factor, f2, &r1,&r2, 0) )
    {
      rpd_setspeed(rpd, ret);
      rpd_setframe(rpd, -1);

      if( !wait )
	return 0;
      else
	{
	  int off = 0;

	  readnum = 4;	/* STX + ETX + 2 chars */
	  timeout = 1000*len/ret;
	  res = ttyutil_read(rpd->fd,inbytes,readnum,timeout+1000);
	  /*	
	    printf("segplay response:\n");
	    for( i=0; i<readnum; i++ )
	    printf("[%02x] ",inbytes[i]);
	    printf("\n");
	    */
	  if( res == -2 )
	    {
	      bsdsyslog(LOG_NOTICE,"%s: %s: segplay timed out",
			rpd->devname, rpd->port );
	      fail = 1;
	    }
	  else if( res == -1 )
	    {
	      bsdsyslog(LOG_NOTICE,"%s: %s: segplay read error",
			rpd->devname, rpd->port );
	      fail = 1;
	    }
	  else if( res != readnum )
	    {
	      bsdsyslog(LOG_NOTICE,"%s: %s: segplay read %d chars, not %d",
			rpd->devname, rpd->port,res,readnum );
	      fail = 1;
	    }
	  if( inbytes[0] == 0xa )
		off = 1;
	  if( inbytes[off+0] != PANA_BEGIN || inbytes[off+1] == 'E' )
	    {
	      bsdsyslog(LOG_NOTICE,"%s: %s: segplay bad response",
			rpd->devname, rpd->port);
	      fail = 1;
	    }
	}
    }
  else {
    bsdsyslog(LOG_NOTICE, "%s: %s: segplay i/o failed",rpd->devname,
	      rpd->port);
    fail = 1;
  }
  
  if( fail ) {
    rpd_error(rpd);
    return -1;
  }
  else {
    rpd_setframe(rpd, f2);
    rpd_setspeed(rpd, 0);
    rpd_clear_error(rpd);
    return 0;
  }
}
  
/* ------------------------------------------------------------------------ */

pana_cancel(rpd)
     RPD_ptr rpd;
{
  int r1, r2;
  
  if (pana_ttyio(rpd->fd, pana_lookup("Cancel"), 0, 0, &r1, &r2, 1))
    { 
      bsdsyslog(LOG_NOTICE,"%s: %s: can't cancel mode",rpd->devname, 
		rpd->port);
      rpd_error(rpd);
      /* need_to_rebuild_list = 1; */
      return(-1);
    }
  else {
    rpd_clear_error(rpd);
    return(0);
  }
}

pana_clear(rpd)
     RPD_ptr rpd;
{
  int r1, r2;
  
  if (pana_ttyio(rpd->fd, pana_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);
  }
}

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

pana_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 = "pana_ttyio";
  static int	retry = 0;
  int	writenum, readnum;
  int	timeout = 500;
  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, pana_table[loc].cmd_fmt, parm1, parm2 );
  sprintf(outpacket, "%c%s%c", PANA_BEGIN, outbytes, PANA_END );	
  
  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);
  }
  
  readnum = pana_table[loc].nresp+2;	/* STX + readbytes + ETX */
  if( readnum == 0 || !wait )
    return (0);
  
  if( loc == pana_lookup("Load") || loc == pana_lookup("Eject") ||
     loc == pana_lookup("Motor-On") || loc == pana_lookup("Motor-On-2")||
     loc == pana_lookup("Motor-Off"))
    timeout = 25000;
  else if( loc == pana_lookup("Search") )
    timeout = 2000;
  else if( loc == pana_lookup("Find-Free-Video") )
    timeout = 0;	/* dangerous; waits forever */
  else if( loc == pana_lookup("Manual-Record-Mode") )
    timeout = 0;	/* dangerous; waits forever */
  else if( loc == pana_lookup("Manual-Record-Number") )
    timeout = 0;	/* dangerous; waits forever */
  else if( loc == pana_lookup("Record-Remain") )
    timeout = 0;	/* dangerous; waits forever */
  else if( loc == pana_lookup("Record-Clear") )
    timeout = 5000;
  else
    timeout = 500;
  
  if( (num=ttyutil_read(fd,inbytes,readnum,timeout)) == -1 ) {
    bsdsyslog(LOG_NOTICE,"%s: i/o read failed",funcname);
    return (-1);
  }
  if( num==-2 )	/* timeout condition */
    {
      if( DEBUG )
	{
	  if( num == -2 )
	    printf("%s: NO RESPONSE, try %d\n",
		   funcname,retry+1);
	  else
	    printf("%s: ERROR RESPONSE, try %d\n",
		   funcname,retry+1);
	}
      if( retry++ < MAXRETRYS )
	{
	  int rv;
	  
	  rv = pana_ttyio( fd, loc, parm1, parm2, ip1, ip2, wait );
	  retry = 0;
	  return (rv);
	}
      else
	{
	  retry = 0;
	  bsdsyslog(LOG_NOTICE,"%s: i/o timeout\n",funcname);
	  return (-1);
	}
    }
  
  if( num != readnum )
    {
      bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		funcname,readnum,num);
      return -1;
    }
  
  if( readnum > 2 )	/* have a response */
    {
      int errnum = 0;
      int off = 0;
      int i;

      /* have no idea why, but the RT, in on-line-mode #12,
	 is getting back 0xa (10) before the rest of the response */

      if(inbytes[0] == 0xa)
	off = 1;
      if( inbytes[off+0] != PANA_BEGIN || 
	 (inbytes[off+1] == 'E' && inbytes[off+2] != 'J')) {
	if(DEBUG)
	  {
	    printf("* pana error response:\n:");
	    for(i=0;i<readnum;i++)
	      printf("\tinbytes[%d] = %d 0x%lx '%c'\n",
		     i,inbytes[i],inbytes[i],inbytes[i]);
	  }
	sscanf(inbytes+off+1,"E%d",&errnum);
	bsdsyslog(LOG_NOTICE,
		  "pana_ttyio: error reponse: %d",errnum);
	return -1;
      }
      sscanf( inbytes+off+1, pana_table[loc].resp_fmt, ip1, ip2 );
    }
  
  return 0;
}

static pana_buildfreelist(rpd)
     RPD_ptr rpd;
{
  int r1,r2, ret;
  int start, nframes, at, prestate;
  if( !rpd )
    return -1;
  
  if (pana_getstatus(rpd) == -1) return -1;
  prestate = rpd->state.volume_loaded;
  if (prestate == 0) 
    if (pana_cmd(rpd, LOAD) == -1) return(-1);
  
  if (rpd->lastframe == 999999) pana_findlastframe(rpd);

#ifdef OBSOLETE  
  if (pana_search(rpd,1,1) == -1) {
    bsdsyslog(LOG_NOTICE,"%s: %s: pana_buildfreelist: search to 1 failed\n",
	      rpd->devname, rpd->port);
    return(-1);
  }

  if( pana_getframe(rpd) != 1 )
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: can't search to 1\n",rpd->devname,
		rpd->port);
      return -1;
    }

  if((ret = pana_ttyio(rpd->fd, pana_lookup("Manual-Record-Mode"), 
		0,0, &r1,&r2, 1)) )
    {
      if( ret == -1 )
	{
	  bsdsyslog(LOG_NOTICE,"%s: %s: error putting player into record mode",
		    rpd->devname, rpd->port);
	  rpd_error(rpd);
	  return -1;
	}
      else
	{
	  pana_ttyio(rpd->fd,pana_lookup("Record-Clear"), 
		     0,0, &r1,&r2, 1);
	  pana_ttyio(rpd->fd,pana_lookup("Clear"), 
		     0,0, &r1,&r2, 1);
	  pana_ttyio(rpd->fd,pana_lookup("Record-Clear"), 
		     0,0, &r1,&r2, 1);
	}
    }
#endif /* OBSOLETE */
  
  if (pana_search(rpd,1,1) == -1) {
    bsdsyslog(LOG_NOTICE,"%s: %s: pana_buildfreelist: search to 1 failed\n",
	      rpd->devname, rpd->port);
    return(-1);
  }
  
  if( pana_getframe(rpd) != 1 )
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: can't search to 1\n",rpd->devname,
		rpd->port);
      return -1;
    }

  while(1)
    {
      if( pana_ttyio(rpd->fd, pana_lookup("Find-Free-Video"), 
		     0,0, &r1,&r2, 1) )
	{
	  bsdsyslog(LOG_NOTICE,"%s: %s: error finding next free frame",
		    rpd->devname, rpd->port);
	  rpd_error(rpd);
	  return -1;
	}
      start = r1;

      if( start >= rpd->lastframe)
	{
	  break;
	}
      
      at = pana_getframe(rpd);
      nframes = at-start;
      
      if (DEBUG) printf("%s:%s: found %d free frames at %d\n",
			rpd->devname, rpd->port, nframes, start);
      vfree( rpd, start, nframes );

    }
  if (prestate == 0)
    if (pana_cmd(rpd, UNLOAD) == -1) return(-1);
  rpd_clear_error(rpd);
  return(0);
}

/*ARGSUSED*/
pana_record(rpd,f1,f2,speed,wait)
     RPD_ptr	rpd;
     int	f1,f2;
     int     speed;
     int	wait;		/* if true, hangs until done */
{
  int	len;

  if (pana_recordsetup(rpd, f1, f2) == -1) {
    bsdsyslog(LOG_NOTICE,"%s: %s: pana_record: error in pana_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 (pana_recordlen(rpd, len, speed, wait) == -1) {
    bsdsyslog(LOG_NOTICE,"%s: %s: pana_record: error in pana_recordlen",
	      rpd->devname, rpd->port);
    return -1;
  }
  else
    return 0;
}

#ifdef OBSOLETE
/*ARGSUSED*/
pana_record(rpd,f1,f2,speed,wait)
     RPD_ptr	rpd;
     int	f1,f2;
     int     speed;
     int	wait;		/* if true, hangs until done */
{
  int	len, res, fail, off;

  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  if (rpd->lastframe == 999999) pana_findlastframe(rpd);
  if (f1 > rpd->lastframe || f2 > rpd->lastframe) return(-1);

  len = abs(f2-f1) + 1;  /* ie. if f1 = 1 & f2 = 1, then thats a 1 frame
			    recording. */
  if(f1 < 1 || (f2 < f1) || f2 > rpd->lastframe)
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: pana_record: bad parameters\n",
	      rpd->devname,rpd->port );
      return(-1);
    }
  
  if (DEBUG)
    printf("pana_record: recording: frame %d to %d; wait is %d\n",f1,f2,wait);

  if (pana_search(rpd, f1, 1) == -1) return(-1);
  if (pana_ttyio(rpd->fd, pana_lookup("Manual-Record-Number"),len,0,&r1,
		 &r2, 1)) {
    bsdsyslog(LOG_NOTICE,"%s: %s: pana_record: error checking for free frames",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return(-1);
  }

  if (!pana_ttyio(rpd->fd, pana_lookup("Record-Start"), len, 0, &r1, &r2, 0)) {
    if (!wait) return(0);
    else {
      readnum = 4;
      timeout = 1000*len/30;
      res = ttyutil_read(rpd->fd, inbytes, readnum, timeout+1000);
      if (res == -2) {
	bsdsyslog(LOG_NOTICE, "%s: %s: pana_record: timed out",rpd->devname,
		  rpd->port);
	fail = 1;
      }
      else if (res == -1) {
	bsdsyslog(LOG_NOTICE, "%s: %s: pana_record: read error", rpd->devname,
		  rpd->port);
	fail = 1;
      }
      else if (res != readnum ) {
	bsdsyslog(LOG_NOTICE, "%s, %s: pana_record: read %d chars, not %d",
		  rpd->devname, rpd->port, res, readnum);
	fail = 1;
      }
      off = 0;
      if(inbytes[0] == 0xa)
	off = 1;
      if (inbytes[off+0] != PANA_BEGIN || inbytes[off+1] == 'E') {
	  bsdsyslog(LOG_NOTICE, "%s: %s: pana_record: bad response",
		    rpd->devname, rpd->port);
	  fail = 1;
	}
    }
  }
  else fail = 1;
  if( fail ) {
    rpd_error(rpd);
    return -1;
  }
  else {
    rpd_clear_error(rpd);
    return 0;
  }
}
#endif OBSOLETE


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

int pana_recordsetup(rpd, f1, f2)
     RPD_ptr rpd;
     int f1, f2;
{
  int len, r1, r2;

  if (rpd->state.defdisp) pana_disp(rpd, NULL, 4);

  if (rpd->lastframe == 999999) pana_findlastframe(rpd);
  if (f1 > rpd->lastframe || f2 > rpd->lastframe) return(-1);

  len = abs(f2-f1) + 1;  /* ie. if f1 = 1 & f2 = 1, then thats a 1 frame
			    recording. */
  if(f1 < 1 || (f2 < f1) || f2 > rpd->lastframe)
    {
      bsdsyslog(LOG_NOTICE,"%s: %s: pana_record: bad parameters\n",
	      rpd->devname,rpd->port );
      return(-1);
    }
  
  if (DEBUG)
    printf("pana_recordsetup: setting up for frames %d to %d\n",f1,f2);

  if (pana_search(rpd, f1, 1) == -1) return(-1);
  if (pana_ttyio(rpd->fd, pana_lookup("Manual-Record-Number"),len,0,&r1,
		 &r2, 1)) {
    bsdsyslog(LOG_NOTICE,"%s: %s: pana_recordsetup: error checking for free frames",
	      rpd->devname, rpd->port);
    rpd_error(rpd);
    return(-1);
  }
  else return(0);
}
  

/* ------------------------------------------------------------------------ */
int pana_recordlen(rpd, len, speed, wait)
     RPD_ptr rpd;
     int len, speed, wait;
{
  int res, timeout, fail = 0, off, r1, r2, readnum;
  
  if (!pana_ttyio(rpd->fd, pana_lookup("Record-Start"), len, 0, &r1, &r2, 0)) {
    if (!wait) return(0);
    else {
      readnum = 4;
      timeout = 1000*len/30;
      res = ttyutil_read(rpd->fd, inbytes, readnum, timeout+1000);
      if (res == -2) {
	bsdsyslog(LOG_NOTICE, "%s: %s: pana_recordlen: timed out",rpd->devname,
		  rpd->port);
	fail = 1;
      }
      else if (res == -1) {
	bsdsyslog(LOG_NOTICE, "%s: %s: pana_recordlen: read error", rpd->devname,
		  rpd->port);
	fail = 1;
      }
      else if (res != readnum ) {
	bsdsyslog(LOG_NOTICE, "%s, %s: pana_recordlen: read %d chars, not %d",
		  rpd->devname, rpd->port, res, readnum);
	fail = 1;
      }
      off = 0;
      if(inbytes[0] == 0xa)
	off = 1;
      if (inbytes[off+0] != PANA_BEGIN || inbytes[off+1] == 'E') {
	bsdsyslog(LOG_NOTICE, "%s: %s: pana_recordlen: bad response",
		  rpd->devname, rpd->port);
	fail = 1;
      }
    }
  }
  else fail = 1;
  if( fail ) {
    rpd_error(rpd);
    return -1;
  }
  else {
    rpd_clear_error(rpd);
    return 0;
  }
}
/* ------------------------------------------------------------------------ */

pana_setfuncs( disc )
     
     RPD_ptr	disc;
{
  disc->reset = pana_reset;
  disc->proddev = pana_proddev;
  disc->cmd = pana_cmd;
  disc->search = pana_search;
  disc->segplay = pana_segplay;
  disc->getspeed = rpd_getspeed_f;
  disc->getframe = pana_getframe;
  disc->varspeed = pana_varspeed;
  disc->jog = pana_jog;
  if (disc->devtype == PANA_2026F || disc->devtype == PANA_3031F) {
    disc->record = pana_record;
    disc->recordsetup = pana_recordsetup;
    disc->recordlen = pana_recordlen;
    disc->buildfreelist = pana_buildfreelist;
  }
  else {
    disc->record = NULL;
    disc->recordsetup = NULL;
    disc->recordlen = NULL;
    disc->buildfreelist = NULL;
  }
  if (disc->devtype == PANA_3031F || disc->devtype == PANA_3032F)
    disc->lastframe = 999999;
  else disc->lastframe = 24000;
  pana_FillTable();
}

int pana_findlastframe(rpd)
     RPD_ptr rpd;
{
  if (rpd->lastframe != 999999) return 0;
  if (pana_getframe(rpd) > 36000) {
    rpd->lastframe = 54000;
    return 0;
  }
  else {
    if (pana_search(rpd, 36001, 1) == -1) {
      rpd->lastframe = 36000;
      return 0;
    }
    else {
      rpd->lastframe = 54000;
      return 0;
    }
  }
}


