/***************************************************************************
*   NAME:  IWUTIL.C $Revision: 1.29 $
**  COPYRIGHT:
**  "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: iwutil.c $
* Revision 1.29  1995/12/04 18:00:07  teckert
* Added DMA translation enabling for DOS boxes.
* Revision 1.28  1995/11/28 10:52:55  teckert
* Changed init code to prevent multiple sequential opens and to allow the 
* mixer to run concurrently with a DOS box app.
* Revision 1.27  1995/11/21 17:24:41  sdsmith
* Changes for new InitCallback function
* Revision 1.26  1995/07/06 19:07:08  sdsmith
* Additions for Save Settings
* Revision 1.25  1995/06/28 11:37:42  teckert
* Rewrote patch caching strategy to handle low memory conditions.
* Revision 1.24  1995/06/05 16:13:44  teckert
* Added support for DPMI allocated buffer
* Revision 1.23  1995/05/30 15:57:32  teckert
* Revision 1.22  1995/05/30 15:51:17  teckert
* Disabled CODEC sharing, added windows 95 check for FixSelector()
* Revision 1.20  1995/05/16 21:43:23  sdsmith
* Fixed CODEC mode settings
* Revision 1.19  1995/05/11 16:15:28  teckert
* Added code to switch mixer into mode 3 when mixer starts up
* Revision 1.18  1995/05/07 18:49:59  teckert
* Added system.ini buffer sizes
* Revision 1.17  1995/05/04 11:24:08  teckert
* Fixed bug, erroneous result returned from HWAllocate if LoadKernel failed.
* Revision 1.16  1995/05/04 08:20:49  unknown
* Fixed save of init string of card init error
* Revision 1.15  1995/05/03 14:30:52  teckert
* Revision 1.14  1995/04/28 16:02:24  teckert
* Bug fixed (Defered notes and MCI pause-unpause anomaly)
* Revision 1.13  1995/04/20 10:56:26  teckert
* Revision 1.12  1995/04/20 04:53:43  teckert
* put SetupMixer call into first time initialization
* Revision 1.11  1995/04/19 15:04:10  teckert
* Added function to pagelock segment without moving it to lower memory
* Revision 1.10  1995/04/18 15:37:32  teckert
* Updated promotion table to allow CODEC_MIX to be separately allocated
* Revision 1.9  1995/04/04 15:35:16  teckert
* Fixed hardware acquisition code
* Revision 1.8  1995/03/30 16:38:48  teckert
* Updated arbitration support
* Revision 1.7  1995/03/17 17:05:00  teckert
* Removed GetDMA and FreeDMA functions
* Revision 1.6  1995/03/16 15:56:01  teckert
* Added corrections to hardware acquisition code
* Revision 1.5  1995/03/15 18:30:56  teckert
* Added support for scalable acquisition resolution in the HWAllocate and
* HWFree functions
* Revision 1.4  1995/03/03 11:14:18  sdsmith
* Added os.FullInit = 1 before call to iw_load_kernel
* Revision 1.3  1995/03/01 16:56:30  unknown
* Added file header
* Revision 1.1  1995/02/23 15:14:59  sdsmith
* Initial revision
***************************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <dos.h>
#include <iw.h>        
#include <globals.h>
#include "wave.h"
#include "interwav.h"
#include "viwd.h"
#include "os.h"
	
int nKernelLoaded = 0;
extern struct load_kernel_cfg os;
extern unsigned long gfpBuffer;          // iw_buffer address
int nFirstAlloc = 1;
char tmp_buf[256];
char far *mix_settings;
HLOCAL h_mix_settings = 0;
#define REQUEST_TABLE_SIZE 32
void redo_patches_assignments(void);

/****************************************************************************

LOCAL DEFINITION:
PromotionTable - table for promoting minimum resource request

DESCRIPTION:
The PromotionTable holds the patterns for resource allocation and can be
modified when specific re-initialization functions are added.  It is used by
the PromoteResourceRequest() function.
*/

