/*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 =  RSMALLOC.C
 *
 * DESCRIPTIVE NAME =  Resource Management Driver - Memory allocation
 *
 *
 *
 * VERSION = V1.01
 *
 * DATE
 *
 * DESCRIPTION :
 *
 * Purpose:
 *
 *
 *
 * FUNCTIONS  :
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 *
 *
 * EXTERNAL FUNCTIONS
 *
*/
#define  INCL_NOBASEAPI
#define  INCL_NOPMAPI
#define  INCL_DOSERRORS
#include <os2.h>

#include <dskinit.h>

#include "dhcalls.h"

#include "strat2.h"
#include "scsi.h"
#include "reqpkt.h"

#define RMCode SwapCode
#include "rmcalls.h"
#include "rmioctl.h"

#include "rsmcons.h"
#include "rsmtypes.h"
#include "rsmproto.h"
#include "rsmextrn.h"



/*--------------------------------------------------------*/
/*                                                        */
/*   RMAlloc                                              */
/*   -------                                              */
/*                                                        */
/*                                                        */
/*                                                        */
/*--------------------------------------------------------*/

PUCHAR FAR RMAlloc( USHORT cBytes )
{
  PBUFHEADER pBUF = 0;
  BUFINFO    BufInfo;

  /**
   ** Adjust cBytes
   **
   ** Enforce MIN_BUF_SIZE
   ** Subtract HEADER size and round to 8-Byte boundary.
   **/
  cBytes += sizeof(USHORT);

  if ( cBytes < MIN_BUF_SIZE )
  {
    cBytes = MIN_BUF_SIZE;
  }

  cBytes  = (cBytes + 0x7L) & ~0x7L;

  if ( FindFreeBlock( cBytes, &BufInfo  ) )
  {
    pBUF = AllocBuf( cBytes, &BufInfo );

    memset( &pBUF->pNext, 0, pBUF->Size - sizeof(USHORT) );

    pBUF = (PBUFHEADER) &pBUF->pNext;
  }

  return ( (PUCHAR) pBUF );
}




/*--------------------------------------------------------*/
/*                                                        */
/*   FindFreeBlock                                        */
/*   -------------                                        */
/*                                                        */
/*                                                        */
/*                                                        */
/*--------------------------------------------------------*/


USHORT NEAR FindFreeBlock( USHORT cBytes, PBUFINFO pBUFInfo )
{
  PBUFHEADER     pBUF, pBUFPrev, pBUFSPrev, pBUFS;

  SHORT          Excess;
  USHORT         PrevExcess;

  USHORT         i;

  pBUF       = pBUFChain;
  pBUFPrev   = 0;

  pBUFS      = 0;
  pBUFSPrev  = 0;

  PrevExcess = -1;

  while ( pBUF )
  {
    /**
     ** pBUFS points at 'Best-Fit' candidate
     **
     **/
    Excess = pBUF->Size - cBytes;
    if ( (Excess > 0) && ((USHORT) Excess < PrevExcess) )
    {
      pBUFS      = pBUF;
      pBUFSPrev  = pBUFPrev;
      PrevExcess = Excess;
    }

    pBUFPrev = pBUF;
    pBUF     = pBUF->pNext;
  }

  pBUFInfo->pBUF     = pBUFS;
  pBUFInfo->pBUFPrev = pBUFSPrev;

  return ( (pBUFS) ? 1 : 0 );

}


/*--------------------------------------------------------*/
/*                                                        */
/*   AllocBuf                                             */
/*   --------                                             */
/*                                                        */
/*                                                        */
/*                                                        */
/*--------------------------------------------------------*/

