Hi,
I have been struggling with this problem for some time, so any help
from someone who has written a PCMCIA device driver would be greatly
appreciated!

I have written a working Win2K device driver for a PCI device, and am
now writing a device driver for the PCMCIA version of the device (some
code included below). My driver does not receive interrupts although
the call to IoConnectInterrupt() succeeds. I have gone through the
PSCR smartcard device driver code in the DDK samples. I am also
familiar with the DisabeISAToPCIRouting registry setting - didn't make
a difference in my case.

Is there something different one needs to do to receive interrupts in
a PCMCIA device driver as compared to a PCI device driver?

Thanks!

----------------- Some code from the driver ------------------------

GLOBALS Globals;

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, PolarisAddDevice)
#pragma alloc_text (PAGE, PolarisCreateClose)
#pragma alloc_text (PAGE, PolarisDispatchIoctl)
#pragma alloc_text (PAGE, PolarisReadWrite)
#pragma alloc_text (PAGE, PolarisDispatchPnp)
#pragma alloc_text (PAGE, PolarisUnload)
#endif

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
// Save the RegistryPath.
Globals.RegistryPath.MaximumLength = RegistryPath->Length +
sizeof(UNICODE_NULL);
Globals.RegistryPath.Length = RegistryPath->Length;
Globals.RegistryPath.Buffer = ExAllocatePoolWithTag (
PagedPool,
Globals.RegistryPath.MaximumLength,
POLARIS_POOL_TAG);

if (Globals.RegistryPath.Buffer) {
RtlCopyUnicodeString(&Globals.RegistryPath, RegistryPath);
} else {
PolarisDebugPrint ((0, "Couldn't allocate pool for registry
path."));
return STATUS_INSUFFICIENT_RESOURCES;
}

// Populate the Dispatch routines
DriverObject->MajorFunction[IRP_MJ_PNP] =
PolarisDispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER] =
PolarisDispatchPower;
DriverObject->MajorFunction[IRP_MJ_CREATE] =
PolarisCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
PolarisCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
PolarisDispatchIoctl;
DriverObject->MajorFunction[IRP_MJ_READ] =
PolarisReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] =
PolarisReadWrite;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] =
PolarisCleanup;

// Populate AddDevice/DriverUnload
DriverObject->DriverExtension->AddDevice =
PolarisAddDevice;
DriverObject->DriverUnload =
PolarisUnload;

return STATUS_SUCCESS;
}


/*
* This routine adds a device to the system
*/
NTSTATUS
PolarisAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
PPOLARIS_DEVICE_INFO pDeviceInfo = NULL;
POWER_STATE powerState;

PAGED_CODE();

// Create a device
status = IoCreateDevice (DriverObject,
sizeof (POLARIS_DEVICE_INFO),
NULL,
FILE_DEVICE_NETWORK,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);
if (!POLARIS_SUCCESS (status)) {
return status;
}

// Initialize the device extension.
pDeviceInfo = (PPOLARIS_DEVICE_INFO) deviceObject->DeviceExtension;

// Set the initial state of the FDO
INITIALIZE_PNP_STATE(pDeviceInfo);

// Store the PDO
pDeviceInfo->UnderlyingPDO = PhysicalDeviceObject;

// Set device power state
pDeviceInfo->DevicePowerState = PowerDeviceD0;

// Tell lower driver the power state
powerState.DeviceState = pDeviceInfo->DevicePowerState;
PoSetPowerState ( deviceObject, DevicePowerState, powerState );

// Tell the Plug & Play system that this device will need an
interface
status = IoRegisterDeviceInterface (
PhysicalDeviceObject,
(LPGUID) &GUID_CISCO_AIRO_DEVICE_INTERFACE_CLASS,
NULL,
&pDeviceInfo->InterfaceName);

