/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP 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 =  RMHELP.C
 *
 * DESCRIPTIVE NAME =  Helpers for COM.SYS
 *
 *
 *
 * VERSION = V0.1
 *
 * DATE  09/07/94
 *
 * DESCRIPTION : (see above)
 *
 * Purpose: (see above)
 *
 *
 *
 * FUNCTIONS  :
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 *
 *
 * EXTERNAL FUNCTIONS
 *
*/
#define INCL_NOPMAPI
#include <os2.h>
#include <string.h>

#ifndef FAR
#define FAR far
#endif

#include "rmbase.h"
#include "cominfo.h"
#include "rmhelp.h"  // brings in rmcalls.h

#define USESIRQ     1         //Driver does indeed use IRQs....

/*--------------------------------------------------------*/
/* # D E F I N E S                                        */
/*--------------------------------------------------------*/
#ifndef MAXADN                          //Maximum number of adapters
#define MAXADN 4
#endif
#ifndef MAXPORT                         //Maximum number of I/O ranges
#define MAXPORT 1
#endif
#ifndef MAXIRQ                          //Maximum number of IRQs
#define MAXIRQ  1
#endif
#define MAXRESOURCE (MAXPORT+MAXIRQ)


/*--------------------------------------------------------*/
/* Set the DRIVERSTRUCT and ADAPTERSTRUCT data areas      */
/*--------------------------------------------------------*/

/*----------------------------------------------*/
/* Driver Description                           */
/*----------------------------------------------*/
DRIVERSTRUCT DriverStruct =
{
   "COM.SYS",                                /* DrvrName                */
   "Asynchronous Communications Driver",     /* DrvrDescript            */
   "IBM OS/2",                               /* VendorName              */
   CMVERSION_MAJOR,                          /* MajorVer                */
   CMVERSION_MINOR,                          /* MinorVer                */
   1994,9,17,                                /* Date                    */
   DRF_STATIC,                               /* DrvrFlags               */
   DRT_OS2,                                  /* DrvrType                */
   DRS_CHAR,                                 /* DrvrSubType             */
   NULL                                      /* DrvrCallback            */
};

/*----------------------------------------------*/
/* Adapter Description 0                        */
/*----------------------------------------------*/
ADAPTERSTRUCT AdapterStruct=
{
  "SERIAL_# Serial Controller",      /* AdaptDescriptName; */
  AS_NO16MB_ADDRESS_LIMIT,           /* AdaptFlags;        */
  AS_BASE_COMM,                      /* BaseType;          */
  AS_SUB_SERIAL,                     /* SubType;           */
  AS_INTF_16450,                     /* InterfaceType;     */
  AS_HOSTBUS_OTHER,                  /* HostBusType;       */
  AS_BUSWIDTH_16BIT,                 /* HostBusWidth;      */
  NULL                               /* pAdjunctList;      */
};

UCHAR LComName[] = KEY_COMM;

UCHAR FSComName[] = "COM#: ";


/*----------------------------------------------*/
/* GLOBAL VARS FOR RM                           */
/*                                              */
/* RM.LIB needs these declared                  */
/*----------------------------------------------*/
PFN             RM_Help               = 0L;
ULONG           RMFlags               = 0L;
ULONG           Device_Help           = 0L;
PFN             RM_Help0              = 0L;
PFN             RM_Help3              = 0L;

/*----------------------------------------------*/
/* GLOBAL HANDLE VARIABLES                      */
/*                                              */
/* These variables get the handles for          */
/* drivers, adapter (only 1), and resources.    */
/* The RMHELP wrappers are written so that most */
/* drivers using RMHELP as a template will not  */
/* have to reference these directly.            */
/*                                              */
/*----------------------------------------------*/

USHORT    curadn  = 0;        /* Current Adapter Number           */
USHORT    cSerialNodes = 0;   /* Number of other SERIAL_# Nodes   */
HDRIVER   hDriver = NULL;     /* global handle to driver          */
HADAPTER  hAdapter[MAXADN];   /* global handle to adapter         */
HRESOURCE hResPorts[MAXADN];  /* global handles to ports resource */
HRESOURCE hResIRQs[MAXADN];   /* global handles to IRQs resource  */

VOID FAR RMHELP_SetDevHelp(ULONG dhaddr)
{
     Device_Help = dhaddr;
}

/*--------------------------------------------------------*/
/* RMHELP_CreateDriver - registers to RM susbsystem       */
/*                       (which initializes the DD to     */
/*                        RM connection)                  */
/*                                                        */
/*--------------------------------------------------------*/

