/***************************************************************************
*   NAME:  INITC.C $Revision: 1.35 $
**  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: initc.c $
* Revision 1.35  1995/12/14 14:04:08  unknown
* Removed HWAllocate and HWFree from Disable function.
* Revision 1.34  1995/11/28 10:52:04  teckert
* Changed init code to prevent multiple sequential opens and to allow the 
* mixer to run concurrently with a DOS box app.
* Revision 1.32  1995/11/22 10:45:31  sdsmith
* Added register of InitCallback to VxD
* Revision 1.31  1995/11/02 13:28:18  mks
* Removed INTERWAVE env warning if under Windows 95
* Revision 1.30  1995/10/06 19:52:02  mleibow
* Driver now prints error message when INTERWAVE environment variable missing.
* Revision 1.29  1995/09/13 11:03:28  teckert
* Checked for 3.95 as reported version for 16-bit windows 95
* Revision 1.28  1995/09/05 15:55:59  teckert
* Revision 1.27  1995/09/05 10:26:50  teckert
* Added flag for Windows 95
* Revision 1.26  1995/07/06 19:06:58  sdsmith
* Additions for Save Settings
* Revision 1.25  1995/06/06 16:41:36  teckert
* Added DPMI buffer allocation
* Revision 1.24  1995/05/30 17:51:57  teckert
* Added check of the returned code from HWAllocate in Enable
* Revision 1.23  1995/05/16 21:43:33  sdsmith
* Fixed CODEC mode settings
* Revision 1.22  1995/05/11 16:17:43  teckert
* Added code to read the patch editor directory during init
* Revision 1.21  1995/05/09 17:23:29  teckert
* Added bandwidth sensitive DMA buffer size adjustment
* Revision 1.20  1995/05/08 09:02:54  teckert
* Added pload dma buf for dram transfers
* Revision 1.19  1995/05/07 18:50:17  teckert
* Added system.ini buffer sizes
* Revision 1.18  1995/05/04 08:20:48  unknown
* Fixed save of init string of card init error
* Revision 1.17  1995/05/03 14:31:08  teckert
* Revision 1.16  1995/04/26 11:06:00  teckert
* Fixed non-VxD initialization and exit
* Revision 1.15  1995/04/20 05:57:06  teckert
* Bug Fixes
* Revision 1.14  1995/04/20 03:24:47  teckert
* Revision 1.13  1995/04/20 03:08:43  teckert
* Changed array "buf" to "profile_buf" (winice was finding a different buf)
* Revision 1.12  1995/04/20 02:23:17  teckert
* Added HWAllocate HWFree frame around Midi_Init
* Revision 1.11  1995/04/19 18:36:46  teckert
* Added patch load buffer allocation 
* Revision 1.10  1995/04/19 17:11:17  sdsmith
* Added new mixer setting save code
* Revision 1.9  1995/04/19 15:39:59  sdsmith
* Added support for saving mixer settings
* Revision 1.8  1995/04/19 15:04:59  teckert
* Changed Enable and Disable functions (now they don't load and unload the kernel
* Revision 1.7  1995/04/18 15:36:49  teckert
* Removed reference to old VxD device ID
* Revision 1.6  1995/04/10 13:47:42  teckert
* added allocation bug fixes
* Revision 1.5  1995/04/04 15:34:52  teckert
* Fixed hw acquisition code
* Revision 1.4  1995/03/30 16:38:33  teckert
* Updated arbitration support
* Revision 1.3  1995/03/16 10:23:18  teckert
* Removed GetInitFile function (old Ultrasound artifact) and rewrote Enable and
* disable functions to use new acquisition scheme.
* Revision 1.2  1995/03/01 16:56:21  unknown
* Added file header
* Revision 1.1  1995/02/23 15:14:59  sdsmith
* Initial revision
***************************************************************************/
#include <dos.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <iw.h>
#include <globals.h>
#include <os.h>
#include <mmsystem.h>
#include "interwav.h"
#include "midi.h"
#include <viwd.h>

#define SZCODE _based(_segname("_CODE"))

struct DDS{
    DWORD region_size;
    DWORD offset;
    WORD segment_selector;
    WORD buffer_ID;
    DWORD physical_address;
};
struct DDS dds_buf;

extern int nKernelLoaded;
extern unsigned char iw_board_type;
char RFAR *pload_buff;

char SZCODE STR_INTERWAVEWAVEOUT[] = "InterWave Waveform Output";
char SZCODE STR_INTERWAVEWAVEIN[] = "InterWave Waveform Input";

