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

SDK.C

Bus enumerator for the 9050SDK.
Enumerate a standard comm card (serial or modem) piggybacked on
the 9050SDK card.

Copyright PLX Technology, 1997

Changes
970204	Ryan	Genesis - Enumerate a standard com card piggybacked on SDK

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

///////////////////////////////////////////////////////////////////////
#define WANTVXDWRAPS
#include <basedef.h>
#include <vmm.h>
#include <debug.h>
#include <vmmreg.h>
#include <vwin32.h>
#include <vxdwraps.h>
#include <configmg.h>
#include <regstr.h>

#include <pci.h>

///////////////////////////////////////////////////////////////////////
#define ENUMERATOR "PLXENUM"// Our enumerator
#define DEVCLASS REGSTR_KEY_PORTS_CLASS	// Class for serial and parallel devices
#define DEVTYPE "*PNP0500"	// Standard PC COM port
#define IO_COUNT 5	// number of port address descriptors

// #define DEVTYPE "*PNP0501"	// 16550A-compatible port

// Getting I/O functions straight is always a pain - map them here
#include "plxio.h"
#define OUTP(p,v) Outp(p,v)
#define OUTPD(p,v) Outpd(p,v)
#define INP(p) Inp(p)
#define INPD(p) Inpd(p)

///////////////////////////////////////////////////////////////////////
#ifdef DEBUG
	#define ASSERT(e) if(!(e)){Debug_Printf("Assertion failure in " __FILE__ ", line %d: " #e "\r\n", __LINE__);\
		_asm int 1\
		}
#else
	#define ASSERT(e)
#endif

#pragma CM_PAGEABLE_DATA
#pragma CM_PAGEABLE_CODE

///////////////////////////////////////////////////////////////////////
CONFIGRET CM_HANDLER OnEnumerate(CONFIGFUNC cf, SUBCONFIGFUNC scf, DEVNODE tonode, DEVNODE aboutnode, ULONG flags);

////////////////////////////////////////////////////////////////////
// CM's resource requirements for comm card (completed later)
static IRQ_RESOURCE irq = {
	{fIRQD_Share, 0, 0, 0}
};

static IO_RESOURCE io = {
	{IO_COUNT, IOType_Range, 0, 0, 0, 0, 0},
	{
		// {Align, nPorts, Min, Max, Flags, Alias, Decode}
		// For standard COM ports...
		{(WORD)~0x07, 0x08, 0x03f8, 0x03ff, 0, 4, 3},
		{(WORD)~0x07, 0x08, 0x02f8, 0x02ff, 0, 4, 3},
		{(WORD)~0x07, 0x08, 0x03e8, 0x03ef, 0, 4, 3},
		{(WORD)~0x07, 0x08, 0x02e8, 0x02ef, 0, 4, 3},
		// For COM5 and above...
		{(WORD)~0x07, 0x08, 0x0000, 0x03ff, 0, 4, 3},
	}
};

///////////////////////////////////////////////////////////////////////
#ifdef DEBUG
void
ShowConfigRet(char * str, CONFIGRET cr)
{
	DEBUG_RETURN_CR_NAMES

	Debug_Printf("%s: %s\n", str, lpszReturnCRName[cr]);
	return ;
}
#else
#define ShowConfigRet(s, c)
#endif

///////////////////////////////////////////////////////////////////////
#ifdef DEBUG
void
ShowAllocLogConf(DEVNODE device)
{
	CMCONFIG config;	// Device's config (allocated by CM)
	char deviceid[MAX_DEVICE_ID_LEN];
	int ii;
	CONFIGRET cr;
	DEBUG_RETURN_CR_NAMES

	cr = CM_Get_Device_ID(device, deviceid, sizeof(deviceid), 0);
	if (cr != CR_SUCCESS)
	{
		ShowConfigRet("CM_Get_Device_ID", cr);
		return;
	}
	Debug_Printf("Device: %.10s ", deviceid);

	cr = CM_Get_Alloc_Log_Conf(&config, device, CM_GET_ALLOC_LOG_CONF_ALLOC);
	if (cr != CR_SUCCESS)
	{
		ShowConfigRet("CM_Get_Alloc_Log_Conf", cr);
		return;
	}

	Debug_Printf("Ports: ");
	if (!config.wNumIOPorts)
		Debug_Printf("(none) ");
	for (ii = 0; ii < config.wNumIOPorts; ii++)
		Debug_Printf("%4.4x-%4.4x, ", config.wIOPortBase[ii], config.wIOPortLength[ii]);

	Debug_Printf("Irqs: ");
	if (!config.wNumIRQs)
		Debug_Printf("(none) ");
	for (ii = 0; ii < config.wNumIRQs; ii++)
		Debug_Printf("%2.2x-%2.2x, ", config.bIRQRegisters[ii], config.bIRQAttrib[ii]);

	Debug_Printf("\n");
	return;
}
#else
#define ShowAllocLogConf(x)
#endif

