/*
$Revision:   1.27  $
$Date:   Jan 26 1999 17:57:54  $
*/

/*
 * Copyright 1993-1996
 * Quatech Inc.
 * Akron, Ohio, U.S.A.
 * All Rights Reserved
 *
 *
 * Title:    FDBCKBY.C
 *
 * Function: Byte synchronous feedback program
 *
 * Notes:    This program transmits and receives byte synchronous frames
 *           using two MPA-series boards (or both channels of an MPA-102).
 *
 *           This routine requires 2 boards in the same system with an
 *           interconnecting cable to run.  One board must be set for base
 *           address 0x320 and IRQ-3, the other for base address 0x300
 *           and IRQ-5.
 *
 *           A straight-through cable can be used if one board is DTE and
 *           the other is DCE.  The MPA-100 can be configured for
 *           DTE or DCE with jumper selections.  The MPA-200/300 must be
 *           purchased as either DTE or DCE and cannot be user-configured.
 *           The MPAP-100 is only available for DTE.
 *
 *           If this program is run using two DTEs or two DCEs, a null-modem
 *           cable must be used.  At a minimum, the Tx and Rx data signals
 *           must be cross-connected.
 *
 *           By default, this program transmits its Tx clock for the receiver
 *           to use as a receive clock.  If this desired, the clock signals
 *           should also be cross-connected.  Otherwise, the output Tx clock
 *           can be ignored, or turned off by changing pMPA_cfg->clock_source
 *           to 0x02.
 *
 *           With an MPA-102, set one channel for DCE and the other
 *           channel for DTE.  A one-to-one cable can be used between
 *           the two connectors.  Use base address 0x300 and IRQ-5.
 *
 *           DMA is not supported for byte synchronous communications.
 *
 *           There are no command line options for this program.
 *
 *
 * DISCLAIMER --- This program is for demonstration purposes only.
 *
 *                Quatech makes no claims about the suitability of this
 *                example program for any specific application.
 *
 */


// Ensure that board type macro has been set.
#if   defined MPA200
#elif defined MPA102
#else
   #error You must define a board type macro for the compiler!
   #error Use /DMPA102 for the MPA-102.
   #error Use /DMPA200 for the MPA-100/200/300 (or MPAP-x00)
#endif


// Syncdrive include files used

#ifdef __OS2__
#include <os2.h>
#include "syncos2.h"
#endif

#ifdef _WINDOWS
#include <windows.h>
   #ifndef __WIN95__
        #include "snc31dll.h"
   #else
        #include "snc95dll.h"
   #endif
#endif

#include "syncdriv.h"
#include "mpa-x00.h"

// Standard C include files used

#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>


// Character serving as end-of-data indicator.
#define EDI_CHAR 3


// Global variables

// Counter elements to count frames transmitted.
//char counter[5];
long    counter;
char digits[15];


// This is the string we will transmit.
char *pMsgString = "Jim's Chicago Style Pizza and German Beer Stand ";


// Channel 1 configuration array pointer.
struct channel_cfg _far *pMPA_cfg_1;


// Channel 2 configuration array pointer.
struct channel_cfg _far *pMPA_cfg_2;


// Buffer length, including space for overhead and the counter bytes.
short BufferLen;


// Transmit buffer pointer.
union com_block _far *pTxBuffer;


// Receive buffer pointer.
union com_block _far *pRxBuffer;


#ifdef _WINDOWS
// Windows memory handles for config arrays.
HGLOBAL  ConfigHandle_1;
HGLOBAL  ConfigHandle_2;
#endif


// Other miscellaneous variables.
short       rc;



