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

/* vdp50_driver.c : routines for DEC_VDP-50 videodisc player */

#include "structs.h"
#include "vdp50.h"
#define MAXRETRYS 0		/* the number of times we try before giving up */

/* after the vdp50 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 VDP50_SETTLE_TIME 200

int		vdp50_debug = 0;
static	UBYTE	inbytes[16];
static	UBYTE	outbytes[16];
static 	int	not_loaded = 0;
static  int     homeframe = 1;

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

static int
vdp50_IntToFrame(n,f)

	int	n;
	UBYTE *	f;
{	
int 	i; 
char 	s[10];

	f[0] = f[1] = f[2] = 0;
	sprintf(s,"%05d",(n));
	for( i=0; i<5; i++ )
	    f[(i+1)/2] |= (s[i]-'0') << (i%2?4:0);
	f[0] |= 0xf0;
	return 0;
}

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

static int
vdp50_FrameToInt(f)

	UBYTE *	f;
{	
int i; 
char s[10];

	s[5] = 0;
	for( i=0; i<5; i++ )
	    s[i] = ((f[(i+1)/2] >> (i%2?4:0)) & 0x0f) + '0';
	return atoi(s);
}

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

static int
vdp50_IntToJumpArg(n,j)

	int	n;
	UBYTE *	j;
{
int 	i; 
char 	s[10];

	j[0] = j[1] = 0;
	sprintf(s,"%03d",(n));
	for( i=0; i<3; i++ )
	    j[(i+1)/2] |= ((s[i]-'0') << (i%2?4:0));
	return 0;
}

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

#ifdef OBSOLETE
static int
vdp50_RevisionToInts(r,hn,fn)
	
	UBYTE *	r;
	int *	hn;
	int *	fn;
{	
	*hn = r[1] * 0xff + r[0];
	*fn = r[3] * 0xff + r[2];
	return 0;
}
#endif /* OBSOLETE */
/* ------------------------------------------------------------------------ */

static int
vdp50_SetAudioState( rpd, b )

	RPD_ptr	rpd;
	UBYTE *	b;
{
	*b = 0;
	if( rpd->state.audio1_on )
		BITS_ON( *b, VDP50_CH1_ON );
	else
		BITS_OFF( *b, VDP50_CH1_ON );

	if( rpd->state.audio2_on )
		BITS_ON( *b, VDP50_CH2_ON );
	else
		BITS_OFF( *b, VDP50_CH2_ON );
	return 0;
}

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

