/*static char *SCCSID = "@(#)moncalls.c	6.6 92/01/31";*/

#define INCL_ERRORS
#define INCL_16
#define INCL_NOXLATE_DOS16
#define INCL_DOSDEVICES
#define INCL_DOSSEMAPHORES
#define INCL_OS2STD

#include <os2def16.h>
#include <bsedos16.h>
#include <drt.h>
#include <bseerr.h>
#include <filemode.h>
#include <moncalls.h>

#ifdef MMIOPH
   extern void Mon_Sys_Trace( USHORT mon_major_code,
                                  USHORT mon_minor_code,
                                  USHORT mon_data_len,
                                  char far *mon_data);
#endif //MMIOPH

extern USHORT near ValidAddr( PBYTE pbDataBuf);
extern USHORT near ValidBuffs( PBYTE pbInBuf, PBYTE pbOutBuf);

extern pascal ENTER_DLL_CONFORM(void);
extern pascal EXIT_DLL_CONFORM(unsigned int);

///////////////////////////////////////////////////////////////////////////
//  Instance data
///////////////////////////////////////////////////////////////////////////
// All data is initialized so that it will be in one segment, values are
// unimportant.  (Each segment costs 4K)

LISTHEADER  OpenList = {0,0,0};

OPENEDDEVICES Device_List[MAX_OPENED_DEVICES] = {0,0,0,0};


///////////////////////////////////////////////////////////////////////////
// Declare routines and PRAGMA them
///////////////////////////////////////////////////////////////////////////
   struct OPENEDDEVICES *ValidOpenedDevice( HMONITOR hmon);
   struct OPENEDDEVICES *GetOpenedItem( char far *pszDevName);


   APIRET  APIENTRY DosMonOpen( PSZ pszDevName,
                                PHMONITOR phmon);
   APIRET  APIENTRY DosMonReg( HMONITOR hmon,
                               PUSHORT  pbInBuf,
                               PUSHORT  pbOutBuf,
                               USHORT   fPosition,
                               USHORT   usIndex);
   APIRET  APIENTRY DosMonClose( HMONITOR hmon);

//************************************************************************
//************************************************************************
//   Start of API entry points
//************************************************************************
//************************************************************************

//****************** START OF SPECIFICATIONS *******************
//
// SUBROUTINE NAME: DosMonOpen
//
// DESCRIPTIVE NAME: Open a connection to a device monitor.
//
// FUNCTION: DosMonOpen issues a call to open the specified
//           device (Devname) on behalf of the caller - a
//           (monitor) process - requesting that a connection
//           to the monitor (itself) be opened.  On successfully
//           establishing this connection,  DosMonOpen returns
//           the handle of the device for the monitor.
//
// NOTES: This is a 16-bit PROTECT MODE ONLY API - part of
//        the MONCALLS dynamic link package, provided as
//        the interface between monitors and device drivers.
//
//        This call must be made before a set of buffers
//        can be registered as a monitor (i.e., before a
//        Call DosMonReg).
//
//        A process calls DosMonOpen once for each device it
//        will be registering monitors with.  A single process
//        that calls DosMonOpen for the SAME device will be
//        returned the SAME device handle.
//
//        A process can open a maximum of MAX_OPENED_DEVICES
//        (as of 9/9/86).
//
//             Pre- & Post- Invocation of trace occurs only if
//             trace is 'on' for monitor API.
//
// ENTRY POINT: DosMonOpen
//     LINKAGE:  FAR CALL
//
// INPUT:
//    pszDevName  = 16:16 selector/offset address of device name string.
//
//    phmon       = 16:16 selector/offset address of handle (returned)
//
// EXIT-NORMAL:  Returns NO_ERROR
//
// EXIT-ERROR:   Returns error code:
//                              ERROR_MON_INVALID_DEVNAME
//                              ERROR_MON_INVALID_PARMS
//                              error returned from Dosopen
//
// EFFECTS:
//
//            DosMonOpen issues a DosOpen call to the device
//            with the reserved 'R' flag (bit 3) in the Open
//            Mode word used to indicate that the DosOpen is
//            for a character device monitor, returning the
//            handle of the device for the monitor.
//
// INTERNAL REFERENCES:
//          Mon_Sys_Trace
//          ValidAddr
//          GetOpenedItem
//
// EXTERNAL REFERENCES:
//          DosSemRequest
//          DosSemClear
//          DosOpen
//          DosClose
//          DosQHandType
//
//****************** END OF SPECIFICATIONS *********************

