/*****************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 2.1 source code is provided to you solely for   */
/*    the purpose of assisting you in your development of OS/2 2.x device    */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = CLSAMPLE.C
 *
 * DESCRIPTIVE NAME = PCMCIA Client Services Sample Code
 *
 *
 *
 *
 * The following IBM OS/2 2.1 source code is provided to you solely for
 * the purpose of assisting you in your development of OS/2 2.x device
 * drivers. You may use this code in accordance with the IBM License
 * Agreement provided in the IBM Device Driver Source Kit for OS/2. This
 * Copyright statement may not be removed.
 *
 *
 *
 * VERSION =
 *
 * DATE        6/22/93
 *
 * DESCRIPTION This is PCMCIA client services driver skeleton.
 *
 * FUNCTIONS   This driver supports the following strategy commands.
 *
 *              0x00  INIT
 *              0x0d  OPEN DEVICE
 *              0x0e  CLOSE DEVICE
 *              0x10  GENERIC IOCtl
 *              0x1F  INIT COMPLETE
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
 ***************************************************************************/

#define  INCL_DOSMISC
#define  INCL_DOSINFOSEG
#define  INCL_16

#include "os2.h"
#include "error.h"
#include "devhdr.h"
#include "dhcalls.h"
#include "client.h"
#include "clsample.h"
#include "string.h"

#ifdef WAT
/* Pragmas for MSC Calling Convention */
#pragma aux (cdecl) CBHandler;
#pragma aux (cdecl) CBMTDHandler;
#pragma aux (cdecl) CSCall;
#pragma aux (cdecl) CBEntry;
#pragma aux (cdecl) STRENTRY;
#pragma aux (cdecl) StrProc;
#endif

extern void  far  CBEntry();
extern ULONG      CSCall();

extern void   STRENTRY();
USHORT StrProc(PREQPACKET);
int    far  Init(PREQPACKET);
int    far  InitComp(PREQPACKET);
int    far  Open(PREQPACKET);
int    far  Close(PREQPACKET);
int    far  IOCtl(PREQPACKET);
int    far  DeInstall(PREQPACKET);
ULONG  far  f_CSCall_Wait(USHORT, USHORT, ULONG, USHORT, PUCHAR);
ULONG  near CSCall_Wait(USHORT, USHORT, ULONG, USHORT, PUCHAR);
void   far  TimerHandler();
void   far  CardRemoval(USHORT, USHORT, USHORT, USHORT, USHORT, USHORT, PUCHAR);
BOOL   near WaitArgPkt();
BOOL   near ReleaseArgPkt();

/*
** pragma definition
*/
#pragma alloc_text(InitCode, Init)
#pragma alloc_text(SwapCode, InitComp)
#pragma alloc_text(SwapCode, Open)
#pragma alloc_text(SwapCode, Close)
#pragma alloc_text(SwapCode, IOCtl)
#pragma alloc_text(SwapCode, DeInstall)
#pragma alloc_text(SwapCode, CardRemoval)
#pragma alloc_text(SwapCode, WaitArgPkt)
#pragma alloc_text(SwapCode, ReleaseArgPkt)

DEVICEHDR devhdr = {
        (void far *) 0xFFFFFFFF,                             /* link         */
        (DEV_CHAR_DEV | DEV_SHARE | DEV_30| DEVLEV_3 ),      /* attr         */
        (PUSHORT) STRENTRY,                                  /* &strategy    */
        (USHORT)0,                                           /* &IDCroutine  */
        "SAMPLEDD",
        {0},
        DEV_INITCOMPLETE
};

/*
** global data
*/
PFN         Device_Help = 0;               /* storage area for DevHlp calls   */
PFN         CSEntry     = 0;               /* storage area for Card Services  */
USHORT      opencount = 0;                 /* count of DosOpens               */
USHORT      savepid = 0;                   /* save the caller's PID           */
UCHAR       PCMCIA_DD[]="PCMCIA$ ";        /* name of card services driver    */
ATTACHAREA  AttachArea = {0};              /* data area to hold address       */
USHORT      hClient = 0;                   /* client handle                   */
USHORT      hMemory = 0;                   /* memory handle                   */
USHORT      status = 0;                    /* status from CSCall              */
USHORT      devType = 0;                   /* device type                     */
USHORT      CL_status = 0;                 /* swappable code seg lock status  */
USHORT      cntWaiting = 0;                /* waiting request from app.       */
ULONG       lockhandle = 0;                /* handle for locked segment       */
UCHAR       ArgPacket[100] = {0};          /* argument packet buffer          */