void init_config_array_1(void)
{

   // Set up all values in the configuration array for the transmitter.

   pMPA_cfg_1->signature            = 0xcc;

#ifdef MPA102
   pMPA_cfg_1->structure_type       = 0x93;
#endif
#ifdef MPA200
   pMPA_cfg_1->structure_type       = 0x90;
#endif

   pMPA_cfg_1->clock_rate           = 9830400L;
   pMPA_cfg_1->board_number         = 0;
   pMPA_cfg_1->channel              = 0;
   pMPA_cfg_1->operating_mode       = 0x04;
   pMPA_cfg_1->line_control         = 0xf0;
   pMPA_cfg_1->options              = 0;
   pMPA_cfg_1->base_address         = 0x300;
   pMPA_cfg_1->tx_dma_channel       = 0;
   pMPA_cfg_1->rx_dma_channel       = 0;
   pMPA_cfg_1->tx_interrupt         = 5;
   pMPA_cfg_1->rx_interrupt         = 5;
   pMPA_cfg_1->crc_mode             = 0x01;
   pMPA_cfg_1->clock_source         = 0x06;     // Transmit rx and tx clocks
   pMPA_cfg_1->rx_baud_rate         = 9600;
   pMPA_cfg_1->tx_baud_rate         = 9600;
   pMPA_cfg_1->read_timeout         = 0;
   pMPA_cfg_1->write_timeout        = 0;
   pMPA_cfg_1->sync_char_1          = 0x16;
   pMPA_cfg_1->sync_char_2          = 0x16;
   pMPA_cfg_1->protocol_dependent0  = 0xff;
   pMPA_cfg_1->protocol_dependent1  = 0;
   pMPA_cfg_1->protocol_dependent2  = 0;
   pMPA_cfg_1->protocol_dependent3  = 0;
   pMPA_cfg_1->protocol_dependent4  = 0;
   pMPA_cfg_1->protocol_dependent5  = 0;
   pMPA_cfg_1->protocol_dependent6  = 0;
   pMPA_cfg_1->protocol_dependent7  = 0;

#ifdef MPA102
   printf("Channel 1: configuration status = %04x \n", config_MPA102(pMPA_cfg_1));
#endif
#ifdef MPA200
   printf("Channel 1: configuration status = %04x \n", config_MPA200(pMPA_cfg_1));
#endif


   //   Set up Receive Mask for BISYNC mode --- must be done after config.

   rc  = sync_command(pMPA_cfg_1, 5, 1, 1);  // SDI character
   rc |= sync_command(pMPA_cfg_1, 5, 2, 1);  // SDI character
   rc |= sync_command(pMPA_cfg_1, 5, 3, 2);  // EDI character
   rc |= sync_command(pMPA_cfg_1, 5, 0x16, 4);  // sync character
   rc |= sync_command(pMPA_cfg_1, 5, 0xff, 3);  // pad character

   if (rc)
      {
      printf("Error %04x executing sync_command 5!\n", rc);
      exit(-1);
      }
}


void init_config_array_2(void)
{

   // Set up all values in the configuration array for the receiver.

   pMPA_cfg_2->signature            = 0xcc;

#ifdef MPA102
   pMPA_cfg_2->structure_type       = 0x93;
   pMPA_cfg_2->board_number         = 0;
   pMPA_cfg_2->channel              = 1;
   pMPA_cfg_2->base_address         = 0x300;
   pMPA_cfg_2->tx_interrupt         = 5;
   pMPA_cfg_2->rx_interrupt         = 5;
#endif
#ifdef MPA200
   pMPA_cfg_2->structure_type       = 0x90;
   pMPA_cfg_2->board_number         = 1;
   pMPA_cfg_2->channel              = 0;
   pMPA_cfg_2->base_address         = 0x320;
   pMPA_cfg_2->tx_interrupt         = 3;
   pMPA_cfg_2->rx_interrupt         = 3;
#endif

   pMPA_cfg_2->clock_rate           = 9830400L;
   pMPA_cfg_2->operating_mode       = 0x04;
   pMPA_cfg_2->line_control         = 0xf0;
   pMPA_cfg_2->options              = 0;
   pMPA_cfg_2->tx_dma_channel       = 0;
   pMPA_cfg_2->rx_dma_channel       = 0;
   pMPA_cfg_2->crc_mode             = 0x01;
   pMPA_cfg_2->clock_source         = 0x06;
   pMPA_cfg_2->rx_baud_rate         = 9600;
   pMPA_cfg_2->tx_baud_rate         = 9600;
   pMPA_cfg_2->read_timeout         = 0;
   pMPA_cfg_2->write_timeout        = 0;
   pMPA_cfg_2->sync_char_1          = 0x16;
   pMPA_cfg_2->sync_char_2          = 0x16;
   pMPA_cfg_2->protocol_dependent0  = 0xff;
   pMPA_cfg_2->protocol_dependent1  = 0;
   pMPA_cfg_2->protocol_dependent2  = 0;
   pMPA_cfg_2->protocol_dependent3  = 0;
   pMPA_cfg_2->protocol_dependent4  = 0;
   pMPA_cfg_2->protocol_dependent5  = 0;
   pMPA_cfg_2->protocol_dependent6  = 0;
   pMPA_cfg_1->protocol_dependent7  = 0;

#ifdef MPA102
   printf("Channel 2: configuration status = %04x \n", config_MPA102(pMPA_cfg_2));
#endif
#ifdef MPA200
   printf("Channel 2: configuration status = %04x \n", config_MPA200(pMPA_cfg_2));
#endif


   //   Set up Receive Mask for BISYNC mode --- must be done after config.

   rc  = sync_command(pMPA_cfg_2, 5, 1, 1);  // SDI character
   rc |= sync_command(pMPA_cfg_2, 5, 2, 1);  // SDI character
   rc |= sync_command(pMPA_cfg_2, 5, 3, 2);  // EDI character
   rc |= sync_command(pMPA_cfg_2, 5, 0x16, 4);  // sync character
   rc |= sync_command(pMPA_cfg_2, 5, 0xff, 3);  // pad character

   if (rc)
      {
      printf("Error %04x executing sync_command 5!\n", rc);
      exit(-1);
      }

}


