/******************************************************************************
 * CommAm.h - Serial port wrapper for the Apple II Disk Transfer utility.
 *
 * $Header: Big:Archives/Apple II/AdtAm/Amiga/RCS/CommAm.c,v 1.4 2000/02/02 16:16:24 AGMS Exp $
 *
 * Implemented by Alexander G. M. Smith, Ottawa Canada, agmsmith@achilles.net,
 * agmsmith@bix.com, 71330.3173@compuserve.com, and various other places
 * including the Ottawa Freenet.
 *
 * This code (just the serial port stuff) is put into the public domain
 * by AGMS, so you can copy it, hack it up, sell it, and do whatever
 * you want to it.
 *
 * Compile with the SAS C compiler, version 6.58.
 *
 * $Log: CommAm.c,v $
 * Revision 1.4  2000/02/02  16:16:24  AGMS
 * Uses SAS specific functions now.
 *
 * Revision 1.3  2000/01/30  17:37:06  AGMS
 * Added writing a string to the serial port.
 *
 * Revision 1.2  2000/01/29  23:18:52  AGMS
 * Initially working.
 *
 * Revision 1.1  2000/01/28  15:13:04  AGMS
 * Initial revision
 */

#ifdef __SASC
  #define __USE_SYSBASE 1
    /* Need this to make the exec.library headers use the library base global
    variable SysBase in SAS C (otherwise it just uses location $4). */
#endif

#include <stdio.h>
#include <string.h>

#include <proto/exec.h>

#ifndef DEVICES_SERIAL_H
  #include <devices/serial.h>
#endif

#ifdef _GST
  extern struct ExecBase *SysBase;
    /* If using precompiled headers (GST - global symbol table) in SAS C, the
    extern declaration in the headers doesn't take effect. */
#endif /* _GST */


#include "CommAm.h"  /* Prototypes for our functions */



/******************************************************************************
 * Data structures needed for talking to serial.device.
 */

struct MsgPort *MyMessagePort;
  /* For receiving replies to device requests.  NULL if unallocated. */

struct IOExtSer *MyIORequest;
  /* Commands to serial.device are put into this IO request record,
  NULL if not allocated yet. */

BOOL DeviceWasOpened;
  /* TRUE if the serial.device was OpenDevice'd and needs to be closed when the
  program exits. */



/******************************************************************************
 * Opens the serial.device driver and sets up the related data structures.
 * Returns TRUE if successful.
 */

int comm_open (char *DeviceName, int UnitNumber, unsigned int BaudRate)
{
  int ErrorCode;

  MyMessagePort = CreateMsgPort ();
  if (MyMessagePort == NULL)
  {
    printf ("comm_open: Out of memory for CreateMsgPort.\n");
    return FALSE;
  }

  MyIORequest = (struct IOExtSer *)
    CreateExtIO (MyMessagePort, sizeof (struct IOExtSer));
  if (MyIORequest == NULL)
  {
    printf ("comm_open: Out of memory for CreateIORequest.\n");
    return FALSE;
  }

  MyIORequest->io_SerFlags = 0; /* Not shared, not 7-wire handshaking */
  ErrorCode =  OpenDevice (DeviceName, UnitNumber,
  (struct IORequest *) MyIORequest, 0L);
  if (ErrorCode != 0)
  {
    printf ("comm_open: OpenDevice for %s unit %d failed, code %d.\n",
    DeviceName, (int) UnitNumber, ErrorCode);
    return FALSE;
  }
  DeviceWasOpened = TRUE;

  /* Set the baud rate, and other options. */

  if (MyIORequest->io_RBufLen < 512) /* Need buffer space for at least */
    MyIORequest->io_RBufLen = 512;   /* 1 disk sector plus headers.    */

  MyIORequest->io_ExtFlags &= ~(SEXTF_MSPON | SEXTF_MARK); /* No parity */
  MyIORequest->io_Baud = BaudRate;
  MyIORequest->io_ReadLen = 8;  /* Bits per character to read. */
  MyIORequest->io_WriteLen = 8; /* Bits per character to write. */
  MyIORequest->io_StopBits = 1; /* Number of stop bits. */
  MyIORequest->io_SerFlags |= SERF_XDISABLED; /* Turn off Xon/Xoff */
  MyIORequest->io_SerFlags &= ~SERF_EOFMODE; /* No special EOF chars. */
  MyIORequest->io_SerFlags &= ~SERF_SHARED;
  MyIORequest->io_SerFlags &= ~SERF_RAD_BOOGIE;
  MyIORequest->io_SerFlags &= ~SERF_PARTY_ON; /* Even less parity. */

  MyIORequest->IOSer.io_Command = SDCMD_SETPARAMS;
  ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  if (ErrorCode != 0)
  {
    printf ("comm_open: Unable to change serial port settings.\n");
    return FALSE;
  }

  comm_flush ();

  return TRUE;
}