///////////////////////////////////////////////////////////////////////
BOOL
OnSysDynamicDeviceInit()
{
	return TRUE;
}

///////////////////////////////////////////////////////////////////////
BOOL
OnSysDynamicDeviceExit()
{
	return TRUE;
}

///////////////////////////////////////////////////////////////////////
CONFIGRET
OnPnpNewDevnode(DEVNODE devnode, DWORD loadtype)
{
	CONFIGRET cr;

	switch (loadtype)
	{						// select function to perform

	case DLVXD_LOAD_DEVLOADER:	// loadtype == 1
		cr = CM_Register_Enumerator(devnode, OnEnumerate,
			CM_REGISTER_ENUMERATOR_HARDWARE);
		if (cr != CR_SUCCESS)
			return cr;
		return CM_Register_Device_Driver(devnode, NULL, 0,
			CM_REGISTER_DEVICE_DRIVER_REMOVABLE);

	}
	return CR_DEFAULT;
}

///////////////////////////////////////////////////////////////////////////////
BOOL IsComport(WORD port)
{	// Test port -- is it a COM (8250 UART) port?
	// Based on the upper bits of the Interrupt Enable Register always being zero:
	// First, see if it's just empty space
	if (INP(port+1) > 0x0f)
		return FALSE;	// Probably empty space

	// Now set upper IER bits - they should stay reset
	OUTP (port+1, 0xf0);
	return (INP(port+1) <= 0x0f);
}