short transmit(void)
{
   counter++;
   ltoa(counter, digits, 10);

   strcpy ((char *)pTxBuffer->ByteT.comm_buffer,pMsgString);

        strcat(pTxBuffer->ByteT.comm_buffer, digits);

   // Initialize the transmit buffer comm_block


   pTxBuffer->ByteT.buffer_length = strlen((char*)pTxBuffer->ByteT.comm_buffer);
   pTxBuffer->ByteT.buffer_status = 0;
   pTxBuffer->ByteT.idle_flag = 0x1;          // Send pads after message
   pTxBuffer->ByteT.message_type = 0x0;       // Bisync Message Mode
   pTxBuffer->ByteT.message_sdi = 0x02;       // Bisync start of data
   pTxBuffer->ByteT.message_edi = 0x03;       // Bisync end of data
   pTxBuffer->ByteT.num_syncs = 0x02;         // Send extra synchs out

#ifdef __OS2__
   rc = sync_transmit (pMPA_cfg_1,pTxBuffer,0);
#elif defined _WINDOWS
   rc = sync_transmit (pMPA_cfg_1,pTxBuffer,0);
#else  // DOS
   rc = sync_transmit (pMPA_cfg_1,pTxBuffer);
#endif

   if (rc)
      printf("Error starting frame transmission %x", rc);


   // Wait until the frame is transmitted.
   while (!(pTxBuffer->ByteT.buffer_status & (TxDone | TxAbort | TxError)));

   if (pTxBuffer->ByteT.buffer_status & TxError)
      printf("Transmitter Error\n");
   if (pTxBuffer->ByteT.buffer_status & TxAbort)
      printf("Transmitter underrun\n");

   return(pTxBuffer->ByteT.buffer_status);


}

// Routine sets up parameters in the receive buffer and
// initializes the receive hardware

void receive_1(void)
{
   // Initialize the transmit buffer comm_block

   pRxBuffer->ByteR.buffer_length = BufferLen;
   pRxBuffer->ByteR.buffer_status = 0;       // initialize buffer status to 0
   pRxBuffer->ByteR.message_type = 0x0;      // Bisync Message Mode
   pRxBuffer->ByteR.num_pads = 0x1;          // set pad count

#ifdef __OS2__
   rc = sync_receive(pMPA_cfg_2,pRxBuffer,0);
#elif defined _WINDOWS
   rc = sync_receive(pMPA_cfg_2,pRxBuffer,0);
#else  // DOS
   rc = sync_receive(pMPA_cfg_2,pRxBuffer);
#endif

   if (rc)
      printf("Error starting frame transmission %x", rc);


}


// Routine monitors the receive buffer status, waiting for the MessageDone
// status.

short receive_2(void)
{
   while (!(pRxBuffer->ByteR.buffer_status & (RxDone | RxFull | RxAbort)));

   if (pRxBuffer->ByteR.buffer_status & RxAbort)
      printf("Received Abort\n");
   if (pRxBuffer->ByteR.buffer_status & RxFull)
       printf("Received Overrun\n");

   return(pRxBuffer->ByteR.buffer_status);

}


