/*++ Copyright (c) 1995 Microsoft Corporation Module Name: busdata.c Abstract: This module contains code to query/set pnp bios slot data. Author: Shie-Lin Tzong (shielint) Apr-25-1995 Environment: Kernel mode only. Revision History: --*/ #include "busp.h" PUCHAR MbpFindNextPnpEndTag ( IN PUCHAR BusData, IN LONG Limit ); #pragma alloc_text(PAGE,MbpGetBusData) #pragma alloc_text(PAGE,MbpGetCompatibleDeviceId) #pragma alloc_text(PAGE,MbpGetSlotResources) #pragma alloc_text(PAGE,MbpGetSlotResourceRequirements) #pragma alloc_text(PAGE,MbpSetSlotResources) #pragma alloc_text(PAGE,MbpFindNextPnpEndTag) NTSTATUS MbpGetBusData ( ULONG BusNumber, PULONG SlotNumber, PVOID *BusData, PULONG Length, PBOOLEAN DockingSlot ) /*++ Routine Description: This function returns the pnp bios bus data to the caller. Caller is responsible to release the data buffer. No mater what the returned status is, this routine always returns a valid next slot number. Arguments: BusNumber - specifies the desired bus. SlotNumber - specifies a variable to indicate the slot whoes data is desired and to receive the next slot number (-1 if no more slot.) BusData - supplies a variable to receive the data buffer pointer. Length - supplies a variable to receive the length of the data buffer DockingSlot - supplies a variable to receive if the slot is a docking station slot. Return Value: NTSTATUS code --*/ { NTSTATUS status; PPNP_BIOS_DEVICE_NODE busData; PUCHAR p, source; ULONG size, bufferSize, nextSlot = 0; USHORT junk; PB_PARAMETERS biosParameters; PAGED_CODE(); // // If registry data is availble, we will get it from registry data. // Note, the registry data is available at init time only. // source = NULL; *DockingSlot = FALSE; if (PbBiosRegistryData) { // // First skip Pnp Bios installation check data // p = (PUCHAR)(PbBiosRegistryData + 1); size = ((PKEY_VALUE_FULL_INFORMATION)PbBiosKeyInformation)->DataLength; size -= sizeof(PNP_BIOS_INSTALLATION_CHECK) + sizeof(CM_PARTIAL_RESOURCE_LIST); while (size >= sizeof(PNP_BIOS_DEVICE_NODE)) { if (*SlotNumber == ((PPNP_BIOS_DEVICE_NODE)p)->Node) { // // Find the desired slot data, determine next slot and // do some more checks. // bufferSize = ((PPNP_BIOS_DEVICE_NODE)p)->Size; size -= bufferSize; if (size >= sizeof(PNP_BIOS_DEVICE_NODE)) { nextSlot = ((PPNP_BIOS_DEVICE_NODE)(p + bufferSize))->Node; } else { nextSlot = (ULONG) -1; } if (((PPNP_BIOS_DEVICE_NODE)p)->DeviceType[0] == BASE_TYPE_DOCKING_STATION) { if (BusNumber == MbpBusNumber[0]) { // // If this is a docking station slot and the target BusNumber is the first bus, // return the data and indicate this is a docking station slot. // *DockingSlot = TRUE; source = p; } break; } // // If this is a device in the deocking station, it can only belong to BusNumber 1 // If this is a device in the system board, it can only belong to BusNumber 0 // if (((PPNP_BIOS_DEVICE_NODE)p)->DeviceAttributes & DEVICE_DOCKING) { if (BusNumber == MbpBusNumber[1]) { source = p; } } else if (BusNumber == MbpBusNumber[0]) { source = p; } break; } size -= ((PPNP_BIOS_DEVICE_NODE)p)->Size; p = p + ((PPNP_BIOS_DEVICE_NODE)p)->Size; } if (!source) { if (*SlotNumber == 0) { p = (PUCHAR)(PbBiosRegistryData + 1); *SlotNumber = ((PPNP_BIOS_DEVICE_NODE)p)->Node; } else { *SlotNumber = nextSlot; } return STATUS_NO_SUCH_DEVICE; } } else { // // Registry data is not available. Call pnp bios or hardware to // get the max buffer size if necessary. // if (MbpMaxDeviceData == 0) { biosParameters.Function = PNP_BIOS_GET_NUMBER_DEVICE_NODES; biosParameters.u.GetNumberDeviceNodes.NumberNodes = &junk; biosParameters.u.GetNumberDeviceNodes.NodeSize = (PUSHORT)&MbpMaxDeviceData; status = PbHardwareService(&biosParameters); if (!NT_SUCCESS(status)) { DebugPrint((DEBUG_BREAK, "GetBusData: calling BIOS GET_NUMBER_NODES failed.\n")); *SlotNumber = 0; return STATUS_NO_SUCH_DEVICE; } else { bufferSize = MbpMaxDeviceData; } } } // // Allocate space to return the slot data // busData = (PPNP_BIOS_DEVICE_NODE) ExAllocatePoolWithTag ( PagedPool, bufferSize, 'bPnP'); if (busData == NULL) { // // Leave Slot unchanged, so it wil be the next slot number to try. This gives // us another chance to retry the operation. // return STATUS_INSUFFICIENT_RESOURCES; } // // If the data is available already copy it to data buffer and return. // if (source) { RtlMoveMemory(busData, source, bufferSize); *SlotNumber = nextSlot; *BusData = busData; *Length = busData->Size; } else { // // Else we need to resort to Pnp Bios runtime API or hardware ... // nextSlot = *SlotNumber; biosParameters.Function = PNP_BIOS_GET_DEVICE_NODE; biosParameters.u.GetDeviceNode.NodeBuffer = busData; biosParameters.u.GetDeviceNode.Node = (PUSHORT)&nextSlot; biosParameters.u.GetDeviceNode.Control = GET_CURRENT_CONFIGURATION; status = PbHardwareService(&biosParameters); if (!NT_SUCCESS(status)) { *SlotNumber = 0; ExFreePool(busData); return STATUS_NO_SUCH_DEVICE; } if (nextSlot == 0xFF) { nextSlot = (ULONG) -1; } // // Make sure the slot matches the bus number. // if (*SlotNumber != (ULONG)busData->Node) { // // This happens when *SlotNumber == 0. In this case, bios // gives us the first valid slot data. // *SlotNumber = (ULONG)busData->Node; return STATUS_NO_SUCH_DEVICE; } else { *SlotNumber = nextSlot; } if ((busData->DeviceType[0] == BASE_TYPE_DOCKING_STATION && BusNumber != MbpBusNumber[0]) || (busData->DeviceAttributes & DEVICE_DOCKING && BusNumber == MbpBusNumber[0]) || (!(busData->DeviceAttributes & DEVICE_DOCKING) && BusNumber == MbpBusNumber[1]) ) { ExFreePool(busData); return STATUS_NO_SUCH_DEVICE; } else { *BusData = busData; *Length = busData->Size; } } return STATUS_SUCCESS; } NTSTATUS MbpGetCompatibleDeviceId ( PVOID BusData, ULONG IdIndex, PWCHAR Buffer ) /*++ Routine Description: This function returns the desired pnp bios id for the specified Busdata and Id index. If Id index = 0, the Hardware ID will be return; if id index = n, the Nth compatible id will be returned. Arguments: BusData - supplies a pointer to the pnp bios slot/device data. IdIndex - supplies the index of the compatible id desired. Buffer - supplies a pointer to a buffer to the compatible Id. Caller must make sure the buffer is big enough. Return Value: NTSTATUS code --*/ { NTSTATUS status = STATUS_SUCCESS; PPNP_BIOS_DEVICE_NODE slotData; PUCHAR p, end; UCHAR tag; ULONG count = 0; LONG size; UNICODE_STRING unicodeString; ANSI_STRING ansiString; UCHAR eisaId[8]; ULONG id; PAGED_CODE(); slotData = (PPNP_BIOS_DEVICE_NODE)BusData; if (IdIndex == 0) { // // Caller is asking for hardware id // id = slotData->ProductId; } else { // // caller is asking for compatible id // IdIndex--; p = (PUCHAR)(slotData + 1); size = slotData->Size - sizeof(PNP_BIOS_DEVICE_NODE); end = p + size; // // Skip all the resource descriptors to find compatible Id descriptor // p = MbpFindNextPnpEndTag(p, size); // skip allocated resources if (!p) { return STATUS_NO_MORE_ENTRIES; } p += 2; p = MbpFindNextPnpEndTag(p, (LONG)(end - p)); // skip possible resources if (!p) { return STATUS_NO_MORE_ENTRIES; } p += 2; status = STATUS_NO_MORE_ENTRIES; while ((tag = *p) != TAG_COMPLETE_END) { ASSERT (tag == TAG_COMPLETE_COMPATIBLE_ID); if (count == IdIndex) { id = *(PULONG)(p + 1); status = STATUS_SUCCESS; break; } else { count++; } // // Advance to next compatible id. // p += (TAG_COMPLETE_COMPATIBLE_ID & SMALL_TAG_SIZE_MASK) + 1; } } if (NT_SUCCESS(status)) { PbDecompressEisaId(id, eisaId); RtlInitAnsiString(&ansiString, eisaId); RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, TRUE); swprintf(Buffer, L"BIOS\\*%s", unicodeString.Buffer); RtlFreeUnicodeString(&unicodeString); } return status; } NTSTATUS MbpGetSlotResources ( IN ULONG BusNumber, IN PVOID BusData, IN OUT PCM_RESOURCE_LIST *CmResources, OUT PULONG Length ) /*++ Routine Description: This function returns the desired pnp bios slot/device resource in CM format. Caller is responsible to free the resource buffer. Arguments: BusData - supplies a pointer to the pnp bios slot/device data. CmResources - supplies a variable to receive the returned resource list. Length - supplies a variable to receive the length of the data buffer Return Value: NTSTATUS code --*/ { PPNP_BIOS_DEVICE_NODE slotData; PUCHAR p; NTSTATUS status; PAGED_CODE(); slotData = (PPNP_BIOS_DEVICE_NODE)BusData; p = (PUCHAR)(slotData + 1); status = PbBiosResourcesToNtResources ( BusNumber, slotData->Node, &p, PB_CM_FORMAT, (PUCHAR *) CmResources, Length ); return status; } NTSTATUS MbpGetSlotResourceRequirements ( ULONG BusNumber, PVOID BusData, PIO_RESOURCE_REQUIREMENTS_LIST *IoResources, PULONG Length ) /*++ Routine Description: This function returns the desired pnp bios slot/device resource in IO format. Caller is responsible to free the resource buffer. Arguments: BusData - supplies a pointer to the pnp bios slot/device data. IoResources - supplies a variable to receive the returned resource requirements list. Length - supplies a variable to receive the length of the data buffer Return Value: NTSTATUS code --*/ { PPNP_BIOS_DEVICE_NODE slotData; PUCHAR p; LONG size; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); *IoResources = NULL; *Length = 0; slotData = (PPNP_BIOS_DEVICE_NODE)BusData; p = (PUCHAR)(slotData + 1); size = slotData->Size - sizeof(PNP_BIOS_DEVICE_NODE); // // Skip allocated resource descriptors // p = MbpFindNextPnpEndTag(p, size); // skip allocated resources if (p) { p += 2; status = PbBiosResourcesToNtResources ( BusNumber, slotData->Node, &p, PB_IO_FORMAT, (PUCHAR *)IoResources, Length ); } return status; } NTSTATUS MbpSetSlotResources ( PVOID *BusData, PCM_RESOURCE_LIST CmResources, ULONG Length ) /*++ Routine Description: This function sets the caller specified resource to pnp bios slot/device data. Arguments: BusData - supplies a pointer to the pnp bios slot/device data. CmResources - supplies a variable to receive the returned resource list. Length - supplies the length of the resource data Return Value: NTSTATUS code --*/ { NTSTATUS status; PPNP_BIOS_DEVICE_NODE slotData; PUCHAR p, pEnd, src, requirements; PUCHAR biosResources; ULONG biosResourceSize, totalSize; LONG size; PB_PARAMETERS biosParameters; PAGED_CODE(); slotData = (PPNP_BIOS_DEVICE_NODE)(*BusData); p = (PUCHAR)(slotData + 1); size = slotData->Size - sizeof(PNP_BIOS_DEVICE_NODE); pEnd = p + size; // // Skip allocated resource descriptors to find requirements list // p = MbpFindNextPnpEndTag(p, size); // skip allocated resources if (!p) { DebugPrint((DEBUG_BREAK, "SetResource:Could not find allocated resource END tag\n")); return STATUS_UNSUCCESSFUL; } p += 2; requirements = p; size = (ULONG)pEnd - (ULONG)p; status = PbCmResourcesToBiosResources ( CmResources, requirements, &biosResources, &biosResourceSize ); if (NT_SUCCESS(status)) { p = ExAllocatePoolWithTag( PagedPool, size + sizeof(PNP_BIOS_DEVICE_NODE) + biosResourceSize, 'bPnP'); if (!p) { status = STATUS_INSUFFICIENT_RESOURCES; } else { RtlMoveMemory(p, slotData, sizeof(PNP_BIOS_DEVICE_NODE)); slotData = (PPNP_BIOS_DEVICE_NODE)p; p += sizeof(PNP_BIOS_DEVICE_NODE); RtlMoveMemory(p, biosResources, biosResourceSize); p += biosResourceSize; RtlMoveMemory(p, requirements, size); totalSize = size + sizeof(PNP_BIOS_DEVICE_NODE) + biosResourceSize; slotData->Size = (USHORT)totalSize; // // call Pnp Bios to set the resources // biosParameters.Function = PNP_BIOS_SET_DEVICE_NODE; biosParameters.u.SetDeviceNode.Node = slotData->Node; biosParameters.u.SetDeviceNode.NodeBuffer = slotData; biosParameters.u.SetDeviceNode.Control = SET_CONFIGURATION_NOW; status = PbHardwareService (&biosParameters); if (NT_SUCCESS(status)) { // // Update slotdat pointer // ExFreePool(*BusData); *BusData = slotData; } else { ExFreePool(slotData); } } } return status; } NTSTATUS MbpGetDockInformation ( OUT PHAL_SYSTEM_DOCK_INFORMATION *DockInfo, PULONG Length ) /*++ Routine Description: This function returns the docking station Id and serial number. Caller must free the Buffer. Arguments: DockInfo - supplies a pointer to a variable to receive the dock information. Length - supplies a pointer to a variable to receive the length of the information. Return Value: NTSTATUS code --*/ { PB_DOCKING_STATION_INFORMATION biosDockInfo; PB_PARAMETERS biosParameters; NTSTATUS status; PUNICODE_STRING unicodeString; ANSI_STRING ansiString; UCHAR eisaId[8]; PHAL_SYSTEM_DOCK_INFORMATION dock; ULONG dockIdLength = 0, serialLength = 0; USHORT dockState; PAGED_CODE(); // // Invoke pnp bios to get docking station information // biosParameters.Function = PNP_BIOS_GET_DOCK_INFORMATION; biosParameters.u.GetDockInfo.DockingStationInfo = &biosDockInfo; biosParameters.u.GetDockInfo.DockState = &dockState; status = PbHardwareService(&biosParameters); if (!NT_SUCCESS(status)) { return STATUS_UNSUCCESSFUL; } // // Allocate memory to return dock information // dock = (PHAL_SYSTEM_DOCK_INFORMATION)ExAllocatePool ( PagedPool, sizeof(HAL_SYSTEM_DOCK_INFORMATION)); if (dock == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } if (dockState == SYSTEM_NOT_DOCKED) { // // System is not docked, simply return the dock state. // dock->DockState = SystemUndocked; } else { // // else system is docked, remember its dock id and serial number. // dock->DockState = SystemDocked; #if 1 MbpBusExtension[0]->DockingStationId = biosDockInfo.LocationId; MbpBusExtension[0]->DockingStationSerialNumber = biosDockInfo.SerialNumber; ExAcquireFastMutex (&MbpMutex); if (biosDockInfo.Capabilities && DOCKING_CAPABILITIES_MASK != DOCKING_CAPABILITIES_COLD_DOCKING) { MbpBusExtension[0]->DockingStationDevice->Flags |= DEVICE_FLAGS_EJECT_SUPPORTED; } ExReleaseFastMutex (&MbpMutex); #else if (biosDockInfo.LocationId != UNKNOWN_DOCKING_IDENTIFIER) { // // If docking station location Id is present... // unicodeString = &MbpBusExtension[0]->DockingStationId; PbDecompressEisaId(biosDockInfo.LocationId, eisaId); RtlInitAnsiString(&ansiString, eisaId); dockIdLength = sizeof(WCHAR) * (ansiString.Length + 1); unicodeString->Buffer = (PWCHAR)ExAllocatePool ( PagedPool, dockIdLength); if (unicodeString->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } unicodeString->MaximumLength = (USHORT)dockIdLength; RtlAnsiStringToUnicodeString(unicodeString, &ansiString, FALSE); } // if (biosDockInfo.SerialNumber != 0) { // // If docking station serial number is present ... // serialLength = sizeof(ULONG) * 2 * sizeof(WCHAR) + sizeof(WCHAR); unicodeString = &MbpBusExtension[0]->DockingStationSerialNo; unicodeString->Buffer = (PWCHAR)ExAllocatePool ( PagedPool, serialLength); if (unicodeString->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } unicodeString->MaximumLength = (USHORT)serialLength; unicodeString->Length = sizeof(ULONG) * 2 * sizeof(WCHAR); swprintf(unicodeString->Buffer, L"%8x", biosDockInfo.SerialNumber); // } #endif } dock->DeviceBusType = Internal; dock->DeviceBusNumber = MbpBusNumber[0]; dock->SlotNumber = DOCK_VIRTUAL_SLOT_NUMBER; *Length = sizeof(HAL_SYSTEM_DOCK_INFORMATION); *DockInfo = dock; return STATUS_SUCCESS; } NTSTATUS MbpReplyEjectEvent ( ULONG SlotNumber, BOOLEAN Eject ) /*++ Routine Description: This function sends message to pnp bios for event processing. Arguments: SlotNumber - specifies the slot whoes data is desired. Eject - indicates if EJECT operation should be performed. Return Value: NTSTATUS code --*/ { NTSTATUS status; PB_PARAMETERS biosParameters; USHORT message; PAGED_CODE(); // // Else we need to resort to Pnp Bios runtime API or hardware ... // biosParameters.Function = PNP_BIOS_SEND_MESSAGE; if (Eject) { biosParameters.u.SendMessage.Message = OK_TO_CHANGE_CONFIG; } else { biosParameters.u.SendMessage.Message = ABORT_CONFIG_CHANGE; } status = PbHardwareService(&biosParameters); return status; } PUCHAR MbpFindNextPnpEndTag ( IN PUCHAR BusData, IN LONG Limit ) /*++ Routine Description: This function searches the Pnp BIOS device data for the frist END_TAG encountered. Arguments: BusData - supplies a pointer to the pnp bios resource descriptor. Limit - maximum length of the search. Return Value: The address of the END_TAG location. Null if not found. --*/ { UCHAR tag; USHORT size; tag = *BusData; while (tag != TAG_COMPLETE_END && Limit > 0) { // // Determine the size of the BIOS resource descriptor // if (!(tag & LARGE_RESOURCE_TAG)) { size = (USHORT)(tag & SMALL_TAG_SIZE_MASK); size += 1; // length of small tag } else { size = *(BusData + 1); size += 3; // length of large tag } BusData += size; Limit -= size; tag = *BusData; } if (tag == TAG_COMPLETE_END) { return BusData; } else { return NULL; } } BOOLEAN MbpConfigAboutToChange ( VOID ) /*++ Routine Description: This function is invoked by low level component to signal a dock\undock event is about to happen. This function is called at DPC level. Arguments: SlotNumber - supplies the docking connector slot number. Return Value: TRUE - allow the change to happen; FALSE - ignore it for now. --*/ { PDEVICE_DATA deviceData; HAL_BUS_INFORMATION busInfo; PBUS_HANDLER busHandler; // // Get docking station slot data // deviceData = MbpFindDeviceData (MbpBusExtension[0], DOCK_VIRTUAL_SLOT_NUMBER); if (deviceData) { // // if docking station slot is present // ASSERT (deviceData->Flags & DEVICE_FLAGS_DOCKING_STATION); // // If it is presently docked, the change is about-to-undock. // We need to notify Eject callback. // DebugPrint((DEBUG_MESSAGE, "pnpbios: About to UNDOCK...\n")); busHandler = MbpBusExtension[0]->BusHandler; busInfo.BusType = busHandler->InterfaceType; busInfo.ConfigurationType = busHandler->ConfigurationType; busInfo.BusNumber = busHandler->BusNumber; busInfo.Reserved = 0; ExNotifyCallback ( MbpEjectCallbackObject, &busInfo, (PVOID)busHandler->BusNumber ); } else { DebugPrint((DEBUG_MESSAGE, "pnpbios: About to DOCK...\n")); // // Otherwise, it is about-to-dock. Simply let it happen. // return TRUE; } return FALSE; } VOID MbpConfigChanged ( VOID ) /*++ Routine Description: This function is invoked by low level component to signal a dock-changed, a system-device-changed, or a config-change_failed event. We simply notify bus check callbacks. This function is called at DPC level. Arguments: None. Return Value: None. --*/ { PBUS_HANDLER busHandler; ULONG i; // // Notify buscheck for ALL the supported buses and queue check bus requests. // for (i = 0; i <= 1; i++) { busHandler = MbpBusExtension[i]->BusHandler; if (busHandler) { MbpQueueCheckBus(busHandler); } } }