//-------------------------------------------------------------------------
// WINNT driver for PCIVME interface from ARW Elektronik, Germany ---------
// the main body of the driver
//
// (c) 1999-2004 ARW Elektronik
//
// this source code is published under GPL (Open Source). You can use, redistrubute and
// modify it unless this header is not modified or deleted. No warranty is given that
// this software will work like expected.
// This product is not authorized for use as critical component in life support systems
// wihout the express written approval of ARW Elektronik Germany.
//
// Please announce changes and hints to ARW Elektronik
//
// $Log: pcivme_drv.c,v $
// Revision 1.3 2004/07/24 07:07:26 klaus
// Update copyright to 2004
//
//
// what who when
// started AR 15.06.1999
// first release 1.0 AR 17.10.1999
// fixed error in PLX9050Bug AR 28.02.2000
// PLX9050Bugfix bug fixed AR 03.03.2000
// PCICC32 CAMAC Interface conflict solved AR 03.03.2000
// Version 1.1 released AR 03.03.2000
// register all used resources, the idle too AR 25.11.2001
// changed resource allocation caused by WIN2000 AR 08.06.2002
//
//-------------------------------------------------------------------------
// INCLUDES
//
#include <ntddk.h>
#include <devioctl.h>
#include <pcivme_drv.h>
#include <pcivme_v.h>
#include <pcivme_io.h>
#include <pcivme_i.h>
#include <pcivme.h>
#include <pciif.h>
#include <pcivme_fifo.h>
//------------------------------------------------------------------------
// DEFINES
//
#ifndef DWORD
#define DWORD ULONG
#endif
#ifndef WORD
#define WORD USHORT
#endif
#define CTL_INDEX(x) ((x >> 2) & 0x7FF) // get user control code as index
#define IRQ_LIST_LENGTH 128 // max count of irqs in FIFO for each file_obj
#define RESOURCE_ENTRY_COUNT 6 // WIN2000 forces to claim all entries
#define DOS_DEVICE_NAME L"\\DosDevices\\PCIVME:"
//------------------------------------------------------------------------
// GLOBALS
//
//------------------------------------------------------------------------
// FUNCTIONS
//
//------------------------------------------------------------------------
// exchange the pointer to Bus Error
//
PBOOLEAN ExchangePointer(PBOOLEAN *current, PBOOLEAN next)
{
PBOOLEAN pb;
pb = *current;
*current = next;
return pb;
}
//------------------------------------------------------------------------
// get the vmemm number out of the filename
//
NTSTATUS InterpreteFileName(PCHAR name, int *nVmemm)
{
char *ptr = name;
char *n = "vmemm";
int h = -1; // high part
int l = -1; // low part
if (*ptr == '\\') ptr++; // jump over leading ...
while (*n) // compare the basename
{
n++;
ptr++;
}
else
return STATUS_NO_SUCH_FILE;
h = *ptr - '0'; // get the number
ptr++;
l = *ptr - '0';
if (*ptr == 0) // still over the end ??
{
l = h;
h = 0;
}
else
ptr++;
if ((h < 0) || (l < 0) || (*ptr != 0)) // anything wrong ??
return STATUS_NO_SUCH_FILE;
*nVmemm = (h * 10) + l; // calculate number
if (*nVmemm >= PCIVME_MAX_VMEMM) // out of range ??
return STATUS_NO_SUCH_FILE;
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// the ultimate driver unload
VOID PCIVMEUnload(PDRIVER_OBJECT driverObj)
{
int i;
UNICODE_STRING symbol_name;
DEVICE_EXT *ext = (DEVICE_EXT*)(driverObj->DeviceObject->DeviceExtension);
int nPCIADAs = ext->nPCIADAs;
PCIADA *pciada;
KdPrint(("PCIVMEUnload()\n"));
switch (ext->nInitState)
{
case 8:
case 7:
// stop interrupts and shut off
PCIVMEDeInitPCIADAs(driverObj->DeviceObject);
PCIVMEDisConnectInterrupt(driverObj->DeviceObject);
// remove interrupt lists
for (i = 0; i < nPCIADAs; i++)
{
pciada = &ext->pciada[i];
// removeQueueFromList(...)
while (IsListEmpty(&pciada->IrqListList) == FALSE)
{
PLIST_ENTRY pList;
FIFO_LIST *next;
KdPrint(("RemoveHeadList(0x%08x)\n", &pciada->IrqListList));
pList = RemoveHeadList(&pciada->IrqListList);
next = CONTAINING_RECORD(pList, FIFO_LIST, entry);
ExFreePool((PVOID)next);
}
}
case 6:
// InitializeIRPQueue has no counterpart
case 5:
// KeInitializeDpc has no counterpart
case 4:
// release io spaces
for (i = 0; i < nPCIADAs; i++)
{
pciada = &ext->pciada[i];
if (pciada->pvVirtLcr != NULL)
MmUnmapIoSpace(pciada->pvVirtLcr, LCR_SPACE);
if (pciada->pvVirtIfr != NULL)
MmUnmapIoSpace(pciada->pvVirtIfr, IFR_SPACE);
}
case 3:
// HalGetInterruptVector has no counterpart
case 2:
// HalTranslateBusAddress has no counterpart
case 1:
PCIVMEFreeResources(driverObj->DeviceObject);
default:
case 0:
RtlInitUnicodeString(&symbol_name, DOS_DEVICE_NAME);
// delete the symbolicLink in the registry
IoDeleteSymbolicLink( &symbol_name);
// delete the deviceObject
IoDeleteDevice(driverObj->DeviceObject);
}
KdPrint(("PCIVMEUnload() OK.\n"));
}
//------------------------------------------------------------------------
// called at CreateFile()
NTSTATUS PCIVMEOpen(PDEVICE_OBJECT deviceObj, PIRP Irp)
{
NTSTATUS result = STATUS_SUCCESS;
ANSI_STRING name;
int nVmemm;
int i;
DEVICE_EXT *pDevExt = (DEVICE_EXT *)(deviceObj->DeviceExtension);
PCIADA *pciada;
FILE_OBJ *file_obj = (FILE_OBJ *)NULL;
name.Buffer = NULL;
name.MaximumLength = 80;
result = RtlUnicodeStringToAnsiString(&name, &(Irp->Tail.Overlay.OriginalFileObject->FileName), TRUE);
if (result != STATUS_SUCCESS) goto fin;
result = InterpreteFileName(name.Buffer, &nVmemm);
if (result != STATUS_SUCCESS) goto fin;
KdPrint(("PCIVMEOpen(%d)\n", nVmemm));
RtlFreeAnsiString(&name);
file_obj = (FILE_OBJ *)ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJ),'nepo');
if (file_obj == (FILE_OBJ *)NULL)
{
result = STATUS_NO_MEMORY;
goto fin;
}
file_obj->uwAssociatedVMEMM = (USHORT) nVmemm;
file_obj->bAddressModifier = 0x39;
file_obj->bAccessType = BYTE_ACCESS;
file_obj->bIncrement = BYTE_ACCESS; // increments each byte
file_obj->dwAccessBase = 0; // normal setting for all but extended
file_obj->bQueueIrq = FALSE;
result = InitializeFIFO(IRQ_LIST_LENGTH, &file_obj->pIrqListHandle);
if (result != STATUS_SUCCESS) goto fin;
Irp->Tail.Overlay.OriginalFileObject->FsContext = (PVOID)file_obj;
result = PCIVMEScanVMEMM(deviceObj);
if (result != STATUS_SUCCESS) goto fin;
for (i = 0; i < pDevExt->nPCIADAs; i++)
{
pciada = &pDevExt->pciada[i];
if (pciada->wModuleNumber == nVmemm)
{
pDevExt->vmemm[nVmemm] = pciada; // create association
pciada->dwLinkCount++;
break;
}
}
if (i >= pDevExt->nPCIADAs)
{
result = STATUS_NO_SUCH_FILE;
goto fin;
}
fin:
Irp->IoStatus.Status = result;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
// be careful when releasing allocated memory !
if (result != STATUS_SUCCESS)
if (file_obj != (FILE_OBJ *)NULL)
if (file_obj->pIrqListHandle != (PVOID)NULL)
DestroyFIFO(file_obj->pIrqListHandle);
return result;
}
//------------------------------------------------------------------------
// called at close()
NTSTATUS PCIVMEClose(PDEVICE_OBJECT deviceObj, PIRP Irp)
{
DEVICE_EXT *pDevExt = (DEVICE_EXT *)(deviceObj->DeviceExtension);
FILE_OBJ *file_obj = (FILE_OBJ *)NULL;
PCIADA *pciada;
file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
KdPrint(("PCIVMEClose(%d)\n", file_obj->uwAssociatedVMEMM));
if (file_obj != (FILE_OBJ *)NULL)
{
pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];
pciada->dwLinkCount--;
// remove the ListEntry(s) associated with this path
removeQueueFromList(file_obj, pciada);
// empty and remove the interrupt queue (if there is anything stored)
DestroyFIFO(file_obj->pIrqListHandle);
ExFreePool(file_obj);
Irp->Tail.Overlay.OriginalFileObject->FsContext = (FILE_OBJ *)NULL;
}
KdPrint(("PCIVMEClose OK\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// called at
NTSTATUS PCIVMEShutdown(PDEVICE_OBJECT deviceObj, PIRP irp )
{
UNREFERENCED_PARAMETER(irp);
KdPrint(("PCIVMEShutdown()\n"));
// deinit interfaces and interrupts
PCIVMEDeInitPCIADAs(deviceObj);
KdPrint(("PCIVMEShutdown() OK\n"));
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// called at ioctl()
NTSTATUS PCIVMEDeviceControl(PDEVICE_OBJECT deviceObj, PIRP Irp)
{
PIO_STACK_LOCATION IrpStack;
int nIndex;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
nIndex = CTL_INDEX(IrpStack->Parameters.DeviceIoControl.IoControlCode);
KdPrint(("PCIVMEDeviceControl(%d / 0x%08x)\n", nIndex, Irp->Tail.Overlay.OriginalFileObject));
if (nIndex > CTL_INDEX(PCIVME_LAST_CTL_CODE))
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
KdPrint(("PCIVMEDeviceControl() FAIL.\n"));
return STATUS_UNSUCCESSFUL;
}
return ioctl[nIndex](deviceObj, Irp, IrpStack);
}
//------------------------------------------------------------------------
// called at read()
NTSTATUS PCIVMERead(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PVOID pOutputBuffer = ((void *)(MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)));
LARGE_INTEGER *fileOffset = (LARGE_INTEGER *)&Irp->Tail.Overlay.OriginalFileObject->CurrentByteOffset;
FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
DEVICE_EXT *pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);
PCIADA *pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];
register ULONG Address = IrpStack->Parameters.Read.ByteOffset.LowPart + file_obj->dwAccessBase;
ULONG storeLength = 0;
PBOOLEAN pbPrevBusError;
KdPrint(("PCIVMERead(%d)\n", file_obj->uwAssociatedVMEMM));
// do here in between what has to be done -----------------
if (Address & file_obj->dwAddressMask) // don't do unaligned transfers
Status = STATUS_DATATYPE_MISALIGNMENT;
else
{
register ULONG Length = IrpStack->Parameters.Read.Length;
register ULONG blockLength;
register ULONG pageAddress;
register ULONG toNextPage;
KIRQL oldIrql;
Length &= ~file_obj->dwAddressMask; // align to integer increments
storeLength = Length;
// lock other users out
KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);
// check for modifier
if (pciada->bModifier != file_obj->bAddressModifier)
{
WRITE_REGISTER_UCHAR(pciada->pbModifier, file_obj->bAddressModifier);
pciada->bModifier = file_obj->bAddressModifier;
}
// do the read ---
file_obj->bBusError = FALSE;
pbPrevBusError = ExchangePointer(&pciada->pbBusError, &file_obj->bBusError);
while (Length)
{
pageAddress = Address & ~VME_ADR_MASK;
if (pageAddress != pciada->dwVMEPage)
{
WRITE_REGISTER_ULONG(pciada->pdwVMEAdr, pageAddress);
pciada->dwVMEPage = pageAddress;
}
toNextPage = (pageAddress + VME_ADR_MASK + 1) - Address;
blockLength = (toNextPage < Length) ? toNextPage : Length;
KdPrint(("Address 0x%08x, blockLength %d, Length %d\n",
Address, blockLength, Length));
file_obj->fRead(pOutputBuffer , blockLength, (PVOID)((PUCHAR)pciada->pvVME + (Address & VME_ADR_MASK)));
Length -= blockLength;
Address += blockLength;
pOutputBuffer = (PVOID)((PUCHAR)pOutputBuffer + blockLength);
}
// release the lock
KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
ExchangePointer(&pciada->pbBusError, pbPrevBusError);
if (file_obj->bBusError) Status = STATUS_ACCESS_VIOLATION;
if (file_obj->bIncrement) // only when increment to next is on
*fileOffset =
RtlLargeIntegerAdd(*fileOffset, RtlConvertUlongToLargeInteger(storeLength));
}
// do here in between what has to be done end -------------
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = storeLength;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
KdPrint(("PCIVMERead(), Status = 0x%08x\n", Status));
return Status;
}
//------------------------------------------------------------------------
// called at write()
NTSTATUS PCIVMEWrite(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
PVOID pInputBuffer = ((void *)(MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)));
LARGE_INTEGER *fileOffset = (LARGE_INTEGER *)&Irp->Tail.Overlay.OriginalFileObject->CurrentByteOffset;
FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
DEVICE_EXT *pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);
PCIADA *pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];
register ULONG Address = IrpStack->Parameters.Write.ByteOffset.LowPart + file_obj->dwAccessBase;
ULONG storeLength = 0;
PBOOLEAN pbPrevBusError;
KdPrint(("PCIVMEWrite(%d)\n", file_obj->uwAssociatedVMEMM));
// do here in between what has to be done -----------------
if (Address & file_obj->dwAddressMask) // don't do unaligned transfers
Status = STATUS_DATATYPE_MISALIGNMENT;
else
{
register ULONG Length = IrpStack->Parameters.Write.Length;
register ULONG blockLength;
register ULONG pageAddress;
register ULONG toNextPage;
KIRQL oldIrql;
Length &= ~file_obj->dwAddressMask; // align to integer increments
storeLength = Length;
// check for modifier
// lock other users out
KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);
if (pciada->bModifier != file_obj->bAddressModifier)
{
WRITE_REGISTER_UCHAR(pciada->pbModifier, file_obj->bAddressModifier);
pciada->bModifier = file_obj->bAddressModifier;
}
// do the read ---
file_obj->bBusError = FALSE;
pbPrevBusError = ExchangePointer(&pciada->pbBusError, &file_obj->bBusError);
while (Length)
{
pageAddress = Address & ~VME_ADR_MASK;
if (pageAddress != pciada->dwVMEPage)
{
WRITE_REGISTER_ULONG(pciada->pdwVMEAdr, pageAddress);
pciada->dwVMEPage = pageAddress;
}
toNextPage = (pageAddress + VME_ADR_MASK + 1) - Address;
blockLength = (toNextPage < Length) ? toNextPage : Length;
KdPrint(("Address 0x%08x, blockLength %d, Length %d\n",
Address, blockLength, Length));
file_obj->fWrite((PVOID)((PUCHAR)pciada->pvVME + (Address & VME_ADR_MASK)) , blockLength, pInputBuffer);
Length -= blockLength;
Address += blockLength;
pInputBuffer = (PVOID)((PUCHAR)pInputBuffer + blockLength);
}
// release the lock
KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
ExchangePointer(&pciada->pbBusError, pbPrevBusError);
if (file_obj->bBusError) Status = STATUS_ACCESS_VIOLATION;
if (file_obj->bIncrement) // only when increment to next is on
*fileOffset = RtlLargeIntegerAdd(*fileOffset, RtlConvertUlongToLargeInteger(storeLength));
}
// do here in between what has to be done end -------------
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = storeLength;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
KdPrint(("PCIVMEWrite(), Status = 0x%08x\n", Status));
return Status;
}
#ifdef DO_CLEANUP
//------------------------------------------------------------------------
// called at cancel of a path
NTSTATUS PCIVMECancel(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
PIRP pIrpCancel;
KdPrint(("PCIVMECancel()\n"));
// remove all queued IRPs of this file_obj
do
{
pIrpCancel = RemoveIRPfromQueue(device_Obj, file_obj);
if (pIrpCancel == (PIRP)NULL)
{
IoReleaseCancelSpinLock(pIrpCancel->CancelIrql);
break;
}
else
{
IoAcquireCancelSpinLock(pIrpCancel->CancelIrql);
// mark irp as not pending
IoSetCancelRoutine(pIrpCancel, NULL);
IoReleaseCancelSpinLock(pIrpCancel->CancelIrql);
pIrpCancel->IoStatus.Status = STATUS_CANCELLED;
pIrpCancel->IoStatus.Information = 0;
}
} while (pIrpCancel != (PIRP)NULL);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
KdPrint(("PCIVMECancel(OK)\n"));
return STATUS_SUCCESS;
}
#endif
//------------------------------------------------------------------------
// search for pciada's
//
NTSTATUS SearchDevices(PDEVICE_OBJECT device_Obj)
{
PCI_SLOT_NUMBER SlotNumber;
PCI_COMMON_CONFIG pci_config;
PCIADA *pciada;
ULONG length;
int *found;
int i,j,k;
KdPrint(("SearchDevices()\n"));
// prepare structures ----------------------------------------
found = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->nPCIADAs;
*found = 0;
for (i = 0; i < PCIVME_MAX_PCIADA; i++)
{
pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[i];
pciada->Bus = -1;
pciada->Slot.u.AsULONG = 0xFFFFFFFF;
}
// search for pciada's ---------------------------------------
SlotNumber.u.bits.Reserved = 0;
for (j = 0; j < PCI_MAX_BUSES; j++)
{
for (i = 0; i < PCI_MAX_DEVICES; i++)
{
SlotNumber.u.bits.DeviceNumber = i;
for (k = 0; k < PCI_MAX_FUNCTION; k++)
{
SlotNumber.u.bits.FunctionNumber = k;
length = HalGetBusData( PCIConfiguration, // Bustype
j, // PCI-Busnumber
SlotNumber.u.AsULONG, // Slotnumber
(PVOID) &(pci_config), // Pointer for the PCI-Information
sizeof(PCI_COMMON_CONFIG) );
if ((pci_config.VendorID == PCIVME_VENDOR_ID) &&
(pci_config.DeviceID == PCIVME_DEVICE_ID) &&
(pci_config.u.type0.SubSystemID == PCIVME_SUBSYS_ID) &&
(pci_config.u.type0.SubVendorID == PCIVME_SUBVEN_ID) &&
(pci_config.u.type0.BaseAddresses[2]))
{
pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[*found];
memcpy(&pciada
->PCIDevice
, &pci_config
, sizeof(pci_config
));
pciada->Slot = SlotNumber;
pciada->Bus = j;
KdPrint(("PCIADA found @ Bus/Slot %d/%d.\n", pciada->Bus, pciada->Slot.u.AsULONG));
(*found)++;
if (*found >= PCIVME_MAX_PCIADA) return STATUS_SUCCESS;
}
}
}
}
return STATUS_SUCCESS;
}
//---------------------------------------------------------------
// function to call for bug fix of PLX9050 build in bug
//
NTSTATUS PLX9050BugFix(PDEVICE_OBJECT device_Obj)
{
DEVICE_EXT *DeviceExtension = (DEVICE_EXT*)device_Obj->DeviceExtension;
int i;
ULONG dwData;
PCIADA *pciada;
KdPrint(("PLX9050BugFix()\n"));
for (i = 0; i < DeviceExtension->nPCIADAs; i++)
{
pciada = &DeviceExtension->pciada[i];
if ((dwData = pciada->PCIDevice.u.type0.BaseAddresses[0]) & 0x80)
{
KdPrint(("Changing address 0:0x%p with 4:0x%p\n",
pciada->PCIDevice.u.type0.BaseAddresses[0],
pciada->PCIDevice.u.type0.BaseAddresses[4]));
pciada->PCIDevice.u.type0.BaseAddresses[0] = // exchange
pciada->PCIDevice.u.type0.BaseAddresses[4];
pciada->PCIDevice.u.type0.BaseAddresses[4] = dwData;
if (HalSetBusDataByOffset(PCIConfiguration, pciada->Bus,
pciada->Slot.u.AsULONG,
(PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[0],
0x10, 4) != 4)
return STATUS_UNSUCCESSFUL;
if (HalSetBusDataByOffset(PCIConfiguration, pciada->Bus,
pciada->Slot.u.AsULONG,
(PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[4],
0x20, 4) != 4)
return STATUS_UNSUCCESSFUL;
}
if ((dwData = pciada->PCIDevice.u.type0.BaseAddresses[1]) & 0x80)
{
KdPrint(("Changing address 1:0x%p with 5:0x%p\n",
pciada->PCIDevice.u.type0.BaseAddresses[1],
pciada->PCIDevice.u.type0.BaseAddresses[5]));
pciada->PCIDevice.u.type0.BaseAddresses[1] = // exchange
pciada->PCIDevice.u.type0.BaseAddresses[5];
pciada->PCIDevice.u.type0.BaseAddresses[5] = dwData;
if (HalSetBusDataByOffset(PCIConfiguration, pciada->Bus,
pciada->Slot.u.AsULONG,
(PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[1],
0x14, 4) != 4)
return STATUS_UNSUCCESSFUL;
if (HalSetBusDataByOffset(PCIConfiguration, pciada->Bus,
pciada->Slot.u.AsULONG,
(PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[5],
0x24, 4) != 4)
return STATUS_UNSUCCESSFUL;
}
}
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// reserve resources for PCIADAs
//
NTSTATUS PCIVMEExtractResources(PCIADA *pciada, PCM_RESOURCE_LIST pList)
{
PCM_RESOURCE_LIST pResourceList;
PCM_FULL_RESOURCE_DESCRIPTOR pFullDescriptor;
PCM_PARTIAL_RESOURCE_LIST pPartialList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialDescriptor;
int i;
int bug = 0;
KdPrint(("PCIVMEExtractResources()\n"));
pResourceList = pList;
pFullDescriptor = pResourceList->List;
pPartialList = &pFullDescriptor->PartialResourceList;
for (i=0; i<(int)pPartialList->Count; i++)
{
pPartialDescriptor = &pPartialList->PartialDescriptors[i];
switch (pPartialDescriptor->Type)
{
case CmResourceTypeInterrupt:
pciada->Irql = (KIRQL)pPartialDescriptor->u.Interrupt.Level;
pciada->Vector = pPartialDescriptor->u.Interrupt.Vector;
pciada->Affinity = pPartialDescriptor->u.Interrupt.Affinity;
KdPrint(("Irq : Irql: %d, Vector: %d, Affinity: %d\n",
pciada->Irql, pciada->Vector, pciada->Affinity));
break;
case CmResourceTypeDma:
KdPrint(("Dma : \n"));
break;
case CmResourceTypePort:
KdPrint(("Port : 0x%p\n", pPartialDescriptor->u.Port.Start));
break;
case CmResourceTypeMemory:
// special handling of PLXBUG here because of WIN2000
// WIN2000 doesn't recognize late address changes
if (!bug)
{
if (i == 0)
{
pciada->pvPhysLcr = pPartialDescriptor->u.Memory.Start;
if (pciada->pvPhysLcr.LowPart & 0x80)
bug = 1;
}
}
else
{
if (i == 3)
pciada->pvPhysLcr = pPartialDescriptor->u.Memory.Start;
}
if (i == 2)
pciada->pvPhysIfr = pPartialDescriptor->u.Memory.Start;
KdPrint(("Memory : 0x%p\n", (PUCHAR)pPartialDescriptor->u.Memory.Start.LowPart));
break;
}
}
if (pciada->Irql == 0)
return STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT;
KdPrint(("PCIVMEExtractResources() OK.\n"));
return STATUS_SUCCESS;
}
NTSTATUS PCIVMEReserveResources(PDEVICE_OBJECT device_Obj)
{
PCM_RESOURCE_LIST pList = NULL;
NTSTATUS result = STATUS_SUCCESS;
int i;
DEVICE_EXT *pDevExt = (DEVICE_EXT*)(device_Obj->DeviceExtension);
int nPCIADAs = pDevExt->nPCIADAs;
PCIADA *pciada;
UNICODE_STRING DriverClassName;
KdPrint(("PCIVMEReserveResources()\n"));
// prepare resource claiming
RtlInitUnicodeString(&DriverClassName, L"PCICC32");
// cycle through all busses and slots assigned to PCIADAs
for (i = 0; i < nPCIADAs; i++)
{
pciada = &pDevExt->pciada[i];
result = HalAssignSlotResources(NULL, &DriverClassName, device_Obj->DriverObject, device_Obj,
PCIBus, pciada->Bus, pciada->Slot.u.AsULONG, &pList);
if (result != STATUS_SUCCESS)
break;
result = PCIVMEExtractResources(pciada, pList);
if (result != STATUS_SUCCESS)
break;
}
// its my part to free allocated resources
if (pList != NULL) ExFreePoolWithTag(pList,'nepo');
KdPrint(("PCIVMEReserveResources(0x%08x)\n", result));
return result;
};
//------------------------------------------------------------------------
// free resources from PCIADAs
//
NTSTATUS PCIVMEFreeResources(PDEVICE_OBJECT device_Obj)
{
CM_RESOURCE_LIST ResList;
BOOLEAN bConflict;
UNICODE_STRING DriverClassName;
KdPrint(("PCIVMEFreeResources()\n"));
RtlInitUnicodeString(&DriverClassName, L"PCIVME");
ResList.Count = 0;
IoReportResourceUsage(&DriverClassName, device_Obj->DriverObject,
&ResList, sizeof(ResList), device_Obj,
NULL, 0, FALSE, &bConflict);
return STATUS_SUCCESS;
};
//------------------------------------------------------------------------
// translate memory resources to neutral for PCIADAs
//
NTSTATUS PCIVMETranslateBusAddresses(PDEVICE_OBJECT device_Obj)
{
int i;
NTSTATUS result = STATUS_SUCCESS;
int nPCIADAs = ((DEVICE_EXT*)(device_Obj->DeviceExtension))->nPCIADAs;
ULONG memType0, memType2;
PCIADA *pciada;
KdPrint(("TranslateBusAddresseses()\n"));
for (i = 0; i < nPCIADAs; i++)
{
pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[i];
memType0 = memType2 = 0;
if (!(HalTranslateBusAddress(PCIBus, pciada->Bus, pciada->pvPhysLcr, &memType0,
&pciada->pvPhysLcr)) ||
!(HalTranslateBusAddress(PCIBus, pciada->Bus, pciada->pvPhysIfr, &memType2,
&pciada->pvPhysIfr)))
{
result = STATUS_UNSUCCESSFUL;
break;
}
if ((memType0) || (memType2))
{
result = STATUS_UNSUCCESSFUL;
break;
}
}
return result;
}
//------------------------------------------------------------------------
// map address spaces to virtual addresses
//
NTSTATUS PCIVMEMapIOspace(PDEVICE_OBJECT device_Obj)
{
int i;
DEVICE_EXT *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
int nPCIADAs = pDevExt->nPCIADAs;
PCIADA *pciada;
KdPrint(("PCIVMEMapIOspace()\n"));
for (i = 0; i < nPCIADAs; i++)
{
pciada = &pDevExt->pciada[i];
if ((pciada->pvVirtLcr = MmMapIoSpace(pciada->pvPhysLcr, LCR_SPACE, FALSE)) == NULL)
return STATUS_UNSUCCESSFUL;
if ((pciada->pvVirtIfr = MmMapIoSpace(pciada->pvPhysIfr, IFR_SPACE, FALSE)) == NULL)
return STATUS_UNSUCCESSFUL;
KdPrint(("PCIADA %d: LCR 0x%08x IFR 0x%08x\n",
i, pciada->pvVirtLcr, pciada->pvVirtIfr));
pciada->pwIntCSR = (PUSHORT)((PUCHAR)pciada->pvVirtLcr + 0x4C);
pciada->pwCntrl = (PUSHORT)((PUCHAR)pciada->pvVirtLcr + 0x50);
}
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// initializes and registers a DPC routine for each pciada
//
NTSTATUS InitializeCustomDPCObjects(PDEVICE_OBJECT device_object)
{
int i;
int nPCIADAs = ((DEVICE_EXT*)(device_object->DeviceExtension))->nPCIADAs;
PCIADA *pciada;
KdPrint(("InitializeCustomDPCObject()\n"));
for (i = 0; i < nPCIADAs; i++)
{
pciada = &((DEVICE_EXT*)(device_object->DeviceExtension))->pciada[i];
KeInitializeDpc(&pciada->kDPCobj, fMyDefferedRoutine, (PVOID)device_object);
}
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// initializes the queue for storing IRPs waiting for vectors
//
NTSTATUS InitializeIRPQueue(PDEVICE_OBJECT device_Obj)
{
DEVICE_EXT *pDevExt = ((DEVICE_EXT*)(device_Obj->DeviceExtension));
KdPrint(("InitializeIRPQueue()\n"));
KeInitializeSpinLock(&pDevExt->IRPLock);
InitializeListHead(&pDevExt->IRPList);
return STATUS_SUCCESS;
}
//------------------------------------------------------------------------
// init structures a.s.o.
//
VOID PCIVMESoftInit(PDEVICE_OBJECT device_Obj)
{
int i;
PCIADA *pciada;
for (i = 0; i < PCIVME_MAX_PCIADA; i++)
{
pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[i];
pciada->pvPhysLcr.QuadPart = pciada->pvPhysIfr.QuadPart = 0;
pciada->pvVirtLcr = pciada->pvVirtIfr = NULL;
pciada->bConnected = FALSE; // connection still not verified
pciada->bWordMode = TRUE;
pciada->bSysControl = FALSE;
pciada->wModuleNumber = 0xFFFF;
pciada->wFPGAVersion = 0xFFFF;
pciada->wModuleType = 1; // always VMEMM
pciada->InterruptObject = NULL;
pciada->Irql = 0;
pciada->Vector = 0;
pciada->Affinity = 0;
pciada->dwLinkCount = 0;
KeInitializeSpinLock(&pciada->IrqListLock);
KeInitializeSpinLock(&pciada->AccessLock);
InitializeListHead(&pciada->IrqListList); // start of list of irq fifos
pciada->nInterruptHandlers = 0;
}
// no vmemm associated to any PCIADA
for (i = 0; i < PCIVME_MAX_VMEMM; i++)
((DEVICE_EXT*)(device_Obj->DeviceExtension))->vmemm[i] = NULL;
}
//------------------------------------------------------------------------
// the ultimate starting point of a driver
NTSTATUS DriverEntry(PDRIVER_OBJECT driverObj, PUNICODE_STRING regPath )
{
UNREFERENCED_PARAMETER(regPath);
PDEVICE_OBJECT device_object; // pointer to the device object
UNICODE_STRING device_name;
UNICODE_STRING symbol_name;
NTSTATUS result = STATUS_SUCCESS;
int nPCIADAs; // count of PCIADAs
DEVICE_EXT *DeviceExtension = NULL;
KdPrint(("DriverEntry() ---%d.%d---------------------------------\n", (DRIVER_VERSION >> 16) & 0xff, DRIVER_VERSION & 0xff));
driverObj->DriverUnload = PCIVMEUnload;
driverObj->MajorFunction[IRP_MJ_CREATE] = PCIVMEOpen;
driverObj->MajorFunction[IRP_MJ_CLOSE] = PCIVMEClose;
driverObj->MajorFunction[IRP_MJ_READ] = PCIVMERead;
driverObj->MajorFunction[IRP_MJ_WRITE] = PCIVMEWrite;
driverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PCIVMEDeviceControl;
#ifdef DO_CLEANUP
driverObj->MajorFunction[IRP_MJ_CLEANUP] = PCIVMECancel;
#endif
driverObj->MajorFunction[IRP_MJ_SHUTDOWN] = PCIVMEShutdown;
RtlInitUnicodeString(&device_name, L"\\Device\\PCIVME");
/* DeviceObject durch IO-Manager erzeugen */
result = IoCreateDevice( driverObj, // DriverObject received by the DriverEntry Call
sizeof(DEVICE_EXT), // required Memory for the DeviceExtension
&device_name, // Name of the device in the device-Directory
FILE_DEVICE_UNKNOWN, // Device-ID
0, // Device-Characteristics normal 0
FALSE, // TRUE : one Thread can open the driver
&device_object); // DeviceObject returned from the IO-Manager
// defines how the data are handled between user / kernel Adress-Space
device_object->Flags |= DO_DIRECT_IO;
#if 0
// register the shutdown notification entry
IoRegisterShutdownNotification(device_object);
#endif
// anounce driver as symbolic device ---------------------------------
if (result == STATUS_SUCCESS)
{
/* now the symbolic Link is created. If there is no S.L. a program cannot connect to the driver */
RtlInitUnicodeString(&symbol_name, DOS_DEVICE_NAME);
result = IoCreateSymbolicLink(&symbol_name,&device_name);
if (result != STATUS_SUCCESS)
{
IoDeleteDevice(device_object);
return result;
}
}
else
return result;
DeviceExtension = (DEVICE_EXT*)device_object->DeviceExtension;
DeviceExtension->actualIrp = NULL;
DeviceExtension->driverObj = driverObj;
DeviceExtension->nInitState = 0;
// init pciada structures ------------------------------------
PCIVMESoftInit(device_object);
// search for PCIADAs ----------------------------------------
result = SearchDevices(device_object);
nPCIADAs = DeviceExtension->nPCIADAs;
if ((result != STATUS_SUCCESS) || !(nPCIADAs))
{
PCIVMEUnload(driverObj);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
// request exclusive ownership of .. ---------------------------------
if ((result = PCIVMEReserveResources(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return result;
}
else
DeviceExtension->nInitState++;
// fix PLX9050 Bug -------------------------------------------
if ((result = PLX9050BugFix(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return result;
}
// translate BUS relative addresses ----------------------------------
if ((result = PCIVMETranslateBusAddresses(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
else
DeviceExtension->nInitState++;
// translate Interrupt Resources used --------------------------------
if ((result = PCIVMETranslateInterrupts(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
else
DeviceExtension->nInitState++;
// map address spaces to virtual addresses ---------------------------
if ((result = PCIVMEMapIOspace(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
else
DeviceExtension->nInitState++;
// initialze my custom DPC objects -----------------------------------
if ((result = InitializeCustomDPCObjects(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return result;
}
else
DeviceExtension->nInitState++;
// initialze the queue for IRPs waiting for vectors ------------------
if ((result = InitializeIRPQueue(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return result;
}
else
DeviceExtension->nInitState++;
// connect interrupts to service routines ----------------------------
if ((result = PCIVMEConnectInterrupt(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
else
DeviceExtension->nInitState++;
// scan all connected VMEMM for info and later use -------------------
if ((result = PCIVMEScanVMEMM(device_object)) != STATUS_SUCCESS)
{
PCIVMEUnload(driverObj);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
device_object->Flags &= ~DO_DEVICE_INITIALIZING;
KdPrint(("DriverEntry() OK.\n"));
return result;
}