home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / ascomlib.zip / bloatcom.c next >
Text File  |  2000-08-26  |  43KB  |  1,662 lines

  1. #pragma strings(readonly)
  2.  
  3. #define INCL_DOSDEVICES
  4. #define INCL_DOSDEVIOCTL
  5. #define INCL_DOSPROCESS
  6. #define INCL_DOSSEMAPHORES
  7. #define INCL_DOSDATETIME
  8. #define INCL_DOSERRORS
  9.  
  10. #include <os2.h>
  11.  
  12. #include <malloc.h>
  13. #include <memory.h>
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <process.h>
  17.  
  18. #include "bloatcom.h"
  19.  
  20.  
  21.  
  22. #define EHB_IGNORE                       0x00
  23. #define EHB_DISABLE                      0x08
  24. #define EHB_ENABLE                       0x10
  25. #define EHB_AUTO                         0x18
  26. #define EHB_MASK                         0x18
  27.  
  28. /* Receive Trigger Levels */
  29. #define EHB_RTL_1                        0x00
  30. #define EHB_RTL_4                        0x20
  31. #define EHB_RTL_8                        0x40
  32. #define EHB_RTL_14                       0x60
  33.  
  34. /* Transmit Buffer Load Counts */
  35. #define EHB_TBLC_1                       0x00
  36. #define EHB_TBLC_16                      0x80
  37.  
  38.  
  39.  
  40.  
  41. #define EVENT_DATAAVAILABLE              1
  42. #define EVENT_TIMEOUT                    2
  43. #define EVENT_CARRIER_LOST               3
  44.  
  45.  
  46. #pragma pack(1)
  47. typedef struct _eqrd
  48. {
  49.   ULONG ulBitRate;
  50.   BYTE  bFraction;
  51.   ULONG ulMinBitRate;
  52.   BYTE  bMinFraction;
  53.   ULONG ulMaxBitRate;
  54.   BYTE  bMaxFraction;
  55. }EXTQUERYRATEDATA;
  56. #pragma pack()
  57.  
  58.  
  59.  
  60.  
  61. typedef struct _MODULOBUFFER
  62. {
  63.    HMTX hmtxLock;                        /* Lock buffer access */
  64.    HEV hevDataAvailable;                 /* Posted when data is available in the data area */
  65.    HEV hevBufferEmpty;                   /* Posted when there is no data available in the data area */
  66.    ULONG iRead;                          /* Read position */
  67.    ULONG iWrite;                         /* Write position */
  68.    ULONG cbBuffer;                       /* Size of data buffer */
  69.    ULONG cbUsed;                         /* Bytes used in buffer */
  70.    ULONG cbFree;                         /* Bytes free in buffer */
  71.    BYTE data[1];                         /* Data area */
  72. }MODULOBUFFER, *PMODULOBUFFER;
  73.  
  74. typedef struct _THREADPAUSE
  75. {
  76.    HEV hevPause;
  77.    HEV hevPaused;
  78.    HEV hevResume;
  79. }THREADPAUSE, *PTHREADPAUSE;
  80.  
  81.  
  82. typedef struct _COMPORT
  83. {
  84.    HFILE hCom;
  85.    ULONG cInstances;
  86.    ULONG bps;
  87.    APIRET rc;
  88.    PMODULOBUFFER inbuf;                  /* Receive buffer */
  89.    PMODULOBUFFER outbuf;                 /* Transmitbuffer */
  90.    TID tidReceive;
  91.    TID tidTransmit;
  92.    #ifdef __TOOLKIT45__
  93.    PVOID pThreadStacks;
  94.    #endif
  95.    HEV hevTerminate;
  96.    HEV hevCarrierLost;                   /* Event sempahore indicating carrier lost */
  97.    PTHREADPAUSE pauseReceive;
  98.    PTHREADPAUSE pauseTransmit;
  99.    BOOL fCheckCarrier;
  100. }COMPORT;
  101.  
  102.  
  103. typedef struct _TIMER
  104. {
  105.    HEV hEvent;
  106.    HTIMER hTimer;
  107. }TIMER;
  108.  
  109.  
  110. /*
  111.  * Internal functions
  112.  */
  113. #ifdef __TOOLKIT45__
  114. static void _System receive_thread(ULONG param);
  115. static void _System transmit_thread(ULONG param);
  116. #else
  117. static void _Optlink receive_thread(void* param);
  118. static void _Optlink transmit_thread(void* param);
  119. #endif
  120.  
  121.  
  122. static BOOL _Optlink init_port(HFILE hCom);
  123.  
  124.  
  125. /*
  126.  * Modulo buffer functions
  127.  */
  128. static PMODULOBUFFER _Optlink init_modulo_buffer(ULONG cbBuffer);
  129. static void _Optlink term_modulo_buffer(PMODULOBUFFER modbuf);
  130. BOOL _Optlink open_modulo_buffer(PMODULOBUFFER modbuf);
  131. BOOL _Optlink close_modulo_buffer(PMODULOBUFFER modbuf);
  132. void _Inline reset_modulo_buffer(PMODULOBUFFER modbuf);
  133.  
  134. ULONG _Inline read_modulo_buffer(PMODULOBUFFER modbuf, PBYTE p, ULONG cb);
  135. ULONG _Inline peek_modulo_buffer(PMODULOBUFFER modbuf, PBYTE p, ULONG cb);
  136. ULONG _Inline write_modulo_buffer(PMODULOBUFFER modbuf, PBYTE p, ULONG cb);
  137.  
  138.  
  139. PTHREADPAUSE _Optlink init_threadpause(void);
  140. BOOL _Optlink term_threadpause(PTHREADPAUSE ptp);
  141. BOOL _Inline pause_thread(PTHREADPAUSE ptp);
  142. BOOL _Inline resume_thread(PTHREADPAUSE ptp);
  143. void _Inline check_thread_pause(PTHREADPAUSE ptp);
  144.  
  145.  
  146.  
  147.  
  148. PCOMPORT _Optlink comOpen(PSZ pszComName, ULONG cbInBuffer, ULONG cbOutBuffer)
  149. {
  150.    APIRET rc = NO_ERROR;
  151.    PCOMPORT cp = NULL;
  152.    HFILE hCom = NULLHANDLE;
  153.    ULONG ulAction = 0;
  154.  
  155.    /* Open port */
  156.    if((rc = DosOpen(pszComName, &hCom, &ulAction, 0L, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE, 0L)) == NO_ERROR)
  157.    {
  158.       char achMemName[64] = "";
  159.       PVOID pAlloc = NULL;
  160.  
  161.       #ifdef DEBUG_TERM
  162.       printf("i hCom = %08x\n", hCom);
  163.       #endif
  164.  
  165.       /* Allocate core structure */
  166.       sprintf(achMemName, "\\SHAREMEM\\BloatCom.%08x", hCom);
  167.       puts(achMemName);
  168.       if((rc = DosAllocSharedMem(&pAlloc, achMemName, sizeof(COMPORT), PAG_READ | PAG_WRITE | PAG_COMMIT)) == NO_ERROR)
  169.       {
  170.          cp = (PCOMPORT)pAlloc;
  171.          memset(cp, 0, sizeof(COMPORT));
  172.          cp->hCom = hCom;
  173.       }
  174.       else
  175.       {
  176.          #ifdef DEBUG_TERM
  177.          puts("e Couldn't allocate memory for COMPORT structure");
  178.          printf("%d\n", rc);
  179.          #endif
  180.          DosClose(hCom);
  181.       }
  182.    }
  183.    else
  184.    {
  185.       #ifdef DEBUG_TERM
  186.       puts("e Couldn't open comport");
  187.       #endif
  188.    }
  189.  
  190.    if(cp)
  191.    {
  192.       THREADCREATE tc = { 0 };
  193.       ULONG cbAlloc = 0;
  194.  
  195.       if(cbInBuffer == 0)
  196.       {
  197.          RXQUEUE rxq = { 0 };
  198.          ULONG cbPinout = 0;
  199.          ULONG cbDinout = sizeof(rxq);
  200.  
  201.          if((rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_GETINQUECOUNT, NULL, 0, &cbPinout, &rxq, sizeof(RXQUEUE), &cbDinout)) == NO_ERROR)
  202.          {
  203.             cbInBuffer = rxq.cb;
  204.             #ifdef DEBUG_TERM
  205.             printf("> Receive buffer set to system default size (%u bytes).\n", cbInBuffer);
  206.             #endif
  207.          }
  208.          else
  209.          {
  210.             #ifdef DEBUG_TERM
  211.             puts("e Could not query system inqueue size");
  212.             #endif
  213.          }
  214.       }
  215.  
  216.       if(cbOutBuffer == 0)
  217.       {
  218.          RXQUEUE rxq = { 0 };
  219.          ULONG cbPinout = 0;
  220.          ULONG cbDinout = sizeof(rxq);
  221.  
  222.          if((rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_GETOUTQUECOUNT, NULL, 0, &cbPinout, &rxq, sizeof(RXQUEUE), &cbDinout)) == NO_ERROR)
  223.          {
  224.             cbOutBuffer = rxq.cb;
  225.             #ifdef DEBUG_TERM
  226.             printf("> Transmit buffer set to system default size (%u bytes).\n", cbOutBuffer);
  227.             #endif
  228.          }
  229.          else
  230.          {
  231.             #ifdef DEBUG_TERM
  232.             puts("e Could not query system outqueue size");
  233.             #endif
  234.          }
  235.       }
  236.  
  237.       /* 115200 BPS */
  238.       comSetBaudrate(cp, 115200UL);
  239.  
  240.       /* 8, n, 1 */
  241.       comSetLineControl(cp, 8, 0, 0);
  242.  
  243.       init_port(cp->hCom);
  244.  
  245.       /* Initialize receive buffer */
  246.       cp->inbuf = init_modulo_buffer(cbInBuffer);
  247.       cp->pauseReceive = init_threadpause();
  248.  
  249.       /* Initialize transmit buffer */
  250.       cp->outbuf = init_modulo_buffer(cbOutBuffer);
  251.       cp->pauseTransmit = init_threadpause();
  252.  
  253.       rc = DosCreateEventSem(NULL, &cp->hevCarrierLost, 0UL, FALSE);
  254.  
  255.       rc = DosCreateEventSem(NULL, &cp->hevTerminate, 0UL, FALSE);
  256.  
  257.       #ifdef __TOOLKIT45__
  258.       cp->pThreadStacks = malloc(8192);
  259.  
  260.       /* Start receive thread */
  261.       memset(&tc, 0, sizeof(tc));
  262.       tc.cbSize = sizeof(THREADCREATE);
  263.       tc.pfnStart = receive_thread;
  264.       tc.lParam = (ULONG)cp;
  265.       tc.lFlag = CREATE_READY | STACK_SPARSE;
  266.       tc.cbStack = 4096;
  267.       tc.pStack = (PBYTE)cp->pThreadStacks+4096;
  268.       if((rc = DosCreateThread2(&tc)) == NO_ERROR)
  269.       {
  270.          cp->tidReceive = (TID)tc.pTid;
  271.       }
  272.  
  273.       /* Start transmit thread */
  274.       memset(&tc, 0, sizeof(tc));
  275.       tc.cbSize = sizeof(THREADCREATE);
  276.       tc.pfnStart = transmit_thread;
  277.       tc.lParam = (ULONG)cp;
  278.       tc.lFlag = CREATE_READY | STACK_SPARSE;
  279.       tc.cbStack = 4096;
  280.       tc.pStack = (PBYTE)cp->pThreadStacks+8192;
  281.       if((rc = DosCreateThread2(&tc)) == NO_ERROR)
  282.       {
  283.          cp->tidTransmit = (TID)tc.pTid;
  284.       }
  285.       #else
  286.       cp->tidReceive = _beginthread(receive_thread, NULL, 16384, (void*)cp);
  287.       cp->tidTransmit = _beginthread(transmit_thread, NULL, 16384, (void*)cp);
  288.       #endif
  289.    }
  290.    return cp;
  291. }
  292.  
  293. BOOL _Optlink comClose(PCOMPORT cp)
  294. {
  295.    APIRET rc = NO_ERROR;
  296.  
  297.    rc = DosPostEventSem(cp->hevTerminate);
  298.  
  299.    /* Wait for both com-threads to terminate */
  300.    DosWaitThread(&cp->tidReceive, DCWW_WAIT);
  301.    DosWaitThread(&cp->tidTransmit, DCWW_WAIT);
  302.  
  303.    puts("> Threads should be dead by now");
  304.  
  305.    DosCloseEventSem(cp->hevTerminate);
  306.  
  307.    DosCloseEventSem(cp->hevCarrierLost);
  308.  
  309.    /* Release receive thread's resources */
  310.    term_modulo_buffer(cp->inbuf);
  311.    term_threadpause(cp->pauseReceive);
  312.  
  313.    /* Release transmitter thread's resources */
  314.    term_modulo_buffer(cp->outbuf);
  315.    term_threadpause(cp->pauseTransmit);
  316.  
  317.    #ifdef __TOOLKIT45__
  318.    /* Release thread stack memory */
  319.    free(cp->pThreadStacks);
  320.    #endif
  321.  
  322.    /* Close communications port */
  323.    DosClose(cp->hCom);
  324.  
  325.    DosFreeMem(cp);
  326.  
  327.    _heapmin();
  328.  
  329.    return TRUE;
  330. }
  331.  
  332. /*
  333.  * Warning -- not finished.
  334.  */
  335. PCOMPORT _Optlink comAquireFromHandle(HFILE hCom)
  336. {
  337.    PCOMPORT cp = NULL;
  338.    APIRET rc = NO_ERROR;
  339.    PVOID pBuffer = NULL;
  340.    char achMemName[64] = "";
  341.  
  342.    sprintf(achMemName, "\\SHAREMEM\\BloatCom.%08x", hCom);
  343.    if((rc = DosGetNamedSharedMem(&pBuffer, achMemName, PAG_READ | PAG_WRITE)) == NO_ERROR)
  344.    {
  345.       cp = (PCOMPORT)pBuffer;
  346.    }
  347.  
  348.    if(cp)
  349.    {
  350.       open_modulo_buffer(cp->inbuf);
  351.       open_modulo_buffer(cp->outbuf);
  352.  
  353.       cp->cInstances++;                  /* Needs to be protected */
  354.    }
  355.  
  356.    /*
  357.     * To Do: Add comReleaseAccess to an exitlist so access will be released at process termination
  358.     */
  359.  
  360.    return cp;
  361. }
  362.  
  363. /*
  364.  * Warning -- not finished.
  365.  */
  366. void _Optlink comDeaquireAccess(PCOMPORT cp)
  367. {
  368.    cp->cInstances--;                     /* Needs to be protected */
  369.  
  370.    close_modulo_buffer(cp->outbuf);
  371.    close_modulo_buffer(cp->inbuf);
  372.  
  373.    DosFreeMem(cp);
  374. }
  375.  
  376. BOOL _Optlink comSetBaudrate(PCOMPORT cp, ULONG bps)
  377. {
  378.    BOOL fSuccess = FALSE;
  379.    ULONG cbPinout = 0;
  380.    ULONG cbDinout = 0;
  381.  
  382.    if(bps <= 0x0000ffff)
  383.    {
  384.       USHORT usRate = (USHORT)bps;
  385.  
  386.       cbPinout = sizeof(usRate);
  387.       cbDinout = 0;
  388.  
  389.       cp->rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_SETBAUDRATE, &usRate, sizeof(usRate), &cbPinout, NULL, 0L, &cbDinout);
  390.    }
  391.    else
  392.    {
  393.       typedef struct _esrp
  394.       {
  395.          ULONG ulBitRate;
  396.          BYTE  bFraction;
  397.       }EXTSETRATEPARMS;
  398.       EXTSETRATEPARMS esr = { 0, 0 };
  399.  
  400.       cbPinout = sizeof(esr);
  401.       cbDinout = 0;
  402.  
  403.       esr.ulBitRate = bps;
  404.       esr.bFraction = 0;
  405.  
  406.       cp->rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &esr, sizeof(esr), &cbPinout, NULL, 0, &cbDinout);
  407.    }
  408.  
  409.    if(cp->rc == NO_ERROR)
  410.    {
  411.       fSuccess = TRUE;
  412.    }
  413.  
  414.    return fSuccess;
  415. }
  416.  
  417.  
  418. BOOL _Optlink comSetLineControl(PCOMPORT cp, BYTE bDataBits, BYTE bParity, BYTE bStopBits)
  419. {
  420.    LINECONTROL lc = { 0 };
  421.    BOOL fSuccess = FALSE;
  422.  
  423.    lc.bDataBits = bDataBits; /* 8 */
  424.    lc.bParity = bParity;     /* N */
  425.    lc.bStopBits = bStopBits; /* 1 */
  426.  
  427.    if((cp->rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_SETLINECTRL, &lc, sizeof(lc), NULL, NULL, 0L, NULL)) == NO_ERROR)
  428.    {
  429.       fSuccess = TRUE;
  430.    }
  431.    return fSuccess;
  432. }
  433.  
  434.  
  435. static BOOL _Optlink init_port(HFILE hCom)
  436. {
  437.    BOOL fSuccess = FALSE;
  438.    APIRET rc = NO_ERROR;
  439.    DCBINFO dcb = { 0 };
  440.    ULONG cbPinout = 0;
  441.    ULONG cbDinout = 0;
  442.    BOOL fCtsRts = TRUE;
  443.    BOOL fXonXoff = FALSE;
  444.    BOOL fEHB = TRUE;
  445.  
  446.    cbPinout = 0;
  447.    cbDinout = sizeof(DCBINFO);
  448.    if((rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, &cbPinout, &dcb, sizeof(DCBINFO), &cbDinout)) == NO_ERROR)
  449.    {
  450.       dcb.fbTimeout &= ~MODE_NO_WRITE_TIMEOUT;
  451.       dcb.fbTimeout &= ~(MODE_READ_TIMEOUT | MODE_NOWAIT_READ_TIMEOUT);
  452.       dcb.fbTimeout |= MODE_WAIT_READ_TIMEOUT;
  453.  
  454.       /* dcb.usReadTimeout  = 100 * 5; */
  455.       dcb.usReadTimeout  = 10;           /* .1 second timeout */
  456.       dcb.usWriteTimeout = 100 * 10;     /* ten second write timeout */
  457.  
  458.       dcb.fbCtlHndShake |= MODE_DTR_CONTROL;
  459.  
  460.       if(fCtsRts)
  461.       {
  462.          /* Enable CTS/RTS */
  463.          dcb.fbCtlHndShake |= MODE_CTS_HANDSHAKE;
  464.          dcb.fbFlowReplace |= MODE_RTS_HANDSHAKE;
  465.          dcb.fbFlowReplace &= ~MODE_RTS_CONTROL;
  466.       }
  467.       else
  468.       {
  469.          /* Disable CTS/RTS */
  470.          dcb.fbCtlHndShake &= ~MODE_CTS_HANDSHAKE;
  471.          dcb.fbFlowReplace &= ~MODE_RTS_HANDSHAKE;
  472.       }
  473.  
  474.       if(fXonXoff)
  475.       {
  476.          dcb.fbFlowReplace |= (MODE_AUTO_TRANSMIT | MODE_AUTO_RECEIVE);
  477.       }
  478.       else
  479.       {
  480.          dcb.fbFlowReplace &= ~(MODE_AUTO_TRANSMIT | MODE_AUTO_RECEIVE);
  481.       }
  482.  
  483.       if(fEHB)
  484.       {
  485.          dcb.fbTimeout &= ~(EHB_MASK | EHB_RTL_14 | EHB_TBLC_16);
  486.          dcb.fbTimeout |= EHB_ENABLE | EHB_RTL_8 | EHB_TBLC_16;
  487.       }
  488.  
  489.       if((rc = DosDevIOCtl(hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, sizeof(DCBINFO), NULL, NULL, 0L, NULL)) == NO_ERROR)
  490.       {
  491.          fSuccess = TRUE;
  492.       }
  493.    }
  494.    return fSuccess;
  495. }
  496.  
  497.  
  498.  
  499. /*
  500.  * Return CDC (CarrierDetect) flag as boolean
  501.  */
  502. BOOL _Optlink comCarrier(PCOMPORT cp)
  503. {
  504.    APIRET rc = NO_ERROR;
  505.    ULONG cbPinout = 0;
  506.    ULONG cbDinout = 0;
  507.    UCHAR uchControl = 0;
  508.    BOOL fCarrier = FALSE;
  509.  
  510.    cbDinout = sizeof(uchControl);
  511.    if((cp->rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL, 0, &cbPinout, &uchControl, sizeof(uchControl), &cbDinout)) == NO_ERROR)
  512.    {
  513.       if(uchControl & DCD_ON)
  514.       {
  515.          fCarrier = TRUE;
  516.       }
  517.    }
  518.    return fCarrier;
  519. }
  520.  
  521.  
  522.  
  523. BOOL _Optlink comLowerDTR(PCOMPORT cp)
  524. {
  525.    BOOL fSuccess = FALSE;
  526.    APIRET rc = NO_ERROR;
  527.    DCBINFO dcb = { 0 };
  528.    ULONG cbPinout = 0;
  529.    ULONG cbDinout = 0;
  530.  
  531.    cbPinout = 0;
  532.    cbDinout = sizeof(DCBINFO);
  533.    if((rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, &cbPinout, &dcb, sizeof(DCBINFO), &cbDinout)) == NO_ERROR)
  534.    {
  535.       dcb.fbCtlHndShake &= ~MODE_DTR_CONTROL;
  536.       if((rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, sizeof(DCBINFO), NULL, NULL, 0L, NULL)) == NO_ERROR)
  537.       {
  538.          fSuccess = TRUE;
  539.       }
  540.    }
  541.    return fSuccess;
  542. }
  543.  
  544. BOOL _Optlink comRaiseDTR(PCOMPORT cp)
  545. {
  546.    BOOL fSuccess = FALSE;
  547.    APIRET rc = NO_ERROR;
  548.    DCBINFO dcb = { 0 };
  549.    ULONG cbPinout = 0;
  550.    ULONG cbDinout = 0;
  551.  
  552.    cbPinout = 0;
  553.    cbDinout = sizeof(DCBINFO);
  554.    if((rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, &cbPinout, &dcb, sizeof(DCBINFO), &cbDinout)) == NO_ERROR)
  555.    {
  556.       dcb.fbCtlHndShake |= MODE_DTR_CONTROL;
  557.       if((rc = DosDevIOCtl(cp->hCom, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, sizeof(DCBINFO), NULL, NULL, 0L, NULL)) == NO_ERROR)
  558.       {
  559.          fSuccess = TRUE;
  560.       }
  561.    }
  562.    return fSuccess;
  563. }
  564.  
  565. BOOL _Optlink comHangup(PCOMPORT cp)
  566. {
  567.    BOOL fSuccess = FALSE;
  568.  
  569.    fSuccess = comLowerDTR(cp);
  570.    if(fSuccess)
  571.    {
  572.       DosSleep(1000);
  573.       fSuccess = comRaiseDTR(cp);
  574.    }
  575.    return fSuccess;
  576. }
  577.  
  578.  
  579. /*
  580.  * Read as much data as possible up to cbRead bytes.
  581.  */
  582. BOOL _Optlink comRead(PCOMPORT cp, PVOID pBuffer, ULONG cbRead, PULONG pcbActual)
  583. {
  584.    APIRET rc = NO_ERROR;
  585.    BOOL fSuccess = FALSE;
  586.  
  587.    /* Lock the in-buffer */
  588.    if((rc = DosRequestMutexSem(cp->inbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  589.    {
  590.       *pcbActual = read_modulo_buffer(cp->inbuf, pBuffer, cbRead);
  591.  
  592.       #if defined(DEBUG_TERM) && DEBUG_LEVEL >= 5
  593.       printf("> %s read %u bytes.\n", __FUNCTION__, (*pcbActual));
  594.       #endif
  595.  
  596.       fSuccess = TRUE;
  597.  
  598.       /* Release the in-buffer */
  599.       rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  600.    }
  601.    return fSuccess;
  602. }
  603.  
  604. /*
  605.  * Read cbRead bytes. Don't return until all has been read, or carrier list in case
  606.  * carrier monitoring has been enabled.
  607.  */
  608. BOOL _Optlink comReadBuffer(PCOMPORT cp, PVOID pBuffer, ULONG cbRead, PULONG pcbActual)
  609. {
  610.    APIRET rc = NO_ERROR;
  611.    BOOL fSuccess = FALSE;
  612.    ULONG cbTotalRead = 0;
  613.  
  614.    while(1)
  615.    {
  616.       if(cp->fCheckCarrier)
  617.       {
  618.          if((rc = DosWaitEventSem(cp->hevCarrierLost, SEM_IMMEDIATE_RETURN)) == NO_ERROR)
  619.          {
  620.             /* If carrier is lost, break out and return failiure */
  621.             break;
  622.          }
  623.       }
  624.  
  625.       /* Lock the in-buffer */
  626.       if((rc = DosRequestMutexSem(cp->inbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  627.       {
  628.          ULONG cbActual = 0UL;
  629.  
  630.          cbActual = read_modulo_buffer(cp->inbuf, (PBYTE)pBuffer + cbTotalRead, cbRead-cbTotalRead);
  631.  
  632.          cbTotalRead += cbActual;
  633.          if(pcbActual)
  634.          {
  635.             (*pcbActual) = cbTotalRead;
  636.          }
  637.  
  638.          #if defined(DEBUG_TERM) && DEBUG_LEVEL >= 5
  639.          printf("> %s: read %u of %u bytes.\n", __FUNCTION__, cbTotalRead, cbRead);
  640.          #endif
  641.  
  642.          if(cbTotalRead == cbRead)
  643.          {
  644.             rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  645.             fSuccess = TRUE;
  646.             break;
  647.          }
  648.  
  649.          /* Release the in-buffer */
  650.          rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  651.       }
  652.       DosSleep(0);
  653.    }
  654.    return fSuccess;
  655. }
  656.  
  657.  
  658. /*
  659.  * Writes as much data as possible to the transmitbuffer.
  660.  */
  661. BOOL _Optlink comWrite(PCOMPORT cp, PVOID pBuffer, ULONG cbWrite, PULONG pcbActual)
  662. {
  663.    APIRET rc = NO_ERROR;
  664.    BOOL fSuccess = FALSE;
  665.  
  666.    if((rc = DosRequestMutexSem(cp->outbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  667.    {
  668.       *pcbActual = write_modulo_buffer(cp->outbuf, pBuffer, cbWrite);
  669.  
  670.       #ifdef DEBUG_TERM
  671.       printf("> %s: wrote %u bytes.\n", __FUNCTION__, (*pcbActual));
  672.       #endif
  673.  
  674.       fSuccess = TRUE;
  675.  
  676.       rc = DosReleaseMutexSem(cp->outbuf->hmtxLock);
  677.    }
  678.    return fSuccess;
  679. }
  680.  
  681. /*
  682.  * Writes data to transmit buffer
  683.  */
  684. BOOL _Optlink comWriteBuffer(PCOMPORT cp, PVOID pBuffer, ULONG cbWrite, PULONG pcbActual)
  685. {
  686.    APIRET rc = NO_ERROR;
  687.    BOOL fSuccess = FALSE;
  688.    ULONG cbWritten = 0;
  689.  
  690.    while(cbWrite)
  691.    {
  692.       if(cp->fCheckCarrier)
  693.       {
  694.          if((rc = DosWaitEventSem(cp->hevCarrierLost, SEM_IMMEDIATE_RETURN)) == NO_ERROR)
  695.          {
  696.             break;
  697.          }
  698.       }
  699.  
  700.       if((rc = DosRequestMutexSem(cp->outbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  701.       {
  702.          ULONG cbActual = write_modulo_buffer(cp->outbuf, (PBYTE)pBuffer+cbWritten, cbWrite);
  703.  
  704.          #ifdef DEBUG_TERM
  705.          printf("> %s: wrote %u bytes.\n", __FUNCTION__, (*pcbActual));
  706.          #endif
  707.  
  708.          cbWrite -= cbActual;
  709.          cbWritten += cbActual;
  710.  
  711.          if(pcbActual)
  712.          {
  713.             (*pcbActual) = cbWritten;
  714.          }
  715.  
  716.          if(cbWrite == 0)
  717.          {
  718.             fSuccess = TRUE;
  719.          }
  720.  
  721.          rc = DosReleaseMutexSem(cp->outbuf->hmtxLock);
  722.       }
  723.    }
  724.  
  725.    return fSuccess;
  726. }
  727.  
  728.  
  729.  
  730. SHORT _Optlink comReadChar(PCOMPORT cp)
  731. {
  732.    APIRET rc = NO_ERROR;
  733.    SHORT sRet = -1;
  734.  
  735.    /* Request access to the buffer */
  736.    if((rc = DosRequestMutexSem(cp->inbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  737.    {
  738.       BYTE b = 0;
  739.  
  740.       if(read_modulo_buffer(cp->inbuf, &b, 1UL))
  741.       {
  742.          /* printf("> %s read '%c' character.\n", __FUNCTION__, b); */
  743.  
  744.          sRet = (SHORT)b;
  745.       }
  746.  
  747.       rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  748.    }
  749.    return sRet;
  750. }
  751.  
  752. SHORT _Optlink comPeekChar(PCOMPORT cp)
  753. {
  754.    APIRET rc = NO_ERROR;
  755.    SHORT sRet = -1;
  756.  
  757.    /* Request access to the buffer */
  758.    if((rc = DosRequestMutexSem(cp->inbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  759.    {
  760.       BYTE b = 0;
  761.  
  762.       if(peek_modulo_buffer(cp->inbuf, &b, 1UL))
  763.       {
  764.          /* printf("> %s read '%c' character.\n", __FUNCTION__, b); */
  765.  
  766.          sRet = (SHORT)b;
  767.       }
  768.  
  769.       rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  770.    }
  771.    return sRet;
  772. }
  773.  
  774.  
  775. /*
  776.  * Read a single character; wait until it becomes available for a speciefied amout of time.
  777.  */
  778. SHORT comReadCharWait(PCOMPORT cp, ULONG ulMS)
  779. {
  780.    APIRET rc = NO_ERROR;
  781.    SHORT sRet = -1;
  782.  
  783.    if((rc = DosWaitEventSem(cp->inbuf->hevDataAvailable, ulMS)) == NO_ERROR)
  784.    {
  785.       /* Request access to the buffer */
  786.       if((rc = DosRequestMutexSem(cp->inbuf->hmtxLock, (ULONG)SEM_INDEFINITE_WAIT)) == NO_ERROR)
  787.       {
  788.          BYTE b = 0;
  789.  
  790.          if(read_modulo_buffer(cp->inbuf, &b, 1UL))
  791.          {
  792.             /* printf("> %s read '%c' character.\n", __FUNCTION__, b); */
  793.  
  794.             sRet = (SHORT)b;
  795.          }
  796.          rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  797.       }
  798.    }
  799.    return sRet;
  800. }
  801.  
  802.  
  803.  
  804. BOOL _Optlink comFlushTransmitBuffer(PCOMPORT cp)
  805. {
  806.    BOOL fSuccess = FALSE;
  807.    SEMRECORD aSemRec[2] = { 0 };
  808.    int i = 0;
  809.    HMUX hmuxEvent = NULLHANDLE;
  810.  
  811.    /* Wait for outbuffer to empty */
  812.    aSemRec[i].hsemCur = (HSEM)cp->outbuf->hevBufferEmpty;
  813.    aSemRec[i++].ulUser = 0;
  814.  
  815.    if(cp->fCheckCarrier)
  816.    {
  817.       /* Monitor carrier-lost event semaphore */
  818.       aSemRec[i].hsemCur = (HSEM)cp->hevCarrierLost;
  819.       aSemRec[i++].ulUser = 1;
  820.    }
  821.  
  822.    /* Create a multiple wait semaphore */
  823.    if((cp->rc = DosCreateMuxWaitSem(NULL, &hmuxEvent, i, aSemRec, DCMW_WAIT_ANY)) == NO_ERROR)
  824.    {
  825.       ULONG ulUser = 0;
  826.  
  827.       /* Wait for an event */
  828.       if((cp->rc = DosWaitMuxWaitSem(hmuxEvent, (ULONG)SEM_INDEFINITE_WAIT, &ulUser)) == NO_ERROR)
  829.       {
  830.          switch(ulUser)
  831.          {
  832.             case 0:
  833.                fSuccess = TRUE;
  834.                break;
  835.  
  836.             case 1:
  837.                fSuccess = FALSE;
  838.                break;
  839.          }
  840.       }
  841.       cp->rc = DosCloseMuxWaitSem(hmuxEvent);
  842.    }
  843.    return fSuccess;
  844. }
  845.  
  846.  
  847.  
  848.  
  849.  
  850.  
  851. BOOL _Optlink comWaitForEvent(PCOMPORT cp, PTIMER ptim, ULONG fl)
  852. {
  853.    HMUX hmuxTermOrChar = NULLHANDLE;
  854.    SEMRECORD aSemRec[3] = { 0 };
  855.    ULONG cSemRec = 0;
  856.    BOOL fSuccess = FALSE;
  857.    ULONG i = 0;
  858.    APIRET rc = NO_ERROR;
  859.    ULONG cPosts = 0;
  860.  
  861.    if(cp->fCheckCarrier)
  862.    {
  863.       aSemRec[i].hsemCur = (HSEM)cp->hevCarrierLost;
  864.       aSemRec[i].ulUser = EVENT_CARRIER_LOST;
  865.       i++;
  866.    }
  867.  
  868.    if(fl & FLG_TIMEOUT)
  869.    {
  870.       aSemRec[i].hsemCur = (HSEM)ptim->hEvent;
  871.       aSemRec[i].ulUser = EVENT_TIMEOUT;
  872.       i++;
  873.    }
  874.  
  875.    aSemRec[i].hsemCur = (HSEM)cp->inbuf->hevDataAvailable;
  876.    aSemRec[i].ulUser = EVENT_DATAAVAILABLE;
  877.    i++;
  878.  
  879.    cSemRec = i;
  880.  
  881.    /* Create a multiple wait semaphore which waits for in characters to get available or for timeout event sempahore to get posted */
  882.    rc = DosCreateMuxWaitSem(NULL, &hmuxTermOrChar, cSemRec, aSemRec, DCMW_WAIT_ANY);
  883.    if(rc == NO_ERROR)
  884.    {
  885.       ULONG ulUser = 0;
  886.  
  887.       if((rc = DosWaitMuxWaitSem(hmuxTermOrChar, (ULONG)SEM_INDEFINITE_WAIT, &ulUser)) == NO_ERROR)
  888.       {
  889.          switch(ulUser)
  890.          {
  891.             case EVENT_DATAAVAILABLE:
  892.                fSuccess = TRUE;
  893.                break;
  894.  
  895.             case EVENT_TIMEOUT:
  896.             case EVENT_CARRIER_LOST:
  897.                fSuccess = FALSE;
  898.                break;
  899.          }
  900.       }
  901.  
  902.       rc = DosCloseMuxWaitSem(hmuxTermOrChar);
  903.    }
  904.    return fSuccess;
  905. }
  906.  
  907.  
  908. BOOL _Optlink comWaitFor(PCOMPORT cp, PBYTE buf, ULONG cb, PTIMER ptim, ULONG fl)
  909. {
  910.    HMUX hmuxTermOrChar = NULLHANDLE;
  911.    SEMRECORD aSemRec[3] = { 0 };
  912.    ULONG cSemRec = 0;
  913.    BOOL fSuccess = FALSE;
  914.    ULONG i = 0;
  915.    APIRET rc = NO_ERROR;
  916.    ULONG cPosts = 0;
  917.  
  918.    if(fl & FLG_CARRIER_LOST)
  919.    {
  920.       aSemRec[i].hsemCur = (HSEM)cp->hevCarrierLost;
  921.       aSemRec[i].ulUser = EVENT_CARRIER_LOST;
  922.       i++;
  923.    }
  924.  
  925.    if(fl & FLG_TIMEOUT)
  926.    {
  927.       aSemRec[i].hsemCur = (HSEM)ptim->hEvent;
  928.       aSemRec[i].ulUser = EVENT_TIMEOUT;
  929.       i++;
  930.    }
  931.  
  932.    aSemRec[i].hsemCur = (HSEM)cp->inbuf->hevDataAvailable;
  933.    aSemRec[i].ulUser = EVENT_DATAAVAILABLE;
  934.    i++;
  935.  
  936.    cSemRec = i;
  937.  
  938.    /* Create a multiple wait semaphore which waits for in characters to get available or for timeout event sempahore to get posted */
  939.    if((rc = DosCreateMuxWaitSem(NULL, &hmuxTermOrChar, cSemRec, aSemRec, DCMW_WAIT_ANY)) == NO_ERROR)
  940.    {
  941.       ULONG ulUser = 0;
  942.       BOOL fDone = FALSE;
  943.  
  944.       i = 0;
  945.  
  946.       while(!fDone)
  947.       {
  948.          if((rc = DosWaitMuxWaitSem(hmuxTermOrChar, (ULONG)SEM_INDEFINITE_WAIT, &ulUser)) == NO_ERROR)
  949.          {
  950.             char ch = 0;
  951.             switch(ulUser)
  952.             {
  953.                case EVENT_DATAAVAILABLE:
  954.                   ch = (char)comReadChar(cp);
  955.                   if(buf[i++] == ch)
  956.                   {
  957.                      if(i == cb)
  958.                      {
  959.                         fSuccess = TRUE;
  960.                         fDone = TRUE;
  961.                      }
  962.                   }
  963.                   else
  964.                   {
  965.                      i = 0;
  966.                   }
  967.                   break;
  968.  
  969.                case EVENT_TIMEOUT:
  970.                   fDone = TRUE;
  971.                   break;
  972.  
  973.                case EVENT_CARRIER_LOST:
  974.                   fDone = TRUE;
  975.                   break;
  976.             }
  977.          }
  978.          else
  979.          {
  980.             printf("*** ERROR: DosWaitMuxWaitSem() returned %u.\n", rc);
  981.          }
  982.       }
  983.  
  984.       rc = DosCloseMuxWaitSem(hmuxTermOrChar);
  985.    }
  986.    else
  987.    {
  988.       printf("***ERROR: DosCreateMuxWaitSem() returned %u.\n", rc);
  989.    }
  990.  
  991.    return fSuccess;
  992. }
  993.  
  994. BOOL _Optlink comReadWait(PCOMPORT cp, PBYTE pBuffer, ULONG cbRead, PULONG pcbActual, PTIMER ptim, ULONG fl)
  995. {
  996.    HMUX hmuxTermOrChar = NULLHANDLE;
  997.    SEMRECORD aSemRec[3] = { 0 };
  998.    ULONG cSemRec = 0;
  999.    BOOL fSuccess = FALSE;
  1000.    ULONG i = 0;
  1001.    APIRET rc = NO_ERROR;
  1002.    ULONG cPosts = 0;
  1003.  
  1004.    *pcbActual = 0;
  1005.  
  1006.    if(fl & FLG_CARRIER_LOST)
  1007.    {
  1008.       aSemRec[i].hsemCur = (HSEM)cp->hevCarrierLost;
  1009.       aSemRec[i].ulUser = EVENT_CARRIER_LOST;
  1010.       i++;
  1011.    }
  1012.  
  1013.    if(fl & FLG_TIMEOUT)
  1014.    {
  1015.       aSemRec[i].hsemCur = (HSEM)ptim->hEvent;
  1016.       aSemRec[i].ulUser = EVENT_TIMEOUT;
  1017.       i++;
  1018.    }
  1019.  
  1020.    aSemRec[i].hsemCur = (HSEM)cp->inbuf->hevDataAvailable;
  1021.    aSemRec[i].ulUser = EVENT_DATAAVAILABLE;
  1022.    i++;
  1023.  
  1024.    cSemRec = i;
  1025.  
  1026.    /* Create a multiple wait semaphore which waits for in characters to get available or for timeout event sempahore to get posted */
  1027.    if((rc = DosCreateMuxWaitSem(NULL, &hmuxTermOrChar, cSemRec, aSemRec, DCMW_WAIT_ANY)) == NO_ERROR)
  1028.    {
  1029.       ULONG ulUser = 0;
  1030.       BOOL fDone = FALSE;
  1031.  
  1032.       i = 0;
  1033.  
  1034.       while(cbRead)
  1035.       {
  1036.          if((rc = DosWaitMuxWaitSem(hmuxTermOrChar, (ULONG)SEM_INDEFINITE_WAIT, &ulUser)) == NO_ERROR)
  1037.          {
  1038.             char ch = 0;
  1039.             switch(ulUser)
  1040.             {
  1041.                case EVENT_DATAAVAILABLE:
  1042.                   ch = (char)comReadChar(cp);
  1043.                   *pBuffer++ = ch;
  1044.                   cbRead--;
  1045.                   *(pcbActual) += 1;
  1046.                   break;
  1047.  
  1048.                case EVENT_TIMEOUT:
  1049.                   fDone = TRUE;
  1050.                   break;
  1051.  
  1052.                case EVENT_CARRIER_LOST:
  1053.                   fDone = TRUE;
  1054.                   break;
  1055.             }
  1056.          }
  1057.          else
  1058.          {
  1059.             printf("*** ERROR: DosWaitMuxWaitSem() returned %u.\n", rc);
  1060.          }
  1061.       }
  1062.  
  1063.       rc = DosCloseMuxWaitSem(hmuxTermOrChar);
  1064.    }
  1065.    else
  1066.    {
  1067.       printf("***ERROR: DosCreateMuxWaitSem() returned %u.\n", rc);
  1068.    }
  1069.  
  1070.    return fSuccess;
  1071. }
  1072.  
  1073.  
  1074.  
  1075.  
  1076. void _Optlink comMonitorCarrier(PCOMPORT cp, BOOL fState)
  1077. {
  1078.    printf("> %s: Attempt to pause receive thread\n", __FUNCTION__);
  1079.  
  1080.    if(pause_thread(cp->pauseReceive))
  1081.    {
  1082.       ULONG cPosts = 0;
  1083.       APIRET rc = NO_ERROR;
  1084.  
  1085.       #ifdef DEBUG_TERM
  1086.       printf("> %s: Receive thread paused\n", __FUNCTION__);
  1087.       #endif
  1088.  
  1089.       rc = DosQueryEventSem(cp->hevCarrierLost, &cPosts);
  1090.       if(!rc && cPosts)
  1091.       {
  1092.          rc = DosResetEventSem(cp->hevCarrierLost, &cPosts);
  1093.       }
  1094.  
  1095.       cp->fCheckCarrier = fState;
  1096.  
  1097.       if(resume_thread(cp->pauseReceive))
  1098.       {
  1099.          #ifdef DEBUG_TERM
  1100.          printf("> %s: Receive thread resumed\n", __FUNCTION__);
  1101.          #endif
  1102.       }
  1103.    }
  1104. }
  1105.  
  1106.  
  1107. HEV _Optlink queryInDataAvailableSemHandle(PCOMPORT cp)
  1108. {
  1109.    return cp->inbuf->hevDataAvailable;
  1110. }
  1111.  
  1112. HEV _Optlink queryCarrierLostSemHandle(PCOMPORT cp)
  1113. {
  1114.    return cp->hevCarrierLost;
  1115. }
  1116.  
  1117.  
  1118.  
  1119.  
  1120. #ifdef __TOOLKIT45__
  1121. static void _System receive_thread(ULONG param)
  1122. #else
  1123. static void _Optlink receive_thread(void* param)
  1124. #endif
  1125. {
  1126.    PCOMPORT cp = (PCOMPORT)param;
  1127.    APIRET rc = NO_ERROR;
  1128.    BOOL fTerminate = FALSE;
  1129.    const ULONG cbTempMax = 65536;
  1130.    ULONG cbTemp = 0;
  1131.    PBYTE pTemp = malloc(cbTempMax);
  1132.    ULONG cTermPosts = 0;
  1133.  
  1134.    printf("> Receive thread started. cp = %08x, hCom = %08x\n", cp, cp->hCom);
  1135.  
  1136.    while(!cTermPosts)
  1137.    {
  1138.       ULONG cbRead = 0;
  1139.       ULONG cbActual = 0;
  1140.  
  1141.       /* Check if a pause thread has been requested */
  1142.       check_thread_pause(cp->pauseReceive);
  1143.  
  1144.       /* Get data from COM port into temporary buffer */
  1145.       if((rc = DosRead(cp->hCom, pTemp+cbTemp, cbTempMax-cbTemp, &cbActual)) == NO_ERROR)
  1146.       {
  1147.          #ifdef DEBUG_TERM
  1148.          printf("> DosRead() cbActual = %u.\n", cbActual);
  1149.          #endif
  1150.          cbTemp += cbActual;
  1151.       }
  1152.       else
  1153.       {
  1154.          printf("*** ERROR: DosRead(%08x, %08x, %u) returned %u.\n", cp->hCom, pTemp+cbTemp, cbTempMax-cbTemp, rc);
  1155.          printf("           cbActual = %u\n", cbActual);
  1156.       }
  1157.  
  1158.       /*
  1159.        * Check if carrier detect monitoring is enabled
  1160.        * Note: This should be done after DosRead() but _before_ transfering data to the modulo-buffer, since
  1161.        *       it makes sure that the "NO CARRIER" string won't be missed by the application.
  1162.        */
  1163.       if(cp->fCheckCarrier)
  1164.       {
  1165.          if(!comCarrier(cp))
  1166.          {
  1167.             DosPostEventSem(cp->hevCarrierLost);
  1168.          }
  1169.          cp->fCheckCarrier = FALSE;
  1170.       }
  1171.  
  1172.       if(cbTemp)
  1173.       {
  1174.          ULONG cbWritten = 0;
  1175.  
  1176.          /* Request receive buffer access */
  1177.          if((rc = DosRequestMutexSem(cp->inbuf->hmtxLock, SEM_INDEFINITE_WAIT)) == NO_ERROR)
  1178.          {
  1179.             ULONG cbWritten = 0;
  1180.  
  1181.             /* Store data in modulo buffer */
  1182.             cbWritten = write_modulo_buffer(cp->inbuf, pTemp, cbTemp);
  1183.             cbTemp -= cbWritten;
  1184.  
  1185.             /* If entire buffer wasn't transferred, move the rest of the data to the beginning of the temp buffer */
  1186.             if(cbTemp)
  1187.             {
  1188.                memmove(pTemp, pTemp+cbWritten, cbTemp);
  1189.             }
  1190.  
  1191.             /* Release receive buffer access */
  1192.             rc = DosReleaseMutexSem(cp->inbuf->hmtxLock);
  1193.          }
  1194.       }
  1195.       DosSleep(0);
  1196.  
  1197.       /* Check for termination event sempahore */
  1198.       rc = DosQueryEventSem(cp->hevTerminate, &cTermPosts);
  1199.    }
  1200.    free(pTemp);
  1201.  
  1202.    printf("> %s ending.\n", __FUNCTION__);
  1203.  
  1204.    #ifdef __TOOLKIT45__
  1205.    DosExit(EXIT_THREAD, 0UL);
  1206.    #else
  1207.    _endthread();
  1208.    #endif
  1209. }
  1210.  
  1211.  
  1212. #ifdef __TOOLKIT45__
  1213. static void _System transmit_thread(ULONG param)
  1214. #else
  1215. static void _Optlink transmit_thread(void* param)
  1216. #endif
  1217. {
  1218.    PCOMPORT cp = (PCOMPORT)param;
  1219.    APIRET rc = NO_ERROR;
  1220.    ULONG cTermPosts = 0;
  1221.    ULONG cPosts = 0;
  1222.    const ULONG cbTempMax = 65536;
  1223.    PBYTE pTemp = malloc(cbTempMax);
  1224.    HMUX hmuxEvent = NULLHANDLE;
  1225.    BOOL fTerminate = FALSE;
  1226.    SEMRECORD aSemRec[2] = { 0 };
  1227.  
  1228.    printf("> Transmit thread started. cp = %08x, hCom = %08x\n", cp, cp->hCom);
  1229.  
  1230.    /*
  1231.     * Monitor termination event and "data available" event
  1232.     * Note: It's important that termination is first since MuxWait checks events
  1233.     *       in the same order they are added. Termination should have a higher
  1234.     *       priority than in data event.
  1235.     */
  1236.    aSemRec[0].hsemCur = (HSEM)cp->hevTerminate;
  1237.    aSemRec[0].ulUser = 0;
  1238.    aSemRec[1].hsemCur = (HSEM)cp->outbuf->hevDataAvailable;
  1239.    aSemRec[1].ulUser = 1;
  1240.  
  1241.    if((rc = DosCreateMuxWaitSem(NULL, &hmuxEvent, 2, aSemRec, DCMW_WAIT_ANY)) == NO_ERROR)
  1242.    {
  1243.       while(!fTerminate)
  1244.       {
  1245.          ULONG ulUser = 0;
  1246.  
  1247.          /* Wait for termination or new data event */
  1248.          if((rc = DosWaitMuxWaitSem(hmuxEvent, (ULONG)SEM_INDEFINITE_WAIT, &ulUser)) == NO_ERROR)
  1249.          {
  1250.             switch(ulUser)
  1251.             {
  1252.                case 0:
  1253.                   fTerminate = TRUE;
  1254.                   break;
  1255.  
  1256.                case 1:
  1257.                   if((rc = DosRequestMutexSem(cp->outbuf->hmtxLock, SEM_INDEFINITE_WAIT)) == NO_ERROR)
  1258.                   {
  1259.                      ULONG cbTemp = 0;
  1260.  
  1261.                      cbTemp = read_modulo_buffer(cp->outbuf, pTemp, cbTempMax);
  1262.  
  1263.                      /* Loop until all data has been written to the port */
  1264.                      while(cbTemp)
  1265.                      {
  1266.                         ULONG cbActual = 0;
  1267.  
  1268.                         #ifdef DEBUG
  1269.                         printf("> DosWrite(%08x, %08x, %u)\n", cp->hCom, pTemp, cbTemp);
  1270.                         #endif
  1271.  
  1272.                         if((rc = DosWrite(cp->hCom, pTemp, cbTemp, &cbActual)) == NO_ERROR)
  1273.                         {
  1274.                            #ifdef DEBUG
  1275.                            printf("> DosWrite() wrote %u bytes.\n", cbActual);
  1276.                            #endif
  1277.                         }
  1278.                         else
  1279.                         {
  1280.                            printf("*** ERROR: DosWrite(%08x, %08x, %u) returned %u.\n", cp->hCom, pTemp, cbTemp, rc);
  1281.                            printf("           cbActual = %u\n", cbActual);
  1282.                         }
  1283.                         cbTemp -= cbActual;
  1284.                         if(cbTemp)
  1285.                         {
  1286.                            memmove(pTemp, pTemp+cbActual, cbTemp);
  1287.                            DosSleep(0);
  1288.                         }
  1289.                      }
  1290.                      rc = DosReleaseMutexSem(cp->outbuf->hmtxLock);
  1291.                   }
  1292.                   break;
  1293.             }
  1294.          }
  1295.       }
  1296.    }
  1297.  
  1298.    DosCloseMuxWaitSem(hmuxEvent);
  1299.  
  1300.    free(pTemp);
  1301.  
  1302.    printf("> %s ending.\n", __FUNCTION__);
  1303.  
  1304.    #ifdef __TOOLKIT45__
  1305.    DosExit(EXIT_THREAD, 0UL);
  1306.    #else
  1307.    _endthread();
  1308.    #endif
  1309. }
  1310.  
  1311.  
  1312.  
  1313.  
  1314. static PMODULOBUFFER _Optlink init_modulo_buffer(ULONG cbBuffer)
  1315. {
  1316.    PMODULOBUFFER modbuf = NULL;
  1317.    APIRET rc = NO_ERROR;
  1318.    PVOID pAlloc = NULL;
  1319.    ULONG cbAlloc = cbBuffer+(sizeof(MODULOBUFFER)-1);
  1320.  
  1321.    /*
  1322.     * To do: Allocate core structure and in/out modulo buffers in one memory object.
  1323.     */
  1324.  
  1325.    /* Allocate modulo buffer header and modulo buffer data area */
  1326.    if((rc = DosAllocSharedMem(&pAlloc, NULL, cbAlloc, PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_GETTABLE)) == NO_ERROR)
  1327.    {
  1328.       memset(pAlloc, 0, cbAlloc);
  1329.  
  1330.       modbuf = (PMODULOBUFFER)pAlloc;
  1331.    }
  1332.  
  1333.    if(modbuf)
  1334.    {
  1335.       rc = DosCreateEventSem(NULL, &modbuf->hevDataAvailable, DC_SEM_SHARED, FALSE);
  1336.       if(rc == NO_ERROR)
  1337.       {
  1338.          rc = DosCreateEventSem(NULL, &modbuf->hevBufferEmpty, DC_SEM_SHARED, TRUE);
  1339.       }
  1340.       if(rc == NO_ERROR)
  1341.       {
  1342.          rc = DosCreateMutexSem(NULL, &modbuf->hmtxLock, DC_SEM_SHARED, FALSE);
  1343.       }
  1344.  
  1345.       modbuf->cbBuffer = cbBuffer;
  1346.  
  1347.       reset_modulo_buffer(modbuf);
  1348.    }
  1349.    return modbuf;
  1350. }
  1351.  
  1352. static void _Optlink term_modulo_buffer(PMODULOBUFFER modbuf)
  1353. {
  1354.    DosCloseMutexSem(modbuf->hmtxLock);
  1355.    DosCloseEventSem(modbuf->hevBufferEmpty);
  1356.    DosCloseEventSem(modbuf->hevDataAvailable);
  1357.  
  1358.    DosFreeMem(modbuf);
  1359. }
  1360.  
  1361. BOOL _Optlink open_modulo_buffer(PMODULOBUFFER modbuf)
  1362. {
  1363.    APIRET rc = NO_ERROR;
  1364.    if((rc = DosGetSharedMem(modbuf, PAG_READ | PAG_WRITE)) == NO_ERROR)
  1365.    {
  1366.       DosOpenEventSem(NULL, &modbuf->hevDataAvailable);
  1367.       DosOpenEventSem(NULL, &modbuf->hevBufferEmpty);
  1368.       DosOpenMutexSem(NULL, &modbuf->hmtxLock);
  1369.    }
  1370.    return TRUE;
  1371. }
  1372.  
  1373. BOOL _Optlink close_modulo_buffer(PMODULOBUFFER modbuf)
  1374. {
  1375.    DosCloseMutexSem(modbuf->hmtxLock);
  1376.    DosCloseEventSem(modbuf->hevBufferEmpty);
  1377.    DosCloseEventSem(modbuf->hevDataAvailable);
  1378.    DosFreeMem(modbuf);
  1379.    return TRUE;
  1380. }
  1381.  
  1382. void _Inline reset_modulo_buffer(PMODULOBUFFER modbuf)
  1383. {
  1384.    APIRET rc = NO_ERROR;
  1385.    ULONG cPosts = 0;
  1386.  
  1387.    modbuf->iRead = modbuf->iWrite = 0UL;
  1388.    modbuf->cbFree = modbuf->cbBuffer;
  1389.    modbuf->cbUsed = 0;
  1390.  
  1391.    rc = DosResetEventSem(modbuf->hevDataAvailable, &cPosts);
  1392.    rc = DosPostEventSem(modbuf->hevBufferEmpty);
  1393. }
  1394.  
  1395. ULONG _Inline read_modulo_buffer(PMODULOBUFFER modbuf, PBYTE p, ULONG cb)
  1396. {
  1397.    ULONG c = 0;
  1398.    APIRET rc = NO_ERROR;
  1399.    ULONG cPosts = 0;
  1400.  
  1401.    while(cb-- && modbuf->cbUsed)
  1402.    {
  1403.       /* printf("Read %02x\n", p[c]); */
  1404.       p[c++] = modbuf->data[modbuf->iRead];                   /* Copy character from receive buffer aux buffer */
  1405.       modbuf->iRead = (modbuf->iRead+1) % modbuf->cbBuffer;   /* Move read pointer forward */
  1406.       modbuf->cbFree++;
  1407.       modbuf->cbUsed--;
  1408.    }
  1409.    rc = DosQueryEventSem(modbuf->hevDataAvailable, &cPosts);
  1410.    if(modbuf->cbUsed == 0UL && cPosts)
  1411.    {
  1412.       rc = DosResetEventSem(modbuf->hevDataAvailable, &cPosts);
  1413.       #ifdef DEBUG
  1414.       if(rc == NO_ERROR)
  1415.       {
  1416.          puts("> Reset data available semaphore successful");
  1417.       }
  1418.       #endif
  1419.    }
  1420.  
  1421.    rc = DosQueryEventSem(modbuf->hevBufferEmpty, &cPosts);
  1422.    if(modbuf->cbUsed == 0UL && cPosts)
  1423.    {
  1424.       rc = DosResetEventSem(modbuf->hevBufferEmpty, &cPosts);
  1425.    }
  1426.  
  1427.    return c;
  1428. }
  1429.  
  1430. /*
  1431.  * Same as read_modulo_buffer, but it doesn't change buffer pointers or other information
  1432.  */
  1433. ULONG _Inline peek_modulo_buffer(PMODULOBUFFER modbuf, PBYTE p, ULONG cb)
  1434. {
  1435.    ULONG c = 0;
  1436.    APIRET rc = NO_ERROR;
  1437.    ULONG cPosts = 0;
  1438.    ULONG iTemp = modbuf->iRead;
  1439.  
  1440.    cb = min(cb, modbuf->cbUsed);
  1441.    while(cb--)
  1442.    {
  1443.       p[c++] = modbuf->data[iTemp];            /* Copy character from receive buffer aux buffer */
  1444.       iTemp = (iTemp+1) % modbuf->cbBuffer;    /* Move temporary read pointer forward */
  1445.    }
  1446.  
  1447.    return c;
  1448. }
  1449.  
  1450.  
  1451. ULONG _Inline write_modulo_buffer(PMODULOBUFFER modbuf, PBYTE p, ULONG cb)
  1452. {
  1453.    ULONG c = 0;
  1454.  
  1455.    while(cb-- && modbuf->cbFree)
  1456.    {
  1457.       /* printf("Write %02x\n", p[c]); */
  1458.       modbuf->data[modbuf->iWrite] = p[c++];                        /* Copy character to modulo buffer */
  1459.       modbuf->iWrite = (modbuf->iWrite + 1) % modbuf->cbBuffer;     /* Move write pointer forward */
  1460.       modbuf->cbFree--;
  1461.       modbuf->cbUsed++;
  1462.    }
  1463.    if(modbuf->cbUsed)
  1464.    {
  1465.       APIRET rc = NO_ERROR;
  1466.       ULONG cPosts = 0;
  1467.       rc = DosQueryEventSem(modbuf->hevDataAvailable, &cPosts);
  1468.       if(!cPosts)
  1469.       {
  1470.          rc = DosPostEventSem(modbuf->hevDataAvailable);
  1471.          #ifdef DEBUG
  1472.          if(rc == NO_ERROR)
  1473.          {
  1474.             puts("> Posted data available semaphore");
  1475.          }
  1476.          #endif
  1477.       }
  1478.    }
  1479.    else
  1480.    {
  1481.       APIRET rc = NO_ERROR;
  1482.       ULONG cPosts = 0;
  1483.       rc = DosQueryEventSem(modbuf->hevBufferEmpty, &cPosts);
  1484.       if(!cPosts)
  1485.       {
  1486.          rc = DosPostEventSem(modbuf->hevBufferEmpty);
  1487.       }
  1488.    }
  1489.  
  1490.    return c;
  1491. }
  1492.  
  1493.  
  1494.  
  1495.  
  1496.  
  1497. PTIMER _Optlink init_timer(void)
  1498. {
  1499.    APIRET rc = NO_ERROR;
  1500.    PTIMER ptim = (PTIMER)malloc(sizeof(TIMER));
  1501.  
  1502.    memset(ptim, 0, sizeof(TIMER));
  1503.    if((rc = DosCreateEventSem(NULL, &ptim->hEvent, DC_SEM_SHARED, FALSE)) != NO_ERROR)
  1504.    {
  1505.       free(ptim);
  1506.       ptim = NULL;
  1507.    }
  1508.    return ptim;
  1509. }
  1510.  
  1511. BOOL _Optlink term_timer(PTIMER ptim)
  1512. {
  1513.    APIRET rc = NO_ERROR;
  1514.  
  1515.    if((rc = DosCloseEventSem(ptim->hEvent)) == NO_ERROR)
  1516.    {
  1517.       return TRUE;
  1518.    }
  1519.    return FALSE;
  1520. }
  1521.  
  1522. BOOL _Optlink start_timer(PTIMER ptim, ULONG ulMS)
  1523. {
  1524.    APIRET rc = NO_ERROR;
  1525.    ULONG cPosts = 0;
  1526.    BOOL fSuccess = FALSE;
  1527.  
  1528.    rc = DosQueryEventSem(ptim->hEvent, &cPosts);
  1529.    if(rc == NO_ERROR && cPosts)
  1530.    {
  1531.       DosResetEventSem(ptim->hEvent, &cPosts);
  1532.    }
  1533.    if((rc = DosAsyncTimer(ulMS, (HSEM)ptim->hEvent, &ptim->hTimer)) == NO_ERROR)
  1534.    {
  1535.       fSuccess = TRUE;
  1536.    }
  1537.    return fSuccess;
  1538. }
  1539.  
  1540. BOOL _Optlink stop_timer(PTIMER ptim)
  1541. {
  1542.    APIRET rc = NO_ERROR;
  1543.    rc = DosStopTimer(ptim->hTimer);
  1544.    if(rc == NO_ERROR)
  1545.    {
  1546.       return TRUE;
  1547.    }
  1548.    return FALSE;
  1549. }
  1550.  
  1551. BOOL _Optlink timeout(PTIMER ptim)
  1552. {
  1553.    APIRET rc = NO_ERROR;
  1554.  
  1555.    if((rc = DosWaitEventSem(ptim->hEvent, SEM_IMMEDIATE_RETURN)) == NO_ERROR)
  1556.    {
  1557.       return TRUE;
  1558.    }
  1559.    return FALSE;
  1560. }
  1561.  
  1562.  
  1563.  
  1564. static PTHREADPAUSE _Optlink init_threadpause(void)
  1565. {
  1566.    PTHREADPAUSE ptp = malloc(sizeof(THREADPAUSE));
  1567.    if(ptp)
  1568.    {
  1569.       APIRET rc = NO_ERROR;
  1570.  
  1571.       memset(ptp, 0, sizeof(THREADPAUSE));
  1572.  
  1573.       rc = DosCreateEventSem(NULL, &ptp->hevPause, 0UL, FALSE);
  1574.       if(rc == NO_ERROR)
  1575.       {
  1576.          rc = DosCreateEventSem(NULL, &ptp->hevPaused, DCE_AUTORESET, FALSE);
  1577.       }
  1578.       if(rc == NO_ERROR)
  1579.       {
  1580.          rc = DosCreateEventSem(NULL, &ptp->hevResume, DCE_AUTORESET, FALSE);
  1581.       }
  1582.    }
  1583.    return ptp;
  1584. }
  1585.  
  1586. static BOOL _Optlink term_threadpause(PTHREADPAUSE ptp)
  1587. {
  1588.    APIRET rc = NO_ERROR;
  1589.  
  1590.    rc = DosCloseEventSem(ptp->hevPause);
  1591.    rc = DosCloseEventSem(ptp->hevPaused);
  1592.    rc = DosCloseEventSem(ptp->hevResume);
  1593.    free(ptp);
  1594.  
  1595.    return TRUE;
  1596. }
  1597.  
  1598. static BOOL _Inline pause_thread(PTHREADPAUSE ptp)
  1599. {
  1600.    APIRET rc = NO_ERROR;
  1601.    BOOL fPaused = FALSE;
  1602.  
  1603.    /* Tell thread to pause */
  1604.    rc = DosPostEventSem(ptp->hevPause);
  1605.    if(rc == NO_ERROR)
  1606.    {
  1607.       /* For for thread to acknowledge pause event */
  1608.       rc = DosWaitEventSem(ptp->hevPaused, (ULONG)SEM_INDEFINITE_WAIT);
  1609.       if(rc == NO_ERROR)
  1610.       {
  1611.          ULONG cPosts = 0;
  1612.  
  1613.          /* Reset pause event semaphore */
  1614.          rc = DosResetEventSem(ptp->hevPause, &cPosts);
  1615.  
  1616.          fPaused = TRUE;
  1617.       }
  1618.    }
  1619.    return fPaused;
  1620. }
  1621.  
  1622. static BOOL _Inline resume_thread(PTHREADPAUSE ptp)
  1623. {
  1624.    APIRET rc = NO_ERROR;
  1625.    BOOL fResumed = FALSE;
  1626.  
  1627.    rc = DosPostEventSem(ptp->hevResume);
  1628.    if(rc == NO_ERROR)
  1629.    {
  1630.       fResumed = TRUE;
  1631.    }
  1632.    return fResumed;
  1633. }
  1634.  
  1635. void _Inline check_thread_pause(PTHREADPAUSE ptp)
  1636. {
  1637.    APIRET rc = NO_ERROR;
  1638.  
  1639.    rc = DosWaitEventSem(ptp->hevPause, SEM_IMMEDIATE_RETURN);
  1640.    if(rc == NO_ERROR)
  1641.    {
  1642.       ULONG cPosts = 0;
  1643.  
  1644.       printf("> %s: Pause requested\n", __FUNCTION__);
  1645.  
  1646.       /* Post paused state notification event */
  1647.       rc = DosPostEventSem(ptp->hevPaused);
  1648.       if(rc == NO_ERROR)
  1649.       {
  1650.          printf("> %s: Paused event posted\n", __FUNCTION__);
  1651.  
  1652.          printf("> %s: Wait for resume event\n", __FUNCTION__);
  1653.          rc = DosWaitEventSem(ptp->hevResume, SEM_INDEFINITE_WAIT);
  1654.          if(rc == NO_ERROR)
  1655.          {
  1656.             printf("> %s: Got resume event\n", __FUNCTION__);
  1657.          }
  1658.       }
  1659.       DosResetEventSem(ptp->hevResume, &cPosts);
  1660.    }
  1661. }
  1662.