/*
 * Protocol:
 *
 * Host sends command length (1 byte) + command
 * A command consistf of:
 *  bcpl filename + 0 byte + 
 *  command specific data (0-6 bytes) + command type (1 byte)
 * Command specific data:
 * - CREATE/SETATTR: attributes (NYI)
 * - RENAME: strlen of the second bcpl string
 * - READ/WRITE  : 4 byte offset, 2 byte length
 *
 * Answer:
 * - status of operation (0 for success)
 * - Thereafter, if return is 0 and op is
 *   - GETATTR, a 12 byte attribute structure is sent
 *   - READ,    the data is sent.
 *   - READDIR, a list of cstrings are sent, the last is a length 0 entry
 *   - GETDEVS, a list of (cstring + 14 bytes), the last ...
 */

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef _IBMR2
# include <sys/select.h>
#endif
#include <termios.h>	/* for tcdrain() */
#include "nfs_prot.h"
#include "mp.h"

/* Since the psion is little endian, we have to do some conversion */
/* Note: this should work for little endian hosts too */
extern int errno;
extern char *sys_errlist[];


void
short2pstr(l, s)
  unsigned int l;
  unsigned char *s;
{
  s[0] = l & 0xff;
  s[1] = (l & 0xff00) >> 8;
}

void
long2pstr(l, s)
  unsigned int l;
  unsigned char *s;
{
  s[0] = l & 0xff;
  s[1] = (l & 0xff00) >> 8;
  s[2] = (l & 0xff0000) >> 16;
  s[3] = (l & 0xff000000) >> 24;
}

unsigned int
pstr2long(s)
  unsigned char *s;
{
  int l = s[0];

  l |= s[1] <<  8; l |= s[2] << 16;
  return l | (s[3] << 24);
}


int
senddata(p, len)
  char *p;
  int len;
{
  int ret;
  fd_set writefd;
  struct timeval to;

  psion_alive = fd_is_still_alive(psionfd,1);
  if(!psion_alive)
    {
      if(debug) printf("Senddata: Not alive...\n");
      return 1;
    }

  if(debug > 1) printf("\tSending: %#x, %d\n", (unsigned int)p, len);
  while((ret = write(psionfd, p, len)) < len)
    {
      if(debug > 1)
	fprintf(stderr, "\t\twrite(%d, %#x, %d) = %d\n", psionfd, (unsigned int)p, len, ret);
      if(ret > 0)
	{
	  p += ret; len -= ret; continue;
	}
      if(ret < 0)
        {
	  perror("Write"); return 1;
	}
      /* Wait a second if write returns 0
         (AIX Feature: nonblocking, even if its not required) */
      FD_ZERO(&writefd); FD_SET(psionfd, &writefd);
      to.tv_sec = 2; to.tv_usec = 0; 
      if((ret = select(psionfd+1, 0, &writefd, 0, &to)) <= 0)
	{
	  if(debug)
	    fprintf(stderr, "writeselect returned %d\n", ret);
	  return 1; 
	}
    }
  if(debug > 1)
    fprintf(stderr, "\t\twrite(%d, %#x, %d) = %d\n", psionfd, (unsigned int)p, len, ret);

#ifndef linux
  /*
   * tcdrain is broken on my linux version, strace reports 
   * ioctl(3, TCSBRK, 0x1) = 0
   */
  if(tcdrain(psionfd))
    perror("tcdrain");
#endif
  return 0;
}

/*
 * How to make buggy hardware flow control work.
 * If the psion is loosing data at receiving, wait half a second, then
 * send some 0's to replace the missing data.
 * We should really use a crc algorithm.
 */
int
getbyte()
{
  static unsigned char readbuf[256];
  static int idx = 0, len = 0;
  int err;

  if(idx == len)
    {
      fd_set readfd;
      struct timeval to;

      errno = 0;
      to.tv_sec = 2; to.tv_usec = 0; 
      FD_ZERO(&readfd); FD_SET(psionfd, &readfd);
      err = select(psionfd+1, &readfd, 0, 0, &to);
      if(err <= 0)
        {
	  fprintf(stderr, "select: %s\n",
	     err ?  sys_errlist[errno] : "timeout");
	  return EOF;
	}
      idx = 0;
      if(debug > 1) { printf("\t\tFilling read buffer..."); fflush(stdout); }
      len = read(psionfd, readbuf, 256);
      if(debug > 1) printf("got %d bytes\n", len);
      if(len <= 0)
        {
	  perror("read"); len = 0;
	  return EOF;
	}
    }
  return readbuf[idx++];
}

int
sendcmd(cmd, fname, rest, restlen)
  char *fname, *rest;
  int cmd, restlen;
{
  int l;
  unsigned char str[300];

  str[1] = l = strlen(fname);
  bcopy(fname, str+2, l+1);
  l += 3;
  if(restlen)
    {
      bcopy(rest, str+l, restlen);
      l += restlen;
    }
  str[l++] = cmd;
  if(l-2 > 255)
    {
      printf("Warning, length too long (%d)\n", l);
      return 1;
    }
  *str = l-1;

  if(debug > 1)
    printf("\tSending %d ('%s'+%d) %d bytes\n", cmd, fname, restlen, l);
  return senddata(str, l);
}

int
getcount(str, num)
  unsigned char *str; int num;
{
  int c;

  if(debug > 1) {printf("\tGoing to read %d bytes...",num);fflush(stdout);}
  while(num-- > 0)
    if((c = getbyte()) == EOF || c == TIMEOUT)
      {
	if(debug) printf("Bad connection to psion... (%d)\n", c);
	return -53;
      }
    else
      *str++ = c;
  if(debug > 1) printf("\tRead ok\n");
  return 0;
}

int
getstr(str)
  unsigned char *str;
{
  int c;
  unsigned char *s = str;

  if(debug > 1) {printf("\tGoing to read string...");fflush(stdout);}
  for(;;)
    {
      if((c = getbyte()) == EOF || c == TIMEOUT)
        {
	  if(debug) printf("Bad connection to psion... received EOF\n");
	  return 1;
	}
      *s++ = c;
      if(c == 0)
        {
	  if(debug > 1) printf("\tgot string (%s)\n", str);
	  return 0;
	}
    }
}

int
sendop(cmd, fname, rest, restlen)
  char *fname, *rest;
  int cmd, restlen;
{
  int i;

  if(!(i = sendcmd(cmd, fname, rest, restlen)) && fd_is_still_alive(psionfd,1))
    i = getbyte();
  if(debug > 1) printf("\tReceived (op %d) %d\n", cmd, i);
  if(i == EOF || i == TIMEOUT) i = -58;
  return i;
}