vdp50_cmd(rpd,num)
     RPD_ptr	rpd;
     int 	num;
{
  static char *funcname = "vdp50_cmd";
  int	success = 0;
  UBYTE	args[10];	/* assumes these are all 0 */
  VDP50_Frame	bcdf;
  VDP50_DiscID	id;
  int	frame;
  int forked = 0;
  
  /* note that with the audio, must set the bits corresponding
     to the current state of the software */
  
  switch(num)
    {
    case INDEX_ON:
      BITS_ON( args[0], VDP50_INDEX_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_INDEX_PKT, args, 1 ) )
	{
	  rpd->state.indexon = 1;
	  success = 1;
	}
      break;
    case INDEX_OFF:
      BITS_OFF( args[0], VDP50_INDEX_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_INDEX_PKT, args, 1 ) )
	{
	  rpd->state.indexon = 0;
	  success = 1;
	}
      break;
    case INDEX_TOGGLE:
      if (rpd->state.indexon)
	BITS_OFF( args[0], VDP50_INDEX_ON );
      else BITS_ON( args[0], VDP50_INDEX_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_INDEX_PKT, args, 1 ) )
	{
	  rpd->state.indexon = !rpd->state.indexon;
	  success = 1;
	}
      break;
    case A1_ON:
      vdp50_SetAudioState( rpd, &args[0] );
      BITS_ON( args[0], VDP50_CH1_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_AUDIO_PKT, args, 1 ) )
	{
	  rpd->state.audio1_on = 1;
	  success = 1;
	}
      break;
    case A1_OFF:
      vdp50_SetAudioState( rpd, &args[0] );
      BITS_OFF( args[0], VDP50_CH1_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_AUDIO_PKT, args, 1 ) )
	{
	  rpd->state.audio1_on = 0;
	  success = 1;
	}
      break;
    case A2_ON:
      vdp50_SetAudioState( rpd, &args[0] );
      BITS_ON( args[0], VDP50_CH2_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_AUDIO_PKT, args, 1 ) )
	{
	  rpd->state.audio2_on = 1;
	  success = 1;
	}
      break;
    case A2_OFF:
      vdp50_SetAudioState( rpd, &args[0] );
      BITS_OFF( args[0], VDP50_CH2_ON );
      if( !vdp50_ttyio( rpd->fd, VDP50_AUDIO_PKT, args, 1 ) )
	{
	  rpd->state.audio2_on = 0;
	  success = 1;
	}
      break;
    case SQUELCH_ON:
    case SQUELCH_OFF:
      return (1);
      break;
    case STARTLOAD:
      vdp50_ttyio( rpd->fd, VDP50_LOAD_PKT, args, 0 );
      return(VDP50_LOAD_TIMEOUT);  /* returns milliseconds */
      break;
    case ENDLOAD:
      if (ttyutil_read(rpd->fd, inbytes,
		       vdp50_table[VDP50_LOAD_PKT].resp_bytes+2,
		       500) == -1) {
	bsdsyslog(LOG_NOTICE,"%s: i/o timeout",funcname);
	rpd_error(rpd);
	return(-1);
      }
      rpd->state.volume_loaded = 1;
      bcopy( &inbytes[1], bcdf, 3 );
      if( !vdp50_LoadIsNormal(bcdf) )
	{
	  homeframe = frame = vdp50_FrameToInt( bcdf );
	  bsdsyslog(LOG_NOTICE,"%s, %s: abnormal load: at frame %d\n",
		    rpd->devname, rpd->port,frame );
	}
      if( !vdp50_ttyio( rpd->fd, VDP50_ID_PKT, args, 1 ) )
	{
	  bcopy( &inbytes[1], id, 3 );
	  sprintf(rpd->volume,"#%02x%02x%02x",
		  id[0],id[1],id[2] );
	}
      rpd_setspeed(rpd,0);
      rpd_setframe(rpd, homeframe);
      success = 1;
      return(2);  /* returns seconds */
      break;
    case FORKLOAD:
      if (vdp50_getstatus(rpd) == -1) return -1;
      if (not_loaded == 0) return(-1);
      forked = 1;
      if (fork() > 0) return(0);
    case LOAD:
      if( !vdp50_ttyio( rpd->fd, VDP50_LOAD_PKT, args, 1 ) )
	{
	  rpd->state.volume_loaded = 1;
	  bcopy( &inbytes[1], bcdf, 3 );
	  if( !vdp50_LoadIsNormal(bcdf) )
	    {
	      homeframe = frame = vdp50_FrameToInt( bcdf );
	      bsdsyslog(LOG_NOTICE,"%s, %s: abnormal load: at frame %d\n",
			rpd->devname, rpd->port,frame );
	    }
	  if( !vdp50_ttyio( rpd->fd, VDP50_ID_PKT, args, 1 ) )
	    {
	      bcopy( &inbytes[1], id, 3 );
	      sprintf(rpd->volume,"#%02x%02x%02x",
		      id[0],id[1],id[2] );
	      /* bsdsyslog(LOG_NOTICE,"%s: vdp50 loaded volume id '%s'\n", rpd->devname,rpd->volume);*/
	    }
	  sleep(2);
	  rpd_setspeed (rpd,0);
	  rpd_setframe (rpd, homeframe);
	  success = 1;
	}
      else
	{
	  bsdsyslog(LOG_NOTICE,"%s, %s: load failure\n",rpd->devname, rpd->port);
	  rpd->state.volume_loaded = 0;
	}
      if( vdp50_cmd(rpd,INDEX_OFF) )
	success = 0;
      if( vdp50_varspeed(rpd,0) )
	success = 0;
      if( vdp50_cmd(rpd,A1_ON) )
	success = 0;
      if( vdp50_cmd(rpd,A2_ON) )
	success = 0; 
      break;
    case UNLOAD:
      if( !vdp50_ttyio( rpd->fd, VDP50_UNLOAD_PKT, args, 1 ))
	{
	  if( inbytes[1] == VDP50_UNLOAD_OK )
	    {
	      rpd->state.volume_loaded = 0;
	      rpd->volume[0] = 0;
	      success = 1;
	    }
	  else
	    {
	      if( inbytes[1] & VDP50_LASER_ON )
		bsdsyslog(LOG_NOTICE,"%s, %s: laser still on after unload\n",
			  rpd->devname,rpd->port);
	      if( inbytes[1] & VDP50_MOTOR_ON )
		bsdsyslog(LOG_NOTICE,"%s, %s: motor still on after unload\n",
			  rpd->devname, rpd->port);
	    }
	  rpd_setspeed (rpd, 0);
	  rpd_setframe (rpd, -1);
	}
      else
	{
	  bsdsyslog(LOG_NOTICE,"%s, %s: unload failure\n",rpd->devname,rpd->port);
	}
      break;
    case EJECT:
      if( vdp50_ttyio( rpd->fd, VDP50_EJECT_PKT, args, 1 ))
	{
	  bsdsyslog(LOG_NOTICE,"%s, %s: eject failure\n",rpd->devname, rpd->port);
	}
      rpd_setspeed (rpd, 0);
      rpd_setframe (rpd, -1);
      break;
      
    default:
      bsdsyslog(LOG_NOTICE,"%s, %s: %s: illegal request %d\n",
		rpd->devname, rpd->port,funcname,num);
      break;
    }
  if (forked) _exit(0);
  if( success ) {
    rpd_clear_error(rpd);
    return(0);
  }
  else {
    rpd_error(rpd);
    /* need_to_rebuild_list = 1; */
    return -1;
  }
}

