/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   digipci.c

Abstract:

   Simple driver which reads PCI Config space for a usermode application.

Author:

   Michael Zarns

--*/
#include "digilib.h"
#include "digipci.h"

#define DIGIPCI DEBUG_00000001

NTSTATUS
PciDeviceCreateClose( 
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
{
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	return STATUS_SUCCESS;
}


NTSTATUS
PciDeviceIoControl( 
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
{
	PIO_STACK_LOCATION pIos      = IoGetCurrentIrpStackLocation( pIrp );
	PDIGI_PCI_DATA     pPciData  = pIrp->AssociatedIrp.SystemBuffer;
	ULONG              Ioctl     = pIos->Parameters.DeviceIoControl.IoControlCode;
	NTSTATUS Status = STATUS_UNSUCCESSFUL;

	DigiDump( DIGIPCI, ( "DigiPci!DeviceIoControl: pIrp 0x%x, Ioctl 0x%x\n", pIrp, Ioctl ) );

	if ( pIos->Parameters.DeviceIoControl.InputBufferLength  != sizeof *pPciData ||
	     pIos->Parameters.DeviceIoControl.OutputBufferLength != sizeof *pPciData )
	{
		Status = STATUS_BUFFER_TOO_SMALL;
		goto Exit;
	}

	if ( Ioctl == IOCTL_DIGIPCI_GET_DATA )
	{
		ULONG ul;
		PCI_COMMON_CONFIG PciConfig = {0};

#if !NT5_BUILD
		ul = HalGetBusData( PCIConfiguration, pPciData->BusNumber, pPciData->SlotNumber, &PciConfig, PCI_COMMON_HDR_LENGTH );
#else
		// Hal routines not supported in whistler and beyond.
		Status = STATUS_UNSUCCESSFUL;
		goto Exit;
#endif
		if ( ul == 0 )
		{
			Status = STATUS_INVALID_PARAMETER_1;
			goto Exit;
		}
		if ( ul == 2 )
		{
			Status = STATUS_INVALID_PARAMETER_2;
			goto Exit;
		}

		pPciData->VendorId    = PciConfig.VendorID;
		pPciData->DeviceId    = PciConfig.DeviceID;
		pPciData->SubVendorId = PciConfig.u.type0.SubVendorID;
		pPciData->SubSystemId = PciConfig.u.type0.SubSystemID;
		pPciData->RevisionId  = PciConfig.RevisionID;
		Status = STATUS_SUCCESS;
	}

Exit:

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = NT_SUCCESS( Status ) ? sizeof *pPciData : 0;

	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	return Status;
}


STATIC UNICODE_STRING gSymbolicName = {0};
STATIC PDEVICE_OBJECT gpDevice      = NULL;


VOID
DriverUnload(
	IN PDRIVER_OBJECT pDriver )
/*++

Description:

	NT calls this routine before unloading the driver.  Do any necessary cleanup.

--*/
{
	DigiDump( DIGIPCI, ( "DigiPci!DriverUnload\n" ) );

	if ( gSymbolicName.Buffer )
	{
		IoDeleteSymbolicLink( &gSymbolicName );
		DigiStrFreeUnicodeString( &gSymbolicName );
	}

	if ( gpDevice )
		IoDeleteDevice( gpDevice );

	MemPrintQuit();
	DigiDisplayMemoryLeaks();
}


STATIC BOOLEAN DontLoadDriver = FALSE;

NTSTATUS
DriverEntry(
	IN PDRIVER_OBJECT pDriver, 
	IN PUNICODE_STRING pRegPath )
/*++

Description:

	Driver main entrypoint.  NT calls this routine when first loading the driver.

--*/
{
	NTSTATUS Status;
	DigiInitMem( 'icPD' );	// DPci for DigiPci driver
	DigiDebugInit( pRegPath, 0xFFFFFFFF, DEBUGMODE_CONSOLE );

	DigiDump( DIGIPCI, ( "\nDigiPci!DriverEntry:\n"
	                     "   gDebugMask         = 0x%x\n"
	                     "   gDebugMode         = 0x%x\n"
	                     "   DigiDontLoadDriver = 0x%x\n\n",
	                     &gDebugMask, &gDebugMode, &DontLoadDriver ) );

	if ( DigiDbgBreakOnEntryEnabled() )
		DbgBreakPoint();
	if ( DontLoadDriver )
	{
		Status = STATUS_CANCELLED;
		goto Exit;
	}

	pDriver->DriverUnload                         = DriverUnload;
	pDriver->MajorFunction[IRP_MJ_CREATE]         = PciDeviceCreateClose;
	pDriver->MajorFunction[IRP_MJ_CLOSE]          = PciDeviceCreateClose;
	pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PciDeviceIoControl;

	{
		UNICODE_STRING DeviceName = {0};
		Status = DigiStrDeviceName( &DeviceName, DIGIPCI_DEVICENAME );

		if ( NT_SUCCESS( Status ) )
			Status = IoCreateDevice( pDriver, 0, &DeviceName, FILE_DEVICE_DIGIPCI, 0, FALSE, &gpDevice );

		if ( NT_SUCCESS( Status ) )
			Status = DigiStrSymbolicName( &gSymbolicName, DIGIPCI_DEVICENAME );

		if ( NT_SUCCESS( Status ) )
			Status = IoCreateSymbolicLink( &gSymbolicName, &DeviceName );

		if ( DeviceName.Buffer )
			DigiStrFreeUnicodeString( &DeviceName );
	}

Exit:
	if ( NT_ERROR( Status ) )
		DriverUnload( pDriver );

	return Status;
}
