/*
$Revision:   1.29  $
$Date:   Jan 26 1999 17:57:56  $

*/

/*
 * Copyright 1993-1997
 * Quatech Inc.
 * Akron, Ohio, U.S.A.
 * All Rights Reserved
 *
 *
 * Title:    LPBCKBY.C
 *
 * Function: Byte synchronous loopback program
 *
 * Notes:    This program transmits and receives byte synchronous frames
 *           using one MPA-series board.  Because the program uses the
 *           internal loopback functions of the SCC, no cable or
 *           connector should be used.
 *
 *           DMA is not supported for byte synchronous communications.
 *
 *
 *
 * 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.
long    counter;
char digits[15];


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


// configuration array pointer.
struct channel_cfg _far *pMPA_cfg;


// 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;


// Count used to track byte streaming progress.
USHORT  ReadCount;


#ifdef _WINDOWS
// Windows memory handle for config array.
HGLOBAL ConfigHandle;
#endif


// Flag to indicate block mode or message mode.
short   BlockMode = FALSE;


// Flag to indicate byte streaming in block mode.
short   streaming = FALSE;


// Other miscellaneous variables.
short rc;




void init_config_array(void)
{
   // Set up all initial values in the configuration array.

   pMPA_cfg->signature           = 0xcc;

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

   pMPA_cfg->clock_rate          = 9830400L;
   pMPA_cfg->board_number        = 0;        // arbitrarily chosen
   pMPA_cfg->channel             = 0;
   pMPA_cfg->operating_mode      = 0x04;
   pMPA_cfg->line_control        = 0xf0;
   pMPA_cfg->options             = 0x10;     // enable loop back mode
   pMPA_cfg->base_address        = 0x300;
   pMPA_cfg->tx_dma_channel      = 0;
   pMPA_cfg->rx_dma_channel      = 0;
   pMPA_cfg->tx_interrupt        = 5;
   pMPA_cfg->rx_interrupt        = 5;
   pMPA_cfg->crc_mode            = 0x01;
   pMPA_cfg->clock_source        = 0x16;
   pMPA_cfg->rx_baud_rate        = 9600;
   pMPA_cfg->tx_baud_rate        = 9600;
   pMPA_cfg->read_timeout        = 0;
   pMPA_cfg->write_timeout       = 0;
   pMPA_cfg->sync_char_1         = 0x16;
   pMPA_cfg->sync_char_2         = 0x16;
   pMPA_cfg->protocol_dependent0 = 0xff;
   pMPA_cfg->protocol_dependent1 = 0;
   pMPA_cfg->protocol_dependent2 = 0;
   pMPA_cfg->protocol_dependent3 = 0;
   pMPA_cfg->protocol_dependent4 = 0;
   pMPA_cfg->protocol_dependent5 = 0;
   pMPA_cfg->protocol_dependent6 = 0;
   pMPA_cfg->protocol_dependent7 = 0;

}


// Convert ascii hex to unsigned int.
USHORT hextoui(char *numbertext)
{
    return((USHORT)strtol(numbertext, (char **)NULL, 16));
}

// Convert ascii decimal to unsigned int.
USHORT atoui(char *numbertext)
{
    return((USHORT)strtol(numbertext, (char **)NULL, 10));
}

// Convert ascii decimal to unsigned char.
UCHAR atouc(char *numbertext)
{
    return((UCHAR)strtol(numbertext, (char **)NULL, 10));
}

// Convert ascii decimal to unsigned long.
ULONG atoul(char *numbertext)
{
    return((ULONG)strtol(numbertext, (char **)NULL, 10));
}


void process_options(int argc, char *argv[])
{
   int     index;
   char    *charptr;

   // Loop through all command line parameters, skipping the program name.
   for (index = 1; index <= (argc-1); index++)
      {
      charptr = argv[index];

      // First character of parameter must be a slash.
      if (*charptr == '/')
         {
         charptr++;

         // The switch type follows the slash.  Decode the type and
         //   pass a pointer to the variable data to the conversion
         //   routine.

         switch (*charptr)
            {
            // Change base address.
            case 'a':
            case 'A':
               pMPA_cfg->base_address = hextoui(++charptr);
               break;

            // Change IRQ.
            case 'i':
            case 'I':
               pMPA_cfg->tx_interrupt = atouc(++charptr);
               pMPA_cfg->rx_interrupt = pMPA_cfg->tx_interrupt;
               break;

            // Change baud rate.
            case 'b':
            case 'B':
               pMPA_cfg->rx_baud_rate = atoul(++charptr);
               pMPA_cfg->tx_baud_rate = pMPA_cfg->rx_baud_rate;
               break;

            case 'k':
            case 'K':
               BlockMode = TRUE;
               charptr++;
               if (*charptr == 's' || *charptr == 'S')
                  streaming = TRUE;
               break;


            default:
               printf("\nLPBCKBY (options)\n");
               printf("\nOptions:\n");
               printf("/A<base address in hex>\n");
               printf("/I<irq>\n");
               printf("/B<baud rate>\n");
               printf("/K for block mode\n");
               printf("/KS for block mode with byte streaming\n");
               exit(-1);
               break;
            }
         }
      }

}

short transmit(void)
{
   int stringl;
   stringl = _fstrlen((char*)pMsgString);

        counter++;
        ltoa(counter, digits, 10);

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

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

   // For message mode, insert a null to facilitate printing the string.
   // For block mode without streaming, insert two pad characters also.
   // For block mode with streaming, don't insert anything.
        
        if (!streaming)
                {
                if (BlockMode)
                        {
                        pTxBuffer->ByteT.comm_buffer[stringl+8]  = 0xff;
                        pTxBuffer->ByteT.comm_buffer[stringl+9]  = 0xff;
                        pTxBuffer->ByteT.comm_buffer[stringl+10] = 0;
                        }
                else            // Message mode.
                        pTxBuffer->ByteT.comm_buffer[stringl+8]  = 0;
                }


         
        // 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 = 1;           // Send pads after message
        
        if (BlockMode)
                pTxBuffer->ByteT.message_type = 1;     // Bisync block mode
        else
                { 
                pTxBuffer->ByteT.message_type = 0;     // 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
                }

        if (streaming)
                pTxBuffer->ByteT.misc_options = 0x01;   // Enable byte streaming.
        

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

   if (rc)
        {
        printf("Error starting frame transmission %x", rc);
        return(pTxBuffer->ByteT.buffer_status);
        }

   // If not streaming, wait for the frame to complete.
        if (!streaming)
                {
                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

        if (BlockMode)
                {
                pRxBuffer->ByteR.message_type = 1;     // Bisync block mode 
                pRxBuffer->ByteR.num_pads = 2;                  // Terminate after two block_term chars.
                pRxBuffer->ByteR.block_term = 0xff;     // block_term character.
                }
        else
                {
                pRxBuffer->ByteR.message_type = 0;     // Bisync message mode
                pRxBuffer->ByteR.num_pads = 1;         // Wait for extra pad characters
                }

        if (streaming)
                pRxBuffer->ByteR.misc_options = 0x01;   // Enable byte streaming.


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

   if (rc)
      printf("Error starting frame reception %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("\nRx 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);

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

   // Release the channel.
   rc = sync_release(pMPA_cfg);

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

   // Release the channel config array memory.

   free(pMPA_cfg);


}



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




void main(int argc, char *argv[])
{
   char *ptr;
   int  error;
   USHORT count;
   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 = malloc(sizeof(struct channel_cfg))) == NULL)
      {
      printf("Cannot allocate config array!\n");
      exit(-1);
      }


   printf("\n*** Quatech MPA-series adapter loopback example program ***\n\n");
   printf("Byte synchronous loopback on a single board.\n");
   printf("No loopback cable required!\n");


   // Initialize the channel configuration array.

   init_config_array();

   // Check the SyncDrive version.
   rc = sync_version(pMPA_cfg, &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); 

   
   // Now process any options that override the defaults.

   if (argc > 1)
       process_options(argc, argv);


    // Buffer length is the string, the com_block structure overhead, and
    //  some extra space for the counter bytes.  If data streaming is
         //  turned on, make a large buffer that can contain many passes.

         if (streaming)
                 BufferLen = 1000;
         else
                 BufferLen = strlen(pMsgString) + COMBLOCK_OVERHEAD + 0x10;


    // Allocate 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);
       }


   // Configure and open the channel.

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

   // Print configuration details to the screen.

   printf("\nBase address = %x\n", pMPA_cfg->base_address);
   printf("Transmit/Receive interrupt = %i\n",pMPA_cfg->tx_interrupt);
   printf("Clockrate = %.li\n",pMPA_cfg->clock_rate);
   printf("Baudrate = %.li\n",pMPA_cfg->tx_baud_rate);

        if (BlockMode)
                {
                printf("Running byte-sync block mode.\n");
                if (streaming)
                        printf(" Data streaming enabled.  Frame 00000001 will repeat forever.\n");
                }
        else
                printf("Running byte-sync message mode.\n");

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


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

        if (!BlockMode)
                {
                rc  = sync_command(pMPA_cfg, 5, 1, 1); // SDI character
                rc |= sync_command(pMPA_cfg, 5, 2, 1); // SDI character
                rc |= sync_command(pMPA_cfg, 5, 3, 2); // EDI character
                rc |= sync_command(pMPA_cfg, 5, 0x16, 4);    // sync character
                rc |= sync_command(pMPA_cfg, 5, 0xff, 3); // pad character
        
                if (rc)
                        {
                        printf("Error %04x executing sync_command 5!\n", rc);
                        exit(-1);
                        }
                }


        // For byte streaming, initialize the read counter and get
        //              things rolling before entering the while loop.
        if (streaming)
                {
                ReadCount = 0;
      receive_1();            // initialize receiver
      transmit();             // initialize and transmit buffer
                }


   while(1)
      {

                if (!streaming)
                        
                        {
                        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
                                if (BlockMode)
                                        pRxBuffer->ByteR.comm_buffer[pRxBuffer->ByteR.buffer_pointer - 2] = 0;
                                else
                                        pRxBuffer->ByteR.comm_buffer[pRxBuffer->ByteR.buffer_pointer - 5] = 0;
        
                        printf("%s\n", pRxBuffer->ByteR.comm_buffer);
                        }

                else    // streaming

                        {
                   // If the receive buffer has wrapped past the end, print everything
                        //   from the current point to the end, then reset the counter.
                        count = pRxBuffer->ByteR.buffer_pointer - ReadCount;
                        if (pRxBuffer->ByteR.buffer_pointer < ReadCount)
                                {
                                printf("%*.*s", BufferLen - ReadCount, 
                                                                           BufferLen - ReadCount, 
                                                            pRxBuffer->ByteR.comm_buffer + ReadCount);
                                fflush(stdout);
                                ReadCount = 0;
                                }

                        // If 100 bytes have accumulated, print them.
                        count = pRxBuffer->ByteR.buffer_pointer - ReadCount;
                        if ((pRxBuffer->ByteR.buffer_pointer - ReadCount) > 100)
                                {
                                printf("%100.100s", pRxBuffer->ByteR.comm_buffer + ReadCount);
                                fflush(stdout);
                                ReadCount += 100;
                                if (ReadCount == BufferLen)
                                        ReadCount = 0;
                                }
                        }

                                                                                                                                                                                                  


#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

      }
}