/* ------------------------------------------------------------------------ */
static int
vdp50_FindSpeed( desired, range, speed, dir)
     int desired, *range, *speed, *dir;
{
  if (desired == 0) return(0);  /* should be a stop, not varspeed */
  if (desired < 0 ) *dir = VDP50_REV;
  else *dir = VDP50_FWD;
  desired = abs(desired);

  if (desired == 30) {
    *range = VDP50_NORMAL_PLAY;
    *speed = 1;
    return(30);
  }
  if (desired < 30) {
    *range = VDP50_SLOW_PLAY;
    *speed = 30/desired;
    return(30/(*speed));
  }

  /* according to manual, max fast speed is 20*30 = 600 fps */

  if (desired > 30 && desired < 601) {
    *range = VDP50_FAST_PLAY;
    *speed = desired/30;
    if (*speed > VDP50_MAX_FAST_SPEED) *speed = VDP50_MAX_FAST_SPEED;
    return(*speed*30);
  }
  if (desired > 600) {
    *range = VDP50_SCAN_PLAY;
    /*
        the manual claims that scan is "about 75 times the normal play speed";
        so 30*75 = 2250 fps
    */
    *speed = 75;
    return(2250);
  }

  /* added to keep Saber from complaining */
  return (0);
}


static int
vdp50_SendSpeed( rpd, range, speed )

	RPD_ptr	rpd;
	int	range;
	int	speed;
{
  VDP50_Speed	s;
  
  vdp50_IntToSpeedArg(speed,s);
  vdp50_SetSpeedRange(s,range);
  if( vdp50_ttyio( rpd->fd, VDP50_SPEED_PKT, s, 1 ) ) {
    /* need_to_rebuild_list = 1; */
    rpd_error(rpd);
    return -1;
  }
  rpd_clear_error(rpd);
  return 0;
}

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

vdp50_varspeed(rpd,num)

	RPD_ptr	rpd;
	int	num;
{
  static char *funcname = "vdp50_varspeed";
  int	success = 0;
  UBYTE	args[10];
  int	range;
  int	speed;
  int	dir;
  int     realspeed;
  
  realspeed = vdp50_FindSpeed(num,&range,&speed,&dir);
  
  if (!realspeed) {
    if (vdp50_ttyio(rpd->fd,VDP50_HALT_PKT,args,1))
      {
	bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't stop\n",
		  rpd->devname,rpd->port,funcname);
	/* need_to_rebuild_list = 1; */
	rpd_error(rpd);
	return(-1);
      }
    rpd_setframe (rpd, -1);
    vdp50_getframe (rpd);
    rpd_setspeed (rpd, 0);
    rpd_clear_error(rpd);
    return(0);
  }
  
  if (range == VDP50_SCAN_PLAY) {
    vdp50_SetScanDirection(args[0], dir);
    if (vdp50_ttyio(rpd->fd, VDP50_SCAN_PKT, args, 1)) {
      bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't scan\n",
		rpd->devname, rpd->port, funcname);
      rpd_error(rpd);
      /* need_to_rebuild_list = 1; */
    }
    else 
      success = 1;
  }
  else {
    if( vdp50_SendSpeed(rpd,range,speed) )
      {
	/* need_to_rebuild_list = 1; */
	bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't set speed\n",
		  rpd->devname, rpd->port,funcname);
      }
    else
      {
	vdp50_SetPlayDirection(args[0],dir);
	if( vdp50_ttyio(rpd->fd,VDP50_PLAY_PKT,args,1) ) {
	  bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't play\n",
		    rpd->devname, rpd->port,funcname);
	  /* need_to_rebuild_list = 1; */
	  rpd_error(rpd);
	}
	else {
	  success = 1;
	}
      }	
  }
    
  rpd_setframe (rpd, -1);
  
  if( success ) {
    rpd_clear_error(rpd);
    if (dir == VDP50_REV) {
      rpd_setspeed (rpd, -1*realspeed);
      return(-1*realspeed);
    } else {
      rpd_setspeed (rpd, realspeed);
      return(realspeed);
    }
  } else {
    return COULDNT_CHANGE_SPEED;
  }
}

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