USHORT FAR RMHELP_CreateDriver()
{
   USHORT     rc;
   HANDLELIST HandleList;

   memset(hResPorts,0,MAXADN*sizeof(HRESOURCE));
   memset(hResIRQs,0,MAXADN*sizeof(HRESOURCE));

   if (rc = RMCreateDriver(&DriverStruct,&hDriver) )
      return( rc );

   HandleList.cMaxHandles = 1;

   if ( !(RMKeyToHandleList(HANDLE_PHYS_TREE,
                            "SERIAL*",
                            &HandleList) ) ) {
      cSerialNodes = HandleList.cHandles;
   }


   return(rc);

}

/*--------------------------------------------------------*/
/* RMHELP_DestroyDriver - deregisters the driver from     */
/*                        RM sub-system and releases all  */
/*                        claimed resources               */
/*                                                        */
/*--------------------------------------------------------*/

VOID FAR RMHELP_DestroyDriver()
{
     RMDestroyDriver(hDriver);

     /*-------------------------*/
     /* Null out global handles */
     /*-------------------------*/

     hDriver = NULL;
     memset(hAdapter,0,MAXADN*sizeof(HRESOURCE));
     memset(hResPorts,0,MAXADN*MAXPORT*sizeof(HRESOURCE));
     memset(hResIRQs,0,MAXADN*MAXIRQ*sizeof(HRESOURCE));

}

/*--------------------------------------------------------*/
/* RMHELP_CreateAdapter - tells RM to associate adapter   */
/*                        to driver and resources to      */
/*                        adapater                        */
/*                                                        */
/*--------------------------------------------------------*/

USHORT FAR RMHELP_CreateAdapter(USHORT adn)
{
     UCHAR       ResourceBuf[ sizeof(HRESOURCE) * MAXRESOURCE ];
     PAHRESOURCE pResourceList = (PAHRESOURCE)ResourceBuf;
     ADJUNCT     adj;
     USHORT      rescnt;


     adj.pNextAdj=NULL;
     adj.AdjLength=sizeof(ADJUNCT);
     adj.AdjType=ADJ_ADAPTER_NUMBER;
     adj.Adapter_Number = adn + cSerialNodes ;

     rescnt=0;                          //no resources to begin with

     if (hResPorts[adn] != 0) {
        pResourceList->hResource[rescnt++]=hResPorts[adn];
     }


     if (hResIRQs[adn] != 0) {
        pResourceList->hResource[rescnt++] = hResIRQs[adn];
     }

     pResourceList->NumResource=rescnt;
     AdapterStruct.pAdjunctList=&adj;

     RMCreateAdapter(hDriver,
                     &hAdapter[adn],
                     &AdapterStruct,
                     NULL,
                     pResourceList) ;



     return(0);
}

/*--------------------------------------------------------*/
/* RMHELP_FreeAdapter -   tells RM to free adapter and    */
/*                        resources allocated to adapter  */
/*                                                        */
/*--------------------------------------------------------*/

VOID FAR RMHELP_FreeAdapter(USHORT adn)
{

   if (hResPorts[adn] != 0) {
        RMDeallocResource(hDriver,hResPorts[adn]);
   }

   if (hResIRQs[adn] != 0) {
        RMDeallocResource(hDriver,hResIRQs[adn]);
   }

   if (hAdapter[adn] != 0) {
        RMDestroyAdapter(hDriver,hAdapter[adn]);
   }
}

/*--------------------------------------------------------*/
/* RMHELP_AllocPorts - attempts to claim a port resource */
/*                       from RM subsystem                */
/*                                                        */
/* ARGS:                                                  */
/*                                                        */
/* USHORT baseport - base IO port.                        */
/* USHORT nPorts   - number of prts inclusive of base port*/
/*                                                        */
/*--------------------------------------------------------*/
USHORT FAR RMHELP_AllocPorts(USHORT MachType, USHORT baseport,USHORT nPorts)
{
   RESOURCESTRUCT Resource;


   if (hResPorts[curadn] == 0) {

      Resource.ResourceType             = RS_TYPE_IO;
      Resource.IOResource.BaseIOPort    = baseport;
      Resource.IOResource.NumIOPorts    = nPorts;

      if ( (MachType == MACH_EISA) || (MachType == MACH_UCHNL) ) {
           Resource.IOResource.IOAddressLines=16;
      } else {
           Resource.IOResource.IOAddressLines=10;
      }

      Resource.IOResource.IOFlags       = RS_IO_MULTIPLEXED;
      return(RMAllocResource( hDriver, &hResPorts[curadn], &Resource ));
   }

   return(RMHELP_ERR_RESOURCECLAIMED);
}