/******************************************************************************
 * Called at program exit to clean up globally allocated things.
 */

void comm_close (void)
{
  if (DeviceWasOpened)
  {
    CloseDevice ((struct IORequest *) MyIORequest);
    DeviceWasOpened = FALSE;
  }

  if (MyIORequest != NULL)
  {
    DeleteExtIO ((struct IORequest *) MyIORequest);
    MyIORequest = NULL;
  }

  if (MyMessagePort != NULL)
  {
    DeleteMsgPort (MyMessagePort);
    MyMessagePort = NULL;
  }
}



/******************************************************************************
 * Return the number of bytes available in the read buffer.
 */

int comm_avail (void)
{
  int ErrorCode;

  if (MyIORequest == NULL)
    return 0;

  MyIORequest->IOSer.io_Command = SDCMD_QUERY;
  ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  if (ErrorCode != 0)
  {
    printf ("comm_avail: Unable to determine number of bytes read so far.\n");
    return 0;
  }

  return (int) (MyIORequest->IOSer.io_Actual);
}



/******************************************************************************
 * Write a single byte to the serial port.
 */

void comm_putc ( unsigned char c )       /* sends char out port */
{
  int ErrorCode;

  char  Buffer [1];

  if (MyIORequest == NULL)
    return; /* Not open yet. */

  MyIORequest->IOSer.io_Command = CMD_WRITE;
  MyIORequest->IOSer.io_Length = 1; /* Write 1 byte. */
  Buffer[0] = c;
  MyIORequest->IOSer.io_Data = Buffer;
  ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  if (ErrorCode != 0)
  {
    printf ("comm_putc: Unable to write to serial device.\n");
  }
}



/******************************************************************************
 * Write a NUL terminated string to the serial port.
 */

void comm_puts (char *String)
{
  int ErrorCode;

  if (MyIORequest == NULL)
    return; /* Not open yet. */

  MyIORequest->IOSer.io_Command = CMD_WRITE;
  MyIORequest->IOSer.io_Length = strlen (String);
  MyIORequest->IOSer.io_Data = String;
  ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  if (ErrorCode != 0)
  {
    printf ("comm_puts: Unable to write to serial device.\n");
  }
}



/******************************************************************************
 * Read a single byte from the serial port.  Will wait if none has been
 * received yet.
 */

int comm_getc (void)
{
  char  Buffer [1];
  int   ErrorCode;

  if (MyIORequest == NULL)
    return 0; /* Not open yet. */

  MyIORequest->IOSer.io_Command = CMD_READ;
  MyIORequest->IOSer.io_Length = 1; /* Read 1 byte. */
  MyIORequest->IOSer.io_Data = Buffer;
  ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  if (ErrorCode != 0)
  {
    printf ("comm_getc: Unable to read from serial device.\n");
    return 0;
  }

  return Buffer[0];
}



/******************************************************************************
 * Flushes all waiting characters in the receive buffer.
 */

void comm_flush (void)
{
  int ErrorCode;

  if (MyIORequest == NULL)
    return; /* Not open yet. */

  MyIORequest->IOSer.io_Command = CMD_CLEAR;
  ErrorCode = DoIO ((struct IORequest *) MyIORequest);
  if (ErrorCode != 0)
  {
    printf ("comm_flush: Unable to clear the serial device (reset read pointers).\n");
    return;
  }
}
