/*static char *SCCSID = "@(#)bvsmem.c	6.1 90/11/16";*/
/*static char *SCCSID = "@(#)bvsmem.c	6.4 90/01/30";*/
/****************************************************************************/
/*									    */
/*			 IBM/Microsoft Confidential			    */
/*									    */
/*		   Copyright (c) IBM Corporation  1987, 1990		    */
/*		   Copyright (c) Microsoft Corp.  1987, 1990		    */
/*			     All Rights Reserved			    */
/*									    */
/****************************************************************************/

#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <doscalls.h>
#include <dos.h>
#include <error2.h>

#include <os2def.h>

#include "bvsldrvr.h"
#include "bvsdata.h"

/***	pchGrowGlobal -- Grow the Global Segment (BVSGSEG) by specified amount
*
*	This routine grows BVSGSEG and returns the offset of the new
*	data area that has been set to zeroes.
*
*	PSEUDOCODE
*
*	ENTRY	none
*
*	EXIT	return	-  0 if initializations succeeded
*			   errorcode otherwise
*/
char far * near
pchGrowGlobal(amount)
unsigned amount;
{
    char far *pch;
    unsigned long cursize,newsize;	/*@S36*/

    pch = (char far *)&cbBvsGSeg;
    amount = (amount + 1) & 0xFFFE;	/* Allocate words, not bytes, B703185*/

    if ( !DOSSIZESEG ( FP_SEG(pch), &cursize) ) { /*@S36*/
	newsize = (unsigned long) cbBvsGSeg + (unsigned long) amount; /*@S36*/

	if ( (newsize < 0x10000)	/* Only one segment, @S36*/
	    && ( (newsize <= cursize) || /* Reallocate not required, @S36*/
	    !DOSREALLOCSEG ((unsigned short)newsize, FP_SEG(pch)) )) { /*@S36*/

	    FP_OFF(pch) = cbBvsGSeg;
	    cbBvsGSeg += amount;
	    memset(pch,0,amount);	/* Init new memory to zeroes */

	    return (pch);		/*@S36*/
	}
    }					/*@S36*/
    return (NULL);			/* Error return, @S36*/
}

/***	uGrowDevGroup - grow BVSGSEG by 1 DEVGROUP
*
*	This routine grows BVSGSEG to make one more DEVGROUP available
*	for use by the VDH loading routines.
*
*	PSEUDOCODE
*
*	ENTRY	none
*
*	EXIT	return	-  0 if initializations succeeded
*			   errorcode otherwise
*/
unsigned near
uGrowDevGroup()
{
    unsigned rc = 0;

    if (pDevGroups[cDevGroups] = (DEVGROUP far *)pchGrowGlobal(sizeof(DEVGROUP))) {
	pDevGroups[cDevGroups]->flags = 0; /* initialize the device entry */
	vInitializeEntry(&pDevGroups[cDevGroups]->devEnt);
	pDevGroups[cDevGroups]->devEnt.iDevice = cDevGroups;
	cDevGroups++;
    } else {
	rc = ERROR_VIO_INTERNAL_RESOURCE;
    }
    return (rc);
}

/***	fGetFreeCallTable - get a free call table entry
*
*	This routine scans the list of free call tables, allocating
*	one that is not being used.
*
*	BUGBUG - this routine needs per system serialization
*
*	PSEUDOCODE:
*
*	    for (each entry in the table)
*		if (the entry is free)
*		    set the pointer to the call table
*		    mark the entry in use
*		    return TRUE
*	    set the pointer to NULL
*	    return FALSE
*
*	ENTRY	ppcalltbl   - pointer to the call table pointer
*
*	EXIT   *ppcalltbl   - pointer to the call table allocated
*		return	    - TRUE if successful
*			      FALSE otherwise
*/