vdp50_jog(rpd,num)

	RPD_ptr	rpd;
	int	num;
{
  int	success = 0;
  UBYTE	args[10];
  int	i;
  int	dir;

  if(rpd->speed)
    vdp50_varspeed(rpd, 0);
  
  if( num<0 ) 
    {
      num = (-num);
      dir = VDP50_REV;
    }
  else
    dir = VDP50_FWD;
  
  vdp50_SetStepDirection( args[0], dir );
  
  for( i=0; i<abs(num); i++ )
    {
      if( !vdp50_ttyio(rpd->fd, VDP50_STEP_PKT, args, 1) )
	{
	  if( !(inbytes[1] && VDP50_FRAME_NOT_FOUND) )
	    success = 1;	
	}
    }
  
  if( success ) {
    if (rpd_getframe(rpd) < 0)
      rpd_setframe (rpd, vdp50_getframe(rpd));
    else 
      if (dir == VDP50_FWD) 
	rpd_setframe (rpd, rpd_getframe(rpd) + 1);
      else 
	rpd_setframe (rpd, rpd_getframe(rpd) - 1);
    rpd_setspeed (rpd,0);
    rpd_clear_error(rpd);
    return(0);
  } else {
    /* need_to_rebuild_list = 1; */
    rpd_error(rpd);
    return -1;
  }
  
}

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

vdp50_getframe(rpd)
     RPD_ptr	rpd;
{
  static char *funcname = "vdp50_getframe";
  int	success = 0;
  UBYTE	args[10];
  VDP50_Status	st;
  VDP50_Frame	bcdf;
  int		frame;
  
  if (rpd_getspeed (rpd) == 0 && rpd_getframe(rpd) >= 0) 
    return (rpd_getframe(rpd));
  
  if( vdp50_ttyio(rpd->fd, VDP50_STATUS_PKT, args, 1) )
    bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't get player status\n",
	      rpd->devname, rpd->port, funcname);
  else
    {
      bcopy( &inbytes[1], st, 2 );
      bcopy( &inbytes[3], bcdf, 3 );
      frame = vdp50_FrameToInt( bcdf );
      success = 1;
    }
  
  if( success ) {
    if (rpd_getspeed(rpd) == 0)
      rpd_setframe (rpd, frame);
    else
      rpd_setframe(rpd, -1);
    rpd_clear_error(rpd);
    return(frame);
  } else {
    /* need_to_rebuild_list = 1; */
    rpd_error(rpd);
    rpd_setframe(rpd, -1);
    return -1;
  }
}

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

