/* $Header: pd:zvmrcs/serial.c,v 1.1 1993/04/07 18:47:36 rvillari Exp $ */
/*
  Serial Input/Output routines
*/

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <exec/memory.h>
#include <stdio.h>
#include "serial_proto.h"
#include "debug_proto.h"

struct SerialData* sld = 0;

/* PROMISE:  Returns 0 on error, opened serial port on success */
struct SerialData* OpenSerial(char* serialName, int unitNumber, ULONG serialFlags) {
  struct SerialData* serialData = (struct SerialData*) AllocMem(sizeof(struct SerialData), MEMF_CLEAR | MEMF_PUBLIC);
  struct IOExtSer* io;

  if (!serialData) {
    mainError("Couldn't allocate memory for SerialData");
    goto bad;
  }

  /* create a message port for when the serial device returns stuff to us */
  serialData->SerPort = CreatePort(0, 0);
  if (serialData->SerPort == 0) {
    mainError("Couldn't create a port");
    goto bad;
  }

  /* Create the request block for passing info to and from the serial device. */
  serialData->SerReq = (struct IOExtSer *)CreateExtIO(serialData->SerPort,sizeof(struct IOExtSer));
  if (serialData->SerReq == 0) {
    mainError("Couldn't create ExtIO");
    goto bad;
  }

  /* creating an alias! */
  io = serialData->SerReq;
  io->io_SerFlags = serialFlags;

  /* OPEN the serial.device */
  if (OpenDevice (serialName, unitNumber, (struct IORequest*)serialData->SerReq, 0) != 0) {
    debugString(serialName, "Couldn't open device");
    debugInt(unitNumber, "Unit");
    goto bad;
  }

#ifdef DEBUG
  debugInt(serialData->SerReq->io_ReadLen);
  debugInt(serialData->SerReq->io_Baud);
  debugInt(serialData->SerReq->io_CtlChar);
  debugInt(serialData->SerReq->io_WriteLen);
  debugInt(serialData->SerReq->io_StopBits);
  debugInt(serialData->SerReq->io_RBufLen);
  debugInt(serialData->SerReq->io_SerFlags);
  debugInt(serialData->SerReq->io_TermArray.TermArray0);
  debugInt(serialData->SerReq->io_TermArray.TermArray1);
#endif

  /* try to change the serial parameters */
  io->io_SerFlags |= serialFlags;
  io->IOSer.io_Command = SDCMD_SETPARAMS;
  if (DoIO ((struct IORequest*)io)) goto bad;

  return serialData;

 bad:
  if (serialData) {
    if (serialData->SerReq) DeleteExtIO((struct IORequest*)serialData->SerReq);
    if (serialData->SerPort) DeletePort(serialData->SerPort);
    FreeMem(serialData, sizeof(struct SerialData));
  }
  return 0;
}

/* PROMISE: close the serial port */
void CloseSerial(struct SerialData* serialData) {
  CloseDevice((struct IORequest*)serialData->SerReq);
  DeleteExtIO((struct IORequest*)serialData->SerReq);
  DeletePort(serialData->SerPort);
  FreeMem(serialData, sizeof(struct SerialData));
}

/* PROMISE: change the serial parameters, returns non-zero on error */
int ChangeData(struct SerialData* serialData, ULONG baudRate, UBYTE numBits, UBYTE stopBits, ULONG serialFlags, ULONG readBufferLength) {
  int error;
  struct IOExtSer* io;
  
  io = serialData->SerReq;
  
  io->io_ReadLen       = numBits;
  io->io_BrkTime       = 50000;
  io->io_Baud          = baudRate;
  io->io_CtlChar       = 0x11130000L;
  io->io_WriteLen      = numBits;
  io->io_StopBits      = stopBits;
  io->io_RBufLen       = readBufferLength;
  io->io_SerFlags      = serialFlags;
  io->IOSer.io_Command = SDCMD_SETPARAMS;
  io->io_TermArray.TermArray0 = 0x51040303;
  io->io_TermArray.TermArray1 = 0x03030303;
  
  error = DoIO ((struct IORequest*)io);

  return error;
}

/* SERIAL I/O functions */