/*--------------------------------------------------------*/
/* RMHELP_AllocIRQ   - attempts to claim a IRQ resource   */
/*                       from RM subsystem                */
/*                                                        */
/* ARGS:                                                  */
/*                                                        */
/* USHORT IRQlevel - IRQ level to claim                   */
/*                                                        */
/*--------------------------------------------------------*/
USHORT FAR RMHELP_AllocIRQ(USHORT MachType, USHORT IRQlevel)
{
     RESOURCESTRUCT Resource;

     if (hResIRQs[curadn] == 0) {
          Resource.ResourceType          = RS_TYPE_IRQ;
          Resource.IRQResource.PCIIrqPin = RS_PCI_INT_NONE;
          if (MachType == MACH_UCHNL)
             Resource.IRQResource.IRQFlags  = RS_IRQ_SHARED;
          else
             Resource.IRQResource.IRQFlags  = RS_IRQ_MULTIPLEXED;
          Resource.IRQResource.IRQLevel  = IRQlevel;
          return(RMAllocResource(hDriver,&(hResIRQs[curadn]),&Resource));
     }

     return(RMHELP_ERR_RESOURCECLAIMED);
}



/*****************************************************************************/
/*****************************************************************************/
/********                                               **********************/
/********  All COM specific code appears below this     **********************/
/********                divider!!!                     **********************/
/********                                               **********************/
/********                                               **********************/
/*****************************************************************************/
/*****************************************************************************/

/*--------------------------------------------------------*/
/* RMHELP_GetPorts   - attempts to claim resources for a  */
/*                     COM port                           */
/*                                                        */
/* ARGS:  ptr = COM driver ComInfo structure              */
/*                                                        */
/*--------------------------------------------------------*/
USHORT FAR RMHELP_GetPorts(USHORT MachType,PCOMINFO pComInfo)
{
     USHORT rc = 0;

     curadn = pComInfo->ci_port_number;

     if ( (pComInfo->ci_port != 0) &&
          (rc = RMHELP_AllocPorts(MachType, pComInfo->ci_port,8)) ) {
        return(rc);
     }

     if ( (pComInfo->ci_irq != 0) &&
          (rc=RMHELP_AllocIRQ(MachType, pComInfo->ci_irq)) ) {
       return(rc);
     }

     return(0);
}

/*----------------------------------------------------------*/
/* RMHELP_PortInitComplete - Bind resources to the port     */
/*                                                          */
/*      We also examine the ComInfo structure for the       */
/*      hardware type and update our structures accordingly */
/*                                                          */
/* ARGS:  pComInfo = COM driver ComInfo structure           */
/*                                                          */
/*----------------------------------------------------------*/

USHORT FAR RMHELP_PortInitComplete(PCOMINFO pComInfo, USHORT BusType)
{
   USHORT rc=0;

   switch (BusType) {
      case MACH_EISA:
         AdapterStruct.HostBusType=AS_HOSTBUS_EISA;
         break;
      case MACH_UCHNL:
         AdapterStruct.HostBusType=AS_HOSTBUS_uCHNL;
         break;
      case MACH_PCMCIA:
         AdapterStruct.HostBusType=AS_HOSTBUS_PCMCIA;
         break;
      case MACH_ISA:
         AdapterStruct.HostBusType=AS_HOSTBUS_ISA;
         break;
      default:
         AdapterStruct.HostBusType=AS_HOSTBUS_PLANAR;
         break;
   }

   switch (pComInfo->ci_flagx & FX_CHIP_MASK) {
      case FX_8250:
      case FX_16450:
         AdapterStruct.InterfaceType=AS_INTF_16450;
         break;
      case FX_16550A:
         AdapterStruct.InterfaceType=AS_INTF_16550;
         break;
   }

   /*-------------------------------*/
   /* If not pcmcia modem, create   */
   /* an adapter                    */
   /*-------------------------------*/

   if (!(pComInfo->ci_flagx1 & FX1_PCMCIA_MODEM)) {
      rc = RMHELP_CreateAdapter( pComInfo->ci_port_number );
      if (!rc){
        pComInfo->ci_hAdapter = hAdapter[pComInfo->ci_port_number];
        rc = RMHELP_CreateLDev(pComInfo);
      }
   }

   return( rc  );
}