vdp50_getstatus(rpd)

	RPD_ptr	rpd;
{
  static char *funcname = "vdp50_getstatus";
  int	success = 0;
  UBYTE	args[10];
  VDP50_Status	st;
  VDP50_Frame	bcdf;

  if( vdp50_ttyio(rpd->fd, VDP50_STATUS_PKT, args, 1) )
    bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't get player status\n",
	      rpd->devname,rpd->port,funcname);
  else
    {
      bcopy( &inbytes[1], st, 2 );
      bcopy( &inbytes[3], bcdf, 3 );
      vdp50_FrameToInt( bcdf );
      success = 1;
      
      if( !(st[0] & VDP50_ST_LOADED) ) {
	not_loaded = 1;
        /* okay; so we know the disc isn't spinning; now we need
           to find out if the lid is open; there is no status bit,
           so we send a command which will fail, and then check the
           error return bits */
	
        args[0] = VDP50_FWD;
        /* don't wait for the response; return immediately */
        vdp50_ttyio(rpd->fd, VDP50_STEP_PKT, args, 0);
	
        /* now pick up the error return */
	
        if(vdp50_ttyio(rpd->fd,VDP50_ERROR_PKT,args,1))
          {
            bsdsyslog(LOG_NOTICE, "%s, %s: %s: failed to get error packet",
                      rpd->devname, rpd->port, funcname);
	    rpd_error(rpd);
	    return(-1);
          }
        else
          {
	    rpd_clear_error(rpd);
            if( inbytes[3] & VDP50_ERR_LID )
              {
#ifdef FULLDEBUG
                bsdsyslog(LOG_NOTICE, "%s, %s: %s: lid is open",
			  rpd->devname, rpd->port, funcname);
#endif /* FULLDEBUG */
                rpd->state.ejected = 1;
              }
	    else {
#ifdef FULLDEBUG
	      bsdsyslog(LOG_NOTICE, "%s, %s: %s: lid is closed",
			rpd->devname, rpd->port, funcname);
#endif /* FULLDEBUG */
	      rpd->state.ejected = 0;
	    }
          }
      }
      else {
	not_loaded = 0;
	rpd->state.ejected = 0;
      }
      /*		
	printf("Status of rpd: <%s>\n",rpd->devname);
	printf("------------------\n");
	
	if( st[0] & VDP50_ST_LOADED );
	else
	printf("disc NOT loaded!!\n");
	
	if( st[0] & VDP50_ST_PLAYING )
	printf("disc playing\n");
	else
	printf("disc NOT playing\n");
	
	if( st[0] & VDP50_ST_SEARCHING )
	printf("disc is searching!!\n");
	
	if( st[0] & VDP50_ST_CH1_ON )
	printf("channel 1 on\n");
	else
	printf("channel 1 off\n");
	
	if( st[0] & VDP50_ST_INDEX_ON )
	printf("index is ON\n");
	
	if( st[0] & VDP50_ST_CH2_ON )
	printf("channel 2 on\n");
	else
	printf("channel 2 off\n");
	
	if( st[1] & VDP50_ST_GENLOCKED );
	else
	printf("player NOT GENLOCKED!\n");
	
	success = 1;
	*/
    }
  if( success ) {
    rpd_clear_error(rpd);
    return(0);
  }
  else {
    /* need_to_rebuild_list = 1; */
    rpd_error(rpd);
    return -1;
  }
}	

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

vdp50_proddev(rpd)
     RPD_ptr rpd;
{
  if (vdp50_getstatus(rpd) == -1) return -1;
  if (rpd->state.ejected) {
    bsdsyslog(LOG_NOTICE,"%s, %s: lid open\n",
	      rpd->devname,rpd->port);
    return -1;
  }
  return 0;
}
/* ------------------------------------------------------------------------ */