APIRET  APIENTRY DosMonOpen( pszDevName, phmon)

PSZ pszDevName;
PHMONITOR phmon;

   {
   register USHORT rc; // Make rc a register var for trace points.
   struct OPENEDDEVICES *tempitem;
   USHORT         len;
   USHORT         actiontaken;
   ULONG          openmode = 0;
   USHORT         handtype;
   USHORT         flagword;
   ENTER_DLL_CONFORM();    /* save registers */


   rc = 0;
   openmode = (((openmode | open2_nonspooled) << 16) | open_autofail |
              open_no_inherit | sharing_deny_none | open_monitor |
              open_for_both);

#ifdef MMIOPH
   for (len=0;((pszDevName[len] != 00) && (len < DEVSTRINGLEN)) ; len++);
   len++;
   Mon_Sys_Trace((USHORT) 0x60,
                 (USHORT) 0x04,
                 (USHORT) len,
                 (char far *) pszDevName);
#endif /* MMIOPH */


/*********************************************************************
* The trace point may produce a "page not present" error in the trace
* output.  This is because we are trying to print out the string in
* the user selector's buffer, and it may not be present at this time.
**********************************************************************/

/***DT  DOSMONOPEN - Open moniter
 *
 *   TRACE MINOR = 0x0202,
 *         TP    = @
 *         TYPE  = (PRE,API),
 *         GROUP = TSK,
 *         DESC  = "(OS) DosMonOpen  Pre_Invocation",
 *         FMT   = " Device name: %ps",
 *         ASCIIZ= (.pszDevName,INDIRECT,255)
 */

   if ( (!(ValidAddr((PBYTE)pszDevName))) || (!(ValidAddr((PBYTE)phmon))) )
      {
      rc = ERROR_MON_INVALID_PARMS;
      }
  else
      {
      /* Get length of pszDevName */
      for (len=0;((pszDevName[len] != 00) && (len < DEVSTRINGLEN)) ; len++);

     if (len >= DEVSTRINGLEN )
         {
         rc = ERROR_MON_INVALID_DEVNAME;
         }
      else
         {
        //Make sure only one thread from this process accesses this list at
        //any one time.
         DosSemRequest(&OpenList.ListRAMSem, (ULONG)INFINITE_TIMEOUT);
         if (BAD_ITEM != (tempitem = GetOpenedItem(pszDevName)))
            {
           // Found item
            *phmon = tempitem->Dev_Handle;
            }
         else
            {
            if (OpenList.Free_Item == BAD_ITEM)
               {
               rc = ERROR_NOT_ENOUGH_MEMORY;
               }
           //Need to do DosOpen2 - MUST specify NONSPOOLED openmode!!!
            else if (!(rc= DosOpen2(pszDevName,
                                 phmon,
                                 &actiontaken,
                                 0L,
                                 4,
                                 1,
                                 openmode,
                                 NULL,
                                 0L)))
               {
               DosQHandType(*phmon, &handtype, &flagword);
               if (handtype != 1)
                  {
                 // Not a character device
                 // *NOTE: this could be changed to support network monitors by
                 // anding (&) rather than comparing (!=)
                  DosClose(*phmon);
                  rc = ERROR_MON_INVALID_DEVNAME;
                  }
               else
                  {
                  //Get item off free list
                  tempitem = OpenList.Free_Item;
                  //Set top of free list
                  OpenList.Free_Item = OpenList.Free_Item->Next;

                  // if there's still an item on free list, correct back pointer
                  if (OpenList.Free_Item != BAD_ITEM)
                     {
                     OpenList.Free_Item->Prev = BAD_ITEM;
                     }

                  //Set pointers of in new node
                  tempitem->Prev = BAD_ITEM;
                  tempitem->Next = OpenList.First_Item;

                  //set back pointer in first node
                  if (OpenList.First_Item != BAD_ITEM)
                     {
                     OpenList.First_Item->Prev = tempitem;
                     }

                  //set new front of list
                  OpenList.First_Item = tempitem;

                  // initialize data in new node
                  tempitem->Dev_Handle = *phmon;

                  //Copy pszDevName to structure
                  len = 0;
                  while(tempitem->Dev_String[len] = pszDevName[len])
                      //NOTE* This should be assignment, not equality!!,
                      //      We use this as an abbreviated STRCPY function.
                     {
                     len++;
                     }
                  }
               }
            }
         DosSemClear(&OpenList.ListRAMSem);
         }
      }

#ifdef MMIOPH
   Mon_Sys_Trace((USHORT) 0x60,
                 (USHORT) 0x84,
                 (USHORT) 2,
                 (char far *) phmon);
#endif /* MMIOPH */

/***DT  DOSMONOPEN - Open moniter
 *
 *   TRACE MINOR = 0x8202,
 *         TP    = @
 *         TYPE  = (API,POST),
 *         GROUP = TSK,
 *         DESC  = "(OS) DosMonOpen  Post_Invocation",
 *         FMT   = " Return code = %w  New handle  = %pw",
 *         REGS  = (.rc),
 *         MEM   = (.phmon,INDIRECT,2)
 */

   return(EXIT_DLL_CONFORM(rc));
   }