void SyncDriveExit(void)
{
   // This exit routine takes care of required cleanup.
   // It's important not to exit with any locked memory lying around!

   // Print current buffer statuses.
   if (pRxBuffer)
      printf("Rx buffer status = %02x \n",pRxBuffer->ByteR.buffer_status);
   if (pTxBuffer)
      printf("Tx buffer status = %02x \n",pTxBuffer->ByteT.buffer_status);

   // Stop the transmitter.
   rc = sync_abort_message(pMPA_cfg_2);

   // Stop the receiver.
   rc = sync_command(pMPA_cfg_1, 4);

   // Release the channels.
   rc = sync_release(pMPA_cfg_1);
   rc = sync_release(pMPA_cfg_2);

   // Free up the Tx and Rx buffers.
   if (pRxBuffer)
      free(pRxBuffer);
   if (pTxBuffer)
      free(pTxBuffer);

   // Release the channel config arrays' memory.
   free(pMPA_cfg_1);
   free(pMPA_cfg_2);


}

void AbnormalExit(int signal)
{
   // This function ensures that the exit handler gets
   // called if Ctrl-C, Ctrl-Break, etc. occurs.
   exit(0);
}





void main(void)
{
   char *ptr;
   USHORT  DriverRelease;
   USHORT  DLLRelease;

   
// Setup handlers for Ctrl-C, Ctrl-Break, etc.
   signal(SIGINT, AbnormalExit);
   signal(SIGTERM, AbnormalExit);

#if defined(__OS2__) || defined(__WIN95__)
   signal(SIGBREAK, AbnormalExit);
#endif

   atexit(SyncDriveExit);


   // Allocate memory for the channel configuration array.

   if (((pMPA_cfg_1 = malloc(sizeof(struct channel_cfg))) == NULL) |
       ((pMPA_cfg_2 = malloc(sizeof(struct channel_cfg))) == NULL))
      {
      printf("Cannot allocate config array!\n");
      exit(-1);
      }


   printf("\n*** Quatech MPA-series adapter feedback example program ***\n\n");
   printf("Byte synchronous transfer between two boards (one if MPA-102).\n");
   printf("See source code for setup requirements.\n");
   printf("Press Ctrl-Break to stop the program.\n\n");


#ifdef MPA102
   printf("This test requires 1 board:  Address 300H and IRQ-5\n\n");
#else
   printf("This test requires 2 boards:  Address 300H and IRQ-5\n\n");
   printf("     second one at:           Address 320H and IRQ-3\n\n");
#endif


   // Initialize the channel configuration arrays.
   // Configure and open the channel.

   init_config_array_1();
   init_config_array_2();

   // Check the SyncDrive version.
   rc = sync_version(pMPA_cfg_1, &DriverRelease, &DLLRelease);
   if (rc != SYNC_SUCCESS)
   {
      printf("Syncdrive release level error!\n\n");
      printf("Driver release level is %04x, DLL release level is %04x\n",
             DriverRelease, DLLRelease);
      exit(-1);
   }
   printf("Syncdrive release level is %04x\n\n", DriverRelease);

   // Buffer length is the string, the com_block structure overhead, and
   //  some extra space for the counter bytes.

   BufferLen = strlen(pMsgString) + COMBLOCK_OVERHEAD + 0x10;


   // Allocate the Tx and Rx buffers.

   if ((pTxBuffer = malloc(BufferLen)) == NULL)
      {
      printf("Buffer Allocation Failed\n");
      exit(-1);
      }

   if ((pRxBuffer = malloc(BufferLen)) == NULL)
      {
      printf("Buffer Allocation Failed\n");
      exit(-1);
      }


#ifdef __WIN95__
   printf("\nFor best performance, run this application from a full screen Command Prompt.\n\n");
#endif
   printf("Press any key to start.\n");
   while(!kbhit());
   getch();


   while(1)
      {
      receive_1();             // initialize receiver
      transmit();              // initialize and transmit buffer
      receive_2();             // wait for receive to finish

      // put terminator in buffer to accommodate the printf statement and print it.
      // Cut off the "junk" (EDI, CRCs, pads) at the end of the frame.

      ptr = strchr(pRxBuffer->ByteR.comm_buffer, EDI_CHAR);
      if (ptr != NULL)
         *ptr = '\0';
      else
         // best guess
         pRxBuffer->ByteR.comm_buffer[pRxBuffer->ByteR.buffer_pointer - 5] = 0;

      printf("%s\n", pRxBuffer->ByteR.comm_buffer);

#ifdef _WINDOWS
      // Windows "QuickWin" app won't respond to Ctrl-Break unless some
      //  console I/O is done.  This can impact performance under DOS,
      //  so we'll make it conditional to Windows.
      if (kbhit())
         getch();
#endif

      }
}
