/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: CE-MUX-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW
 *****************************************************************************
 * $Workfile: O:/mwohler_z_ms-d/z_ms-d/src/working/Classic/MuxDrv/MuxProt/mux_interface.c $
 * $Revision: /main/ms-d/32 $
 * $Date: 2014-03-14 11:54:17 $
 * $Author: mwohler $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 4.2 or 5.0
 *****************************************************************************
 *
 * $Log: O:/mwohler_z_ms-d/z_ms-d/src/working/Classic/MuxDrv/MuxProt/mux_interface.c$ 
 * 
 * /main/ms-d/32   2014-03-14 11:54:17   mwohler 
 * Use latest version of mux protocol implementation based on development for Miami client driver 
 * 
 * /main/ms-d/31   2009-02-18 11:59:04   ctna0256 
 * Set copyright text 
 * 
 * /main/ms-d/30   2008-12-11 14:06:23   ctna0450 
 * Fixed possible problem with mux revision number initialization
 *
 * /main/ms-d/29   2008-11-21 10:44:16   ctna0450
 * Switch to 32 bit buffer handling to allow any mux frame size.
 * Increase max. mux frame size from 244 bytes to 16 kilo bytes.
 *
 * /main/ms-d/28   2008-08-20 11:37:52   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/27   2007-07-04 15:26:14   kr36712
 * acceleration of the port close if mux channel not connected
 *
 * /main/ms-d/26   2007-06-11 17:53:26   kr36712
 * correction:
 * ParseEscape()
 * Dlci_EmptyQueues() for protocol version 4
 * AddWaitEvent()
 *
 * /main/ms-d/25   2007-06-07 17:53:16   kr36712
 * changes for DlciOpen():
 * pepetition of the DlciOpen(), if the answer is lost from the module on
 * the serial interface.
 *
 * /main/ms-d/24   2006-08-24 12:32:12   kr36712
 * correction: the protocol version negotiation does not function after restart of the mux protocol
 *
 * /main/ms-d/23   2006-08-23 17:14:25   wm36264
 * Some minor changes still for Mux Setp 2
 *
 * /main/ms-d/22   2006-08-23 16:51:56   kr36712
 * HDLC error correction 2
 *
 * /main/ms-d/21   2006-07-28 17:01:46   kr36712
 * HDLC error correction
 *
 * /main/ms-d/20   2006-07-27 17:15:05   kr36712
 * HDLC integration
 *
 * /main/ms-d/19   2006-07-12 15:46:14   wm36264
 * Renamend Mux_Tick() -> Mux_MsgTick() and Mux_ResyncTick() -> Mux_TimerTick()
 *
 * /main/ms-d/18   2006-06-22 17:35:33   kr36712
 * 0xF9 resynchronisation integrated
 *
 * /main/ms-d/17   2006-05-16 14:01:27   wm36264
 * Added support for EV_TXEMPTY com event
 *
 * /main/ms-d/16   2006-05-02 12:21:06   KR36712
 * dynamic mux frame size implemented
 *
 * /main/ms-d/15   2006-04-27 14:51:44   wm36264
 * Introduced callback to adjust hardware queue sizes from outside the mux protocol handling
 *
 * /main/ms-d/14   2006-03-03 13:40:29   wm36264
 * Call shutdown callbacks before callback table is deleted
 *
 * /main/ms-d/13   2005-12-08 11:46:30   kr36712
 * some errors:
 * DLCI_InitTab(), Line 96
 * Dlci_Establish(), Line 702
 *
 * /main/ms-d/12   2005-11-30 11:42:57   kr36712
 * DLCI_InitTab():
 * pDlciShutdown = NULL;
 * added
 *
 * /main/ms-d/11   2005-11-24 12:27:05   kr36712
 * shutdown for DLCI added
 *
 * /main/ms-d/10   2005-11-08 17:34:13   kr36712
 *
 * /main/ms-d/9   2005-11-08 17:12:59   kr36712
 * the protocol is suitable for several instances
 *
 * /main/ms-d/8   2005-10-21 13:04:40   WM36264
 * Removed usage of C++ in mux protocol implementation
 *
 * /main/ms-d/7   2005-10-20 13:57:48   WM36264
 * Added proper copyright header
 *
 * /main/ms-d/1   2005-10-07 16:29:12   wm36264
 * First version of Windows CE Muxer
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Cinterion Wireless Modules GmbH
// Copyright (C) 2005-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