UCHAR    MainMsg[]  = "\r\nOS/2 PCMCIA Client Sample Device Driver installed.\r\n";


/****************************************************************************
 *
 * FUNCTION NAME = StrProc
 *
 * DESCRIPTION   = Strategy commands process routine
 *
 * INPUT         = the pointer to the requesst packet
 *
 * OUTPUT        = status
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT StrProc(PREQPACKET rp)          /* Pks near */
{
    PFN       pFunction = 0;
    USHORT    rc;

    if (rp->RPcommand == RPINIT)  /* 0x00 */
        /*
        ** init called by kernel in protected mode ring 3 with IOPL
        */
       {
        return Init(rp);
       }
    else
    {

        /* ********************
        ** * Development Tips *
        ** ********************
        **
        ** When the version of card services does not match this client
        ** driver or when "Register Client" request fails, INIT_FAIURE
        ** flag is set.  Return with error code to the application
        ** without proceeding anything.  Make sure that all swappable
        ** segments are all swapped out.
        **
        */

        if (CL_status & INIT_FAILURE)
        {
           return (RPDONE | RPERR | ERROR_I24_GEN_FAILURE);
        }

        /* ********************
        ** * Development Tips *
        ** ********************
        **
        ** For the information of multiple code segment support,
        ** When the allocation of each procedure is considered, it is
        ** necessary to try to make each segment size close to a 4K
        ** boundary,  becuase the unit swap size is in 4KB memory pages.
        **
        ** Basically all public entry points, Strategy entry routine,
        ** Callback entry routine, Timer handler, and Event procedures
        ** for some events, those with very high frequency of usage,
        ** should be in the main (resident) code segment.
        **
        ** Those routines not needed during interrupt context (timer
        ** or HW) should be moved into the swappable segments.
        **
        ** If IOCtl routine and Event Procedure 2 does not have any
        ** relation, and it is possible to make two swappable code
        ** segments.  In this code, only one swappable code segment
        ** exists.
        **
        ** +----Client Services Driver----------------------------+
        ** | +-------------------------------------------------+  |
        ** | |                                     +---------+ |  |
        ** | | +---------+ +---------+ +---------+ |INIT     | |  |
        ** | | |         | |         | |         | |  Routine| |  |
        ** | | |Strategy | |Callback | |Event    | +---------+ |  |
        ** | | |  Routine| |   Entry | |  Procs 1| +---------+ |  |
        ** | | |         | |         | |         | |Timer    | |  |
        ** | | +---------+ +---------+ +---------+ |  Handler| |  |
        ** | |                                     +---------+ |  |
        ** | +------- Resident Code segement  -----------------+  |
        ** |                                                      |
        ** |   +-----------------------------------------+        |
        ** |   | +---------+ | +---------+ | +---------+ |        |
        ** |   | |         | | |         | | |         | |        |
        ** |   | | IOCtl   | | | Work sub| | |Event    | |        |
        ** |   | |  Routine| | | routins | | |  Procs 2| |        |
        ** |   | |         | | |         | | |         | |        |
        ** |   | +---------+ | +---------+ | +---------+ |        |
        ** |   +------ Swappable Code Segment(s) --------+        |
        ** |                                                      |
        ** +------------------------------------------------------+
        **
        */

        if (!(CL_status & CODE_LOCKED))    /* code is not locked             */
        {
           pFunction = (PFN) Open;
                                           /* lock SwapCode code segment     */
           if (!DevHelp_Lock(SELECTOROF(pFunction),
                             LOCKTYPE_LONG_ANYMEM,
                             0,
                             &lockhandle))
              CL_status |= CODE_LOCKED;    /* set locked status              */
           else
           {
              CL_status &= (~CODE_LOCKED); /* clear locked status            */
              return (RPDONE | RPERR | ERROR_I24_GEN_FAILURE);
           }
        }

        switch(rp->RPcommand)
        {
            case RPINITCOMP:  /* 0x1F */

                rc = InitComp(rp);
                break;

            case RPOPEN:      /* 0x0d */

                rc = Open(rp);
                break;

            case RPCLOSE:     /* 0x0e */

                rc = Close(rp);
                break;

            case RPIOCTL:     /* 0x10 */

                rc = IOCtl(rp);
                break;

            case RPDEINSTALL: /* 0x14 */

                rc = DeInstall(rp);
                break;

                /*
                ** all other commands are ignored
                */
            default:
                rc = RPDONE;
                break;
        }

            /*
            ** If no requests is queueing, unlock the segment.
            */
        if (!cntWaiting)
        {
            if (!DevHelp_UnLock (lockhandle))
                CL_status &= (~CODE_LOCKED);
        }
        return(rc);
    }
}