//****************** START OF SPECIFICATIONS *******************
//
// SUBROUTINE NAME: DosMonReg
//
// DESCRIPTIVE NAME: Register a set of buffers as a monitor.
//
// FUNCTION: DosMonReg issues an IOCTL (category 10, function
//           40 - Monitor Control/register) call to a device
//           to establish a pair of buffer structures (input and
//           output) as part of a chain that monitors an I/O
//           stream.
//
// NOTES: This is a 16-bit PROTECT MODE ONLY API - part of
//        the MONCALLS dynamic link package, provided as
//        the interface between monitors and device drivers.
//
//        A connection between the device and the monitor
//        must have been previously opened with a DosMonOpen
//        call.  The monitor needs the handle to the device
//        to issue this call.
//
//        Positional preference for location in the monitor
//        chain can be indicated.   Default = 0, no preference.
//
//        On calling DosMonReg, the first word of the buffers
//        must contain the total length of those buffers, in
//        bytes, where the length of each buffer >= the length
//        of the device driver buffer + the length of the
//        BufferHeaders.
//
//        The monitor's input and output buffers MUST be in the
//        same segment.
//
//        Pre- & Post- Invocation of trace occurs only if
//        trace is 'on' for monitor API.
//
// ENTRY POINT: DosMonReg
//    LINKAGE:  FAR CALL
//
// INPUT:
//         hmon      = handle obtained from previous DosMonOpen
//                     call.
//
//         pbInBuff  = 16:16 selector/offset address of input
//                     buffer in calling process' address space.
//
//         pbOutBuff = 16:16 selector/offset address of output
//                     buffer in calling process' address space.
//
//         fPosition = positional preference flag
//                          =0 or 3, no preference
//                          =1 or 4, front of list
//                          =2 or 5, back of list
//
//         usIndex    = device specific value. For the keyboard
//                      device it indicates screen group.
//
// EXIT-NORMAL:  Returns NO_ERROR
//
// EXIT-ERROR:   Returns error code:
//                             ERROR_MON_INVALID_HANDLE
//                             ERROR_NOT_ENOUGH_MEMORY
//                             ERROR_MON_BUFFER_TOO_SMALL
//                             ERROR_MON_INVALID_PARMS
//                             ERROR_MONITORS_NOT_SUPPORTED
//
// EFFECTS:
//
//           The Device Driver receives a 'monitor register'
//           request and calls the Monitor Dispatcher:
//           (1)  to create a monitor chain and get its handle
//                via dh_MonitorCreate, if no monitor chain
//                was previously created;
//           (2)  to add the buffer structures to the monitor
//                chain (indicated by the monitor chain handle
//                returned from a previous dh_MonitorCreate
//                call) via dh_Register.
//
// INTERNAL REFERENCES:
//          Mon_Sys_Trace
//          Valid_Buffs
//          ValidOpenedDevice
//
// EXTERNAL REFERENCES:
//          DosSemRequest
//          DosSemClear
//          DosDevIOCtl
//
//****************** END OF SPECIFICATIONS ********************* */

