/*
$Revision:   1.16  $
$Date:   Jan 26 1999 17:57:58  $

*/

/*
 * Copyright 1996
 * Quatech Inc.
 * Akron, Ohio, U.S.A.
 * All Rights Reserved
 *
 *
 * Title:    QSENDBI.C
 *
 * Function: Bit synchronous transmit program using a transmit queue
 *
 * Notes:    This program transmits on a bit synchronous link between two
 *           MPA-series boards connected by a cable.  The receiving board
 *           will typically run the complimenting QRECBI program.
 *
 *           DMA is turned on by default.  Use the /t0 option on the
 *           command line to turn off DMA.
 *
 *           A straight-through cable can be used if one board is DTE and
 *           the other is DCE.  The MPA-100 and MPA-102 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.
 *
 *
 *
 * 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>


#define MAX_BUFFERS 1000


// Global variables

// Number of buffers in the transmit queue.  Set default here.
USHORT num_buffers = 100;

// 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.
USHORT BufferLen = 0;


// Transmit buffer pointers.
union com_block _far *pTxBuffer[MAX_BUFFERS];


// Transmit queue pointer.
struct BufferQueue _far *pTxQueue;


// Queue mode.
ULONG  QueueMode = QUEUE_RING;


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


// Other miscellaneous variables.
USHORT       index;
USHORT       done;
USHORT       this_tx;
USHORT       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             = 0x0;
   pMPA_cfg->operating_mode      = 0x08;
   pMPA_cfg->line_control        = 0xf0;
   pMPA_cfg->options             = 0x0;
   pMPA_cfg->base_address        = 0x300;
   pMPA_cfg->tx_dma_channel      = 3;
   pMPA_cfg->rx_dma_channel      = 0;
   pMPA_cfg->tx_interrupt        = 5;
   pMPA_cfg->rx_interrupt        = 5;
   pMPA_cfg->crc_mode            = 0x82;
   pMPA_cfg->clock_source        = 0x06;
   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         = 0;
   pMPA_cfg->sync_char_2         = 0x7e;
   pMPA_cfg->protocol_dependent0 = 0;
   pMPA_cfg->protocol_dependent1 = 0;
   pMPA_cfg->protocol_dependent2 = 0;
   pMPA_cfg->protocol_dependent3 = 0;        // 0x01 = FIFO mode
   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;

            // Change Tx DMA channel.
            case 't':
            case 'T':
               pMPA_cfg->tx_dma_channel = atouc(++charptr);
               break;

            // Run the queue in oneshot mode.
            case 'o':
            case 'O':
               QueueMode = QUEUE_ONESHOT;
               break;

            // Let queue ring mode continue running despite overruns.
            case 'w':
            case 'W':
               QueueMode |= QUEUE_ALLOW_OVERRUN;
               break;

            // Change number of buffers to use.
            case 'n':
            case 'N':
               num_buffers = atoui(++charptr);
               if(num_buffers > MAX_BUFFERS)
               {
                  printf("ERROR--The maximum number of buffers is %d.\n", MAX_BUFFERS);
                  exit(-1);
               }           
               break;

            // Change size of buffers to use.  (Default to actual size needed.
            // This option is useful only for memory allocation debugging.)
            case 's':
            case 'S':
               BufferLen = atoui(++charptr);
               break;


            default:
               printf("\nQSENDBI (options)\n");
               printf("\nOptions:\n");
               printf("/A<base address in hex>\n");
               printf("/I<irq>\n");
               printf("/B<baud rate>\n");
               printf("/T<transmit dma channel>\n");
               printf("/N<number of buffers>\n");
               printf("/S<size of buffers in bytes>\n");
               printf("/O for oneshot mode\n");
               printf("/W for ignore queue overrun\n");
               exit(-1);
               break;
            }
         }
      }

}

void setup_transmit(union com_block _far *pTxBuffer)
{
   counter++;
   ltoa(counter, digits, 10);

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

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

   // Initialize the transmit buffer comm_block

   pTxBuffer->BitT.buffer_length = strlen((char*)pTxBuffer->BitT.comm_buffer);
   pTxBuffer->BitT.buffer_status = 0;
   pTxBuffer->BitT.idle_flag =  0;


}



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 Tx queue status.
   if (pTxQueue)
      printf("Tx queue status %8lx \n",pTxQueue->QueueStatus);

   // Stop the transmitter.  This will stop the Tx queue as well.
   rc = sync_abort_message(pMPA_cfg);

   // Free the Tx queue.
   rc = sync_free_queue(pMPA_cfg, pTxQueue);

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

   /* Free up the Tx buffers */
   if (pMPA_cfg->tx_dma_channel != 0)
      rc = sync_free_dma_buffers();

   if (pMPA_cfg->tx_dma_channel == 0)
      for (index = 0; index < num_buffers; index++)
         {
         if (pTxBuffer[index])
            free(pTxBuffer[index]);
         }


      // 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[])
{
   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.
   // For Windows, it should be locked memory.


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

   printf("\n*** Quatech MPA-series adapter transmit queue example program ***\n\n");
   printf("Bit synchronous link between two boards, connected by a cable.\n");
   printf("See the source code for cabling info.\n\n");
   printf("Press Ctrl-Break to stop the program.\n\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.
   // Don't set value here if it was set by a command line switch.
   if (BufferLen == 0)
      BufferLen = strlen(pMsgString) + COMBLOCK_OVERHEAD + 0x10;


   // Allocate the Tx buffers.  Use special allocate routine if using DMA. */

   for (index = 0; index < num_buffers; index++)
      {
      if (pMPA_cfg->tx_dma_channel != 0)
       {
       if (sync_alloc_dma_buffer(pMPA_cfg, BufferLen, &pTxBuffer[index]) != 0)
          {
          printf("Tx DMA Buffer %i Allocation Failed\n", index);
          sync_free_dma_buffers();
          exit(-1);
          }
       }
      else
       {
       if ((pTxBuffer[index] = malloc(BufferLen)) == NULL)
          {
          printf("Tx Buffer %i Allocation Failed\n", index);
          exit(-1);
          }
       }
    }

   // Display DMA buffer allocation if DMA buffers have been allocated.
   if (pMPA_cfg->tx_dma_channel != 0)
   {
      printf("Allocated %i DMA buffers of %i bytes (%li bytes total)\n",
          num_buffers, BufferLen, num_buffers * BufferLen);
   }


   // 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 = %03x\n", pMPA_cfg->base_address);
   if (pMPA_cfg->tx_dma_channel != 0)
      printf("Transmit DMA channel = %i\n",pMPA_cfg->tx_dma_channel);
   else
      printf("DMA not in use.\n");
   printf("Transmit/Receive interrupt = %i\n",pMPA_cfg->tx_interrupt);
   printf("Clockrate = %.li\n",pMPA_cfg->clock_rate);
   printf("Baudrate = %.li\n\n",pMPA_cfg->tx_baud_rate);

#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();


   // Initialize the comm_blocks

   for (index = 0; index < num_buffers; index++)
      setup_transmit(pTxBuffer[index]);


   // Create a transmit queue from the com_block array.
   // Run in continuous ring mode.

   rc = sync_register_queue(pMPA_cfg, num_buffers, &pTxBuffer[0], 0,
                            QueueMode | QUEUE_TRANSMIT, &pTxQueue);
   if (rc)
      {
      printf("register queue failed, status = %04x\n", rc);
      exit(-1);
      }

   printf("Tx queue registered, %i buffers\n", num_buffers);


   // Initialize loop control.

   done = FALSE;
   this_tx = pTxQueue->CurrentBuffer;


   // This program transmits only. We'll print the frames as their status goes TX_DONE.


   // Start the transmit queue.

#ifdef __OS2__
   rc = sync_transmit_queue(pMPA_cfg, pTxQueue, 0);
#elif defined _WINDOWS
   rc = sync_transmit_queue(pMPA_cfg, pTxQueue, 0);
#else
   rc = sync_transmit_queue(pMPA_cfg, pTxQueue);
#endif

   if (rc)
      {
      printf("sync_transmit_queue failed, status = %04x\n", rc);
      exit(-1);
      }

   printf("Tx queue now running...\n");



   // Loop until user aborts or a queue error (queue underrun) occurs.
   // Whenever a frame is transmitted, print it and recycle the buffer.

   while ((!done) && (pTxQueue->QueueStatus & QUEUE_RUNNING))
      {
      while (pTxQueue->CurrentBuffer != this_tx)
         {
         puts(pTxBuffer[this_tx]->BitT.comm_buffer);

         // To demonstrate allowing queue overruns, don't service Tx buffers
         //    if the /W option was used on the command line.
         if (!(pTxQueue->QueueMode & QUEUE_ALLOW_OVERRUN))
            setup_transmit(pTxBuffer[this_tx]);

         this_tx = ++this_tx % num_buffers;

#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

         }

      }

}