/****************************************************************************
 *
 * FUNCTION NAME = Init
 *
 * DESCRIPTION   = Initialization routine
 *
 * INPUT         = the pointer to the request packet
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int far Init(PREQPACKET rp)
{
    USHORT lid;
    USHORT ReturnValue;       /* pks */
    register char far *p;

    /*
    ** store DevHlp entry point
    */
    Device_Help = rp->s.Init.DevHlp;       /* save DevHlp entry point        */

    /*
    ** get entry to card services driver PCMCIA$
    */
    if (DevHelp_AttachDD(PCMCIA_DD, (NPBYTE)&AttachArea))
      {
        ReturnValue = (RPDONE | RPERR  | ERROR_I24_GEN_FAILURE);  /* pks */
        return(ReturnValue);
      }
                      /*           */
    OFFSETOF(CSEntry)   = AttachArea.offEPProt;
    SELECTOROF(CSEntry) = AttachArea.selEPProt;

    DosPutMessage(1, 56, MainMsg);
    /*
    ** send back our cs and ds end values to os/2
    */
    rp->s.InitExit.finalCS = (USHORT)Init;
    rp->s.InitExit.finalDS = (USHORT)(MainMsg + (sizeof(MainMsg)));

    return (RPDONE);

}



/****************************************************************************
 *
 * FUNCTION NAME = InitComp
 *
 * DESCRIPTION   = InitComp process routine
 *
 * INPUT         = the pointer to the request packet
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int far InitComp(PREQPACKET rp)
{
    ULONG     rc;
    ULONG     addr;
    USHORT    ReturnValue;       /* modified by pks */
    int       i;

    ReturnValue= (RPDONE) | (RPERR | ERROR_I24_GEN_FAILURE);
    /*
    ** check Card Services version
    */

    /* ********************
    ** * Development Tips *
    ** ********************
    **
    ** If the driver expects that Callback entry point is always called
    ** from Card Services at task time, the following version check
    ** will be required.  Earlier versions of Card Services allowed
    ** callbacks at task and interrupt time.
    **
    */

    CLEAR_ARGPACKET;                   /* clear argument buffer          */

    rc = f_CSCall_Wait(GET_CARDSERVICES_INFO,
                       NULL,
                       NULL,
                       sizeof(GET_CSINFO),
                       (PUCHAR)ArgPacket);

    if (((PGET_CSINFO)ArgPacket)->usCSLevel < CS_LEVEL)
    {
        CL_status |= INIT_FAILURE;
        return(ReturnValue);
    }

    if ((((PGET_CSINFO)ArgPacket)->usCSLevel == CS_LEVEL) &&
        (((PGET_CSINFO)ArgPacket)->usRevision < CS_REVISION))
    {
        CL_status |= INIT_FAILURE;
        return(ReturnValue);  /* modified by pks */
    }

    /*
    ** issue RegisterClient function to CS
    */
    CLEAR_ARGPACKET;                   /* clear argument buffer          */
    addr = (ULONG) ArgPacket;

    ((PREG_CLIENT)ArgPacket)->usAttributes    =
                                        RCA_MEMORY_CDD | RCA_CI_SHARABLE;
    ((PREG_CLIENT)ArgPacket)->usEventMask     = EM_ALL_EVENTS;
    ((PREG_CLIENT)ArgPacket)->usClientData[1] = SELECTOROF(addr);
    ((PREG_CLIENT)ArgPacket)->usVersion       = 0x0200;

    rc = f_CSCall_Wait(REGISTER_CLIENT,
                       NULL,
                       (ULONG)CBEntry,
                       sizeof(REG_CLIENT),
                       (PUCHAR)ArgPacket);

    status  = *((PUSHORT)(&rc));       /* save status from CSCall        */
    hClient = *((PUSHORT)(&rc)+1);     /* save client handle             */

    if (status)
    {
        CL_status |= INIT_FAILURE;
        return(ReturnValue); /* modified PKS */
    }

    return (RPDONE);

}