static unsigned long PromotionTable[16] = 
	{
	/* IWAR_IRQ1            */      IWAR_ALL,
	/* IWAR_IRQ2            */      IWAR_ALL,
	/* IWAR_DMA1            */      IWAR_ALL,
	/* IWAR_DMA2            */      IWAR_ALL,
	/* IWAR_SOMEVOICES      */      IWAR_ALL,
	/* IWAR_ALLVOICES       */      IWAR_ALL,
	/* IWAR_SOMEMEMORY      */      IWAR_ALL,
	/* IWAR_ALLMEMORY       */      IWAR_ALL,
	/* IWAR_CODECPLAY       */      IWAR_ALL,
	/* IWAR_CODECREC        */      IWAR_ALL,
	/* IWAR_CODECMIX        */      IWAR_ALL,
	/* IWAR_PORTIN          */      IWAR_ALL,
	/* IWAR_PORTOUT         */      IWAR_ALL,
	/* bit 0x2000           */      0x0000,         // ignore unknown bits
	/* bit 0x4000           */      0x0000,         // ignore unknown bits
	/* bit 0x8000           */      0x0000          // ignore unknown bits
	};
	
/****************************************************************************

LOCAL DEFINITION:
RequestTable - table of currently open hardware acquisition requests

DESCRIPTION:
Each request for resources is promoted internally and the request may be
promoted again when passed to the VxD, this table keeps track of the fully
promoted requests so that resources are not freed until all acquisition requests
that connect to the resource have been freed.

*/
unsigned long RequestTable[REQUEST_TABLE_SIZE]={0};
unsigned long dwOpenResources=0;

/*****************************************************************************

FUNCTION DEFINITION:
PromoteResourceRequest - promotes the resource request

DESCRIPTION:
When multimedia devices attempt to allocate the minimum amount of resources
required to service the client, this request must be "promoted" to include
any resources that the driver always groups with the requested resource(s).
For instance: if the driver always aquires the MIDI input port and output
port together then a request for IWAR_PORTIN would be promoted to include
IWAR_PORTOUT before the request was passed to the VxD.
The driver would group recources in this way to insure that the kernel can
safely re-initialize the hardware to match the internal state.
As of this revision, the driver uses iw_unload_kernel() and iw_load_kernel() 
to re-initialize the hardware after it was owned by another virtual machine
so all of the resources need to be allocated together.

RETURNS: the bit mask of the promoted resource request
*/
unsigned long PromoteResourceRequest(
	unsigned long dwRequest)
{
	unsigned long dwResult=0;
	unsigned long dwBit;
	int nBit;
	
	for(nBit=0,dwBit=1;nBit<16;nBit++,dwBit<<=1){
		if(dwRequest&dwBit){
			dwResult |= PromotionTable[nBit]; 
		}
	}
		
	return dwResult;
}

/****************************************************************************

FUNCTION DEFINITION:
InitCallback - performs kernel init and de-init functions on behalf of the
               VxD and the ring 3 driver.

INPUTS:
	AX - Function code to be performed
	os - global kernel init structure

RETURNS:
	int - kernel init return code
*/
int InitCallback(void)
{
    unsigned int function_code;
    int rc = IW_OK;

    _asm	mov	function_code,ax

    switch (function_code) {
	case IW_KERNEL_UNLOAD:
	    iw_unload_kernel();
	    break;
	case IW_KERNEL_LOAD:
	    rc = iw_load_kernel(&os);
	    break;
	case IW_KERNEL_SLEEP:
	    if (!h_mix_settings) h_mix_settings = LocalAlloc(LHND, iw_mix_strlen());
	    if (h_mix_settings) {
		mix_settings = (char far *)LocalLock(h_mix_settings);
		iw_get_mixer_settings(mix_settings);
	    }
		iwu_unload_all_patches();
        iwu_clear_marked_patches();
	    iw_unload_kernel();

	    if(wIWDriverFlags & (IWDF_VXDBUFFER|IWDF_DPMIBUFFER)){
            _asm{
                mov ax,810Ch        // Enable DMA Translation
                xor bx,bx
                mov bl,iwl_channel_out
                mov dx,0
                int 4Bh
			
                mov ax,810Ch        // Enable DMA Translation
                xor bx,bx
                mov bl,iwl_channel_in
                mov dx,0
                int 4Bh
            }
	    }
	    break;
	case IW_KERNEL_WAKE:
	    OS_PUSH_DISABLE();
	    if ((rc = iw_init_kernel(&os)) == IW_OK) {
		if ((rc = iw_init_synth(&os)) == IW_OK) {
		    if ((rc = iw_init_codec(&os)) == IW_OK) {
			if ((rc = iw_auto_init(&os)) == IW_OK) {
			    iw_restore_mixer_settings(mix_settings);
			}
		    }
		}
	    }
		if(wIWDriverFlags & (IWDF_VXDBUFFER|IWDF_DPMIBUFFER)){
			_asm{
				mov ax,810Bh        // Disable DMA Translation
				xor bx,bx
				mov bl,iwl_channel_out
				mov dx,0
				int 4Bh
	
				mov ax,810Bh        // Disable DMA Translation
				xor bx,bx
				mov bl,iwl_channel_in
				mov dx,0
				int 4Bh
			}
		}
	
	    if (rc != IW_OK) iw_unload_kernel();
	    OS_POP_FLAGS();
	    break;
	case IW_KERNEL_CODEC_SLEEP:
	    iw_close_codec();
	    break;
	case IW_KERNEL_CODEC_WAKE:
	    rc = iw_init_codec(&os);
	    break;
	default:
	    break;
    }

    return(rc);
}