APIRET  APIENTRY DosMonReg(hmon, pbInBuf, pbOutBuf, fPosition, usIndex)

HMONITOR hmon;
PUSHORT  pbInBuf;
PUSHORT  pbOutBuf;
USHORT   fPosition;
USHORT   usIndex;

   {
#ifdef MMIOPH
   struct RAS_PRE_REG{
                   USHORT   ras_hmon;
                   ULONG    ras_pbInBuf;
                   ULONG    ras_pbOutBuf;
                   USHORT   ras_fPosition;
                   USHORT   ras_usIndex;
        };

   struct RAS_PRE_REG ras_pre_reg;
#endif // MMIOPH

   register USHORT rc; // Make rc a register var for trace points.

   IOC_PARMPACKET ParmPack;
   IOC_DATAPACKET DataPack;
   ENTER_DLL_CONFORM();    /* save registers */

   rc = 0;


#ifdef MMIOPH
   ras_pre_reg.ras_hmon      = (USHORT)hmon;
   ras_pre_reg.ras_pbInBuf   = (ULONG) pbInBuf;
   ras_pre_reg.ras_pbOutBuf  = (ULONG) pbOutBuf;
   ras_pre_reg.ras_fPosition = (USHORT)fPosition;
   ras_pre_reg.ras_usIndex   = (USHORT)usIndex;

   Mon_Sys_Trace((USHORT) 0x60,
                 (USHORT) 0x06,
                 (USHORT) sizeof(struct RAS_PRE_REG),
                 (char far *) &ras_pre_reg);
#endif // MMIOPH

/***DT  DOSMONREG - Register moniter
 *
 *   TRACE MINOR = 0x0204,
 *         TP    = @
 *         TYPE  = (PRE,API),
 *         GROUP = TSK,
 *         DESC  = "(OS) DosMonReg  Pre_Invocation",
 *         FMT   = " Handle = %pw",
 *         FMT   = " Input buffer address = %pa  Output buffer address = %pa",
 *         FMT   = " Position flag = %pw  Index = %pw",
 *         FMT   = " Input buffer length = %pw  Output buffer length = %pw",
 *         MEM   = (.hmon,DIRECT,2),
 *         MEM   = (.pbInBuf,DIRECT,4),
 *         MEM   = (.pbOutBuf,DIRECT,4),
 *         MEM   = (.fPosition,DIRECT,2),
 *         MEM   = (.usIndex,DIRECT,2),
 *         MEM   = (.pbInBuf,INDIRECT,2),
 *         MEM   = (.pbOutBuf,INDIRECT,2)
 */

   if (!(ValidBuffs((PBYTE)pbInBuf,(PBYTE)pbOutBuf)) || (fPosition > 5))
      {
      rc = ERROR_MON_INVALID_PARMS;
      }
   else
      {
      DosSemRequest(&OpenList.ListRAMSem, (ULONG)INFINITE_TIMEOUT);
      if ( BAD_ITEM == (ValidOpenedDevice(hmon)))
         {
         rc = ERROR_MON_INVALID_HANDLE;
         }
      else
         {
         ParmPack.Command_Info = 0;
         DataPack.Placement_Flag = fPosition;
         DataPack.Monitor_Index = usIndex;
         DataPack.Input_Buffer = OFFSETOF(pbInBuf);
         DataPack.Monitor_Buffer_Sel = SELECTOROF(pbInBuf);
         DataPack.Output_Buffer = OFFSETOF(pbOutBuf);

         if (0 != (rc = DosDevIOCtl(&DataPack, &ParmPack, 0x40, 10, hmon)))
            {
            if (rc == ERROR_INVALID_HANDLE)
               {
               rc = ERROR_MON_INVALID_HANDLE;
               }
            else if (rc == ERROR_INVALID_PARAMETER)
               {
               rc = ERROR_MON_INVALID_PARMS;
               }
            else if ((pbInBuf[0]  < pbInBuf[1]) ||
                     (pbOutBuf[0] < pbOutBuf[1]))
               {
               rc = ERROR_MON_BUFFER_TOO_SMALL;
               }
            else if (rc != ERROR_MONITORS_NOT_SUPPORTED)
               {
               rc = ERROR_NOT_ENOUGH_MEMORY;
               }
            }
         }
      DosSemClear(&OpenList.ListRAMSem);
      }
#ifdef MMIOPH
   //Nothing is trace now
   Mon_Sys_Trace((USHORT) 0x60,
                 (USHORT) 0x86,
                 (USHORT) 0,
                 (char far *) NULL);
#endif // MMIOPH

/***DT  DOSMONREG - Register moniter
 *
 *   TRACE MINOR = 0x8204,
 *         TP    = @
 *         TYPE  = (POST,API),
 *         GROUP = TSK,
 *         DESC  = "(OS) DosMonReg  Post_Invocation",
 *         FMT   = " Return code = %w",
 *         FMT   = "  Input buffer length = %pw  Device driver length = %w",
 *         FMT   = " Output buffer length = %pw  Device driver length = %w",
 *         REGS  = (.rc),
 *         MEM   = (.pbInBuf,INDIRECT,4),
 *         MEM   = (.pbOutBuf,INDIRECT,4)
 */
   return(EXIT_DLL_CONFORM(rc));

   }