unsigned near
fGetFreeCallTable (ppcalltbl)
CALLTBL far ** ppcalltbl;
{
    unsigned i;

    for (i = 0; i < cDevGroups; i++) {	/* each entry in table */
	if (!(pDevGroups[i]->flags & dg_calltblinuse)) { /* entry is free */
	    break;			/* Free entry found, @S16*/
	}
    }
    if (i == cDevGroups) {		/* Entry not found, @S16*/
	if (uGrowDevGroup()) {		/* Newly allocated table, @S16*/
	    *ppcalltbl = NULL;		/* pointer is not valid, @S16*/
	    return (FALSE);		/* return failure, @S16*/
	}				/*@S16*/
    }					/*@S16*/

    *ppcalltbl = (CALLTBL far *)pDevGroups[i]->calltbl; /* entry pointer */
    pDevGroups[i]->flags |= dg_calltblinuse; /* mark entry in use */

    for (i = 0; i < cCallVectorEntries; i++) { /*@S16*/
	(**ppcalltbl)[i] = uErrorFunction; /*@S16*/
    }					/*@S16*/
    (**ppcalltbl)[fn_PrintScreen-fn_BufferUpdate] = VdhPrtSc; /*Install default routine, D198,@S16*/
    (**ppcalltbl)[fn_WriteTTY-fn_BufferUpdate] = VdhWrtTTY; /*Install default routine, D198,@S16*/

    return (TRUE);			/* return success */
}

/***	fGetFreeDevTableEntry - get a free device table entry
*
*	This routine scans the device table for entries that are
*	not being used.
*
*	PSEUDOCODE:
*
*	    for (each entry in the table)
*		if (the entry is free)
*		    set the pointer to the device data
*		    mark the entry in use
*		    return TRUE
*	    set the pointer to NULL
*	    return FALSE
*
*	ENTRY	ppdevTable  - pointer to the device information pointer
*
*	EXIT   *ppdevTable  - pointer to the device information
*		return	    - TRUE if successful
*			      FALSE otherwise
*/

unsigned near
fGetFreeDevTableEntry (ppdevTable)
t_dev far ** ppdevTable;
{
    register unsigned i;

    for (i = 0; i < cDevGroups; i++) {	/* each entry in table */
	if (!pDevGroups[i]->devEnt.fValid) { /* Entry is free, @S16*/
	    break;			/* Free entry found, @S16*/
	}
    }
    if (i == cDevGroups) {		/* Entry not found, @S16*/
	if (uGrowDevGroup()) {		/* Newly allocated entry, @S16*/
	    *ppdevTable = NULL; 	/* pointer is not valid, @S16*/
	    return (FALSE);		/* return failure, @S16*/
	}				/*@S16*/
    }					/*@S16*/

    *ppdevTable = (t_dev far *)&pDevGroups[i]->devEnt; /* entry pointer */
    (*ppdevTable)->fValid = TRUE;	/* mark entry in use */
    return (TRUE);			/* return success */
}

/***	vFreeDevTableEntry - release a table entry and unload the dynlinks
*
*	This routine scans the device table entry components, initializing
*	all fields and unloading dynlinks that it finds.
*
*	PSEUDOCODE:
*
*	    for (each component in the table entry)
*		if (the component is a dynlink)
*		    unload the dynlink (DOSFREEMODULE)
*		initialize the component fields (vInitializeEntry)
*	    initialize the Valid field
*
*	ENTRY	pdevTable  - pointer to the table entry to be freed
*
*	EXIT	none
*/

void near
vFreeDevTableEntry (pdevTable)
t_dev far * pdevTable;
{
    register unsigned i;
    unsigned rc;

    for (i = 0; i < cMaxComponent; i++) { /* each entry in table */
	if (pdevTable->cmpComponent[i].hModule != 0) { /* dynlink component */
					/* release the dynlink */
	    rc = DOSFREEMODULE ( pdevTable->cmpComponent[i].hModule);
	}
    }
    vInitializeEntry(pdevTable);	/* initialize the device entry */
    return ;
}

/***	fGetTempBuf - get a temporary buffer
*
*	This routine gets a temporary buffer of the size specified.
*	If the request succeeds, TRUE is returned, otherwise FALSE.
*
*	PSEUDOCODE:
*
*	    DosAllocSeg to get a buffer of the specified size
*	    if no error
*		return TRUE
*	    else
*		return FALSE
*
*	ENTRY	ppchBuffer  - pointer to which to return the buffer address
*		cbBuffer    - number of bytes of buffer needed
*
*	EXIT	return	    - TRUE if the buffer was allocated
*			      FALSE otherwise
*/

