/*++ Copyright (C) 2002 Microsoft Corporation Module Name: mpiosup.c Abstract: This module contains routines that port drivers can use for support of the MPIO package. Author: Environment: kernel mode only Notes: Revision History: --*/ #include "precomp.h" BOOLEAN PortpFindMPIOSupportedDevice( IN PUNICODE_STRING DeviceName, IN PUNICODE_STRING SupportedDevices ); BOOLEAN PortpMPIOLoaded( VOID ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PortGetMPIODeviceList) #pragma alloc_text(PAGE, PortpFindMPIOSupportedDevice) #pragma alloc_text(PAGE, PortpMPIOLoaded) #pragma alloc_text(PAGE, PortIsDeviceMPIOSupported) #endif NTSTATUS PortGetMPIODeviceList( IN PUNICODE_STRING RegistryPath, OUT PUNICODE_STRING MPIODeviceList ) /*++ Routine Description: This routine builds and returns the MPIO SupportedDeviceList by querying the value MPIOSupportedDeviceList under the key 'RegistryPath' (which should be HKLM\System\CurrentControlSet\Control\MPDEV). Arguments: RegistryPath - The Absolute registry path under which MPIOSupportDeviceList lives. Return Value: The MULTI_SZ SupportedDeviceList or NULL. --*/ { RTL_QUERY_REGISTRY_TABLE queryTable[2]; WCHAR defaultIDs[] = { L"\0" }; NTSTATUS status; PAGED_CODE(); // // Zero the table entries. // RtlZeroMemory(queryTable, sizeof(queryTable)); // // The query table has two entries. One for the supporteddeviceList and // the second which is the 'NULL' terminator. // // Indicate that there is NO call-back routine, and to give back the MULTI_SZ as // one blob, as opposed to individual unicode strings. // queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND; // // The value to query. // queryTable[0].Name = L"MPIOSupportedDeviceList"; // // Where to put the strings, the type of the key, default values and length. // queryTable[0].EntryContext = MPIODeviceList; queryTable[0].DefaultType = REG_MULTI_SZ; queryTable[0].DefaultData = defaultIDs; queryTable[0].DefaultLength = sizeof(defaultIDs); // // Try to get the device list. // status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegistryPath->Buffer, queryTable, NULL, NULL); return status; } BOOLEAN PortpFindMPIOSupportedDevice( IN PUNICODE_STRING DeviceName, IN PUNICODE_STRING SupportedDevices ) /*++ Routine Description: This internal routine compares the two unicode strings for a match. Arguments: DeviceName - String built from the current device's inquiry data. SupportedDevices - MULTI_SZ of devices that are supported. Return Value: TRUE - If VendorId/ProductId is found. --*/ { PWSTR devices = SupportedDevices->Buffer; UNICODE_STRING unicodeString; LONG compare; PAGED_CODE(); // // 'devices' is the current buffer in the MULTI_SZ built from // the registry. // while (devices[0]) { // // Make the current entry into a unicode string. // RtlInitUnicodeString(&unicodeString, devices); // // Compare this one with the current device. // compare = RtlCompareUnicodeString(&unicodeString, DeviceName, TRUE); if (compare == 0) { return TRUE; } // // Advance to next entry in the MULTI_SZ. // devices += (unicodeString.MaximumLength / sizeof(WCHAR)); } return FALSE; } BOOLEAN PortpMPIOLoaded( VOID ) /*++ Routine Description: This internal routine is used to determine whether an mpio package is installed by trying to open the MPIO SymLink. NOTE: Perhaps a more exhaustive method can be used in the future. Arguments: NONE Return Value: TRUE - If MPIO is present. --*/ { UNICODE_STRING unicodeName; PDEVICE_OBJECT controlDeviceObject; PFILE_OBJECT fileObject; NTSTATUS status; PAGED_CODE(); // // Build the symlink name. // RtlInitUnicodeString(&unicodeName, L"\\DosDevices\\MPIOControl"); // // Get mpio's deviceObject. // status = IoGetDeviceObjectPointer(&unicodeName, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, &fileObject, &controlDeviceObject); if (NT_SUCCESS(status)) { ObDereferenceObject(fileObject); } return ((status == STATUS_SUCCESS) ? TRUE : FALSE); } BOOLEAN PortIsDeviceMPIOSupported( IN PUNICODE_STRING DeviceList, IN PUCHAR VendorId, IN PUCHAR ProductId ) /*++ Routine Description: This routine determines whether the device is supported by traversing the SupportedDevice list and comparing to the VendorId/ProductId values passed in. Arguments: DeviceList - MULTI_SZ retrieved from the registry by PortGetDeviceList VendorId - Pointer to the inquiry data VendorId. ProductId - Pointer to the inquiry data ProductId. Return Value: TRUE - If VendorId/ProductId is found. --*/ { UNICODE_STRING deviceName; UNICODE_STRING productName; ANSI_STRING ansiVendor; ANSI_STRING ansiProduct; NTSTATUS status; BOOLEAN supported = FALSE; PAGED_CODE(); // // The SupportedDevice list was built in DriverEntry from the services key. // if (DeviceList->MaximumLength == 0) { // // List is empty. // return FALSE; } // // If MPIO isn't loaded, don't claim support for the device. // if (!PortpMPIOLoaded()) { return FALSE; } // // Convert the inquiry fields into ansi strings. // RtlInitAnsiString(&ansiVendor, VendorId); RtlInitAnsiString(&ansiProduct, ProductId); // // Allocate the deviceName buffer. Needs to be 8+16 plus NULL. // (productId length + vendorId length + NULL). // Add another 4 bytes for revision plus one pad, if anyone happens to jam that in. // deviceName.MaximumLength = 30 * sizeof(WCHAR); deviceName.Buffer = ExAllocatePool(PagedPool, deviceName.MaximumLength); // // Convert the vendorId to unicode. // RtlAnsiStringToUnicodeString(&deviceName, &ansiVendor, FALSE); // // Convert the productId to unicode. // RtlAnsiStringToUnicodeString(&productName, &ansiProduct, TRUE); // // 'cat' them. // status = RtlAppendUnicodeStringToString(&deviceName, &productName); if (status == STATUS_SUCCESS) { // // Run the list of supported devices that was captured from the registry // and see if this one is in the list. // supported = PortpFindMPIOSupportedDevice(&deviceName, DeviceList); } return supported; }