vdp50_search(rpd,frame,wait)
	RPD_ptr	rpd;
	int	frame;
	int	wait;		/* if true, hangs until it gets there 
					or times out */
{
  static char *funcname = "vdp50_search";
  int	success = 0;
  UBYTE	args[10];
  VDP50_Frame	bcdf;
  VDP50_JumpArg	jump;
  int	at, last;
  int	dir;
  int	nframes;
  int	readnum;
  int	res;
  
  /* to do the quick jump, see if we're within the
     legal limits */
  
  if (frame < homeframe) frame = homeframe;
  at = vdp50_getframe(rpd);
  /* Inhibit quick jump if getframe failed, but try search anyway */
  if (at == -1) {
    bsdsyslog(LOG_NOTICE, "%s, %s: %s: failed to get frame",
		  rpd->devname, rpd->port, funcname);
  }
  else {
    nframes = frame-at;
  
    if( nframes > 0 )
      dir = VDP50_FWD;
    else if( nframes < 0 )
      dir = VDP50_REV;
    else
      return 0;	/* already there */

  /* RMS - 11/19/87 changed this to bypass any quick jumps;
     1% to 2% of quick jumps miss by one frame;
     this error rate is deemed not worth the speed gain */
  /* danapple - 10/2/88 renable quick jumps but check to make
     sure we're at the right frame.  We still get the vertical
     interval jump speed, although the software has a slight
     delay.  If quick jump fails, do regular search  */
  
    rpd_setframe(rpd, -1);  /* have to make sure that we'll query the
			       player at the next vdp50_getframe(), otherwise
			       the quick jump will always fail */
    nframes = abs(nframes);
  
    if( at > 0 && nframes <= VDP50_MAX_SINGLE_JUMP )
      {
	vdp50_IntToJumpArg( nframes, jump );
	vdp50_SetJumpDirection( jump, dir );
	bcopy( jump, args, 2 );
	/* we don't need to wait for video output to
	   stabilize after a quick jump; it's a 'clean'
	   vertical-interval edit */
	if( !vdp50_ttyio( rpd->fd, VDP50_JUMP_PKT, args, 1) &&
	   frame == vdp50_getframe(rpd))
	  success = 1;
	else {
	  bsdsyslog(LOG_INFO,
		    "%s, %s: %s: failed quick jump",
		    rpd->devname, rpd->port, funcname);
	}
      }
  }
  
  if (!success)
    /* do a normal search */
    {
      vdp50_IntToFrame( frame, bcdf );
      bcopy( bcdf, args, 3 );
      if( !vdp50_ttyio(rpd->fd, VDP50_SEARCH_PKT, args, wait) )
	{
	  if( !wait )
	    return 0;
	  
	  readnum = vdp50_table[VDP50_ACK_PKT].resp_bytes + 2 +
	    vdp50_table[VDP50_SEARCH_PKT].resp_bytes + 2;
	  res = ttyutil_read( rpd->fd, inbytes, readnum,
			     VDP50_SEARCH_TIMEOUT+1000 );
	  if( res == readnum )
	    {	
	      if( inbytes[0] != 
		 vdp50_table[VDP50_ACK_PKT].resp_code
		 ||
		 inbytes[1] != 
		 vdp50_table[VDP50_ACK_PKT].resp_end )
		{
		  bsdsyslog(LOG_NOTICE,"%s, %s: %s: ACK packet mis-match\n",
			    rpd->devname, rpd->port,funcname);
		  rpd_error(rpd);
		  rpd_setframe (rpd, -1);
		  return -1;
		}
	      if( inbytes[2] != 
		 vdp50_table[VDP50_SEARCH_PKT].resp_code
		 ||
		 inbytes[readnum-1] != 
		 vdp50_table[VDP50_SEARCH_PKT].resp_end )
		{
		  bsdsyslog(LOG_NOTICE,"%s, %s: %s: SEARCH packet mis-match\n",
			    rpd->devname,rpd->port,funcname);
		  rpd_error(rpd);
		  rpd_setframe (rpd, -1);
		  return -1;
		}
	      bcopy( &inbytes[4], bcdf, 3 );
	      last = vdp50_FrameToInt(bcdf);
	      if( inbytes[3] & VDP50_FRAME_NOT_FOUND )
		{	/* must search back to last valid frame */
		  if( !vdp50_search( rpd, last, 1 ) ) {
		    bsdsyslog(LOG_NOTICE,
			      "%s, %s: %s: frame not found: now at %d, not %d\n",
			      rpd->devname,rpd->port,funcname,at,frame);
		  }
		  else
		    {
		      bsdsyslog(LOG_NOTICE,"%s, %s: %s: search failed, resetting rpd\n",
				rpd->devname,rpd->port,funcname);
		      if( vdp50_reset(rpd) )
			bsdsyslog(LOG_NOTICE,"%s, %s: %s: RESET FAILED!\n",
				  rpd->devname,rpd->port,funcname);
		    }
		}
	      else
		success = 1;
	      /* wait for video output to stabilize */
	      mpause(VDP50_SETTLE_TIME);
	    }
	}
      else {
	bsdsyslog(LOG_NOTICE,"%s, %s: %s: search error\n",
		  rpd->devname,rpd->port,funcname);
      }
    }
  
  if( success ) {
    rpd_setframe (rpd, frame);
    rpd_setspeed (rpd, 0);
    rpd_clear_error(rpd);
    return(0);
  } else {
    rpd_error(rpd);
    rpd_setframe (rpd, -1);
    return -1;
  }
}

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

