/*
$Revision:   1.18  $
$Date:   22 Jan 1999 14:21:08  $

*/

/*
 * Copyright 1995-1996
 * Quatech Inc.
 * Akron, Ohio, U.S.A.
 * All Rights Reserved
 *
 *
 * Title:    THREADTX.C
 *
 * Function: Bit synchronous transmit program demonstrating Syncdrive for
 *           OS/2 thread support.
 *
 * 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 QRECBI program, which uses a receive
 *           queue.  This allows the frames coming rapidly from THREADTX
 *      to be received.
 *
 *           DMA is not used in this example program.
 *
 *           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 receives its
 *           Rx clock, so the clock signals should also be cross-connected.
 *
 *           Alternatively, the pMPA_cfg->clock_source could be set to 0x16,
 *           which would cause the Rx clock to be locally sourced from the
 *           baud rate generator.  This would avoid the need for cross-
 *           connected clock signals.  If this is done, the user must specify
 *           the proper baud rate (/b option) on the command line when the
 *           program is started.
 *
 *
 * DISCLAIMER --- This program is for demonstration purposes only.
 *
 *                Quatech makes no claims about the suitability of this
 *                example program for any specific application.
 *
 */

#ifndef __OS2__
#error "This example is for OS/2 only!"
#endif



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


// System include files used

#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#include <os2.h>

#include <signal.h>
#include <stddef.h>


// Syncdrive include files used
#include "syncos2.h"
#include "syncdriv.h"
#include "mpa-x00.h"

// Standard C include files used

#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "string.h"

#define     UINT        unsigned int
#define     UCHAR       unsigned char
#define     ULONG       unsigned long

#define     MAX_THREADS     100



// Global variables

// configuration array pointer.
struct channel_cfg *pMPA_cfg;

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

// Thread handles.
TID   threads[MAX_THREADS];

// Number of threads to run.
ULONG NumThreads = 20;     // default 20

// Number of milliseconds sync_transmit will wait to access the driver.
ULONG DLLTimeout = 10000;  // default 10000 (10 seconds)

// Semaphore used to track completion of the transmit threads.
HEV   MySem;
ULONG PostCount;

// Other miscellaneous variables.
short rc;


void init_config_array()
   {
   int error;

   // 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             = 9830400;
   pMPA_cfg->board_number           = 0;
   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         = 0;
   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           = 2400;
   pMPA_cfg->tx_baud_rate           = 2400;
   pMPA_cfg->read_timeout           = 0;
   pMPA_cfg->write_timeout          = 0x1000;       // about 4 seconds
   pMPA_cfg->sync_char_1            = 0x0;
   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;
   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.
UINT hextoui(char *numbertext)
{
    return((UINT)strtol(numbertext, (char **)NULL, 16));
}

// Convert ascii decimal to unsigned int.
UINT atoui(char *numbertext)
{
    return((UINT)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 (ignored if receiving the Rx clock).
                case 'b':
                case 'B':
                    pMPA_cfg->rx_baud_rate = atoul(++charptr);
                    pMPA_cfg->tx_baud_rate = pMPA_cfg->rx_baud_rate;
                    break;

            // Change number of threads.
                case 'n':
                case 'N':
                    NumThreads = atoul(++charptr);
                    if (NumThreads > MAX_THREADS)
                       {
                       NumThreads = MAX_THREADS;
                       printf("Number of threads requested exceeds the maximum allowed.\n");
                       }
                    break;

            // Change frame timeout.
                case 't':
                case 'T':
                    pMPA_cfg->write_timeout = atoul(++charptr);
                    break;

            // Change DLL timeout.
                case 'd':
                case 'D':
                    DLLTimeout = atoul(++charptr);
                    break;

                default:
                    printf("\nTHREADTX (options)\n");
                    printf("\nOptions:\n");
                    printf("/A<base address in hex>\n");
                    printf("/I<irq>\n");
                    printf("/B<baud rate>\n");
                    printf("/N<number of Tx threads>\n");
                    printf("/T<driver frame timeout>\n");
                    printf("/D<sync_transmit() timeout>\n");
                    printf("(No DMA supported)\n");
                    exit(-1);
                    break;
            }
        }
    }

}