/****************************************************************************
 *
 * FUNCTION NAME = Open
 *
 * DESCRIPTION   = Open process routine
 *
 * INPUT         = the pointer to the request packet
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int far Open(PREQPACKET rp)
{
    return (RPDONE);

}

/****************************************************************************
 *
 * FUNCTION NAME = Close
 *
 * DESCRIPTION   = Close process routine
 *
 * INPUT         = the pointer to the request packet
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int far Close(PREQPACKET rp)
{
    return (RPDONE);                   /* return 'done' status to caller */

}

/****************************************************************************
 *
 * FUNCTION NAME = IOCtl
 *
 * DESCRIPTION   = IOCtl process routine
 *
 * INPUT         = the pointer to the request packet
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int far IOCtl(PREQPACKET rp)
{
    ULONG     rc;
    int       i;
    USHORT    ReturnValue; /* pks */

    /* ********************
    ** * Development Tips *
    ** ********************
    **
    ** To support multiple threads, if the buffer for Argument Packet
    ** is not available, block the thread and wait until the buffer is
    ** availble.
    */
    ReturnValue=(RPDONE | RPERR | ERROR_I24_GEN_FAILURE); /* PKS */
    if (!WaitArgPkt())
       return(ReturnValue);  /* PKS */

    /*
    ** please add your unique process here..
    **
    ** for example....   issue OpenMemory to CS
    */

    CLEAR_ARGPACKET;                   /* clear argument buffer          */

    ((POPN_MEMORY)ArgPacket)->usSocket     = 1;
    ((POPN_MEMORY)ArgPacket)->usAttributes = OMA_EXCLUSIVE;
    ((POPN_MEMORY)ArgPacket)->ulOffset     = 0;

    rc = f_CSCall_Wait(OPEN_MEMORY,
                       hClient,
                       (ULONG)NULL,
                       sizeof(OPN_MEMORY),
                       (PUCHAR)ArgPacket);

    status  = *((PUSHORT)(&rc));       /* save status from CSCall        */
    hMemory = *((PUSHORT)(&rc)+1);     /* save memory handle             */


    if (!ReleaseArgPkt())
       rc = ReturnValue;      /* pks */

    return (RPDONE);

}

/****************************************************************************
 *
 * FUNCTION NAME = DeInstall
 *
 * DESCRIPTION   = DeInstall process routine
 *
 * INPUT         = the pointer to the request packet
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int far DeInstall(PREQPACKET rp)
{
    USHORT ReturnValue;
    ReturnValue = RPDONE | RPERR | ERROR_I24_BAD_COMMAND ;
    return(ReturnValue);

}


/***************************************************************************
 *
 * FUNCTION NAME = CSCall_Wait
 *
 * DESCRIPTION   = This routine calls to Card Services.   If CS returns
 *                 "BUSY" return code, it will retry 10 times.
 *
 * INPUT         = function argument
 *                 handle argument
 *                 pointer argument
 *                 Arglength argument
 *                 ArgPointer argument
 *
 * OUTPUT        = Status argument
 *                 handle
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

ULONG FAR f_CSCall_Wait(USHORT function, USHORT clHandle,
                        ULONG  ptrArg,   USHORT lnArg,  PUCHAR argPtrArg)
{
    return(CSCall_Wait(function, clHandle, ptrArg,
                       lnArg, argPtrArg));
}


ULONG near CSCall_Wait(USHORT function, USHORT clHandle,    /* pks near */
                  ULONG  ptrArg,   USHORT lnArg,  PUCHAR argPtrArg)
{
    int    i;
    ULONG  rc;

    /* ********************
    ** * Development Tips *
    ** ********************
    **
    ** When Card Sevice returns BUSY status, this driver starts the timer
    ** and retry certain times.  Temporally it tries 10 time.  This value
    ** should be adjusted.
    */

    for (i = 0; i < 10; i++)         /* It is just sample                  */
    {                                /* It will retry maximum 10 times     */
        rc = CSCall(function,
                    clHandle,
                    ptrArg,
                    lnArg,
                    argPtrArg);

        if (*((PUSHORT)(&rc)) == BUSY)    /* check r/c BUSY or not         */
        {
            /* ********************
            ** * Development Tips *
            ** ********************
            **
            ** Currenly TIMERCOUNT is 2 ticks.
            ** TIMERCOUNT should be adjusted when you debug the code.
            **
            */

            if (!DevHelp_TickCount ((NPFN)TimerHandler,
                                    TIMERCOUNT))
            {
                DISABLE;
                CL_status |= TIMER_START;
                                          /* until timer expired           */
                                          /*   it block this thread        */
                while (CL_status & TIMER_START)
                {
                    DevHelp_ProcBlock ((ULONG)(&CL_status), -1L, 0);
                    DISABLE;
                }
            }
            else
                return(rc);
        }
        else
        {
            return(rc);
        }

    }
    return(rc);

}