/****************************************************************************

FUNCTION DEFINITION:
HWAllocate - allocates the hardware and initializes the Kernel

DESCRIPTION:
Allocates the hardware resources by calling the VxD API if a version of the
VxD is installed that supports arbitration.  It re-initializes any resources
that had been acquired by some other virtual machine

RETURNS: index into the RequestTable on success, -1 on failure
*/
int HWAllocate(
	unsigned long dwResource)         // Requested resource code
{                                          
	static unsigned long dwUninitializedResources=IWAR_ALL;
	union RESOURCE request;
	union RESOURCE need_init;
	int nIndex;             // Index into the RequestTable
	int ret;

	for(nIndex=0;nIndex<REQUEST_TABLE_SIZE;nIndex++){
		if(!RequestTable[nIndex])
			break;
	}
	if(nIndex==REQUEST_TABLE_SIZE){
#ifdef DEBUG
		OutputDebugString("ERROR IN HWAllocate(): No more handles");
#endif  
		return -1;
	}                            

	dwResource = PromoteResourceRequest(dwResource);
	
	if(!dwResource){
#ifdef DEBUG
		OutputDebugString("ERROR IN HWAllocate(): Bad resource request");
#endif  
		return -1;
	}

	request.dword = dwResource;

	if(wIWDriverFlags&IWDF_VXDARBITRATION){
		_asm{
			mov     dx,IWAPI_ACQUIREINTERWAVE
			mov     ax,request.loword
			mov     di,request.hiword
			call    dword ptr[Virtual_API]
			jc      Failed_Acquisition
			mov     request.loword,ax
			mov     request.hiword,di
			mov     need_init.loword,dx
			mov     need_init.hiword,si
		}
		if(0){
Failed_Acquisition:
			return -1;
		}
		
		dwResource=request.dword;
	}else{
		need_init.dword=dwResource&dwUninitializedResources;
		dwUninitializedResources &= ~(dwResource);
	}
	RequestTable[nIndex]=dwResource;
	/* The next section of code deals directly with the initialization and
	   reinitialization of the hardware resources.  As the resolution of the
	   hardware acquisition becomes more detailed this section will be
	   rewritten to accomodate those changes.
	   P.S. The memory is always left in a GF1 compatible state if not
		acquired so it needs to be reset even if no other VM had
		used it.
	*/
	if(need_init.dword){ // Initialize any resources that have been in
						// use by another VM since last freed
		if(!nFirstAlloc){
			if(iw_mix_strlen()>256)
				MessageBox(NULL,"Mixer String > 256","INTERWAV.DRV",MB_OK);
			iw_get_mixer_settings(tmp_buf);
		}
		if(!nFirstAlloc){
			iw_restore_mixer_settings(tmp_buf);
		}else{
			nFirstAlloc = 0;
			if(iwl_channel_out==iwl_channel_in){
			    wIWDriverFlags |= IWDF_SHARINGDMABUF;
			    dma_buf_two.vptr = dma_buf_one.vptr;
			    dma_buf_two.paddr = dma_buf_one.paddr;
			}
		}
		gfpBuffer = iw_malloc(dma_buf_one.size);    
	}
	return nIndex;          
}