void transmit(void *argument)
{

   short             index;
   union com_block  *tx_buf;
   APIRET            rc;

   // Allocate a Tx buffer.
   tx_buf = malloc(0x1000);
   if (tx_buf == NULL)
      {
      printf("Buffer Allocation Failed, buffer %i\n", _threadid);
      exit(-1);
      }

   strcpy (tx_buf->BitT.comm_buffer,pMsgString);

   // Initialize the transmit buffer comm_block

   tx_buf->BitT.buffer_length = strlen(tx_buf->BitT.comm_buffer);
   tx_buf->BitT.buffer_status = 0;
   tx_buf->BitT.idle_flag = 0;


   // Sync_transmit will return on a driver busy timeout or when the frame is done.

   rc = sync_transmit (pMPA_cfg, tx_buf, DLLTimeout);

   if (rc != 0)
      {
      if (rc == DRIVER_BUSY)
         printf("Driver busy timeout.\n");
      else
         printf("Error %04x starting frame transmission\n", rc);
      }
   else
      {
      if (tx_buf->BitT.buffer_status & FrameTimeout)
         printf("Tx Frame Timeout occurred\n");
      if (tx_buf->BitT.buffer_status & TxError)
         printf("Transmitter Error\n");
      if (tx_buf->BitT.buffer_status & TxAbort)
         printf("Transmitter underrun\n");

      printf("%s \n",tx_buf->BitT.comm_buffer); // and print transmitted buffer
      }

   // Free the Tx buffer.
   free(tx_buf);


   // Increment the counter tracking thread completion.
   DosPostEventSem(MySem);

}




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

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

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

   // Release the channel config array memory.
   free(pMPA_cfg);

   DosExit(EXIT_PROCESS,0);
}

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




main(int argc, char *argv[])
{
   int                 index;
   APIRET              rc;
   USHORT              DriverRelease;
   USHORT              DLLRelease;

   signal(SIGINT, OS2AbnormalExit);   // Set up Ctrl-C handler.
   signal(SIGBREAK, OS2AbnormalExit); // Set up Ctrl-Break handler.
   signal(SIGTERM, OS2AbnormalExit);
   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 threaded transmit 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("This program's purpose is to exercise the frame and driver\n");
   printf("timeout features of Syncdrive for OS/2.  It is not intended to be a\n");
   printf("perfect example of multithreaded programming techniques.\n\n");


   // Initialize the channel configuration array.

   init_config_array();


   // Now process any options that override the defaults.

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

   // 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", DriverRelease);


   // Configure and open the channel.

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


   // Print configuration details to the screen.

   printf("\nBase address = %x\n", pMPA_cfg->base_address);
   printf("Transmit interrupt = %i\n",pMPA_cfg->tx_interrupt);
   printf("Clockrate = %.li\n",pMPA_cfg->clock_rate);
   printf("Baudrate = %.li\n",pMPA_cfg->tx_baud_rate);
   printf("Number of transmit threads = %i\n", NumThreads);
   printf("Frame level transmit timeout = %li milliseconds\n", pMPA_cfg->write_timeout);
   printf("DLL sync_transmit() function timeout = %li milliseconds\n\n", DLLTimeout);

   printf("Adjust the number of threads, baud rate, and timeout values to see\n");
   printf("frame and DLL timeouts occur.\n\n");
   printf("Press Ctrl-Break to stop the program.\n\n");
   printf("Press any key to run %i threads...\n", NumThreads);
   while(!kbhit());


// Create the semaphore used to count thread completions.
   DosCreateEventSem("\\SEM32\\THREADTX", &MySem, 0, FALSE);

// Spin off Tx threads.
   for (index = 0; index < NumThreads; index++)
      threads[index] = _beginthread(transmit, NULL, 0x2000, NULL);

   printf("All Tx threads started.\n");

// Wait for the Tx threads to complete, then exit.
   while(1)
      {
      DosSleep(100);    // Sleep for 100 ms.
      DosQueryEventSem(MySem, &PostCount);
      if (PostCount == NumThreads)
         exit(0);
      }

}