vdp50_reset(rpd)

	RPD_ptr	rpd;
{
  int	ret = 0;
  
  if( vdp50_getstatus(rpd) )
    {
      rpd->state.volume_loaded = 0;
      rpd->state.not_responding = 1;
      return -1;
    }
  if( not_loaded )
    {
      rpd->state.volume_loaded = 0;
    }
  else {
    rpd->state.volume_loaded = 1;
    if( vdp50_cmd(rpd,INDEX_OFF) )
      ret = -1;
    if( vdp50_varspeed(rpd,STOP) )
      ret = -1;
    if( vdp50_cmd(rpd,A1_ON) )
      ret = -1;
    if( vdp50_cmd(rpd,A2_ON) )
      ret = -1;       
  }

  rpd_setspeed(rpd, 0);
  rpd_setframe(rpd, -1);

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

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

vdp50_segplay(rpd,f1,f2,speed,wait)
	RPD_ptr	rpd;
	int	f1,f2;
        int     speed;
	int	wait;		/* if true, hangs until done */
{
  static char *funcname = "vdp50_segplay";
  int success = 0;
  UBYTE	args[10];
  VDP50_Frame endf;
  int timeout;
  int res;
  int len;
  int readnum;
  int i;
  int realspeed;
  int range;
  int dir;
  
  /* must 'clip' to valid range; cannot do a SCAN inside a segplay */

  if(speed < -600)
    speed = -600;
  else if(speed > 600)
    speed = 600;

  realspeed = vdp50_FindSpeed(speed,&range,&speed,&dir);
  
  if (!realspeed || (f1 && (f1 == f2))) {
    if (vdp50_ttyio(rpd->fd,VDP50_HALT_PKT,args,1))
      {
	bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't stop\n",
		  rpd->devname,rpd->port,funcname);
	/* need_to_rebuild_list = 1; */
	rpd_error(rpd);
	return(-1);
      }
    if(f1) {
      if( vdp50_search(rpd,f1,1) )	/* must wait for now */ {
	bsdsyslog(LOG_NOTICE,"%s, %s: %s: couldn't search to %d\n",
		  rpd->devname,rpd->port,funcname,f1);
	/* need_to_rebuild_list = 1; */
	return (-1);
      }
      rpd_setframe(rpd, f1);
    }
    else
      rpd_setframe(rpd, vdp50_getframe(rpd));
    rpd_setspeed(rpd, 0);
    rpd_clear_error(rpd);
    return(0);
  }
  
  if( vdp50_SendSpeed(rpd,range,speed) )
    {
      bsdsyslog(LOG_NOTICE,"%s, %s: %s: can't set speed\n",
		rpd->devname,rpd->port,funcname);
      return -1;
    }
  
  if (f1) {
    if( vdp50_search(rpd,f1,1) )	/* must wait for now */ {
      bsdsyslog(LOG_NOTICE,"%s, %s: %s: couldn't search to %d\n",
		rpd->devname,rpd->port,funcname,f1);
      /* need_to_rebuild_list = 1;*/
      return -1;
    }
    len = abs(f1-f2);
    if(!len)
      return 0;
  }
  else {
    if((readnum = vdp50_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);
     actually, it turns out the vdp50 is an intelligent life form,
     and figures out all by itself what direction to go in; but
     we have to maintain the correct speed (direction) in the
     rpd cache, so we still have to go through this stuff
     */

  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)) )
	{
	  vdp50_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;
    }
  
  vdp50_IntToFrame( f2, endf );
  bcopy( endf, args, 3 );
  for( i=0; i<16; i++ )
    inbytes[i] = 0;
	
  if( !vdp50_ttyio(rpd->fd,VDP50_PLAY_U_PKT,args,wait) )
    {
      rpd_setframe (rpd, -1);
      if (dir == VDP50_REV)
	rpd_setspeed (rpd, -1*realspeed);
      else
	rpd_setspeed (rpd, realspeed);

      if( !wait )
	return 0;
      else
	{	/* since it's a play pkt, we must read the input
		   (see vdp50_ttyio() below) */
	  
	  readnum = vdp50_table[VDP50_ACK_PKT].resp_bytes + 2 +
	    vdp50_table[VDP50_PLAY_U_PKT].resp_bytes + 2;
	  timeout = vdp50_TimeToPlay(len,range,speed);
	  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: %s: timed out\n",
			rpd->devname, rpd->port, funcname );
	    }
	  else if( res == -1 ) {
	    bsdsyslog(LOG_NOTICE,"%s, %s: %s: read error\n",
		      rpd->devname, rpd->port, funcname );
	  }
	  else if( res != readnum ) {
	    bsdsyslog(LOG_NOTICE,"%s, %s: %s: read %d chars, not %d\n",
		      rpd->devname, rpd->port, funcname,res,readnum );
	  }
	  else
	    {
	      if( inbytes[0] != 
		 vdp50_table[VDP50_ACK_PKT].resp_code
		 ||
		 inbytes[1] != 
		 vdp50_table[VDP50_ACK_PKT].resp_end )
		{
		  bsdsyslog(LOG_NOTICE,"%s, %s: %s: ACK packet mis-match\n",
			    rpd->devname, rpd->port,funcname);
		  rpd_error(rpd);
		  return -1;
		}
	      if ( inbytes[2] != 
		  vdp50_table[VDP50_PLAY_U_PKT].resp_code
		  ||
		  inbytes[readnum-1] != 
		  vdp50_table[VDP50_PLAY_U_PKT].resp_end )
		{
		  bsdsyslog(LOG_NOTICE,"%s, %s: %s: PLAY_U packet mis-match\n",
			    rpd->devname, rpd->port, funcname);
		  rpd_error(rpd);
		  return -1;
		}
	      /* check for frame-found */
	      if( inbytes[3] & VDP50_FRAME_NOT_FOUND )
		bsdsyslog(LOG_NOTICE,"%s, %s: %s: frame not found\n",
			  rpd->devname,rpd->port,funcname );
	      else
		success = 1;
	    }
	}
    }
  if( success ) {
    rpd_setframe (rpd, f2);
    rpd_setspeed (rpd, 0);
    rpd_clear_error(rpd);
    return(0);
  } else {
    rpd_setframe (rpd, -1);
    rpd_error(rpd);
    return -1;
  }
}

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