PBUFHEADER NEAR AllocBuf( USHORT cBytes, PBUFINFO pBUFInfo )
{
  PBUFHEADER    pBUF, pBUFS, pBUFSPrev;

  SHORT         Excess;

  pBUFS     = pBUFInfo->pBUF;
  pBUFSPrev = pBUFInfo->pBUFPrev;

  Excess = pBUFS->Size - cBytes;
  /**
   ** Check if its worth subdividing the free buffer. If
   ** not provide the whole allocation to the caller
   **/

  if ( Excess >= MIN_BUF_SIZE )
  {
    /**
     ** Create a new header
     **
     ** This will cause allocations to start at the top
     ** of the Free Block and move towards higher addresses.
     **
     **/
     pBUF        = (PBUFHEADER)((PUCHAR) pBUFS + cBytes);

     pBUF->pNext = pBUFS->pNext;
     pBUF->Size  = pBUFS->Size - cBytes;

     if ( pBUFSPrev )
     {
       pBUFSPrev->pNext = pBUF;
     }
     else
     {
       pBUFChain = pBUF;
     }


     pBUF = pBUFS;

  }
  else
  {
    /**
     ** Remove the entire selected free buffer from the chain
     **
     **/

    if ( pBUFSPrev )
    {
      pBUFSPrev->pNext = pBUFS->pNext;
    }
    else
    {
      pBUFChain = pBUFS->pNext;
    }
    pBUF   = pBUFS;
    cBytes = pBUFS->Size;
  }

  cAllocations++;

  ((PUSHORT)pBUF)[0]  = cBytes;

  return( pBUF );
}

/*--------------------------------------------------------*/
/*                                                        */
/*   RMFree                                               */
/*   ------                                               */
/*                                                        */
/*                                                        */
/*                                                        */
/*--------------------------------------------------------*/

VOID FAR RMFree( PUCHAR pUserBuf )
{
  PBUFHEADER  pBUFRet, pBUF, pBUFPrev, pBUFScan, pBUFAdj;
  PUCHAR      pBUFBase = RMPool;

  USHORT      i;
  SHORT       Size;

  if ( SELECTOROF(pUserBuf) != SELECTOROF(pBUFBase) )
  {
    _asm { int 3 }
  }

  (PUCHAR) pBUFRet = (PUCHAR) pUserBuf - sizeof(USHORT);


  if ( !(Size = pBUFRet->Size) || (Size > RMPoolSize) )
  {
    _asm { int 3 }
    return;
  }

  /**
   ** Find nearest buf headers
   **/
  pBUF     = pBUFChain;
  pBUFPrev = 0;

  while ( pBUF && (pBUF < pBUFRet) )
  {
    pBUFPrev   = pBUF;
    pBUF       = pBUF->pNext;
  }


  /**
   ** Create a new header and add to free chain
   **/
  pBUFRet->Size  = Size;
  pBUFRet->pNext = 0;

  if ( !pBUFPrev )
  {
    pBUFRet->pNext  = pBUFChain;
    pBUFChain       = pBUFRet;
    pBUFPrev        = pBUFRet;
  }
  else
  {
    pBUFRet->pNext  = pBUF;
    pBUFPrev->pNext = pBUFRet;
  }

  /**
   ** Scan chain for buffer absorption
   **
   ** Start with pBUFPrev
   **/

  pBUFScan = pBUFPrev;

  while ( pBUFScan->pNext )
  {
    (PBYTE) pBUFAdj = (PBYTE) pBUFScan + pBUFScan->Size;

    /**
     ** If the current buffer (pBUFScan) adsorbed its neighbor
     ** we continue to point to it to see if the combined buffer
     ** can adsorb more of its neighbors. Otherwise we move
     ** on to the next buffer in the free list.
     **/
    if ( pBUFAdj == pBUFScan->pNext )
    {
      pBUFScan->Size  += pBUFAdj->Size;
      pBUFScan->pNext  = pBUFAdj->pNext;
    }
    else
    {
      pBUFScan = pBUFScan->pNext;
    }
  }

  /**
   ** A little sanity checking
   **/
  if ( !--cAllocations )
  {
    /**
     ** We should only have one free block
     ** and it should be at maximum size
     **/
    if ( !pBUFChain->pNext && pBUFChain->Size != RMPoolSize )
    {
      _asm { int 3 }
    }

  }

}