/* PROMISE: returns the number of characters in the Amiga's serial
buffer, returns 0 on error (but 0 is also valid) */

long SerBuffer(struct SerialData* serialData) {
  serialData->SerReq->IOSer.io_Command = SDCMD_QUERY;
  
  DoIO ((struct IORequest*)serialData->SerReq);
  return ((long)serialData->SerReq->IOSer.io_Actual);
}

/* PROMISE: clears the serial port */
void ClearSer(struct SerialData *serialData) {
  serialData->SerReq->IOSer.io_Command = CMD_CLEAR;
  
  DoIO((struct IORequest*)serialData->SerReq);
}

/* PROMISE: aborts the current IO request */
void AbortSer(struct SerialData* serialData) {
  AbortIO((struct IORequest*)serialData->SerReq);
}

/* PROMISE: checks if an IO request is still active */
long CheckSer(struct SerialData* serialData) {
  return CheckIO((struct IORequest*)serialData->SerReq);
}

/* PROMISE: waits for IO request to complete */
long WaitSer(struct SerialData* serialData) {
  int error;
  error = WaitIO((struct IORequest*)serialData->SerReq);
  return error;
}

/* PROMISE: waits for IO request to complete */
void WaitWaitSer(struct SerialData* serialData) {
  int error;
  Wait(1L << serialData->SerReq->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
  error = WaitIO((struct IORequest*)serialData->SerReq);
/*  if (error) printf("Error %d\n", error); */
}

/* PROMISE: reads from serial port, returns number of characters actually read */
long ReadSer(struct SerialData* serialData, char* data, long length, long* actual)
{
  long error;
  serialData->SerReq->IOSer.io_Data = (APTR)data;
  serialData->SerReq->IOSer.io_Length = (ULONG)length;
  serialData->SerReq->IOSer.io_Command = CMD_READ;
  serialData->SerReq->IOSer.io_Actual = 0;

/*
  SendIO((struct IORequest*)serialData->SerReq);
  error = WaitIO((struct IORequest*)serialData->SerReq);
  Wait(1L << serialData->SerReq->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
*/

  error = DoIO((struct IORequest*)serialData->SerReq);
  SetSignal(0L, SerialSignal(serialData));

  *actual = ((long)serialData->SerReq->IOSer.io_Actual);
  return error;
}

/* PROMISE: writes to serial port */
long WriteSer(struct SerialData* serialData, char* data, long length)
{
  long error;

  serialData->SerReq->IOSer.io_Data = (APTR)data;
  serialData->SerReq->IOSer.io_Length = length;
  serialData->SerReq->IOSer.io_Command = CMD_WRITE;
  
  error =  DoIO((struct IORequest*)serialData->SerReq);
  SetSignal(0L, SerialSignal(serialData));
  return error;
}

/* PROMISE: start serial port */
void StartSer(struct SerialData* serialData) {

  serialData->SerReq->IOSer.io_Command = CMD_START;
  DoIO((struct IORequest*)serialData->SerReq);
}

void StopSer(struct SerialData* serialData) {
  serialData->SerReq->IOSer.io_Command = CMD_STOP;
  DoIO((struct IORequest*)serialData->SerReq);
}

/* ASYNCH stuff */

/* PROMISE:  sends an IO request to read from the serial port */
void RecvSer(struct SerialData* serialData, char* data, long length)
{
  serialData->SerReq->IOSer.io_Data = (APTR)data;
  serialData->SerReq->IOSer.io_Length = (ULONG)length;
  serialData->SerReq->IOSer.io_Command = CMD_READ;
  
  SendIO((struct IORequest*)serialData->SerReq);
}

/* PROMISE:  sends an IO request to write to the serial port */
void SendSer(struct SerialData* serialData, char* data, long length)
{
  serialData->SerReq->IOSer.io_Data = (APTR)data;
  serialData->SerReq->IOSer.io_Length = length;
  serialData->SerReq->IOSer.io_Command = CMD_WRITE;
  
  SendIO((struct IORequest*)serialData->SerReq);
}

ULONG SerialSignal(struct SerialData* serialData) {
  return (ULONG)(1L << serialData->SerReq->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
}