int nEnabled = 0;
void InitMidiIn(void);
void MidiInit(void);
char far *os_getenv(const char far *varname);
char profile_buf[8];
DWORD DMA_Buf_Phys;                 
DWORD DMA_Buf_Linr;    
DWORD Virtual_API = NULL;
HINSTANCE ghModule;
extern struct iw_dma_buff dma_buf_one;
extern unsigned long buf_one_size;
extern struct iw_dma_buff dma_buf_two;
extern unsigned long buf_two_size;
struct iw_dma_buff dma_buf_pload;
struct load_kernel_cfg os;
static int nHWHandle = -1;
WORD wIWDriverFlags = 0;
extern char patch_ed_path[];
extern int far InitCallback(void);
								
/****************************************************************************

FUNCTION DEFINITION:
GetDMABuffers - gets the physical address and selector for the DMA buffer

DESCRIPTION:
Uses the VMM to get a pointer to the entry point of the VDD's API and calls the
function to have the VDD report the address and selector of the DMA buffer.

RETURNS: 1 if successful, 0 otherwise
*/
int GetDMABuffers(void)
{
BYTE bReturn = 0;
DWORD dwDOSBuffer;
WORD wVXDVersion;       
HGLOBAL hGlob;
WORD dds_buf_seg, dds_buf_off, dds_hi, dds_lo;
	
/* first, allocate a buffer for patch loading, it doesn't need to be a contiguous
   physical buffer bacause patch loading doesn't use DMA
*/      
	pload_buff = NULL;        
	if((hGlob = GlobalAlloc(GHND,PLOAD_BSIZE))!=0){
		pload_buff = GlobalLock(hGlob);
		dma_buf_pload.vptr = pload_buff;
		dma_buf_pload.paddr = NULL;
		dma_buf_pload.size = PLOAD_BSIZE;
	}
	
/* get API entry to VIWD -- if it is installed */
	_asm{
	mov     ax, 1600h               ; enhanced mode?
	int     2Fh                     ; api call
	test    al, 7Fh                 ; enhance mode running?
	jz      not_running_enhanced    ; no

	mov     ax, 1684h               ; get device API call
	mov     bx, INTERWAV_DEV_ID     ; for the InterWave VxD
	int     2Fh                     ; get api entry point
	mov     word ptr Virtual_API, di; save the callback address
	mov     word ptr Virtual_API + 2, es
	}
	if(Virtual_API){        // if the VxD is loaded first get the version then get the
							// dma buffer
		wIWDriverFlags |= IWDF_VXDPRESENT;
		_asm{
			mov     dx,0                    ; function 0, get version
			call    dword ptr[Virtual_API]
			mov     word ptr wVXDVersion,ax

			mov     dx,1                    ; function 1, get dma buffer
			call    dword ptr[Virtual_API]
			mov     word ptr DMA_Buf_Phys,ax
			mov     word ptr DMA_Buf_Phys + 2,bx
			mov     word ptr DMA_Buf_Linr,0
			mov     word ptr DMA_Buf_Linr + 2,cx
		}   
		
		if(DMA_Buf_Linr){
			wIWDriverFlags |= IWDF_VXDBUFFER;
		}
			
		if(wVXDVersion >= 0x200)                        // Version 2.00 and greater do arbitration
			wIWDriverFlags |= IWDF_VXDARBITRATION;
	}                           
not_running_enhanced:   
	dma_buf_one.size = (unsigned long) GetPrivateProfileInt(
			       "INTERWAV.DRV","RecordBufSize",8,"system.ini")*1024;
	buf_one_size = dma_buf_one.size;

	dma_buf_two.size =  (unsigned long) GetPrivateProfileInt(
			       "INTERWAV.DRV","PlayBufSize",8,"system.ini")*1024;
	buf_two_size = dma_buf_two.size;
			      
	if(!(wIWDriverFlags & IWDF_VXDBUFFER)){                
	// See if we have VDMA services
	    _asm {
		mov ax, 8102h
		xor dx,dx
		int 4bh
		mov dds_buf_seg, si
		mov dds_buf_off, di
		jnc got_dma_services
		jmp no_virtual_dma
	    }
got_dma_services:           
	    if(MAKELONG(dds_buf_off,dds_buf_seg) >= (buf_one_size + buf_two_size)){
		dds_buf.region_size = buf_one_size + buf_two_size;              
	    }else{                    
		dds_buf.region_size = MAKELONG(dds_buf_off,dds_buf_seg);
		buf_one_size = dma_buf_one.size = dds_buf.region_size/2;
		buf_two_size = dma_buf_two.size = buf_one_size;
	    }
	    dds_buf_seg = HIWORD(&dds_buf);
	    dds_buf_off = LOWORD(&dds_buf);
	    // Request a maximum size DMA buffer
	    _asm {
		mov ax,8107h
		xor dx,dx
		mov es, dds_buf_seg
		mov di, dds_buf_off
		int 4bh
		jnc got_buffer
		jmp no_virtual_dma
	    }
got_buffer:
	    // Map the physical DMA address to Linear Memory
	    dds_buf_seg = HIWORD(dds_buf.physical_address);
	    dds_buf_off = LOWORD(dds_buf.physical_address);
	    dds_hi = HIWORD(dds_buf.region_size);
	    dds_lo = LOWORD(dds_buf.region_size);
	    _asm {
		mov ax, 0800h
		mov bx, dds_buf_seg
		mov cx, dds_buf_off
		mov si, dds_hi
		mov di, dds_lo
		int 31h
		mov dds_hi, bx
		mov dds_lo, cx
		jnc got_mapped_buffer
		jmp no_virtual_dma
	    }
got_mapped_buffer:                  
	    dds_buf_seg = AllocSelector(HIWORD(&dds_buf));
	    if (dds_buf_seg) {
		if (SetSelectorBase(dds_buf_seg, MAKELONG(dds_lo, dds_hi))) {
		    SetSelectorLimit(dds_buf_seg, dds_buf.region_size);

		    DMA_Buf_Linr = MAKELONG(0,dds_buf_seg);
		    DMA_Buf_Phys = dds_buf.physical_address;
		    wIWDriverFlags |= IWDF_DPMIBUFFER;
		}
	    }else{
		goto no_virtual_dma;
	    }
		
	    if(0){
no_virtual_dma:
		// if the VxD is not loaded or if it didn't allocate a buffer allocate one here
		dwDOSBuffer = GlobalDosAlloc(buf_one_size + buf_two_size);
		if(!HIWORD(dwDOSBuffer))
		    return 0;
		DMA_Buf_Linr = (DWORD)LOWORD(dwDOSBuffer) << 16;
		DMA_Buf_Phys = (DWORD)HIWORD(dwDOSBuffer) << 4;
	    }
	}

	dma_buf_one.vptr = (unsigned char RFAR *)DMA_Buf_Linr;
	dma_buf_one.paddr = DMA_Buf_Phys;

	dma_buf_two.vptr = (unsigned char RFAR *)(DMA_Buf_Linr + dma_buf_one.size);
	dma_buf_two.paddr = DMA_Buf_Phys + dma_buf_one.size;

return 1;
}             


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