/*----------------------------------------------------------*/
/* RMHELP_PortDidntInstall - Free resources from a port     */
/*                                                          */
/* ARGS:  locus = code to indicate point of failure         */
/*        port  = adapter number to free                    */
/*                                                          */
/*----------------------------------------------------------*/
USHORT FAR RMHELP_PortDidntInstall(USHORT Locus, USHORT Port)
{
     return( RMHELP_FreeAdapter( Port ));
}

/*----------------------------------------------------------*/
/* RMHELP_CreateLDev - Create logical device nodes          */
/*                                                          */
/* ARGS:                                                    */
/*        pComInfo  = pointer to ComInfo                    */
/*                                                          */
/*----------------------------------------------------------*/
USHORT FAR RMHELP_CreateLDev( PCOMINFO pComInfo )
{
  LDEVSTRUCT    RMLDevStr;
  SYSNAMESTRUCT RMSysNameStr;
  ADJUNCT       RMAdjunct;
  HLDEV         hRMLDev;
  HSYSNAME      hRMSysName;
  USHORT        rc=0;

  if (!(pComInfo->ci_hLDev)){
     memset( (PSZ)&RMLDevStr, 0, sizeof(LDEVSTRUCT) );

     memset( (PSZ)&RMSysNameStr, 0, sizeof(SYSNAMESTRUCT) );

     RMLDevStr.LDevClass        = LDEV_CLASS_SERIAL;
     RMLDevStr.pAdjunctList     = &RMAdjunct;
     RMAdjunct.AdjType          = ADJ_DEVICE_NUMBER;
     RMAdjunct.AdjLength        = sizeof(ADJUNCT);
     RMAdjunct.pNextAdj         = NULL;

     RMLDevStr.LDevDescriptName = LComName;
     RMAdjunct.Device_Number = pComInfo->ci_port_number;

     if ( !(rc = RMCreateLDev( (HDRIVER)     hDriver,
                               (PHLDEV)      &hRMLDev,
                               (HDEVICE)     pComInfo->ci_hAdapter,
                               (PLDEVSTRUCT) &RMLDevStr  )) )
     {
       pComInfo->ci_hLDev = hRMLDev;

       RMSysNameStr.SysDescriptName = FSComName;
       RMSysNameStr.pAdjunctList    = NULL;

       FSComName[3] = (UCHAR)(pComInfo->ci_port_number + 1 + '0');

       rc = RMCreateSysName( (HDRIVER)        hDriver,
                             (PHSYSNAME)      &hRMSysName,
                             (HDEVICE)        hRMLDev,
                             (PSYSNAMESTRUCT) &RMSysNameStr  );

       pComInfo->ci_hSysName = hRMSysName;
     }
  }

  return rc;
}


/*----------------------------------------------------------*/
/* RMHELP_DestroyLDev - Destroy logical device nodes        */
/*                                                          */
/* ARGS:  pComInfo = pointer to ComInfo Structure           */
/*                                                          */
/*                                                          */
/*----------------------------------------------------------*/
VOID FAR RMHELP_DestroyLDev( PCOMINFO pComInfo )
{
  if (pComInfo) {
     if (pComInfo->ci_hSysName)
        RMDestroySysName( hDriver, pComInfo->ci_hSysName );
     pComInfo->ci_hSysName = 0;
     if (pComInfo->ci_hLDev)
        RMDestroyLDev( hDriver, pComInfo->ci_hLDev );
     pComInfo->ci_hLDev = 0;
  }
}

/*----------------------------------------------------------*/
/* RMHELP_FindPCMCIA - Find PCMCIA Node to hang LDEV        */
/*                                                          */
/* ARGS:  pComInfo = pointer to ComInfo Structure           */
/*                                                          */
/*                                                          */
/*----------------------------------------------------------*/
VOID FAR RMHELP_FindPCMCIANode( PCOMINFO pComInfo )
{
   HANDLELIST HandleList;

   HandleList.cMaxHandles = 1;

   if ( !(RMKeyToHandleList(HANDLE_PHYS_TREE,
                            "PCMCIA*",
                            &HandleList) ) ) {
      if( HandleList.cHandles )
         pComInfo->ci_hAdapter = HandleList.Handles[0];
      else
         pComInfo->ci_hAdapter = 0;
   } else {
      pComInfo->ci_hAdapter = 0;
   }
}