vdp50_ttyio( fd, pkt, args, wait )

	int	fd;
	int	pkt;
	UBYTE *	args;		/* the argument bytes for the packet */
	int	wait;		/* if true, wait for the reply */
{
static char *funcname = "vdp50_ttyio";
static int	retry = 0;
int	writenum, readnum;
int	timeout;
int	num = 0;

/*	ttyutil_debug = vdp50_debug;*/

	if( vdp50_table[pkt].cmd_code != VDP50_NO_COMMAND )
	{

		outbytes[0] = vdp50_table[pkt].cmd_code;
		num = vdp50_table[pkt].arg_bytes;
		if( num )
			bcopy( args, &outbytes[1], num );
		outbytes[1+num] = vdp50_table[pkt].cmd_end;

		writenum = num+2;		/* cmd + args + end */

		ttyutil_clear(fd);		/* flush input; maybe a mistake ... */

		if( ttyutil_write( fd,outbytes,writenum,0 ) == -1 )
			return (-1);
	}

	readnum = vdp50_table[pkt].resp_bytes+2;	/* resp + args + end */
	if( readnum == 0 || !wait )
		return (0);
	
	/* caller must pick up the final input on these first four packets;
		this is due to uncertain length of
		timeout (too complicated to do here) */

	if( 	pkt == VDP50_SEARCH_PKT ||
		pkt == VDP50_PLAY_U_PKT ||
		pkt == VDP50_PLAY_US_PKT ||
		pkt == VDP50_SCAN_U_PKT )
	{
		return 0;		/* caller must pick up all data */
	}
	else if( pkt == VDP50_LOAD_PKT || pkt == VDP50_UNLOAD_PKT
			|| pkt == VDP50_EJECT_PKT )
		timeout = VDP50_LOAD_TIMEOUT;
	
	else				/* normal timeout (1/2 second) */
		timeout = 1000;

	if( (num=ttyutil_read(fd,inbytes,readnum,timeout)) == -1 ) {
	  bsdsyslog(LOG_NOTICE, "%s: i/o timeout", 
		    funcname);
	  /* need_to_rebuild_list = 1; */
	  return (-1);
	}

	if( num==-2 )	/* timeout condition */
	{
		if( vdp50_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 = vdp50_ttyio( fd, pkt, args, wait );
			retry = 0;
			return (rv);
		}
		else
		{
			retry = 0;
			/* need_to_rebuild_list = 1; */
			bsdsyslog(LOG_NOTICE,"%s: i/o timeout\n",
				  funcname);
			return (-1);
		      }
	}

	if( num != readnum )
	{
	  /* need_to_rebuild_list = 1; */
	  bsdsyslog(LOG_NOTICE,"%s: expected %d bytes, got %d\n",
		    funcname,readnum,num);
	  return -1;
	}

	if( inbytes[0] != vdp50_table[pkt].resp_code )
	{
	  /* need_to_rebuild_list = 1; */
	  bsdsyslog(LOG_NOTICE,"%s: response code does not match command\n",
		    funcname );
	  return -1;
	}

	if( inbytes[num-1] != vdp50_table[pkt].resp_end )
	{
	  /* need_to_rebuild_list = 1; */
	  bsdsyslog(LOG_NOTICE,
		    "%s: response end code does not match command\n",
		    funcname);
	  return -1;
	}

	return 0;
}

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

vdp50_setfuncs( disc )

	RPD_ptr	disc;
{
  disc->proddev = vdp50_proddev;
  disc->reset = vdp50_reset;
  disc->cmd = vdp50_cmd;
  disc->search = vdp50_search;
  disc->segplay = vdp50_segplay;
  disc->getframe = vdp50_getframe;
  disc->getspeed = rpd_getspeed_f;
  disc->varspeed = vdp50_varspeed;
  disc->jog = vdp50_jog;
  disc->buildfreelist = NULL;
  disc->record = NULL;
  disc->lastframe = 54000;
}