/****************************************************************************

FUNCTION DEFINITION:
HWFree - unloads the Kernel and frees the hardware

DESCRIPTION:
Decrements the reference count and frees the hardware by calling the VxD API 
function if the VxD is installed and supports arbitration.

RETURNS: void
*/
void HWFree(
	int nHandle)            // Index into the RequestTable
{                       
	union RESOURCE resources;
					  
	if(nHandle<0||nHandle>REQUEST_TABLE_SIZE){
#ifdef DEBUG
		OutputDebugString("ERROR IN HWFree(): Bad handle");
#endif  
		return;
	}
	resources.dword = RequestTable[nHandle];
	RequestTable[nHandle] = 0;
	// Don't free any resources still in use!
	for(nHandle=0;nHandle<REQUEST_TABLE_SIZE&&resources.dword;nHandle++){
		if(resources.dword&RequestTable[nHandle]){
			resources.dword &= ~(RequestTable[nHandle]);
		}
	}
	
	dwOpenResources &= ~(resources.dword);

	if((wIWDriverFlags&IWDF_VXDARBITRATION)&&resources.dword){
		_asm{                                
			mov     dx,IWAPI_RELEASEINTERWAVE
			mov     ax,resources.loword
			mov     di,resources.hiword
			call    dword ptr[Virtual_API]
		}
	}
}

				 
typedef struct
{
	DWORD   pga_next;
	DWORD   pga_prev;
	DWORD   pga_address;
	DWORD   pga_size;
	WORD    pga_handle;
	WORD    pga_owner;
	BYTE    pga_count;
	BYTE    pga_pglock;
	BYTE    flags;
	BYTE    pga_selcount;
	DWORD   pga_lruprev;
	DWORD   pga_lrunext;
}ARENA32;

DWORD FAR PASCAL GlobalMasterHandle(void);
				 
int FixSelector(WORD wSelector)
{       
	DWORD dwVersion;
	DWORD far *SelTable;
	DWORD far *SelTableStart;
	DWORD far *THHookStart;
	ARENA32 far *arena;
	WORD hMaster;
	WORD hKernel;
	WORD wSelectorIndex;
	WORD reg_bx;
	WORD reg_cx;
	WORD reg_si;
	WORD reg_di;
	WORD sel;
	
	if(!(GetWinFlags() & WF_ENHANCED))
		return 0;
        dwVersion = GetVersion();
	if(LOWORD(dwVersion)>=0x5F03){
		GlobalFix(LOWORD(GlobalHandle(wSelector)));
		return 1;
	}
	
	hMaster = HIWORD( GlobalMasterHandle() );
	sel = AllocSelector(hMaster);
	hKernel = GetModuleHandle("KERNEL");
	THHookStart = (DWORD far *)GetProcAddress(hKernel,"THHOOK");
	if(!THHookStart)
		return 0;
	
	SelTableStart = (DWORD far *)((char far *)THHookStart + 0x18);
	
	// If the following actually fails we will need to add code that will
	// allocate a new selector and point it to the beginning of the
	// selector table
	if(*SelTableStart > 0xFFFF){
		SetSelectorBase(sel,GetSelectorBase(sel) + *SelTableStart);
		SelTable = (DWORD far *)_MK_FP(sel,0);
	}else{
		SelTable = (DWORD far *)_MK_FP(sel,*SelTableStart);
	}
	
	// convert the selector to a global handle
	wSelector = GlobalHandle(wSelector);
	wSelectorIndex = wSelector >> 3;
	
	if(((DWORD)_FP_OFF(SelTable) + wSelectorIndex*4)>0xFFFF)
		return 0;
	if( SelTable[wSelectorIndex] > 0xFFFF )
		return 0;
		
	arena = (ARENA32 far *)_MK_FP(hMaster, (WORD) SelTable[wSelectorIndex] );

	reg_bx = HIWORD(arena->pga_address);
	reg_cx = LOWORD(arena->pga_address);
	reg_si = HIWORD(arena->pga_size);
	reg_di = LOWORD(arena->pga_size);
	_asm{
		mov     ax,0600h
		mov     bx,reg_bx
		mov     cx,reg_cx
		mov     si,reg_si
		mov     di,reg_di
		int     31h
		jc      Failed
	}
	arena->pga_count++;
	arena->pga_pglock++;
	FreeSelector(sel);
	return 1;       
Failed:
	FreeSelector(sel);
	return 0;
}