unsigned near
fGetTempBuf(ppchBuffer, cbBuffer)
char far ** ppchBuffer;
unsigned    cbBuffer;
{
    unsigned i = 0;

    if (!cTmpMemUseCount) {
	if (!fInitTempBufs()) {
	    return (FALSE);
	}
    }
    (char far *)*ppchBuffer = NULL;
    if (cbBuffer <= cbMemoryBlock) {
	while (i < cMemoryBlocks) {
	    if (!(ptmpmemBlock->chmemBlock[i].fInUse)) {
		*ppchBuffer = ptmpmemBlock->chmemBlock[i].chMem;
		memset(*ppchBuffer,0,cbBuffer); /* Initialize it, D261 */
		ptmpmemBlock->chmemBlock[i].fInUse = TRUE;
		cTmpMemUseCount++;
		return (TRUE);
	    } else {
		i++;
	    }
	}
    }
    return (FALSE);
}

/***	vFreeTempBuf - free a temporary buffer
*
*	This routine frees a temporary buffer that was allocated with
*	fGetTempBuf.
*
*	PSEUDOCODE:
*
*	    DosFreeSeg on the buffer
*
*	ENTRY	ppchBuffer  - pointer to which to return the buffer address
*
*	EXIT	return	    - none
*/

void near
vFreeTempBuf(pchBuffer)
char far * pchBuffer;
{
    unsigned rc;

    if ( pchBuffer != NULL ) {		/* @C1 */
	if (FP_SEG(pchBuffer) == FP_SEG(ptmpmemBlock)) {
	    (unsigned long) pchBuffer -= sizeof (unsigned);
	    *((unsigned far *)pchBuffer) = FALSE;
	    cTmpMemUseCount--;
	    if (!cTmpMemUseCount) {
		vReleaseTempBufs();
	    }
	}
    }					/* @C1 */
    return;
}

/***	fInitTempBufs - initialize the array of temporary buffers
*
*	This routine prepares the pool of temporary buffers by allocating
*	a block of memory to be used for the temporary buffers.
*
*	PSEUDOCODE:
*
*	    DosAllocSeg to get the memory block
*	    if no error
*		return TRUE
*	    else
*		return FALSE
*
*	ENTRY	ppchBuffer  - pointer to which to return the buffer address
*		cbBuffer    - number of bytes of buffer needed
*
*	EXIT	return	    - TRUE if the buffer was allocated
*			      FALSE otherwise (and ptr set to 5678:1234)
*/

unsigned near
fInitTempBufs()
{
    unsigned i;

    if ((ptmpmemBlock == NULL) && !cTmpMemUseCount) { /*@S21*/
	if (!DOSALLOCSEG(sizeof (t_tmpmem), ((unsigned far *)&ptmpmemBlock) + 1,
	    umaskAllocSeg)) {		/*@S21*/
	    for (i = 0; i < cMemoryBlocks; i++) {
		ptmpmemBlock->chmemBlock[i].fInUse = FALSE;
	    }
	    return (TRUE);
	}				/*@S21*/
    }

    ptmpmemBlock = (t_tmpmem far *)0x56781234; /*Tag this error,@S21*/
    return (FALSE);
}

/***	vReleaseTempBufs - free temporary buffer memory
*
*	This routine frees the block of memory allocated for temporary
*	buffers.
*
*	PSEUDOCODE:
*
*	    DosFreeSeg on the buffer
*
*	ENTRY	ppchBuffer  - pointer to which to return the buffer address
*		cbBuffer    - number of bytes of buffer needed
*
*	EXIT	return	    - TRUE if the buffer was allocated
*			      FALSE otherwise
*/

void near
vReleaseTempBufs()
{
    unsigned rc;

    if ((ptmpmemBlock != NULL) && (!cTmpMemUseCount)) {
	rc = DOSFREESEG ( *(((unsigned far *)&ptmpmemBlock) + 1));
	(char far *)ptmpmemBlock = NULL;
    }

}

/*JOPT = 03/30/89 STJ, Convert all local routines to NEAR subroutines	*/
/*@S14 = 06/08/89 STJ, Fix initialization of AVIO session, B703158 */
/*@S21 = 07/28/89 STJ, Tag allocation error with 12345678h */
/*@T45 = 08/31/89 TPL, Don't load other BVHs when starting VIO Wndw, B707495 */
/*@S36 = 10/11/89 STJ, Avoid realloc of BVSGSEG where possible, AR05111 */
