/* serpar.c  Copyright 1994 Matti Rintala */

/* $Id: serpar.c 1.6 1994/05/02 09:13:04 Matti_Rintala Exp Matti_Rintala $ */


/* This program opens a very small window and then transfer all
   incoming data from serial device to parallel. This continues until
   the window is closed or a break is sent to the program.
*/

#include <dos.h>

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <devices/serial.h>
#include <devices/parallel.h>
#include <intuition/intuition.h>

#include <proto/exec.h>
#include <proto/intuition.h>

/* Version information */
static const char *version_info = "$VER: SerPar 1.00 " __AMIGADATE__;

/* These are needed by SAS/C for the runback option */
char *__procname = "SerPar";
long __priority = -1;

/* Constants */
#define BUFSIZE 512

#define DEFSERIAL "serial.device"
#define DEFPARALLEL "parallel.device"

#define PORTNAME "SerPar"

#define SERIAL 1
#define PARALLEL 2

/* Some global variables */

struct IOExtSer *ser_req = NULL; /* Serial device request */
struct IOExtPar *par_req = NULL; /* Parallel device request */
struct MsgPort *port = NULL;	/* Port for all IO */
BOOL seropen = FALSE, paropen = FALSE;
char *sername, *parname;
struct Window *window = NULL;

int io;

BYTE buffer[BUFSIZE];		/* Buffer for data */
int buflen;

/* Function prototypes */

static void errormsg(char *s);
static int init(void);
static void cleanup(void);

static void serial_input(void);
static void parallel_output(void);

/* Usage:  serpar [input_device_name [output_device_name]]  */

void main(int argc, char **argv) {

  BOOL running = TRUE;

  /* Handle workbench arguments */
  if (argc == 0) {		/* Started from WorkBench */
    argc = _WBArgc;
    argv = _WBArgv;
  }

  /* First handle command line arguments */
  if (argc > 1) {		/* At least one argument */
    sername = argv[1];		/* Use 1st argument as input device name */
  }
  else {
    sername = DEFSERIAL;
  }

  if (argc > 2) {		/* At least two arguments */
    parname = argv[2];		/* Use 2nd argument as output device name */
  }
  else {
    parname = DEFPARALLEL;
  }
  
  /* Try to initialise all */
  if (!init()) {		/* If init failed */
    return;			/* Quit */
  }

  /* Start reading serial */
  serial_input();
  io = SERIAL;

  /* Main action loop */
  while(running) {
    struct Message *msg;

    /* Wait for message from either serial/parallel or intuition */
    Wait((1<<port->mp_SigBit) | (1<<window->UserPort->mp_SigBit));

    /* Handle all events from intuition */
    while (msg = GetMsg(window->UserPort)) {
      if (((struct IntuiMessage *)msg)->Class ==
	  IDCMP_CLOSEWINDOW) { /* Window closed */
	running = FALSE; /* Stop program */
      }
      ReplyMsg(msg);
    }
    
    /* If serial IO has been completed */
    if (io == SERIAL && CheckIO((struct IORequest *)ser_req)) {
      /* Remove the completion message */
      WaitIO((struct IORequest *)ser_req);
      parallel_output();		/* It's time to output data to parallel */
      io = PARALLEL;
    }
    /* If parallel IO has been completed */
    else if (io == PARALLEL && CheckIO((struct IORequest *)par_req)) {
      /* Remove the completion message */
      WaitIO((struct IORequest *)par_req);
      serial_input();		/* It's time to input data from serial */
      io = SERIAL;
    }
  }

  /* Terminate pending IO */
  switch (io) {
  case SERIAL:
    AbortIO((struct IORequest *)ser_req);
    WaitIO((struct IORequest *)ser_req);
    break;
  case PARALLEL:
    AbortIO((struct IORequest *)par_req);
    WaitIO((struct IORequest *)par_req);
    break;
  default:
    errormsg("No pending IO when stopped!");
  }

  cleanup();			/* Clean up everything */
}

/* errormsg prints an error message */

static void errormsg(char *msg) {

  static struct EasyStruct req = {
    sizeof (struct EasyStruct),
    NULL,
    "SerPar error",
    NULL,
    "QUIT"};

  req.es_TextFormat = msg;
  EasyRequest(NULL, &req, NULL);
}