/***************************************************************************
 *
 * FUNCTION NAME = TimerHandler
 *
 * DESCRIPTION   = This routine is called from Kernel.
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

void far TimerHandler()
{
    USHORT   Awakecount;

    DevHelp_ResetTimer((NPFN)TimerHandler);

    CL_status &= (~TIMER_START);

        /*
        ** It let the thread run and try to call CS again
        */
    DevHelp_ProcRun ((ULONG)(&CL_status), (PUSHORT)(&Awakecount));

    return;

}

/****************************************************************************
 *
 * FUNCTION NAME = CBHandler
 *
 * DESCRIPTION   = callback handler
 *
 * INPUT         = function
 *                 socket
 *                 card status, socket status information
 *                 ClientVal field from ClientData structure
 *                 ClientDS field from ClientData structure
 *                 ClientOff field from ClientData structure
 *                 pointer to buffer
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int CBHandler(USHORT function, USHORT socket, USHORT info,
              USHORT clientVal, USHORT clientDS, USHORT clientOff,
              PUCHAR Buffer)
{
    int       i;
    ULONG     rc;
    PFN       pFunction = 0;

    switch (function)
    {

    /*
    ** please add your unique event process here..
    **
    ** just example....
    */

    /* ********************
    ** * Development Tips *
    ** ********************
    **
    ** It is possible to get the following events from Card Services
    ** when the PC card is not present.
    **
    **   - CARD_INSERTION
    **   - CLIENT_INFO
    **   - INSERTION_REQUEST
    **   - INSERTION_COMPLETE
    **   - REGISTRATION_COMPLETE
    **   - SS_UPDATED
    **
    ** For the information of multiple code segment support,
    ** the code for the events in the above should be in the resident
    ** code segment if still it has a space.
    **
    ** The space means that space to 4K boundary.  The unit of swap
    ** is 4K.  When the allocation of each procedure is considered
    ** it is necessary to try to make each segment size close to
    ** 4K boundary.
    **
    ** The code for the other events should be in the swappable code
    ** segment, and when CARD_INSERTION event is gotten, the segment
    ** should be locked.  When CARD_REMOVAL event is sent, the locked
    ** segment should be released.
    **
    */

    case CARD_INSERTION:

        /* ********************
        ** * Development Tips *
        ** ********************
        **
        ** The fax/modem driver will call COM.SYS IDC entry to register
        ** COM port for the requested card in CARD_INSERTION.
        **
        ** COM.SYS must be called at task time.
        */

        /*
        ** please add your card initialize process here ....
        */
        CLEAR_ARGPACKET;                   /* clear argument packet buffer   */

        ((PGET_CONFGINFO)ArgPacket)->usSocket = socket;

        rc = CSCall_Wait(GET_CONFIGURATIONINFO,
                         NULL,
                         (ULONG)NULL,
                         sizeof(GET_CONFGINFO),
                         (PUCHAR)ArgPacket);

        status  = *((PUSHORT)(&rc));       /* save status from CSCall        */
                                           /* save the first device type     */
        devType = (USHORT)(((PGET_CONFGINFO)ArgPacket)->ucFirstDevType);


           /* ********************
           ** * Development Tips *
           ** ********************
           **
           ** When this client driver suppots requested card,
           ** and this card is only one which this driver recognizes,
           ** lock the segment which has the events procedures.
           **
           */

           if (!(CL_status & CODE_LOCKED))    /* code is not locked           */
           {
              pFunction = (PFN) Open;
                                              /* lock SwapCode code segment   */
              if (!DevHelp_Lock(SELECTOROF(pFunction),
                                LOCKTYPE_LONG_ANYMEM,
                                0,
                                &lockhandle))
                 CL_status |= CODE_LOCKED;    /* set locked status            */
              else
              {
                 CL_status &= (~CODE_LOCKED); /* clear locked status          */
              }
           }

        return(SUCCESS);                   /* always SUCCESS must be returned */


    case CARD_REMOVAL:

        /* ********************
        ** * Development Tips *
        ** ********************
        **
        ** The fax/modem driver will call COM.SYS IDC entry to deregister
        ** COM port for the requested card in CARD_REMOVAL.
        **
        ** COM.SYS must be called at task time.
        */

        CardRemoval(function, socket, info,
                    clientVal, clientDS,
                    clientOff, Buffer);

           /* ********************
           ** * Development Tips *
           ** ********************
           **
           ** When there is no more cards that this driver supports
           ** in the sockets, and there is no request to wait for
           ** process, the locked segment should be unlocked.
           */

           if (!cntWaiting)
           {
               if (!DevHelp_UnLock (lockhandle))
                   CL_status &= (~CODE_LOCKED);
           }

        return(SUCCESS);                   /* always SUCCESS must be returned */

    default:
        return(SUCCESS);

    } /* endswitch */
}


