/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: fdopnp.c Abstract: This module contains the code that handles PNP irps for pcmcia bus driver targeted towards the FDO's (for the pcmcia controller object) Author: Ravisankar Pudipeddi (ravisp) Oct 15 1996 Neil Sandlin (neilsa) June 1 1999 Environment: Kernel mode Revision History : --*/ #include "pch.h" // // Internal References // NTSTATUS PcmciaFdoFilterResourceRequirements( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ); NTSTATUS PcmciaFdoGetHardwareIds( IN PDEVICE_OBJECT Fdo, OUT PUNICODE_STRING HardwareIds ); NTSTATUS PcmciaFdoStartDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, OUT BOOLEAN *PassedDown, OUT BOOLEAN *NeedsRecompletion ); NTSTATUS PcmciaFdoStopDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, OUT BOOLEAN *PassedDown, OUT BOOLEAN *NeedsRecompletion ); NTSTATUS PcmciaFdoRemoveDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ); VOID PcmciaCleanupFdo( IN PFDO_EXTENSION FdoExtension ); NTSTATUS PcmciaFdoDeviceCapabilities( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, OUT BOOLEAN *PassedDown, OUT BOOLEAN *NeedsRecompletion ); NTSTATUS PcmciaAreCardBusCardsSupported( IN PFDO_EXTENSION FdoExtension ); NTSTATUS PcmciaFdoGetAssignedResources( IN PCM_RESOURCE_LIST ResourceList, IN PCM_RESOURCE_LIST TranslatedResourceList, IN PFDO_EXTENSION DeviceExtension ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PcmciaFdoPnpDispatch) #pragma alloc_text(PAGE, PcmciaFdoGetHardwareIds) #pragma alloc_text(PAGE, PcmciaFdoStartDevice) #pragma alloc_text(PAGE, PcmciaFdoStopDevice) #pragma alloc_text(PAGE, PcmciaFdoRemoveDevice) #pragma alloc_text(PAGE, PcmciaFdoFilterResourceRequirements) #pragma alloc_text(PAGE, PcmciaFdoGetAssignedResources) #pragma alloc_text(PAGE, PcmciaFdoDeviceCapabilities) #pragma alloc_text(PAGE, PcmciaAreCardBusCardsSupported) #endif NTSTATUS PcmciaFdoPnpDispatch ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: PNP/Power IRPs dispatch routine for the PCMCIA bus controller Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the IRP Return Value: Status --*/ { PIO_STACK_LOCATION nextIrpStack; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status = Irp->IoStatus.Status; BOOLEAN PassedDown = FALSE; BOOLEAN NeedsReCompletion = FALSE; PAGED_CODE(); #if DBG if (irpStack->MinorFunction > IRP_MN_PNP_MAXIMUM_FUNCTION) { DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x - Unknown PNP irp\n", DeviceObject, irpStack->MinorFunction)); } else { DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x --> %s\n", DeviceObject, Irp, PNP_IRP_STRING(irpStack->MinorFunction))); } #endif switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: { status = PcmciaFdoStartDevice(DeviceObject, Irp, &PassedDown, &NeedsReCompletion); break; } case IRP_MN_QUERY_STOP_DEVICE: { status = STATUS_SUCCESS; break; } case IRP_MN_CANCEL_STOP_DEVICE: { status = STATUS_SUCCESS; break; } case IRP_MN_STOP_DEVICE: { status = PcmciaFdoStopDevice(DeviceObject, Irp, &PassedDown, &NeedsReCompletion); break; } case IRP_MN_QUERY_DEVICE_RELATIONS: { // // Return the list of devices on the bus // status = PcmciaDeviceRelations( DeviceObject, Irp, irpStack->Parameters.QueryDeviceRelations.Type, (PDEVICE_RELATIONS *) &Irp->IoStatus.Information ); break; } case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: { status = PcmciaFdoFilterResourceRequirements(DeviceObject, Irp); PassedDown = TRUE; NeedsReCompletion = TRUE; break; } case IRP_MN_QUERY_REMOVE_DEVICE: { status = STATUS_SUCCESS; break; } case IRP_MN_CANCEL_REMOVE_DEVICE: { status = STATUS_SUCCESS; break; } case IRP_MN_REMOVE_DEVICE:{ status = PcmciaFdoRemoveDevice(DeviceObject, Irp); PassedDown = TRUE ; NeedsReCompletion = TRUE ; break; } case IRP_MN_SURPRISE_REMOVAL: { PcmciaFdoStopDevice(DeviceObject, NULL, NULL, NULL); status = STATUS_SUCCESS; break; } case IRP_MN_QUERY_ID: { UNICODE_STRING unicodeId; if (deviceExtension->Flags & PCMCIA_DEVICE_LEGACY_DETECTED) { RtlInitUnicodeString(&unicodeId, NULL); switch (irpStack->Parameters.QueryId.IdType) { case BusQueryHardwareIDs: { DebugPrint((PCMCIA_DEBUG_INFO, " Hardware Ids for fdo %x\n", DeviceObject)); status = PcmciaFdoGetHardwareIds(DeviceObject, &unicodeId); if (NT_SUCCESS(status)) { Irp->IoStatus.Information = (ULONG_PTR) unicodeId.Buffer; } break; } } } break; } case IRP_MN_QUERY_CAPABILITIES: { status = PcmciaFdoDeviceCapabilities(DeviceObject, Irp, &PassedDown, &NeedsReCompletion); break; } case IRP_MN_QUERY_LEGACY_BUS_INFORMATION: // // If this FDO represents a CardBus bridge, we pass this irp down // to the PCI PDO which will fill in the PCI bus type and number, // otherwise we fail the IRP. // if (!CardBusExtension(deviceExtension)) { status = STATUS_NOT_IMPLEMENTED; } // // if status is still STATUS_NOT_SUPPORTED, then later code will pass // this irp down the stack. // break; default: { DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x - Skipping unsupported irp\n", DeviceObject, Irp)); break; } } if (!PassedDown) { // // Set the IRP status only if we set it to something other than // STATUS_NOT_SUPPORTED. // if (status != STATUS_NOT_SUPPORTED) { Irp->IoStatus.Status = status ; } // // Pass down if success or STATUS_NOT_SUPPORTED. Otherwise, Complete. // if (NT_SUCCESS(status) || (status == STATUS_NOT_SUPPORTED)) { DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x pass %s %08x\n", DeviceObject, Irp, STATUS_STRING(Irp->IoStatus.Status), Irp->IoStatus.Status)); // // Below macro fills status with return of IoCallDriver. It does // not change the Irps status in any way. // PcmciaSkipCallLowerDriver(status, deviceExtension->LowerDevice, Irp); } else { DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x comp %s %08x\n", DeviceObject, Irp, STATUS_STRING(status), status)); IoCompleteRequest(Irp, IO_NO_INCREMENT); } } else if (NeedsReCompletion) { // // Set the IRP status only if we set it to something other than // STATUS_NOT_SUPPORTED. // if (status != STATUS_NOT_SUPPORTED) { Irp->IoStatus.Status = status ; } status = Irp->IoStatus.Status ; DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x comp %s %08x\n", DeviceObject, Irp, STATUS_STRING(status), status)); IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status ; } NTSTATUS PcmciaFdoDeviceCapabilities( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, OUT BOOLEAN *PassedDown, OUT BOOLEAN *NeedsRecompletion ) /*++ Routine Description Records the device capabilities of this pcmcia controller, so 1. they can be used in the power management for the controller and 2. they can be used for determining the capabilities of the child pc-card PDO's of this pcmcia controller. If this is legacy detected pcmcia controller (ISA-based), the pdo for the pcmcia controller is a dummy madeup device - hence the capabilities are filled in by ourselves. Otherwise, the capabilities are obtained by sending down the Irp to the parent bus. In either case, the capabilities are cached in the device extension of the pcmcia controller for future use. Arguments Fdo - Pointer to functional device object of the pcmcia controller Irp - Pointer to the i/o request packet PassedDown - Contains FALSE on entry, which means caller must complete or pass down irp based on status. If set to TRUE, Irp may need to be re-completed... NeedsRecompletion - ...In which case this parameter will be checked Return Value STATUS_SUCCESS Capabilities returned STATUS_INSUFFICIENT_RESOURCES Could not allocate memory to cache the capabilities --*/ { PFDO_EXTENSION fdoExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PDEVICE_CAPABILITIES capabilities; NTSTATUS status; PAGED_CODE(); capabilities = irpStack->Parameters.DeviceCapabilities.Capabilities; fdoExtension = Fdo->DeviceExtension; if (fdoExtension->Flags & PCMCIA_DEVICE_LEGACY_DETECTED) { // // This is a madeup devnode (ISA based PCMCIA controller). // Fill in the capabilities ourselves // RtlZeroMemory(capabilities, sizeof(DEVICE_CAPABILITIES)); // // Non removable, non ejectable // capabilities->Removable = FALSE; capabilities->UniqueID = FALSE; capabilities->EjectSupported = FALSE; // // Address & number need work.. // capabilities->Address = -1; capabilities->UINumber = -1; // // We cannot power down this controller // capabilities->DeviceState[PowerSystemSleeping1] = PowerDeviceD0; capabilities->DeviceState[PowerSystemSleeping2] = PowerDeviceD0; capabilities->DeviceState[PowerSystemSleeping3] = PowerDeviceD0; capabilities->DeviceState[PowerSystemHibernate] = PowerDeviceD3; // // Obviously wake is not supported on this legacy detected // piece of xxxx // capabilities->SystemWake = PowerSystemUnspecified; capabilities->DeviceWake = PowerDeviceUnspecified; capabilities->D1Latency = 0; capabilities->D2Latency = 0; capabilities->D3Latency = 0; status = STATUS_SUCCESS; } else { // // Either a PCI-PCMCIA bridge or PCI-Cardbus bridge // Send this down the stack to obtain the capabilities // ASSERT (fdoExtension->LowerDevice != NULL); status = PcmciaIoCallDriverSynchronous(fdoExtension->LowerDevice, Irp); *PassedDown = TRUE ; *NeedsRecompletion = TRUE ; } if (NT_SUCCESS(status)) { // // NOTE: HACKHACK: // // Here we provide an option to override the device wake of the pcmcia controller. // There are several controllers, notably TI 12xx controllers, which say they // can wake from D3, but really can effectively only do WOL from D2. That's because they // turn of socket power when put into D3. They fixed this on the TI 14xx line. // // So here we update the device wake field, and potentially the device states from the BIOS. // Note that this has to be used carefully, and only override a particular BIOS's settings // where it has been verified the device still works at the lower (more awake) device state. // if (PcmciaControllerDeviceWake) { if (PcmciaControllerDeviceWake < capabilities->DeviceWake) { capabilities->DeviceWake = PcmciaControllerDeviceWake; } if (PcmciaControllerDeviceWake < capabilities->DeviceState[PowerSystemSleeping1]) { capabilities->DeviceState[PowerSystemSleeping1] = PcmciaControllerDeviceWake; } if (PcmciaControllerDeviceWake < capabilities->DeviceState[PowerSystemSleeping2]) { capabilities->DeviceState[PowerSystemSleeping2] = PcmciaControllerDeviceWake; } if (PcmciaControllerDeviceWake < capabilities->DeviceState[PowerSystemSleeping3]) { capabilities->DeviceState[PowerSystemSleeping3] = PcmciaControllerDeviceWake; } } // // Cache the device capabilities in the device extension // for this pcmcia controller. // RtlCopyMemory(&fdoExtension->DeviceCapabilities, capabilities, sizeof(DEVICE_CAPABILITIES)); } else { RtlZeroMemory(&fdoExtension->DeviceCapabilities, sizeof(DEVICE_CAPABILITIES)); } return status; } NTSTATUS PcmciaFdoFilterResourceRequirements( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++ Routine Description Filters Resource requirements for PCMCIA controllers generated by the bus driver controlling the PDO for the PCMCIA controller. Currently adds memory range as an additional resource requirement since the BAR doesn't specify this Note for CardBus controllers: It is necessary to request an attribute memory window here for reading CIS of 16-bit PC-Cards - we need a 24 bit address for that, and this is the most elegant way of doing it - instead of special casing it in PCI driver Arguments DeviceExtension - Pointer to extension for the PCMCIA controller in question IoReqList - Pointer the the original resource requiremnts ejected by the bus driver FilteredRequirements - Pointer to the filtered resource req. list will be returned in this variable Return Value: STATUS_SUCCESS if filtering is successful Any other status - could not filter --*/ { PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension; PIO_RESOURCE_REQUIREMENTS_LIST oldReqList; PIO_RESOURCE_REQUIREMENTS_LIST newReqList; PIO_RESOURCE_LIST oldList, newList; PIO_RESOURCE_DESCRIPTOR ioResourceDesc; ULONG newReqSize; ULONG oldlistSize, newlistSize, altListSize; ULONG index; ULONG IntCount = 0; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; PAGED_CODE(); status = PcmciaIoCallDriverSynchronous(fdoExtension->LowerDevice, Irp); if (!NT_SUCCESS(status)) { return status; } if (Irp->IoStatus.Information == 0) { oldReqList = irpStack->Parameters.FilterResourceRequirements.IoResourceRequirementList; if (oldReqList == NULL) { // // NULL List, nothing to do // return(Irp->IoStatus.Status); } } else { // // Use the returned list // oldReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)Irp->IoStatus.Information; } // // Add an alternative list without the IRQ requirement, if one exists. Model the // new alternative list after the first list. // oldList = oldReqList->List; ioResourceDesc = oldList->Descriptors; altListSize = 0; for (index = 0; index < oldList->Count; index++) { // Count the descriptors, excluding any IRQ descriptors if (ioResourceDesc->Type == CmResourceTypeInterrupt) { IntCount++; } ioResourceDesc++; } if (IntCount) { // // "+1" because we are adding two later, but IO_RESOURCE_LIST already has 1 // altListSize = sizeof(IO_RESOURCE_LIST) + ((oldList->Count+1)-IntCount)*sizeof(IO_RESOURCE_DESCRIPTOR); } // // Add a memory range requirement to what we already have.. // newReqSize = oldReqList->ListSize + oldReqList->AlternativeLists*2*sizeof(IO_RESOURCE_DESCRIPTOR) + altListSize; newReqList = ExAllocatePool(PagedPool, newReqSize); if (newReqList == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(newReqList, oldReqList, FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List)); newReqList->ListSize = newReqSize; newList = newReqList->List; oldList = oldReqList->List; for (index = 0; index < oldReqList->AlternativeLists; index++) { // // Compute the size of the current original list // oldlistSize = sizeof(IO_RESOURCE_LIST) + (oldList->Count-1) * sizeof(IO_RESOURCE_DESCRIPTOR); newlistSize = oldlistSize; RtlCopyMemory(newList, oldList, newlistSize); // // Add memory requirement // ioResourceDesc = (PIO_RESOURCE_DESCRIPTOR) (((PUCHAR) newList) + newlistSize); ioResourceDesc->Option = IO_RESOURCE_PREFERRED; ioResourceDesc->Type = CmResourceTypeMemory; ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive; ioResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE; ioResourceDesc->u.Memory.MinimumAddress.QuadPart = fdoExtension->AttributeMemoryLow; ioResourceDesc->u.Memory.MaximumAddress.QuadPart = fdoExtension->AttributeMemoryHigh; ioResourceDesc->u.Memory.Length = fdoExtension->AttributeMemorySize; ioResourceDesc->u.Memory.Alignment = fdoExtension->AttributeMemoryAlignment; ioResourceDesc++; // // The other - less restrictive - alternative. // ioResourceDesc->Option = IO_RESOURCE_ALTERNATIVE; ioResourceDesc->Type = CmResourceTypeMemory; ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive; ioResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE; ioResourceDesc->u.Memory.MinimumAddress.QuadPart = 0; if ((fdoExtension->Flags & PCMCIA_MEMORY_24BIT) == 0) { ioResourceDesc->u.Memory.MaximumAddress.QuadPart = 0xFFFFFFFF; } else { ioResourceDesc->u.Memory.MaximumAddress.QuadPart = 0xFFFFFF; } ioResourceDesc->u.Memory.Length = fdoExtension->AttributeMemorySize; ioResourceDesc->u.Memory.Alignment = fdoExtension->AttributeMemoryAlignment; newList->Count += 2; newlistSize += 2*sizeof(IO_RESOURCE_DESCRIPTOR); oldList = (PIO_RESOURCE_LIST) (((PUCHAR) oldList) + oldlistSize); newList = (PIO_RESOURCE_LIST) (((PUCHAR) newList) + newlistSize); } if (altListSize != 0) { PIO_RESOURCE_DESCRIPTOR oldResourceDesc; // // Here we add the alternate list which doesn't contain an IRQ requirement. // Note that we use the first "new list" as the "old list". This way, we // pick up the things we added in the previous loop. All we have to do is // copy every descriptor except for the interrupt descriptor. // // Note: newList is still set from previous loop // oldList = newReqList->List; // // First copy the basic structure without the descriptors // RtlCopyMemory(newList, oldList, sizeof(IO_RESOURCE_LIST) - sizeof(IO_RESOURCE_DESCRIPTOR)); oldResourceDesc = oldList->Descriptors; ioResourceDesc = newList->Descriptors; for (index = 0; index < oldList->Count; index++) { if (oldResourceDesc->Type != CmResourceTypeInterrupt) { *ioResourceDesc = *oldResourceDesc; ioResourceDesc++; } else { // // We've deleted a descriptor // newList->Count--; } oldResourceDesc++; } newReqList->AlternativeLists++; } Irp->IoStatus.Information = (ULONG_PTR) newReqList; irpStack->Parameters.FilterResourceRequirements.IoResourceRequirementList = newReqList; // // Free up the old resource reqs // ExFreePool(oldReqList); fdoExtension->Flags |= PCMCIA_FILTER_ADDED_MEMORY; return STATUS_SUCCESS; } NTSTATUS PcmciaFdoGetHardwareIds( IN PDEVICE_OBJECT Fdo, OUT PUNICODE_STRING HardwareIds ) /*++ Routine description: This routine returns the hardware ids for the given 'legacy' pcmcia controller NOTE: this routine is required only for pcmcia controllers detected by this driver itself and registered via IoReportDetectedDevice. Arguments: Fdo - Pointer to the functional device object representing the pcmcia controller CompatibleIds - Pointer to the unicode string which would contain the hardware ids as a multi-string on return Return value: STATUS_SUCCESS Any other status - could not generate compatible ids --*/ { PCSTR strings[2]; PCMCIA_CONTROLLER_TYPE controllerType; ULONG count, index; BOOLEAN found; PAGED_CODE(); controllerType = ((PFDO_EXTENSION)Fdo->DeviceExtension)->ControllerType; found = FALSE; for (index = 0; (PcmciaAdapterHardwareIds[index].ControllerType != PcmciaInvalidControllerType); index++) { if (PcmciaAdapterHardwareIds[index].ControllerType == controllerType) { found = TRUE; break; } } if (found) { strings[0] = PcmciaAdapterHardwareIds[index].Id; } else { DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaGetAdapterHardwareIds: Could not find find hardware id for %x, controllerType %x\n", Fdo, controllerType )); strings[0] = ""; } count = 1; return PcmciaStringsToMultiString(strings, count, HardwareIds); } NTSTATUS PcmciaFdoStartDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, OUT BOOLEAN *PassedDown, OUT BOOLEAN *NeedsRecompletion ) /*++ Routine Description: This routine will start the PCMCIA controller with the supplied resources. The IRP is sent down to the pdo first, so PCI or ISAPNP or whoever sits underneath gets a chance to program the controller to decode the resources. Arguments: Fdo - Functional device object of the PCMCIA controller Irp - Well, it's the start irp, yah? PassedDown - Contains FALSE on entry, which means caller must complete or pass down irp based on status. If set to TRUE, Irp may need to be re-completed... NeedsRecompletion - ...In which case this parameter will be checked Return value: Status --*/ { NTSTATUS status; PFDO_EXTENSION deviceExtension = Fdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PCM_RESOURCE_LIST resList, translatedResList, newResList, newTranslatedResList; PAGED_CODE(); if (deviceExtension->Flags & PCMCIA_DEVICE_STARTED) { // // Start to already started device // DebugPrint((PCMCIA_DEBUG_FAIL,"PcmciaFdoStartDevice: Fdo %x already started\n", Fdo)); return STATUS_SUCCESS; } // // Parse AllocatedResources & get IoPort/AttributeMemoryBase/IRQ info. // status = PcmciaFdoGetAssignedResources(irpStack->Parameters.StartDevice.AllocatedResources, irpStack->Parameters.StartDevice.AllocatedResourcesTranslated, deviceExtension ); if (!NT_SUCCESS(status)) { // // Ha. This is most likely a START for a madeup devnode (report-detected legacy PCMCIA controller) // which has been removed subsequently, hence not reported again with proper resources. // We return an appropriate status // DebugPrint((PCMCIA_DEBUG_FAIL, "Pcmcia: No resources assigned to FDO, probably bogus START for" "non-existent controller\n" )); return STATUS_NO_SUCH_DEVICE; } DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x StartAdapter: AttrMem Base %x\n", Fdo, deviceExtension->PhysicalBase)); DebugPrint((PCMCIA_DEBUG_PNP, " VirtualBase %x AttrMem Size %x\n", deviceExtension->AttributeMemoryBase, deviceExtension->AttributeMemorySize)); if (deviceExtension->Flags & PCMCIA_FILTER_ADDED_MEMORY) { ULONG newSize; ULONG index; PCM_PARTIAL_RESOURCE_LIST resPartialList, translatedResPartialList; PCM_PARTIAL_RESOURCE_DESCRIPTOR resDesc, translatedResDesc; PCM_PARTIAL_RESOURCE_DESCRIPTOR newResDesc, newTranslatedResDesc; // // We need to remove the memory resource requirement // resList= irpStack->Parameters.StartDevice.AllocatedResources; resPartialList = &resList->List[0].PartialResourceList; translatedResList= irpStack->Parameters.StartDevice.AllocatedResourcesTranslated; translatedResPartialList = &translatedResList->List[0].PartialResourceList; newSize = sizeof(CM_RESOURCE_LIST) + (resPartialList->Count-2)*sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); newResList = ExAllocatePool(PagedPool, newSize); if (newResList == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } newTranslatedResList = ExAllocatePool(PagedPool, newSize); if (newTranslatedResList == NULL) { ExFreePool(newResList); return STATUS_INSUFFICIENT_RESOURCES; } newResList->Count = newTranslatedResList->Count = 1; newResList->List[0].InterfaceType = resList->List[0].InterfaceType; newTranslatedResList->List[0].InterfaceType = translatedResList->List[0].InterfaceType; newResList->List[0].BusNumber = resList->List[0].BusNumber; newTranslatedResList->List[0].BusNumber = translatedResList->List[0].BusNumber; newResList->List[0].PartialResourceList.Version = resPartialList->Version; newResList->List[0].PartialResourceList.Revision = resPartialList->Revision; newResList->List[0].PartialResourceList.Count = resPartialList->Count - 1; newTranslatedResList->List[0].PartialResourceList.Version = translatedResPartialList->Version; newTranslatedResList->List[0].PartialResourceList.Revision = translatedResPartialList->Revision; newTranslatedResList->List[0].PartialResourceList.Count = translatedResPartialList->Count - 1; resDesc = resPartialList->PartialDescriptors; translatedResDesc = translatedResPartialList->PartialDescriptors; newResDesc = newResList->List[0].PartialResourceList.PartialDescriptors; newTranslatedResDesc = newTranslatedResList->List[0].PartialResourceList.PartialDescriptors; if (CardBusExtension(deviceExtension)) { // // Remove last memory descriptor - which is what we added // RtlCopyMemory(newResDesc, resDesc, newResList->List[0].PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); RtlCopyMemory(newTranslatedResDesc, translatedResDesc, newTranslatedResList->List[0].PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); } else { // // Remove the only memory descriptor.. // for (index = 0; index < resPartialList->Count; index++, resDesc++, translatedResDesc++, newResDesc++, newTranslatedResDesc++) { if (resDesc->Type != CmResourceTypeMemory) { *newResDesc = *resDesc; *newTranslatedResDesc = *translatedResDesc; } } } irpStack->Parameters.StartDevice.AllocatedResources = newResList; irpStack->Parameters.StartDevice.AllocatedResourcesTranslated = newTranslatedResList; } // // Send this down to the PDO first // status = PcmciaIoCallDriverSynchronous(deviceExtension->LowerDevice, Irp); *PassedDown = TRUE ; // // We set this because the completion routine returns // STATUS_MORE_PROCESSING_REQUIRED, which means it needs to be completed // again. // *NeedsRecompletion = TRUE ; if (deviceExtension->Flags & PCMCIA_FILTER_ADDED_MEMORY) { ExFreePool(newResList); ExFreePool(newTranslatedResList); irpStack->Parameters.StartDevice.AllocatedResources = resList; irpStack->Parameters.StartDevice.AllocatedResourcesTranslated = translatedResList; } if (!NT_SUCCESS(status)) { return status; } // // Give the hardware some time to settle after returning from the pdo // PcmciaWait(256); // // Initialize the hardware // status = PcmciaStartPcmciaController(Fdo); if (NT_SUCCESS(status)) { deviceExtension->Flags |= PCMCIA_DEVICE_STARTED; } // // Remember if cardbus cards will be supported for this controller // if (CardBusExtension(deviceExtension) && !PcmciaAreCardBusCardsSupported(deviceExtension)) { deviceExtension->Flags |= PCMCIA_CARDBUS_NOT_SUPPORTED; } return status; } NTSTATUS PcmciaFdoStopDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp OPTIONAL, OUT BOOLEAN *PassedDown OPTIONAL, OUT BOOLEAN *NeedsRecompletion OPTIONAL ) /*++ Routine Description: IRP_MN_STOP_DEVICE handler for the given pcmcia controller. If Irp is present, it'll send it down first to the PDO. Unhooks the interrupt/cancels poll timer etc. Arguments: Fdo - Pointer to functional device object for the pcmcia controller Irp - If present it's the pointer to the stop Irp initiated by PnP PassedDown - Contains FALSE on entry, which means caller must complete or pass down irp based on status. If set to TRUE, Irp may need to be re-completed... NeedsRecompletion - ...In which case this parameter will be checked. Note: PassedDown and NeedsCompletion are ignored and optional only if Irp is NULL. Return value: STATUS_SUCCESS - Pcmcia controller successfully stopped Other - Stop failed --*/ { PFDO_EXTENSION deviceExtension = Fdo->DeviceExtension; PSOCKET socket; NTSTATUS status; if (!(deviceExtension->Flags & PCMCIA_DEVICE_STARTED)) { // // Already stopped // return STATUS_SUCCESS; } PcmciaFdoDisarmWake(deviceExtension); // // Disable the interrupt // if (deviceExtension->PcmciaInterruptObject) { for (socket = deviceExtension->SocketList; socket; socket = socket->NextSocket) { // // Disable the controller interrupts // (*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, FALSE); (*(socket->SocketFnPtr->PCBEnableDisableWakeupEvent))(socket, NULL, FALSE); // // Apparently IBM ThinkPads like this // PcmciaWait(PCMCIA_ENABLE_DELAY); } } // // the bus driver below us will make us go offline // deviceExtension->Flags |= PCMCIA_FDO_OFFLINE; // // clear pending event // KeCancelTimer(&deviceExtension->EventTimer); // // Send this down to the PDO // if (ARGUMENT_PRESENT(Irp)) { status = PcmciaIoCallDriverSynchronous(deviceExtension->LowerDevice, Irp); *PassedDown = TRUE ; *NeedsRecompletion = TRUE ; if (!NT_SUCCESS(status)) { return status; } } if (deviceExtension->Flags & PCMCIA_USE_POLLED_CSC) { // // cancel the card status change poller // KeCancelTimer(&deviceExtension->PollTimer); deviceExtension->Flags &= ~PCMCIA_USE_POLLED_CSC; } if (deviceExtension->PcmciaInterruptObject) { // // unhook the interrupt // IoDisconnectInterrupt(deviceExtension->PcmciaInterruptObject); deviceExtension->PcmciaInterruptObject = NULL; } // // Unmap any i/o space or memory we might have mapped // if (deviceExtension->Flags & PCMCIA_ATTRIBUTE_MEMORY_MAPPED) { MmUnmapIoSpace(deviceExtension->AttributeMemoryBase, deviceExtension->AttributeMemorySize); deviceExtension->Flags &= ~PCMCIA_ATTRIBUTE_MEMORY_MAPPED; deviceExtension->AttributeMemoryBase = 0; deviceExtension->AttributeMemorySize = 0; } if (deviceExtension->Flags & PCMCIA_SOCKET_REGISTER_BASE_MAPPED) { MmUnmapIoSpace(deviceExtension->CardBusSocketRegisterBase, deviceExtension->CardBusSocketRegisterSize); deviceExtension->Flags &= ~PCMCIA_SOCKET_REGISTER_BASE_MAPPED; deviceExtension->CardBusSocketRegisterBase = 0; deviceExtension->CardBusSocketRegisterSize = 0; } deviceExtension->Flags &= ~PCMCIA_DEVICE_STARTED; return STATUS_SUCCESS; } NTSTATUS PcmciaFdoRemoveDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++ Routine Description: Handles IRP_MN_REMOVE for the pcmcia controller. Stops the adapter if it isn't already, sends the IRP to the PDO first & cleans up the Fdo for this controller and detaches & deletes the device object. Arguments: Fdo - Pointer to functional device object for the controller to be removed Return value: Status --*/ { PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension; PDEVICE_OBJECT pdo, nextPdo, fdo, prevFdo; PPDO_EXTENSION pdoExtension; NTSTATUS status; UNREFERENCED_PARAMETER(Irp); if (fdoExtension->Flags & PCMCIA_DEVICE_STARTED) { // // Stop the fdo first. // PcmciaFdoStopDevice(Fdo, NULL, NULL, NULL); } // // Send this down to the PDO // status = PcmciaIoCallDriverSynchronous(fdoExtension->LowerDevice, Irp); if (!NT_SUCCESS(status)) { return status; } // // If the PdoList in the fdoExtension is non-empty it means: // that the PDOs in the list were not physically removed, but // a soft REMOVE was issued, hence they are still hanging on // and now this controller itself is being REMOVED. // Hence we dispose of those PDOs now // for (pdo = fdoExtension->PdoList; pdo != NULL ; pdo = nextPdo) { DebugPrint((PCMCIA_DEBUG_INFO, "RemoveDevice: pdo %x child of fdo %x was not removed before fdo\n", pdo, Fdo)); pdoExtension = pdo->DeviceExtension; ASSERT (!IsDevicePhysicallyRemoved(pdoExtension)); // // It's possible for this bit to be on, if the device was added, // but never started (because of some other error. //ASSERT (!IsDeviceAlive(pdoExtension)); nextPdo = pdoExtension->NextPdoInFdoChain; if (!IsDeviceDeleted(pdoExtension)) { MarkDeviceDeleted(pdoExtension); PcmciaCleanupPdo(pdo); IoDeleteDevice(pdo); } } MarkDeviceDeleted(fdoExtension); PcmciaCleanupFdo(fdoExtension); // // Remove this from the fdo list.. // prevFdo = NULL; for (fdo = FdoList; fdo != NULL; prevFdo = fdo, fdo = fdoExtension->NextFdo) { fdoExtension = fdo->DeviceExtension; if (fdo == Fdo) { if (prevFdo) { // // Delink this fdo // ((PFDO_EXTENSION)prevFdo->DeviceExtension)->NextFdo = fdoExtension->NextFdo; } else { FdoList = fdoExtension->NextFdo; } break; } } DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x Remove detach & delete\n", Fdo)); IoDetachDevice(((PFDO_EXTENSION)Fdo->DeviceExtension)->LowerDevice); IoDeleteDevice(Fdo); return STATUS_SUCCESS; } VOID PcmciaCleanupContext( IN PPCMCIA_CONTEXT pContext ) /*++ Routine Description Frees up allocated pool associated with a specific controller register context. Arguments pContext - pointer to a PCMCIA_CONTEXT structure Return value none --*/ { pContext->RangeCount = 0; if (pContext->Range) { ExFreePool(pContext->Range); pContext->Range = NULL; } } VOID PcmciaCleanupFdo( IN PFDO_EXTENSION FdoExtension ) /*++ Routine Description Frees up allocated pool, deletes symbolic links etc. for the associated FDO for the pcmcia controller which is to be removed. Arguments FdoExtension - Pointer to the device extension for the FDO of the pcmcia controller which is being removed Return value none --*/ { PSOCKET socket, nextSocket; // // Free the controller register context // PcmciaCleanupContext(&FdoExtension->PciContext); if (FdoExtension->PciContextBuffer) { ExFreePool(FdoExtension->PciContextBuffer); FdoExtension->PciContextBuffer = NULL; } PcmciaCleanupContext(&FdoExtension->CardbusContext); PcmciaCleanupContext(&FdoExtension->ExcaContext); // // Delete symbolic links to this fdo // if (FdoExtension->LinkName.Buffer != NULL) { IoDeleteSymbolicLink(&FdoExtension->LinkName); RtlFreeUnicodeString(&FdoExtension->LinkName); } // // Cleanup the socket structures // for (socket = FdoExtension->SocketList; socket != NULL; socket = nextSocket) { if (socket->CardbusContextBuffer) { ExFreePool(socket->CardbusContextBuffer); socket->CardbusContextBuffer = NULL; } if (socket->ExcaContextBuffer) { ExFreePool(socket->ExcaContextBuffer); socket->ExcaContextBuffer = NULL; } nextSocket = socket->NextSocket; ExFreePool(socket); } FdoExtension->SocketList = NULL; return; } NTSTATUS PcmciaFdoGetAssignedResources( IN PCM_RESOURCE_LIST ResourceList, IN PCM_RESOURCE_LIST TranslatedResourceList, IN PFDO_EXTENSION DeviceExtension ) /*++ Routine Description: Extracts the assigned resources to the pcmcia controller Arguments: ResourceList - Raw resource list TranslatedResourceList DeviceExtension - Device extension of the PCMCIA controller Return value: STATUS_SUCCESS STATUS_UNSUCCESSFUL if resources are not right or enough. --*/ { PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc; PCM_PARTIAL_RESOURCE_LIST partialResourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR partialResourceDesc; ULONG addressSpace; PHYSICAL_ADDRESS physicalAddress; PHYSICAL_ADDRESS translatedAddress; ULONG i; ULONG attributeIndex; PULONG devicePrivate; BOOLEAN translated; PAGED_CODE(); if ((ResourceList == NULL) || (ResourceList->Count <=0) ) { return STATUS_UNSUCCESSFUL; } if (CardBusExtension(DeviceExtension)) { fullResourceDesc=&TranslatedResourceList->List[0]; DeviceExtension->Configuration.InterfaceType = fullResourceDesc->InterfaceType; DeviceExtension->Configuration.BusNumber = fullResourceDesc->BusNumber; partialResourceList = &fullResourceDesc->PartialResourceList; partialResourceDesc = partialResourceList->PartialDescriptors; // // We need just the exca register base // According to PeterJ the first descriptor // for us is the cardbus socket register base. // We trust him. for (i=0; (i < partialResourceList->Count) && (partialResourceDesc->Type != CmResourceTypeMemory); i++, partialResourceDesc++); if (i >= partialResourceList->Count) { return STATUS_UNSUCCESSFUL; }; // // This is memory. We need to map it // DeviceExtension->CardBusSocketRegisterBase = MmMapIoSpace(partialResourceDesc->u.Memory.Start, partialResourceDesc->u.Memory.Length, FALSE); DeviceExtension->CardBusSocketRegisterSize = partialResourceDesc->u.Memory.Length; DeviceExtension->Flags |= PCMCIA_SOCKET_REGISTER_BASE_MAPPED; // // Last BAR is attribute memory window. This will be peeled off later. // It might be a good idea to tack a device private on at the end to // confirm this. However how do we guarantee our device private does // not contain the same data as somebody else's? PnP is lacking here - // we need some convention so that devices can set uniquely identifying stuff // in there - like maybe the device object they own - to identify it is // theirs. Till then this should do. // if (i > (partialResourceList->Count - 2)) { // // No more resources? Bail out. // return STATUS_INSUFFICIENT_RESOURCES; } for (i++, partialResourceDesc++; (i < (partialResourceList->Count - 1));i++,partialResourceDesc++); // // partialResourceDesc points to the last descriptor // ASSERT (partialResourceDesc->Type == CmResourceTypeMemory); DeviceExtension->PhysicalBase = partialResourceDesc->u.Memory.Start; // // This is memory. We need to map it // DeviceExtension->AttributeMemoryBase = MmMapIoSpace(partialResourceDesc->u.Memory.Start, partialResourceDesc->u.Memory.Length, FALSE); DeviceExtension->AttributeMemorySize = partialResourceDesc->u.Memory.Length; DeviceExtension->Flags |= PCMCIA_ATTRIBUTE_MEMORY_MAPPED; // // Finally see if an IRQ is assigned // for (i = 0, partialResourceDesc = partialResourceList->PartialDescriptors; (i < partialResourceList->Count) && (partialResourceDesc->Type != CmResourceTypeInterrupt); i++,partialResourceDesc++); if (i < partialResourceList->Count) { // // We have an interrupt to used for CSC // PCI will ensure that this interrupt is exactly the // same as the one assigned to the functional interrupt // for a cardbus pc-card in this controller's socket // DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaGetAssignedResources: Interrupt resource assigned\n")); DeviceExtension->Configuration.TranslatedInterrupt = *partialResourceDesc; // // Get the raw interrupt resource - needed to enable the interrupt on the controller // fullResourceDesc=&ResourceList->List[0]; partialResourceList = &fullResourceDesc->PartialResourceList; partialResourceDesc = partialResourceList->PartialDescriptors; for (i=0; (i< partialResourceList->Count) && (partialResourceDesc->Type != CmResourceTypeInterrupt); i++, partialResourceDesc++); if (i < partialResourceList->Count) { DeviceExtension->Configuration.Interrupt = *partialResourceDesc; } else { // // Should not happen.. translated descriptor was present, but raw is missing! // Just reset the translated interrupt and pretend no interrupt was assigned // RtlZeroMemory(&DeviceExtension->Configuration.TranslatedInterrupt, sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); } } } else { // // 16-bit pcmcia controller // fullResourceDesc=&ResourceList->List[0]; DeviceExtension->Configuration.InterfaceType = fullResourceDesc->InterfaceType; DeviceExtension->Configuration.BusNumber = fullResourceDesc->BusNumber; partialResourceList = &fullResourceDesc->PartialResourceList; partialResourceDesc = partialResourceList->PartialDescriptors; for (i=0; iCount; i++, partialResourceDesc++) { devicePrivate = partialResourceDesc->u.DevicePrivate.Data; switch (partialResourceDesc->Type) { case CmResourceTypeInterrupt: { if (DeviceExtension->ControllerType != PcmciaCLPD6729 && DeviceExtension->ControllerType != PcmciaPciPcmciaBridge && DeviceExtension->ControllerType != PcmciaNEC98 && DeviceExtension->ControllerType != PcmciaNEC98102) { // We always poll for Cirrus Logic PCI to PCMCIA controllers // and other PCI-PCMCIA bridges // DeviceExtension->Configuration.Interrupt = *partialResourceDesc; } break; } case CmResourceTypePort: { DeviceExtension->Configuration.UntranslatedPortAddress = (USHORT) partialResourceDesc->u.Port.Start.QuadPart; DeviceExtension->Configuration.PortSize = (USHORT) partialResourceDesc->u.Port.Length; break; } case CmResourceTypeMemory: { DeviceExtension->PhysicalBase = partialResourceDesc->u.Memory.Start; DeviceExtension->AttributeMemorySize = partialResourceDesc->u.Memory.Length; attributeIndex = i; break; } } } if ((DeviceExtension->PhysicalBase.QuadPart == 0) || (DeviceExtension->Configuration.UntranslatedPortAddress == 0)) { DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaGetAssignedResources: Need both memory and i/o for pcmcia controller 0x%X\n", DeviceExtension->DeviceObject)); return STATUS_INSUFFICIENT_RESOURCES; } fullResourceDesc=&TranslatedResourceList->List[0]; partialResourceList = &fullResourceDesc->PartialResourceList; partialResourceDesc = &partialResourceList->PartialDescriptors[attributeIndex]; switch (partialResourceDesc->Type) { case CmResourceTypeMemory: DeviceExtension->AttributeMemoryBase = MmMapIoSpace(partialResourceDesc->u.Memory.Start, DeviceExtension->AttributeMemorySize, FALSE); DeviceExtension->Flags |= PCMCIA_ATTRIBUTE_MEMORY_MAPPED; break; case CmResourceTypePort: DeviceExtension->AttributeMemoryBase = (PUCHAR)(partialResourceDesc->u.Port.Start.QuadPart); DeviceExtension->Flags &= ~PCMCIA_ATTRIBUTE_MEMORY_MAPPED; break; default: ASSERT(FALSE); return STATUS_INSUFFICIENT_RESOURCES; } } return STATUS_SUCCESS; } NTSTATUS PcmciaAreCardBusCardsSupported( IN PFDO_EXTENSION FdoExtension ) /*++ Routine Description: Indicates if cardbus cards will be supported on the given PCMCIA controller on this system Currently we support cardbus cards only on: Machines on which BIOS programmed the bus numbers & IntLine Arguments: FdoExtension - Pointer to device extension for the pcmcia controller Return Value: TRUE - if cardbus cards are supported FALSE - if not --*/ { UCHAR byte; PAGED_CODE(); // // Check int line // GetPciConfigSpace(FdoExtension, CFGSPACE_INT_LINE, &byte, 1); if (byte == 0xff) { return FALSE; } // // Check cardbus bus number // GetPciConfigSpace(FdoExtension, CFGSPACE_CARDBUS_BUSNUM, &byte, 1); if (byte == 0) { return FALSE; } // // Check subordinate bus number // GetPciConfigSpace(FdoExtension, CFGSPACE_SUB_BUSNUM, &byte, 1); if (byte == 0) { return FALSE; } // // All tests passed // return TRUE; }