/* init() initialises everything necessary */
static int init(void) {

  struct Screen *scr;

  /* Create IO port */
  if ((port = CreatePort(PORTNAME, 0)) == NULL) {
    errormsg("Cannot create port for IO!");
    cleanup();
    return FALSE;
  }
  
  /* Create IO request block for serial */
  if ((ser_req = (struct IOExtSer *)CreateExtIO(port, sizeof(struct IOExtSer)))
      == NULL) {
    errormsg("Cannot create serial request block!");
    cleanup();
    return FALSE;
  }

  /* Create IO request block for parallel */
  if ((par_req = (struct IOExtPar *)CreateExtIO(port, sizeof(struct IOExtPar)))
      == NULL) {
    errormsg("Cannot create parallel request block!");
    cleanup();
    return FALSE;
  }

  /* Open serial device */
  ser_req->io_SerFlags = SERF_7WIRE; /* Use RTS/CTS */
  if (seropen = OpenDevice(sername, 0, (struct IORequest *)ser_req, 0)) {
    errormsg("Cannot open input device!");
    cleanup();
    return FALSE;
  }

  /* Open parallel device */
  par_req->io_ParFlags = 0;	/* No special flags */
  if (paropen = OpenDevice(parname, 0, (struct IORequest *)par_req, 0)) {
    errormsg("Cannot open output device!");
    cleanup();
    return FALSE;
  }

  /* Now set serial device parameters */
  ser_req->io_SerFlags |= SERF_XDISABLED; /* No XON/XOFF */
  ser_req->IOSer.io_Command = SDCMD_SETPARAMS;
  DoIO((struct IORequest *)ser_req);		/* Set parameters */

  /* Parallel device doesn't need parameter changes */


  /* Open the SerPar window */
  /* First we need information on the screen */
  if ((scr = LockPubScreen(NULL)) == NULL) {
    errormsg("Cannot get default public screen!");
    cleanup();
    return FALSE;
  }
  /* The we open window with minimum height */
  window = OpenWindowTags(NULL,
			  WA_Width, 100,
			  WA_Height, scr->WBorTop +
			             scr->Font->ta_YSize + 1,
			  WA_IDCMP, IDCMP_CLOSEWINDOW,
			  WA_Flags, WFLG_CLOSEGADGET |
			            WFLG_DRAGBAR |
			            WFLG_DEPTHGADGET,
			  WA_Title, "SerPar",
			  WA_PubScreen, (Tag)scr,
			  TAG_DONE);
  /* Release the screen lock */
  UnlockPubScreen(NULL, scr);
  if (!window) {		/* Window creation failed */
    errormsg("Cannot open window!");
    cleanup();
    return FALSE;
  }


  /* If we are here, all is OK */
  return TRUE;
}


/* cleanup() cleans up everything init() initialised */
static void cleanup(void) {

  /* Close SerPar window */
  if (window) {
    CloseWindow(window);
  }
  
  /* First close opened devices */
  if (!seropen) {
    CloseDevice((struct IORequest *)ser_req);
  }
  if (!paropen) {
    CloseDevice((struct IORequest *)par_req);
  }

  /* Then dispose of request blocks */
  if (ser_req) {
    DeleteExtIO((struct IORequest *)ser_req);
  }
  if (par_req) {
    DeleteExtIO((struct IORequest *)par_req);
  }

  /* Dispose of message port */
  if (port) {
    DeletePort(port);
  }
}


/* serial_input() activates serial input */
static void serial_input(void) {
  
  /* Characters are read to buffer */
  ser_req->IOSer.io_Data = buffer;

  /* Ask for number of chars in serial */
  ser_req->IOSer.io_Command = SDCMD_QUERY;
  DoIO((struct IORequest *)ser_req);

  buflen = ser_req->IOSer.io_Actual;
  /* If there are buffered chars, read them (max BUFSIZE) */
  if (buflen > BUFSIZE) {
    buflen = BUFSIZE;
  }
  else if (buflen == 0) {	/* Wait for at least one character */
    buflen = 1;
  }

  /* Now start serial read */
  ser_req->IOSer.io_Length = buflen;
  ser_req->IOSer.io_Command = CMD_READ;
  SendIO((struct IORequest *)ser_req);    
}


/* parallel_output() activates parallel output */
static void parallel_output(void) {

  /* Simply write the buffer to parallel */
  par_req->IOPar.io_Data = buffer;
  par_req->IOPar.io_Length = buflen;
  par_req->IOPar.io_Command = CMD_WRITE;
  SendIO((struct IORequest *)par_req);
}