FUNCTION DEFINITION:
FreeDMABuffers - frees the DMA buffer or disables

DESCRIPTION:

RETURNS: 1 if successful, 0 otherwise
*/
int FreeDMABuffers(void)
{       
HGLOBAL hGlob;

	if(pload_buff){
		hGlob = LOWORD(GlobalHandle(HIWORD(pload_buff)));
		GlobalUnlock(hGlob);
		GlobalFree(hGlob);
	}
	if(!(wIWDriverFlags & IWDF_VXDBUFFER)){
		GlobalDosFree(HIWORD(dma_buf_one.vptr));
	}
}


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

FUNCTION DEFINITION:
Enable - initializes the kernel and the DMA buffers

DESCRIPTION:
Called from the device driver's DriverProc() in response to a DRV_ENABLE message
this function initializes the kernel, gets the DMA buffers and initializes the 
devices.

RETURNS: 1 if successful, 0 otherwise
*/
int Enable(void)
{
int ret;
WORD wHiResources,wLoResources;
DWORD dwVerResult;
unsigned long mem;
char RFAR *lpInterWaveEnv;
void far *lpFunction;
WORD cb_seg, cb_off;

        // Is this Windows 95 ?
        dwVerResult = GetVersion();
        if((LOBYTE(LOWORD(dwVerResult))>3)||
           ((LOBYTE(LOWORD(dwVerResult))==3)&&(HIBYTE(LOWORD(dwVerResult))>=0x5f))){
            wIWDriverFlags |= IWDF_WINDOWS95;
        }
	lpInterWaveEnv = os_getenv("INTERWAVE");
	if (lpInterWaveEnv == 0 && !(wIWDriverFlags & IWDF_WINDOWS95)) {
	    /* This message must be in english.  If INTERWAVE= isn't set, */
	    /* than the .MLS text file won't be found either. */
	    MessageBox(NULL, "INTERWAVE environment variable not set.", "INTERWAV.DRV:", MB_OK);
	    return(0);
	}
	GetPrivateProfileString((LPCSTR)"patch editor",
				(LPCSTR)"patch_editor_dir",
				(LPCSTR)os_getenv("IWDIR"),
				(LPSTR)patch_ed_path,
				256,
				(LPCSTR)lpInterWaveEnv);
	if(patch_ed_path[_fstrlen(patch_ed_path)-1]!='\\'){
	    _fstrcat(patch_ed_path,"\\");
	}
				
	iwu_get_kernel_cfg(&os,0);
	os.FullInit = 1;
#if 0
	if (nCodecVersion && (iw_query_codec((void RFAR *)&i,(void RFAR *)&i,
						 (void RFAR *)&i,(void RFAR *)&i)==IW_CODEC_NOT_FOUND)){
		nCodecVersion = 0;
	}
#endif

	GetDMABuffers();        // Also gets entry point for VxD

	/* Register the InitCallback to the VxD */
	if (Virtual_API) {
            lpFunction = InitCallback;
	    _asm {
                les di, lpFunction
		mov dx, IWAPI_REGISTERDRIVERCALLBACK
		call dword ptr[Virtual_API]
	    }
	}

	_asm mov ax, IW_KERNEL_LOAD
	ret = InitCallback();
	if (ret){
		MessageBox(NULL,iw_error_str(ret),"Error", MB_OK);
		return -1;
	}       
	nKernelLoaded = 1;
	SetupMixer();
	auxInit();

	nHWHandle = HWAllocate(0xFFFF);
	if(nHWHandle==-1){      // If this is -1 then the hardware was not found
		FreeDMABuffers();
		nEnabled = 0;
		return 0;
	}
	MidiInit();             // Scan the patch files
	InitMidiIn();			// Register the midi input interrupt handler
	
	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(wIWDriverFlags & IWDF_VXDARBITRATION){
		HWFree(nHWHandle);
		nHWHandle = -1;
	}
	
	nEnabled = 1;
	return 1;
}
 
 
/****************************************************************************

FUNCTION DEFINITION:                 
Disable - uninitializes the kernel

DESCRIPTION:
Called from the device driver's DriverProc() in response to a DRV_DISABLE message
this function unloads the kernel and uninitializes the devices

RETURNS: 1 to indicate success (it always succeeds, its that good).
*/
int Disable(void)
{               
WORD wHiResources,wLoResources;
HGLOBAL hMixer;
LPCSTR Mixer;
int i;

	if(nEnabled){
	    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
	            }
	    }
		GetPrivateProfileString((LPCSTR)"mixer settings",
					(LPCSTR)"SaveOnWindowsExit",
					(LPCSTR)"false",
					(LPSTR)profile_buf,
					sizeof(profile_buf),
					(LPCSTR)os_getenv("INTERWAVE"));
		if (lstrcmp(profile_buf,"true") == 0) {
			hMixer = GlobalAlloc(GHND, iw_mix_strlen());
			if (hMixer) {
				Mixer = (LPCSTR)GlobalLock(hMixer);
				
				iw_get_mixer_settings((char RFAR *)Mixer);

				WritePrivateProfileString("mixer settings","init",Mixer,
							  (LPCSTR)os_getenv("INTERWAVE"));

				GlobalUnlock(hMixer);
				GlobalFree(hMixer);
			}

		}

		nKernelLoaded = 0;
        _asm mov ax, IW_KERNEL_UNLOAD
        InitCallback();
		
	    // KLUDGE - put the CODEC back in mode 2
	    _asm{
			mov dx,iwl_codec_base
			mov al,0Ch
			out dx,al
			inc dx
			mov al,40h
			out dx,al
		}
	
		if(!(wIWDriverFlags & IWDF_VXDARBITRATION)){
			HWFree(nHWHandle);
		}

		nEnabled = 0;    
		FreeDMABuffers();
	}
	return 1;
}
 
 
/****************************************************************************

FUNCTION DEFINITION:                 
LibMain - Library initialization code.

DESCRIPTION:
Called when the library is loaded into memory this function simply returns 1
to indicate success.

RETURNS: 1 if the initialization was successful and 0 otherwise.
*/
BOOL FAR PASCAL _loadds LibMain(
	HANDLE hInstance,               // Our instance handle.
	WORD wDataSeg,                  // Our data segment selector
	WORD wHeapSize,                 // The heap size from the .def file.
	LPSTR lpCmdLine)                // The command line.
{
	ghModule = hInstance;
	return 1;
}