//****************** START OF SPECIFICATIONS *******************
//
// SUBROUTINE NAME: DosMonClose
//
// DESCRIPTIVE NAME: Close a connection to a device monitor.
//
// FUNCTION:  DosMonClose issues a call to a device, on behalf
//            of the caller (a process), requesting that its
//            connection to a monitor be closed.    That is,
//            the (monitor) process notifies the device that
//            it no longer wishes to monitor the data stream.
//            The handle that the monitor had for the device
//            is "closed".
//
// NOTES: This is a 16-bit PROTECT MODE ONLY API - part of
//        the MONCALLS dynamic link package, provided as
//        the interface between monitors and device drivers.
//
//        When a process 'closes' its handle to a device, all
//        monitors registered with it are shutdown.
//
//        Pre- & Post- Invocation of trace occurs only if
//        trace is 'on' for monitor API.
//
// ENTRY POINT:  DosMonClose
//    LINKAGE:   FAR CALL
//
// INPUT:
//         hmon = handle obtained from previous DosMonOpen call.
//
// EXIT-NORMAL:  Returns NO_ERROR
//
// EXIT-ERROR:   Returns error code:
//                             ERROR_MON_INVALID_HANDLE
//
// EFFECTS:
//
//           DosClose issues a device close request block
//           to the device driver which, in turn, calls the
//           Monitor Dispatcher's routines:
//           (1)  dh_DeRegister to remove the buffers from
//                the monitor chain.
//           (2)  If no more monitors in chain, dh_MonitorCreate
//                to remove the monitor chain.
//
// INTERNAL REFERENCES:
//          Mon_Sys_Trace
//          ValidOpenedDevice
//
// EXTERNAL REFERENCES:
//          DosSemRequest
//          DosSemClear
//          DosClose
//
//****************** END OF SPECIFICATIONS *********************

APIRET  APIENTRY DosMonClose( hmon)