#include "global.h"
#include "mux_interface.h"
#include "mux_callback.h"
#include "mux_msgqueue.h"
#include "ddmpdlch.h"
#include "ddmphdlc.h"


//============================================================================
// private functions
//============================================================================

//////////////////////////////////////////////////////////////////////////////
static pDLCI_INSTANCE_t DLCI_GetProp (
    pMUX_INSTANCE_t      pMux,
    DWORD                dwDLCI
)
{
    // check: is DLCI valid
    if ((dwDLCI < DLCI1) || (dwDLCI >= pMux->dwMaxNumberOfDLCI)) {
        return NULL;
    }
    return &pMux->pDLCIArray[dwDLCI];
}

//////////////////////////////////////////////////////////////////////////////
static DWORD DLCI_SaveProp (
    pMUX_INSTANCE_t      pMux,
    DWORD                dwDLCI,
    DWORD                dwUserData,
    pDLCI_INTERFACE_t    pDLCIInterface
)
{
    pDLCI_INSTANCE_t pDLCI = DLCI_GetProp(pMux, dwDLCI);

    // check: is DLCI valid
    if (pDLCI == NULL) {
        return MUX_ERR_WRONG_DLCI;
    }

    // check DLCI interface
    if (    (NULL == pDLCIInterface->pDlciEstablishResult)
         || (NULL == pDLCIInterface->pDlciReleaseResult)
         || (NULL == pDLCIInterface->pDlciSendDataContinue)
         || (NULL == pDLCIInterface->pDlciReceiveData)
         || (NULL == pDLCIInterface->pDlciReceiveV24Status)
         || (NULL == pDLCIInterface->pDlciShutdown) )
    {
        return MUX_ERR_INVALID_PARAMETER;
    }

    // set DLCI information
    pDLCI->dwUserData     =   dwUserData;
    pDLCI->DLCIInterface  =  *pDLCIInterface;

    // init DLCI
    MEMCLR(&pDLCI->sMP_DLCI, sizeof(MPVAR));
    MEMCLR(&pDLCI->sMP_Info, sizeof(MP_INFOBLOCK));

    pDLCI->sMP_DLCI.State               = DLC_DISCONNECTED;
    pDLCI->sMP_DLCI.sV24Status.DTR      = 1;
    pDLCI->sMP_DLCI.sV24Status.RTS      = 1;
    pDLCI->sMP_DLCI.sV24Status.CTS      = 1;
    pDLCI->sMP_DLCI.sV24Status.DSR      = 1;
    pDLCI->sMP_DLCI.sV24Status.TxEmpty  = 1;
    pDLCI->sMP_DLCI.sV24Status.TxFull   = 0;

    pDLCI->sMP_DLCI.uiFrameSize         = DEF_MAXFRAMESIZE;

    pDLCI->MP_RxRingBuf.Count           = 0;
    pDLCI->MP_RxRingBuf.ReadIndex       = 0;
    pDLCI->MP_RxRingBuf.WriteIndex      = 0;

    pDLCI->MP_TxRingBuf.Count           = 0;
    pDLCI->MP_TxRingBuf.ReadIndex       = 0;
    pDLCI->MP_TxRingBuf.WriteIndex      = 0;

    return MUX_OK;
}

//////////////////////////////////////////////////////////////////////////////
static DWORD DLCI_ClearProp (
    pMUX_INSTANCE_t      pMux,
    DWORD                dwDLCI
)
{
    pDLCI_INSTANCE_t pDLCI = DLCI_GetProp(pMux, dwDLCI);

    // check: is DLCI valid
    if (pDLCI == NULL) {
        return MUX_ERR_WRONG_DLCI;
    }

    // clear DLCI
    MEMCLR(&pDLCI->DLCIInterface, sizeof(DLCI_INTERFACE_t));
    pDLCI->dwUserData = 0;

    return MUX_OK;
}


//============================================================================
// callback functions
//============================================================================