///////////////////////////////////////////////////////////////////////////////
BOOL
SetupLocalIO(WORD Lcr9050Base, WORD LocBase, WORD LocRange)
{	// Setup PCI9050 local IO space (for local address space 1)
	// Lcr9050Base: PCI address of PCI 9050 Local Config Regs
	// LocBase: I/O address of your card piggybacked on SDK
	// LocRange: Range mask for your card (8250 UART is ~0x07)
	// Note - bits 0 and 1 of LocBase and LocRange must be clear! (9050 requirement)
	WORD ii;

 	// Setup 9050 local-side registers:
	// Set range (LAS1RR)
	OUTPD(Lcr9050Base+0x04, (DWORD)(LocRange | 0xffff0001));

	// Set base address re-map (LAS1BA)
	OUTPD(Lcr9050Base+0x18, (DWORD)(LocBase | 0x03000001));

	// Set chip select base (CS1BASE) (Tricky! See 9050 spec)
	for (ii = 1; ii < (1 << 16); ii <<= 1)
	{	// Find first set bit in the local base address
		if (LocBase & ii)
			break;	// Found the first bit!
	}
	OUTPD(Lcr9050Base+0x40, (DWORD)(LocBase | (ii >> 1) | 0x03000001));

	// Set bus region descriptor (LAS1BRD) (see 9050 spec)
	OUTPD(Lcr9050Base+0x2c, 0x40000022);

	// Set interrupts (INTCSR)
	OUTPD(Lcr9050Base+0x4c, 0x00000041);

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
BOOL SetupLocComport(WORD Lcr9050Base, WORD HostComBase)
{	// Find and setup the first comm port of piggybacked comm card
	INT ii;

	for (ii = 0; ii < IO_COUNT; ii++)
	{	
#ifdef DEBUG
		Debug_Printf("Testing local port:%4.4x\n", io.IO_Data[ii].IOR_Min);
#endif
		SetupLocalIO(Lcr9050Base,
			io.IO_Data[ii].IOR_Min, io.IO_Data[ii].IOR_Align);
		if (IsComport(HostComBase))
			return TRUE;
	}
	return FALSE;
}

///////////////////////////////////////////////////////////////////////
CONFIGRET CM_HANDLER
OnEnumerate(CONFIGFUNC cf, SUBCONFIGFUNC scf,
	DEVNODE tonode, DEVNODE aboutnode, ULONG flags)
{
	CONFIGRET cr;

#ifdef DEBUG
	char toid[MAX_DEVICE_ID_LEN], aboutid[MAX_DEVICE_ID_LEN];
	DEBUG_CONFIG_NAMES
	char *subfunc = "";
	static char *substart[] = {"DYNAMIC_START", "FIRST_START"};
	static char *substop[] = {"DYNAMIC_STOP", "HAS_PROBLEM"};
	static char *subremove[] = {"DYNAMIC", "SHUTDOWN", "REBOOT"};
	static char *subtest[] = {"CAN_STOP", "CAN_REMOVE"};
	static char *subapm[] = {"TEST_STANDBY", "TEST_SUSPEND",
		"TEST_STANDBY_FAILED", "TEST_SUSPEND_FAILED",
		"TEST_STANDBY_SUCCEEDED", "TEST_SUSPEND_SUCCEEDED",
		"RESUME_STANDBY", "RESUME_SUSPEND", "RESUME_CRITICAL",
		"UI_ALLOWED"};

	CM_Get_Device_ID(tonode, toid, sizeof(toid), 0);
	CM_Get_Device_ID(aboutnode, aboutid, sizeof(aboutid), 0);
	switch (cf)
		{						// get subfunction name
	case CONFIG_START:
		subfunc = substart[scf];
		break;
	case CONFIG_STOP:
		subfunc = substop[scf];
		break;
	case CONFIG_REMOVE:
		subfunc = subremove[scf];
		break;
	case CONFIG_TEST:
		subfunc = subtest[scf];
		break;
	case CONFIG_APM:
		subfunc = subapm[scf];
		break;
		}
	if (cf < NUM_CONFIG_COMMANDS)
		Debug_Printf("sdk: %s(%s), to:%.10s, about:%.10s\r\n", lpszConfigName[cf], subfunc, toid, aboutid);
	else
		Debug_Printf("sdk: %X(%X), to:%s, about:%s\r\n", cf, toid, aboutid);
#endif

	switch (cf)
	{						// select on function code

	case CONFIG_ENUMERATE:		// cf == 5
	{
		DEVNODE device;			// DEVNODE for device
		LOG_CONF logconf;		// logical configuration
		RES_DES resource;		// resource descriptor handle
		CMCONFIG config;		// Device's config (allocated by CM)

		// Create a DEVNODE for one (and only one) device. 
		cr = CM_Create_DevNode(&device, ""ENUMERATOR"\\"DEVTYPE"\\0000",
			tonode, 0);
		if (cr == CR_ALREADY_SUCH_DEVNODE)
			return CR_SUCCESS;	// nothing to do on reenumeration

		// Create a logical configuration for our device.
		CM_Add_Empty_Log_Conf(&logconf, device, LCPRI_NORMAL,
			BASIC_LOG_CONF | PRIORITY_EQUAL_LAST);

		// Make device's IRQ the same as tonode's
		CM_Get_Alloc_Log_Conf(&config, tonode, CM_GET_ALLOC_LOG_CONF_ALLOC);
		irq.IRQ_Header.IRQD_Req_Mask = 1 << config.bIRQRegisters[0];
		// Add interrupt descriptor
		CM_Add_Res_Des(&resource, logconf, ResType_IRQ,
			&irq, SIZEOF_IRQ, 0);

		// Add I/O descriptor
		CM_Add_Res_Des(&resource, logconf, ResType_IO,
			&io, SIZEOF_IORANGE(IO_COUNT), 0);

		return CR_SUCCESS;
	}

	case CONFIG_SETUP:
	{
		ULONG length;			// length of class name
		char class[64];			// device class

		/*
		The very first time we add the device to the hardware
		tree, CONFIGMG will launch the device installer to load
		a driver. To avoid having the initial dialog say the
		device class is "[DEVCLASS]", force the class in the
		registry key to be "Ports" instead. Similarly force
		the hardware ID so the installer knows which driver to
		install. This knowledge is similar to what you'd discover
		from a real Plug and Play device.
		*/
		
		length = sizeof(class);
		cr = CM_Read_Registry_Value(aboutnode, NULL, REGSTR_KEY_CLASS,
			REG_SZ, class, &length, CM_REGISTRY_HARDWARE);
		if (cr == CR_NO_SUCH_VALUE)
		{
			CM_Write_Registry_Value(aboutnode, NULL, REGSTR_KEY_CLASS,
				REG_SZ, DEVCLASS,  sizeof(DEVCLASS)-1, CM_REGISTRY_HARDWARE);
			CM_Write_Registry_Value(aboutnode, NULL, REGSTR_VAL_HARDWAREID,
				REG_SZ, DEVTYPE, sizeof(DEVTYPE)-1, CM_REGISTRY_HARDWARE);
		}
		return CR_SUCCESS;
	}

	case CONFIG_READY:
	{
		ShowAllocLogConf(aboutnode);
		return CR_SUCCESS;
	}

	case CONFIG_FILTER:
	{
		ShowAllocLogConf(tonode);
		if (tonode == aboutnode)
		{	// Enumerator is still just a device driver
			return CR_SUCCESS;
		}
		ShowAllocLogConf(aboutnode);

		return CR_SUCCESS;
	}


	case CONFIG_START:
	{
		CMCONFIG tocfg;		// Enumerator's config (allocated by CM)
		CMCONFIG aboutcfg;	// Comm device's config (allocated by CM)
		CONFIGRET cr;
		DWORD Space1;

		ShowAllocLogConf(aboutnode);
		if (tonode == aboutnode)
		{	// Enumerator is still just a device driver
			return CR_SUCCESS;
		}

		
		ShowAllocLogConf(tonode);
		cr = CM_Get_Alloc_Log_Conf(&tocfg, tonode, CM_GET_ALLOC_LOG_CONF_ALLOC);
		if (cr != CR_SUCCESS)
		{
			ShowConfigRet("CM_Get_Alloc_Log_Conf", cr);
			return cr;
		}

		cr = CM_Get_Alloc_Log_Conf(&aboutcfg, aboutnode, CM_GET_ALLOC_LOG_CONF_ALLOC);
		if (cr != CR_SUCCESS)
		{
			ShowConfigRet("CM_Get_Alloc_Log_Conf", cr);
			return cr;
		}

		
		/* (Ryan 970213) Potential problem here!!!???
		If the PCI VxD re-enumerates after we set space 1 to
		a standard COM port, it should find a conflict! The I/O
		space for the COM device we've enumerated is the same as
		space 1! (Can we remove the "tonode's" space 1 resource??)
		*/

		// Make sure PCI9050 space1 won't decode I/O space
		// (Should be already be taken care of in EEPROM!)
		OUTPD(tocfg.wIOPortBase[0]+0x18, 0x00);

		// Map PCI9050 Space1 into ISA space
		Space1 = aboutcfg.wIOPortBase[0] | 0x01; // (Bit 0 maps space to I/O)
		cr = CM_Call_Enumerator_Function(tonode, PCI_ENUM_FUNC_SET_DEVICE_INFO,
			0x1c, &Space1, sizeof(DWORD), 0);
		if (cr != CR_SUCCESS)
		{
			ShowConfigRet("CM_Call_Enumerator_Function", cr);
			return cr;
		}

		// Setup the 9050 local-side
		if (!SetupLocComport(tocfg.wIOPortBase[0], aboutcfg.wIOPortBase[0]))
		{	// Comm card does not appear to be there!
#ifdef DEBUG
			Debug_Printf("sdk: SetupLocComport(): Unable to setup comm card\n");
#endif
			cr = CM_Free_Log_Conf(aboutnode, 0x0);
			if (cr != CR_SUCCESS)
			{
				ShowConfigRet("CM_Free_Log_Conf", cr);
				return cr;
			}
			/* (Ryan 970224)
			Text in CONFIGMG.H warns about returning an error
			during CONFIG_START. Unfortunately, we don't know
			what Space1 should be until CONFIG_START, so we can't
			test for the comm card 'til CONFIG_START. Returning an
			error here has not caused problems, in fact the	Device
			Manager shows the device as being 'not present', etc.
			*/
			return CR_DEVICE_NOT_THERE; 
		}
		return CR_SUCCESS;
	}
	default:
		return CR_DEFAULT;
	}
}

///////////////////////////////////////////////////////////////////////
DWORD OnDeviceIoControl(PDIOCPARAMETERS p)
{
	switch (p->dwIoControlCode)
	{
	
	case 1:
#ifdef DEBUG
		Debug_Printf("Issuing CM_Callback_Enumerator call\r\n");
#endif
		CM_CallBack_Enumerator(OnEnumerate, 0);
		break;
	}
	
	return 0;
}

///////////////////////////////////////////////////////////////////////
// End of file