//-------------------------------------------------------------------------
// 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
if (*n
== tolower(*ptr
))
{
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
;
}