//////////////////////////////////////////////////////////////////////////////
PVOID Mux_GetMem_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwSize
)
{
    if (pMux->MuxInterface.pMuxGetMem) {
        return pMux->MuxInterface.pMuxGetMem(pMux->dwMuxUserData, dwSize);
    }
    return NULL;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_FreeMem_cb (
    pMUX_INSTANCE_t  pMux,
    PVOID            pMem
)
{
    if (pMux->MuxInterface.pMuxFreeMem) {
        pMux->MuxInterface.pMuxFreeMem(pMux->dwMuxUserData, pMem);
    }
}

//////////////////////////////////////////////////////////////////////////////
BOOL Mux_Lock_cb (
    pMUX_INSTANCE_t  pMux
)
{
    BOOL fRet = FALSE;
    if (pMux->MuxInterface.pMuxLock) {
        fRet = pMux->MuxInterface.pMuxLock((DWORD)pMux, pMux->dwMuxUserData);
    }
    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_Unlock_cb (
    pMUX_INSTANCE_t  pMux
)
{
    if (pMux->MuxInterface.pMuxUnlock) {
        if (!pMux->MuxInterface.pMuxUnlock((DWORD)pMux, pMux->dwMuxUserData)) {
            DWORD i;
            for (i = 1; i < pMux->dwMaxNumberOfDLCI; i++) {
                Mux_DLCISendDataReady_cb(pMux, i, 0);
            }
        }
    }
}

// MUX frame and device

//////////////////////////////////////////////////////////////////////////////
void Mux_StartResult_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwResult
)
{
    if (pMux->MuxInterface.pMuxStartResult) {
        pMux->MuxInterface.pMuxStartResult((DWORD)pMux, pMux->dwMuxUserData, dwResult);
    }
}

//////////////////////////////////////////////////////////////////////////////
void Mux_Reset_cb (
    pMUX_INSTANCE_t  pMux
)
{
    if (pMux->MuxInterface.pMuxReset) {
        pMux->MuxInterface.pMuxReset((DWORD)pMux, pMux->dwMuxUserData);
    }
    MP_ResetResync(pMux);
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_GetTickCount_cb (
    pMUX_INSTANCE_t  pMux
)
{
    if (pMux->MuxInterface.pMuxGetTickCount) {
        return pMux->MuxInterface.pMuxGetTickCount((DWORD)pMux, pMux->dwMuxUserData);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_DevSendData_cb (
    pMUX_INSTANCE_t  pMux,
    PBYTE            pData,
    DWORD            dwDataLen
)
{
    if (pMux->MuxInterface.pMuxDevSendData) {
        return pMux->MuxInterface.pMuxDevSendData((DWORD)pMux, pMux->dwMuxUserData, pData, dwDataLen);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DevActivate_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwLen
)
{
    if (pMux->MuxInterface.pMuxDevActivate) {
        pMux->MuxInterface.pMuxDevActivate((DWORD)pMux, pMux->dwMuxUserData, dwLen);
    }
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_GetFreeTxBytes_cb (
    pMUX_INSTANCE_t  pMux
)
{
    if (pMux->MuxInterface.pMuxDevGetFreeTxBytes) {
        return pMux->MuxInterface.pMuxDevGetFreeTxBytes((DWORD)pMux, pMux->dwMuxUserData);
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DevSetQueueSizes_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwRxSize,
    DWORD            dwTxSize
)
{
    if (pMux->MuxInterface.pMuxDevSetQueueSizes) {
        pMux->MuxInterface.pMuxDevSetQueueSizes((DWORD)pMux, pMux->dwMuxUserData, dwRxSize, dwTxSize);
    }
}

//////////////////////////////////////////////////////////////////////////////
BOOL Mux_DevGetV24Lines_cb (
    pMUX_INSTANCE_t  pMux,
    BOOL            *pRing,
    BOOL            *pDCD
)
{
    if (pMux->MuxInterface.pMuxDevGetV24Lines) {
        return pMux->MuxInterface.pMuxDevGetV24Lines((DWORD)pMux, pMux->dwMuxUserData, pRing, pDCD);
    }
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
BOOL Mux_DevSetV24Lines_cb (
    pMUX_INSTANCE_t  pMux,
    BOOL             bRing,
    BOOL             bDCD
)
{
    if (pMux->MuxInterface.pMuxDevSetV24Lines) {
        return pMux->MuxInterface.pMuxDevSetV24Lines((DWORD)pMux, pMux->dwMuxUserData, bRing, bDCD);
    }
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
BOOL Mux_DevSetPowerState_cb (
    pMUX_INSTANCE_t  pMux,
    POWER_CMD_e      PwrState
)
{
    if (pMux->MuxInterface.pMuxDevSetPowerState) {
        return pMux->MuxInterface.pMuxDevSetPowerState((DWORD)pMux, pMux->dwMuxUserData, PwrState);
    }
    return FALSE;
}

// DLCI

//////////////////////////////////////////////////////////////////////////////
void Mux_DLCIEstablishResult_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI,
    DWORD            dwResult
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciEstablishResult) {
            pDLCIProp->DLCIInterface.pDlciEstablishResult((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI, dwResult);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DLCIReleaseResult_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI,
    DWORD            dwResult
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciReleaseResult) {
            pDLCIProp->DLCIInterface.pDlciReleaseResult((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI, dwResult);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DLCISendDataReady_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI,
    DWORD            dwLen
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciSendDataContinue) {
            pDLCIProp->DLCIInterface.pDlciSendDataContinue((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI, dwLen);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_DLCIReceiveData_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI,
    PBYTE            pData,
    DWORD            dwDataLen
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciReceiveData) {
            return pDLCIProp->DLCIInterface.pDlciReceiveData((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI, pData, dwDataLen);
        }
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DLCIReceiceV24Status_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI,
    V24STATUS        V24Status
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);

    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciReceiveV24Status) {
            MUX_V24STATUS_t MuxV24;
            
            MuxV24.BreakInd       = V24Status.BreakInd;
            MuxV24.BreakReq       = V24Status.BreakReq;
            MuxV24.CTS            = V24Status.CTS;
            MuxV24.DCD            = V24Status.DCD;
            MuxV24.DSR            = V24Status.DSR;
            MuxV24.DTR            = V24Status.DTR;
            MuxV24.InFlowActive   = V24Status.InFlowActive;
            MuxV24.OutFlowActive  = V24Status.OutFlowActive;
            MuxV24.RI             = V24Status.RI;
            MuxV24.RTS            = V24Status.RTS;
            MuxV24.TxEmpty        = V24Status.TxEmpty;
            MuxV24.TxFull         = V24Status.TxFull;

            pDLCIProp->DLCIInterface.pDlciReceiveV24Status((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI, MuxV24);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DLCIShutdown_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciShutdown) {
            pDLCIProp->DLCIInterface.pDlciShutdown((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
void Mux_DLCIEscReceived_cb (
    pMUX_INSTANCE_t  pMux,
    DWORD            dwDLCI
)
{
    pDLCI_INSTANCE_t pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp) {
        if (pDLCIProp->DLCIInterface.pDlciEscReceived) {
          pDLCIProp->DLCIInterface.pDlciEscReceived((DWORD)pMux, pDLCIProp->dwUserData, dwDLCI);
        }
    }
}


//============================================================================
// public interface
//============================================================================

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_Register (
    DWORD            dwUserData,
    pMUX_INTERFACE_t pMuxInterface,
    DWORD            dwMaxDlci,
    DWORD            dwMpRevision,
    DWORD           *phMuxInstance
)
{
    DWORD            dwMemSize = 0;
    pMUX_INSTANCE_t  pMux = NULL;

    // check interface
    if (     (NULL == pMuxInterface->pMuxDevActivate)
         ||  (NULL == pMuxInterface->pMuxDevGetFreeTxBytes)
         ||  (NULL == pMuxInterface->pMuxDevSetQueueSizes)
         ||  (NULL == pMuxInterface->pMuxDevSendData)
         ||  (NULL == pMuxInterface->pMuxGetTickCount)
         ||  (NULL == pMuxInterface->pMuxMsgAvailable)
         ||  (NULL == pMuxInterface->pMuxReset)
         ||  (NULL == pMuxInterface->pMuxLock)
         ||  (NULL == pMuxInterface->pMuxUnlock)
         ||  (NULL == pMuxInterface->pMuxStartResult)
         ||  (NULL == pMuxInterface->pMuxGetMem)
         ||  (NULL == pMuxInterface->pMuxFreeMem)
    ) {
        return MUX_ERR_INVALID_PARAMETER;
    }
    
    // memory for mux instance
    pMux = pMuxInterface->pMuxGetMem(dwUserData, sizeof(MUX_INSTANCE_t));
    if (pMux == NULL) {
        return MUX_ERR_GET_MEMORY_FAILED;
    }

    MEMCLR(pMux, sizeof(MUX_INSTANCE_t));
    pMux->dwMuxUserData  = dwUserData;
    pMux->MuxInterface = *pMuxInterface;

    // memory for dlci instances
    dwMemSize = sizeof(DLCI_INSTANCE_t) * (dwMaxDlci + 1); // + 1, for DLCI0 (control channel)
    pMux->pDLCIArray = pMuxInterface->pMuxGetMem(dwUserData, dwMemSize);
    if (pMux->pDLCIArray == NULL) {
        Mux_Deregister((DWORD)pMux);
        return MUX_ERR_GET_MEMORY_FAILED;
    }

    // memory for ringbuffers
    pMux->RingBufferStatic.RxBuf = pMuxInterface->pMuxGetMem(dwUserData, __MP_RX_RINGBUF_SIZE * dwMaxDlci);
    if (pMux->RingBufferStatic.RxBuf == NULL) {
        Mux_Deregister((DWORD)pMux);
        return MUX_ERR_GET_MEMORY_FAILED;
    }

    pMux->RingBufferStatic.TxBuf = pMuxInterface->pMuxGetMem(dwUserData, __MP_TX_RINGBUF_SIZE * dwMaxDlci);
    if (pMux->RingBufferStatic.TxBuf == NULL) {
        Mux_Deregister((DWORD)pMux);
        return MUX_ERR_GET_MEMORY_FAILED;
    }

    // Frame and window size
    pMux->dwMpWindowSize = HDLC_DEF_WINDOW_SIZE;
    pMux->dwMpFrameSize = MAXFRAMESIZE;

    // mux protocol revision
    pMux->dwMpRevision = MINMAX(dwMpRevision, (DWORD)MP_PROTOCOL_VERSION_MIN, (DWORD)MP_PROTOCOL_VERSION_MAX);

    pMux->dwMaxNumberOfDLCI = dwMaxDlci + 1;

    MuxMsg_Init(pMux);

    *phMuxInstance = (DWORD)pMux;

    return MUX_OK;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_Deregister (
    DWORD           hMuxInstance
)
{
    pMUX_INSTANCE_t pMux = (pMUX_INSTANCE_t)hMuxInstance;

    if (pMux == NULL) {
        return MUX_ERR_INVALID_PARAMETER;
    }

    if (pMux->pDLCIArray)              pMux->MuxInterface.pMuxFreeMem(pMux->dwMuxUserData, pMux->pDLCIArray);
    if (pMux->RingBufferStatic.RxBuf)  pMux->MuxInterface.pMuxFreeMem(pMux->dwMuxUserData, pMux->RingBufferStatic.RxBuf);
    if (pMux->RingBufferStatic.TxBuf)  pMux->MuxInterface.pMuxFreeMem(pMux->dwMuxUserData, pMux->RingBufferStatic.TxBuf);

    pMux->MuxInterface.pMuxFreeMem(pMux->dwMuxUserData, pMux);

    return MUX_OK;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_Init
(
    DWORD           hMuxInstance,
    DWORD           dwWindowSize,
    DWORD           dwFrameSize,
    DWORD           dwBaudRate
)
{
  pMUX_INSTANCE_t pMux = (pMUX_INSTANCE_t)hMuxInstance;
  
  Mux_Lock_cb(pMux);
  
  pMux->dwMpWindowSize = MINMAX(dwWindowSize, (DWORD)1, (DWORD)7);
  pMux->dwMpFrameSize = MINMAX(dwFrameSize, (DWORD)DEF_MAXFRAMESIZE, (DWORD)MAXFRAMESIZE);
  pMux->dwMpBaudRate = dwBaudRate;
  
  MP_Init(pMux);
  
  MP_AdjustQueueSize(pMux);
  
  Mux_Unlock_cb(pMux);
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_Start (
    DWORD           hMuxInstance
)
{
    pMUX_INSTANCE_t pMux = (pMUX_INSTANCE_t)hMuxInstance;

    Mux_Lock_cb(pMux);

    // process first mux message
    MP_vProc(pMux);
    // establishment of the control channel DLCI0
    MP_PostSimpleUserMessage(pMux, MP_ReqSABM_DLCI0, DLCI0);

    Mux_Unlock_cb(pMux);

    return MUX_OK;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_Stop (
    DWORD           hMuxInstance
)
{
    pMUX_INSTANCE_t pMux = (pMUX_INSTANCE_t)hMuxInstance;

    Mux_Lock_cb(pMux);

    if (pMux->ucInitiator) {
        MP_StartShutdownProc(pMux);
    }

    Mux_Unlock_cb(pMux);
}

//////////////////////////////////////////////////////////////////////////////
void Mux_Shutdown (
    DWORD           hMuxInstance
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            i;

    Mux_Lock_cb (pMux);

    // shutdown callback for all DLCI
    for (i = 1; i < pMux->dwMaxNumberOfDLCI; i++) {
        Mux_DLCIShutdown_cb(pMux, i);
    }

    MuxMsg_Init(pMux);
    // standard initialization functions for the mux parts
    MP_vInitScanner(pMux);
    MP_vMemInit(pMux);
    MP_vStatemachineInit(pMux);
    MP_ResetResync(pMux);

    Mux_Unlock_cb(pMux);
}

//////////////////////////////////////////////////////////////////////////////
DWORD Mux_SendPowerCmd (
    DWORD           hMuxInstance,
    POWER_CMD_e     PwrCmd
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet = MUX_OK;

    Mux_Lock_cb(pMux);

    if (pMux->ucInitiator) {
        switch (PwrCmd)
        {
            case MUX_POWER_MODE_DEFAULT:
                MP_SleepReqEx(pMux, PSC_MODE_DEFAULT);
                break;
            case MUX_POWER_MODE_FULL:
                MP_SleepReqEx(pMux, PSC_MODE_FULL);
                break;
            case MUX_POWER_MODE_SLEEP:
                MP_SleepReqEx(pMux, PSC_MODE_SLEEP);
                break;
            case MUX_POWER_MODE_CYCLIC_SLEEP_SHORT:
                MP_SleepReqEx(pMux, PSC_MODE_CYCLIC_SLEEP_SHORT);
                break;
            case MUX_POWER_MODE_CYCLIC_SLEEP_LONG:
                MP_SleepReqEx(pMux, PSC_MODE_CYCLIC_SLEEP_LONG);
                break;
            case MUX_POWER_MODE_SWITCH_OFF:
                MP_SleepReqEx(pMux, PSC_MODE_SWITCH_OFF);
                break;
            case MUX_POWER_MODE_RESET:
                MP_SleepReqEx(pMux, PSC_MODE_RESET);
                break;
            case MUX_POWER_MODE_CYCLIC_SLEEP_SHORT_CONT:
                MP_SleepReqEx(pMux, PSC_MODE_CYCLIC_SLEEP_SHORT_CONT);
                break;
            case MUX_POWER_MODE_CYCLIC_SLEEP_LONG_CONT:
                MP_SleepReqEx(pMux, PSC_MODE_CYCLIC_SLEEP_LONG_CONT);
                break;
            case MUX_POWER_MODE_CYCLIC_SLEEP_LONG_CONT2:
                MP_SleepReqEx(pMux, PSC_MODE_CYCLIC_SLEEP_LONG_CONT2);
                break;
            default:
                dwRet = MUX_ERR_WRONG_POWER_CMD;
                break;
        }
    } else {
        dwRet = MUX_ERR_CLIENT_NOT_ALLOWED;
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
BOOL Mux_MsgTick (
    DWORD           hMuxInstance
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    BOOL             fRet;

    Mux_Lock_cb(pMux);

    MP_vProc(pMux);

    fRet = MuxMsg_Exist(pMux);

    Mux_Unlock_cb(pMux);

    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
void Mux_TimerTick (
    DWORD           hMuxInstance
)
{
    pMUX_INSTANCE_t pMux = (pMUX_INSTANCE_t)hMuxInstance;

    Mux_Lock_cb(pMux);

    MP_CheckResyncTimeOut(pMux);
    Hdlc_TimerTick(pMux);

    Mux_Unlock_cb(pMux);
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_Establish (
    DWORD                hMuxInstance,
    DWORD                dwUserData,
    DWORD                dwDLCI,
    pDLCI_INTERFACE_t    pDLCIInterface
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    dwRet = DLCI_SaveProp(pMux, dwDLCI, dwUserData, pDLCIInterface);
    if (dwRet == MUX_OK) {
        Hdlc_Init(pMux, (UINT8)dwDLCI, (UINT8)pMux->dwMpWindowSize);
        MP_vInitPacketHandler(pMux, (UINT8)dwDLCI);
        if (pMux->ucInitiator) {
            MP_PostSimpleUserMessage(pMux, MP_ReqSABM, (UINT8)dwDLCI);
        }
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_Release (
    DWORD           hMuxInstance,
    DWORD           dwDLCI
)
{
    pMUX_INSTANCE_t   pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD             dwRet = MUX_OK;

    Mux_Lock_cb(pMux);

    if (pMux->ucInitiator) {
        // check: is DLCI valid
        if (DLCI_GetProp(pMux, dwDLCI)) {
            // Initiate disconnection
            MP_PostSimpleUserMessage(pMux, MP_ReqDISC, (UINT8)dwDLCI);
        } else {
            dwRet = MUX_ERR_WRONG_DLCI;
        }
    } else {
        // clear DLCI
        dwRet = DLCI_ClearProp(pMux, dwDLCI);
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD  Dlci_IsConnected (
    DWORD           hMuxInstance,
    DWORD           dwDLCI
)
{
    pMUX_INSTANCE_t   pMux = (pMUX_INSTANCE_t)hMuxInstance;
    pDLCI_INSTANCE_t  pDLCIProp;
    DWORD             dwRet = MUX_OK;

    Mux_Lock_cb(pMux);

    pDLCIProp = DLCI_GetProp(pMux, dwDLCI);
    if (pDLCIProp == NULL) {
        dwRet = MUX_ERR_WRONG_DLCI;
    } else {
        // is DLCI connected
        if (pDLCIProp->sMP_DLCI.State != DLC_CONNECTED) {
            dwRet = MUX_ERR_DLCI_NOT_CONNECTED;
        }
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_SendData (
    DWORD           hMuxInstance,
    DWORD           dwDLCI,
    PBYTE           pData,
    DWORD           dwDataLen
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet = 0;

    Mux_Lock_cb(pMux);

    if (Dlci_IsConnected(hMuxInstance, dwDLCI) == MUX_OK) {
        dwRet = MP_uiAppDataReceive(pMux, pData, dwDataLen, (UINT8)dwDLCI);
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_ReceiveDataContinue (
    DWORD           hMuxInstance,
    DWORD           dwDLCI
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    dwRet = Dlci_IsConnected(hMuxInstance, dwDLCI);
    if (dwRet == MUX_OK) {
        MP_vAppDataSendCallback(pMux, (UINT8)dwDLCI);
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_SendEsc (
    DWORD           hMuxInstance,
    DWORD           dwDLCI
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    if (pMux->ucInitiator) {
        dwRet = Dlci_IsConnected(hMuxInstance, dwDLCI);
        if (dwRet == MUX_OK) {
            MP_vSendESC(pMux, (UINT8)dwDLCI);
        }
    } else {
        dwRet = MUX_ERR_CLIENT_NOT_ALLOWED;
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_SetV24Status (
    DWORD            hMuxInstance,
    DWORD            dwDLCI,
    MUX_V24STATUS_t  MuxV24Status
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    dwRet = Dlci_IsConnected(hMuxInstance, dwDLCI);
    if (dwRet == MUX_OK) {
        V24STATUS V24Status;
        V24Status.BreakInd       = MuxV24Status.BreakInd;
        V24Status.BreakReq       = MuxV24Status.BreakReq;
        V24Status.CTS            = MuxV24Status.CTS;
        V24Status.DCD            = MuxV24Status.DCD;
        V24Status.DSR            = MuxV24Status.DSR;
        V24Status.DTR            = MuxV24Status.DTR;
        V24Status.InFlowActive   = MuxV24Status.InFlowActive;
        V24Status.OutFlowActive  = MuxV24Status.OutFlowActive;
        V24Status.RI             = MuxV24Status.RI;
        V24Status.RTS            = MuxV24Status.RTS;
        MP_vAppSetV24Status(pMux, V24Status, (UINT8)dwDLCI);
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_GetQueueInformation (
    DWORD           hMuxInstance,
    DWORD           dwDLCI,
    PDWORD          pdwBytesInQueueRx,
    PDWORD          pdwBytesInQueueTx,
    PDWORD          pdwSizeOfQueueRx,
    PDWORD          pdwSizeOfQueueTx
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    dwRet = Dlci_IsConnected(hMuxInstance, dwDLCI);
    if (dwRet == MUX_OK) {
        // tx buffer
        if (pdwBytesInQueueTx)  *pdwBytesInQueueTx = pMux->pDLCIArray[dwDLCI].MP_TxRingBuf.Count;
        if (pdwSizeOfQueueTx)   *pdwSizeOfQueueTx  = pMux->pDLCIArray[dwDLCI].MP_TxRingBuf.BufSize;
        // rx buffer
        if (pdwBytesInQueueRx)  *pdwBytesInQueueRx = pMux->pDLCIArray[dwDLCI].MP_RxRingBuf.Count;
        if (pdwSizeOfQueueRx)   *pdwSizeOfQueueRx  = pMux->pDLCIArray[dwDLCI].MP_RxRingBuf.BufSize;
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dlci_EmptyQueues (
    DWORD           hMuxInstance,
    DWORD           dwDLCI,
    BOOL            fEmptyRx,
    BOOL            fEmptyTx
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    dwRet = Dlci_IsConnected(hMuxInstance, dwDLCI);
    if (dwRet == MUX_OK) {
        if (fEmptyRx) {
            MP_pRxRingBuf pRxBuf = &pMux->pDLCIArray[dwDLCI].MP_RxRingBuf;

            pRxBuf->Count       = 0;
            pRxBuf->ReadIndex   = 0;
            pRxBuf->WriteIndex  = 0;

            // Restart possibly blocked data flow
            MP_vSetFlowControl(pMux, (UINT8)dwDLCI);
        }

        if (fEmptyTx) {
            MP_pTxRingBuf pTxBuf = &pMux->pDLCIArray[dwDLCI].MP_TxRingBuf;

            if (pMux->MP_uiUsedProtVersion < MP_REVISION_04) {
                pTxBuf->Count       = 0;
                pTxBuf->ReadIndex   = 0;
                pTxBuf->WriteIndex  = 0;
            } else {
                pTxBuf->Count       = 0;
                pTxBuf->WriteIndex  = pTxBuf->Packets.MaxWrOffset;
            }
        }
    }

    Mux_Unlock_cb(pMux);

    return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
DWORD Dev_ReceiveData (
    DWORD           hMuxInstance,
    PBYTE           pData,
    DWORD           dwDataLen
)
{
    pMUX_INSTANCE_t  pMux = (pMUX_INSTANCE_t)hMuxInstance;
    DWORD            dwRet;

    Mux_Lock_cb(pMux);

    dwRet = MP_uiDevDataReceive(pMux, pData, dwDataLen);

    Mux_Unlock_cb(pMux);

    return dwRet;
}


//////////////////////////////////////////////////////////////////////////////
void Dev_SendDataContinue (
    DWORD           hMuxInstance,
    DWORD           dwLen
)
{
    pMUX_INSTANCE_t pMux = (pMUX_INSTANCE_t)hMuxInstance;

    Mux_Lock_cb(pMux);

    MP_vDevDataSendCallback(pMux, dwLen);

    Mux_Unlock_cb(pMux);
}