HMONITOR hmon;

   {
   register USHORT rc; // Make rc a register var for trace points.
   struct OPENEDDEVICES *temp;
   USHORT   i;
   ENTER_DLL_CONFORM();    /* save registers */

   rc = 0;

#ifdef MMIOPH
   Mon_Sys_Trace((USHORT) 0x60,
                 (USHORT) 0x05,
                 (USHORT) 2,
                 (char far *) &hmon);
#endif //MMIOPH

/***DT  DOSMONCLOSE - Close moniter
 *
 *   TRACE MINOR = 0x0201,
 *         TP    = @
 *         TYPE  = (PRE,API),
 *         GROUP = TSK,
 *         DESC  = "(OS) DosMonClose Pre_Invocation",
 *         FMT   = " Handle = %pw",
 *         MEM   = (.hmon,DIRECT,2)
 */



   DosSemRequest(&OpenList.ListRAMSem, (ULONG)INFINITE_TIMEOUT);
   if ( BAD_ITEM == (temp = (ValidOpenedDevice(hmon))))
      {
      rc = ERROR_MON_INVALID_HANDLE;
      }
   else
      {
      if (0 != (rc=DosClose(hmon)))
         {
         rc = ERROR_MON_INVALID_HANDLE;
         // error, but go ahead and clean up Device_List anyway
         }

      if (temp->Next != BAD_ITEM)
         {
         temp->Next->Prev=temp->Prev;
         }
      if (temp->Prev != BAD_ITEM)
         {
         temp->Prev->Next=temp->Next;
         }
      if (OpenList.First_Item == temp)
         {
         OpenList.First_Item = temp->Next;
         }

      temp->Prev = BAD_ITEM;
      temp->Next = OpenList.Free_Item;

      if (OpenList.Free_Item != BAD_ITEM)
         {
         OpenList.Free_Item->Prev = temp;
         }

      OpenList.Free_Item = temp;
      temp->Dev_Handle = 0;
      for (i=0; i<DEVSTRINGLEN ;i++)
         {
         temp->Dev_String[i]= 00;
         }
      }
   DosSemClear(&OpenList.ListRAMSem);

#ifdef MMIOPH
   // We trace nothing now
   Mon_Sys_Trace((USHORT) 0x60,
                 (USHORT) 0x85,
                 (USHORT) 0,
                 (char far *) NULL);
#endif //MMIOPH
/***DT  DOSMONCLOSE - Close moniter
 *
 *   TRACE MINOR = 0x8201,
 *         TP    = @
 *         TYPE  = (POST,API),
 *         GROUP = TSK,
 *         DESC  = "(OS) DosMonClose Post_Invocation",
 *         FMT   = " Return code = %w",
 *         REGS  = (.rc)
 */
   return(EXIT_DLL_CONFORM(rc));
   }
//************************************************************************
//************************************************************************
//   Start of worker subroutines
//************************************************************************
//************************************************************************


/********************** START OF SPECIFICATIONS ********************
*
* SUBROUTINE NAME:    GetOpenedItem
*
* DESCRIPTIVE NAME:   Find device in opened device list
*
* NOTES:
*
*
* ENTRY POINT:        GetOpenedItem
*    LINKAGE:         ?
*
* INPUT:
*         pszDevName = device name (string) to search for
*
* EXIT-NORMAL: returns array entry
*
* EXIT-ERROR:  returns BAD_ITEM
*
* EFFECTS:
*
* INTERNAL REFERENCES:
*
* EXTERNAL REFERENCES:
*
*********************** END OF SPECIFICATIONS *********************/

struct OPENEDDEVICES *GetOpenedItem (pszDevName)
char far *pszDevName;

   {
   struct OPENEDDEVICES *tempitem;
   USHORT   i;

   tempitem = OpenList.First_Item;

   while (tempitem != BAD_ITEM)
      {
      for (i=0;
           ((i < DEVSTRINGLEN) &&
            (tempitem->Dev_String[i] == pszDevName[i]));
            i++);
      if ((i < DEVSTRINGLEN) &&
          (tempitem->Dev_String[i] == pszDevName[i]))
         {
         return(tempitem);
         }
      tempitem = tempitem->Next;
      }
  // Not found
   return(BAD_ITEM);
   }


/********************** START OF SPECIFICATIONS ********************
*
* SUBROUTINE NAME:    ValidOpenedDevice
*
* DESCRIPTIVE NAME:   Find device in opened device list
*
* NOTES:
*
*
* ENTRY POINT:        ValidOpenedDevice
*    LINKAGE:         ?
*
* INPUT:
*         hmon = monitor handle to search for
*
* EXIT-NORMAL: returns array entry
*
* EXIT-ERROR:  returns BAD_ITEM
*
* EFFECTS:
*
* INTERNAL REFERENCES:
*
* EXTERNAL REFERENCES:
*
*********************** END OF SPECIFICATIONS *********************/
struct OPENEDDEVICES *ValidOpenedDevice (hmon)
HMONITOR hmon;
   {
   struct OPENEDDEVICES *tempitem;

   tempitem = OpenList.First_Item;

   while (tempitem != BAD_ITEM)
      {
      if (hmon ==  tempitem->Dev_Handle)
         {
         return(tempitem);
         }
      tempitem = tempitem->Next;
      }
  // Not found
  return(BAD_ITEM);
  }