/****************************************************************************
 *
 * FUNCTION NAME = CBMTDHandler
 *
 * DESCRIPTION   = MTD request callback handler
 *
 * INPUT         = function
 *                 socket
 *                 ClientVal field from ClientData structure
 *                 ClientDS field from ClientData structure
 *                 ClientOff field from ClientData structure
 *                 pointer to MTD request packet
 *                 pointer to buffer
 *
 * OUTPUT        = the status
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

int CBMTDHandler(USHORT function, USHORT socket,
                 USHORT clientVal, USHORT clientDS, USHORT clientOff,
                 PMTD_REQPKT pMTDReqPkt, PUCHAR Buffer)
{

    /*
    **
    ** please add your unique MTD event process here...
    **
    */

    return(SUCCESS);
}

/****************************************************************************
 *
 * FUNCTION NAME = CardRemoval
 *
 * DESCRIPTION   = CARD_REMOVAL event routine.
 *
 * INPUT         = function
 *                 socket
 *                 card status, socket status information
 *                 ClientVal field from ClientData structure
 *                 ClientDS field from ClientData structure
 *                 ClientOff field from ClientData structure
 *                 pointer to buffer
 *
 * OUTPUT        = N/A
 *
 * RETURN_NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

void far CardRemoval(USHORT function, USHORT socket, USHORT info,
                     USHORT clientVal, USHORT clientDS, USHORT clientOff,
                     PUCHAR Buffer)
{
    return;
}


/****************************************************************************
 *
 * FUNCTION NAME = WaitArgPkt
 *
 * DESCRIPTION   = Waiting until ArgPacket buffer is availble
 *
 * INPUT         = None
 *
 * OUTPUT        =
 *
 * RETURN_NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

BOOL near WaitArgPkt()     /* pks near */
{
    USHORT   rc;

    while (CL_status & ARGPKT_BUSY) /* until buffer is available block thread */
    {
       rc = DevHelp_ProcBlock ((ULONG)(&ArgPacket), -1L, 0);
       DISABLE;
    }

    if (rc)
      return(FALSE);

    CL_status |= ARGPKT_BUSY;       /* set busy flag                          */
    cntWaiting++;                   /* increment waiting count                */

    return(TRUE);
}


/****************************************************************************
 *
 * FUNCTION NAME = ReleaseArgPkt
 *
 * DESCRIPTION   = Relese ArgPacket buffer
 *
 * INPUT         = None
 *
 * OUTPUT        =
 *
 * RETURN_NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

BOOL near ReleaseArgPkt()
{
    USHORT   Awakecount;
    USHORT   rc;
                                    /* let the other thread run               */
    rc = DevHelp_ProcRun ((ULONG)(&ArgPacket), (PUSHORT)(&Awakecount));

    if (rc)
      return(FALSE);

    CL_status &= (~ARGPKT_BUSY);    /* reset busy flag                        */
    cntWaiting--;                   /* decrement waiting count                */

    return(TRUE);
}