if (!POLARIS_SUCCESS (status)) {
IoDeleteDevice (deviceObject);
PolarisDebugPrint ((2, "PolarisAddDevice: returning status:
%d\n", status));
return status;
}

// Attach device to the device stack
pDeviceInfo->NextLowerDriver = IoAttachDeviceToDeviceStack(
deviceObject,
PhysicalDeviceObject);

//
// Initialize Remove Lock
//
IoInitializeRemoveLock (&pDeviceInfo->RemoveLock ,
POLARIS_POOL_TAG,
1, // MaxLockedMinutes
5); // HighWatermark, this parameter is used only on checked
build.

// Set the flag if the device is not holding a pagefile
// crashdump file or hibernate file.
// Clear the DO_DEVICE_INITIALIZING flag.
// Note: Do not clear this flag until the driver has set the
// device power state and the power DO flags.
deviceObject->Flags |= DO_BUFFERED_IO;
deviceObject->Flags |= DO_POWER_PAGABLE;
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

pDeviceInfo->Self = deviceObject;
pDeviceInfo->Removed = FALSE;
pDeviceInfo->Started = FALSE;
pDeviceInfo->InterruptRegistered = FALSE;
pDeviceInfo->RxBufferLen = 32 * PAGE_SIZE; // Hardcoded ?

#ifdef BLOCKING_READ
pDeviceInfo->ReadEventHandle = NULL;
pDeviceInfo->ReadTimeOut.QuadPart = -(10000 * 2000); // 1 second
timeout, hardcoded
#endif

return status;
}


/*
* This routine handles PnP IRPs
*/
NTSTATUS
PolarisDispatchPnp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PPOLARIS_DEVICE_INFO pDeviceInfo;
PIO_STACK_LOCATION stack;
PDEVICE_OBJECT attachedPDO;
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN irpSkipped = FALSE;

PolarisDebugPrint((3, "PolarisDispatchPnp called\n"));

PAGED_CODE ();

pDeviceInfo = (PPOLARIS_DEVICE_INFO) DeviceObject->DeviceExtension;
attachedPDO = pDeviceInfo->NextLowerDriver;

stack = IoGetCurrentIrpStackLocation (Irp);

if (Deleted == GET_CURRENT_PNP_STATE(pDeviceInfo)) {
// Since the device is removed, we will not hold any IRPs.
// We just fail it.
Irp->IoStatus.Status = STATUS_DELETE_PENDING;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return STATUS_DELETE_PENDING;
}

// Acquire the remove lock
status = IoAcquireRemoveLock (&pDeviceInfo->RemoveLock, Irp);
if (!POLARIS_SUCCESS (status)) {
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}

// Print minor function
PolarisDebugPrint((0, "PnP Minor function: %s\n",
PnPMinorFunctionString(stack->MinorFunction)));

// Process IRP
switch (stack->MinorFunction) {
case IRP_MN_START_DEVICE:

if (!pDeviceInfo->Started) {
// We have to call the underlying driver first
status = PolarisCallLowerDriver(attachedPDO, Irp);

if (status == STATUS_SUCCESS) {
// The device is starting.
status = PolarisStartDevice(DeviceObject, Irp);
if(POLARIS_SUCCESS(status))
{
status =
IoSetDeviceInterfaceState(&pDeviceInfo->InterfaceName, TRUE);
if (!POLARIS_SUCCESS (status)){
PolarisDebugPrint((0, "IoSetDeviceInterfaceState
failed: 0x%x\n", status));

Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);

// Release the remove lock
IoReleaseRemoveLock(&pDeviceInfo->RemoveLock, Irp);

return status;
}

// Set the PnP state to Started
SET_NEW_PNP_STATE(pDeviceInfo, Started);

pDeviceInfo->Started = TRUE;
pDeviceInfo->Removed = FALSE;

HardwareEnableInterrupts(pDeviceInfo);
}
}
}

break;

case IRP_MN_QUERY_REMOVE_DEVICE:
status = PolarisCanRemoveDevice(DeviceObject);
if(POLARIS_SUCCESS(status))
{
SET_NEW_PNP_STATE(pDeviceInfo, RemovePending);
status = PolarisCallLowerDriver(attachedPDO, Irp);
}

break;

case IRP_MN_SURPRISE_REMOVAL:
// The device has been unexpectedly removed from the machine
// and is no longer available for I/O. Stop all access to the
device.
// Release any resources associated with the device, but leave
the
// device object attached to the device stack until the PnP
Manager
// sends a subsequent IRP_MN_REMOVE_DEVICE request.
// You should fail any outstanding I/O to the device. You will
// not get a remove until all the handles open to the device
// have been closed.
pDeviceInfo->Removed = TRUE;
pDeviceInfo->Started = FALSE;

// Release system resources
status = PolarisReleaseResources(DeviceObject);
if(POLARIS_SUCCESS(status))
{
// Set the PnP state to SurpriseRemovePending
SET_NEW_PNP_STATE(pDeviceInfo, SurpriseRemovePending);
}
break;

case IRP_MN_REMOVE_DEVICE:
// Relinquish all resources here.
// Detach and delete the device object so that
// your driver can be unloaded. You get remove
// either after query_remove or surprise_remove.
if(!pDeviceInfo->Removed)
{
pDeviceInfo->Removed = TRUE;
pDeviceInfo->Started = FALSE;

// Stop the Adapter
HardwareStopAdapter(pDeviceInfo);

// Release system resources
status = PolarisReleaseResources(DeviceObject);
if(POLARIS_SUCCESS(status))
{
// Set the PnP state to SurpriseRemovePending
SET_NEW_PNP_STATE(pDeviceInfo, RemovePending);
}

status = PolarisCallLowerDriver(attachedPDO, Irp);
}

if(POLARIS_SUCCESS(status))
{
// Set the PnP state to Deleted
SET_NEW_PNP_STATE(pDeviceInfo, Deleted);

// Disable the interface
IoSetDeviceInterfaceState(&pDeviceInfo->InterfaceName,
FALSE);

// Remove the device and all references
IoDeleteSymbolicLink(&pDeviceInfo->InterfaceName);
IoDetachDevice(pDeviceInfo->NextLowerDriver);
IoDeleteDevice(DeviceObject);
}

break;

case IRP_MN_QUERY_STOP_DEVICE:
status = PolarisCanStopDevice(DeviceObject);
if(POLARIS_SUCCESS(status))
{
SET_NEW_PNP_STATE(pDeviceInfo, StopPending);
status = PolarisCallLowerDriver(attachedPDO, Irp);
}

break;

case IRP_MN_STOP_DEVICE:
if(pDeviceInfo->Started) {

// Stop the Adapter
HardwareStopAdapter(pDeviceInfo);

// Release system resources
status = PolarisReleaseResources(DeviceObject);
if(POLARIS_SUCCESS(status))
{
// Mark the device as stopped.
IoSetDeviceInterfaceState(&pDeviceInfo->InterfaceName,
FALSE);
SET_NEW_PNP_STATE(pDeviceInfo, Stopped);
}

pDeviceInfo->Started = FALSE;
status = PolarisCallLowerDriver(attachedPDO, Irp);
}

break;
default:
// This is an Irp that is only useful for underlying drivers
status = PolarisCallLowerDriver(attachedPDO, Irp);
irpSkipped = TRUE;
break;
}

if (irpSkipped == FALSE) {
// Don't touch the status field of irps we don't process
Irp->IoStatus.Status = status;
}

PolarisDebugPrint((3, "PolarisDispatchPnp status: 0x%x\n",
status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);

// Release the remove lock
IoReleaseRemoveLock(&pDeviceInfo->RemoveLock, Irp);

return status;
}

NTSTATUS
PolarisStartDevice (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PCM_RESOURCE_LIST resourcesTranslated;
PCM_RESOURCE_LIST resources;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTranslated;
PCM_PARTIAL_RESOURCE_LIST partialResourceList;
PCM_PARTIAL_RESOURCE_LIST partialResourceListTranslated;
PCM_FULL_RESOURCE_DESCRIPTOR fullListTranslated;
PIO_STACK_LOCATION stack;
PPOLARIS_DEVICE_INFO pDeviceInfo;
NTSTATUS status = STATUS_SUCCESS;
ULONG index;
BOOLEAN foundInterruptResource = FALSE;
int count = 0;

PAGED_CODE();

// PolarisDebugPrint((3, "PolarisStartDevice called\n"));

pDeviceInfo = (PPOLARIS_DEVICE_INFO) DeviceObject->DeviceExtension;

stack = IoGetCurrentIrpStackLocation (Irp);

//
// Do whatever initialization needed when starting the device:
// gather information about it, update the registry, etc.
//

if ((NULL != stack->Parameters.StartDevice.AllocatedResources) &&
(NULL != stack->Parameters.StartDevice.AllocatedResourcesTranslated))
{

//
// Set up the resource information that we will use to access
the
// controller hardware. We always expect only 1 full set of
resources.
// In that list we expect a DMA channel, an Interrupt vector,
and 2 I/O Port
// ranges. If we don't see all the required resources we will
woof.
//
resourcesTranslated =
stack->Parameters.StartDevice.AllocatedResourcesTranslated;
partialResourceListTranslated =
&resourcesTranslated->List[0].PartialResourceList;
resourceTranslated =
&partialResourceListTranslated->PartialDescriptors[0];

resources =
stack->Parameters.StartDevice.AllocatedResources;
partialResourceList =
&resources->List[0].PartialResourceList;
resource =
&partialResourceList->PartialDescriptors[0];

ASSERT( resourcesTranslated->Count == 1 );

fullListTranslated = &resourcesTranslated->List[0];

// Set up the bus information
pDeviceInfo->BusType = fullListTranslated->InterfaceType;
pDeviceInfo->BusNumber = fullListTranslated->BusNumber;

PolarisDebugPrint((0, "PolarisStartDevice: BusType = %d\n",
pDeviceInfo->BusType));
//
// Parameters.StartDevice.AllocatedResources points to a
// CM_RESOURCE_LIST describing the hardware resources that
// the PnP Manager assigned to the device. This list contains
// the resources in raw form. Use the raw resources to program
// the device.
//
// Parameters.StartDevice.AllocatedResourcesTranslated points
// to a CM_RESOURCE_LIST describing the hardware resources that
// the PnP Manager assigned to the device. This list contains
// the resources in translated form. Use the translated
resources
// to connect the interrupt vector, map I/O space, and map
memory.
//

// If it is a PCI card, do 16-bit I/O operations
if (pDeviceInfo->BusType == PCIBus) {
do8bitIO = 0;
}

for (index = 0; index < partialResourceList->Count; index++,
resource++, resourceTranslated++) {
switch (resource->Type) {
case CmResourceTypePort:
{
// use the second port, device dependent condition
if (pDeviceInfo->BusType == PCIBus) {
if (count == 0 ) {
count ++;
break;
}
}

switch (resourceTranslated->Type) {

case CmResourceTypePort:

// PolarisDebugPrint((2, "Resource
RAW Port: (%x) Length: (%d)\n",
// resource->u.Port.Start.LowPart,
resource->u.Port.Length));
//
// PolarisDebugPrint((2,
// "Resource Translated
Port: (%x) Length: (%d)\n",
//
resourceTranslated->u.Port.Start.LowPart,
//
resourceTranslated->u.Port.Length));

// Set the base I/O address of the device
pDeviceInfo->DeviceBaseIO = (UCHAR
*)resourceTranslated->u.Port.Start.u.LowPart;

break;

case CmResourceTypeMemory:

PolarisDebugPrint((2,
"Resource Translated Memory: (%x) Length:
(%d)\n",
resourceTranslated->u.Memory.Start.LowPart,
resourceTranslated->u.Memory.Length));
//
// Perform memory mapping here.
//

break;

default:
// PolarisDebugPrint((0,
"PolarisStartDevice: Unhandled resource_type (0x%x)\n",
//
resourceTranslated->Type));
TRAP ();
break;
}
}
break;

case CmResourceTypeDma:
{
DEVICE_DESCRIPTION deviceDesc;

PolarisDebugPrint((0, "PolarisStartDevice: DMA -
%04x\n", resourceTranslated->u.Dma.Channel) );

// Call PolarisSetupDMA() here if DMA is needed
}
break;

case CmResourceTypeMemory:

// PolarisDebugPrint((2, "Resource RAW
Memory: (%x) Length: (%d)\n",
// resource->u.Memory.Start.LowPart,
resource->u.Memory.Length));

// PolarisDebugPrint((2, "Resource
Translated Memory: (%x) Length: (%d)\n",
//
resourceTranslated->u.Memory.Start.LowPart,
resourceTranslated->u.Memory.Length));

// Perform memory address mapping with
PolarisMapDeviceAddress().

break;

case CmResourceTypeInterrupt:
// PolarisDebugPrint((0,
"PolarisStartDevice: CmResourceTypeInterrupt\n"));
if ( resourceTranslated->Flags &
CM_RESOURCE_INTERRUPT_LATCHED) {
pDeviceInfo->InterruptMode = Latched;
} else {
pDeviceInfo->InterruptMode = LevelSensitive;
}

pDeviceInfo->ControllerVector =
resourceTranslated->u.Interrupt.Vector;
pDeviceInfo->ProcessorMask =
resourceTranslated->u.Interrupt.Affinity;
pDeviceInfo->ControllerIrql =
(KIRQL)resourceTranslated->u.Interrupt.Level;
pDeviceInfo->SharableVector = TRUE;
pDeviceInfo->SaveFloatState = FALSE;

foundInterruptResource = TRUE;
break;

default:
// PolarisDebugPrint((0, "Unhandled
resource type (0x%x)\n", resource->Type));
break;
}
}

if (foundInterruptResource == TRUE) {
// PolarisDebugPrint((0, "PolarisStartDevice:
Connecting Interrupt\n"));

// Initialize the spin lock
KeInitializeSpinLock(&pDeviceInfo->IntrSpinLock);

// Connect the interrupt
status = IoConnectInterrupt( &pDeviceInfo->InterruptObject,
PolarisISR,
pDeviceInfo,
&pDeviceInfo->IntrSpinLock,
pDeviceInfo->ControllerVector,
pDeviceInfo->ControllerIrql,
pDeviceInfo->ControllerIrql,
pDeviceInfo->InterruptMode,
pDeviceInfo->SharableVector,
pDeviceInfo->ProcessorMask,
pDeviceInfo->SaveFloatState );

if ( POLARIS_SUCCESS(status) ) {
pDeviceInfo->InterruptRegistered = TRUE;
} else {
return status;
}
}

// If successful and got memory/IO space, initialize the H/W
if ( POLARIS_SUCCESS(status) &&
((pDeviceInfo->DeviceVirtualAddress) ||
(pDeviceInfo->DeviceBaseIO)) ) {
// Initialize the Hardware
status = HardwareInitializeAdapter(pDeviceInfo,
pDeviceInfo->DeviceBaseIO);
if (!POLARIS_SUCCESS(status)) {
return STATUS_UNSUCCESSFUL;
}
}
}

// PolarisDebugPrint((0, "PolarisStartDevice: returning status:
0x%x\n", status));
return status;
}

void HardwareEnableInterrupts(PPOLARIS_DEVICE_INFO pDeviceInfo)
{
airo_info *ai = &(pDeviceInfo->ai);
USHORT status;

/* Reset the status register */
status = IN4500( ai, EVSTAT );
OUT4500( ai, EVACK, status );

/* Enable the interrupts */
if (!ai->interruptEnabled) {
OUT4500( ai, EVINTEN, STATUS_INTS );
ai->interruptEnabled = TRUE;
}
}