windows-nt/Source/XPSP1/NT/drivers/wdm/usb/usbccgp/util.c
2020-09-26 16:20:57 +08:00

1132 lines
32 KiB
C

/*
*************************************************************************
* File: UTIL.C
*
* Module: USBCCGP.SYS
* USB Common Class Generic Parent driver.
*
* Copyright (c) 1998 Microsoft Corporation
*
*
* Author: ervinp
*
*************************************************************************
*/
#include <wdm.h>
#include <stdio.h>
#include <usb.h>
#include <usbdlib.h>
#include <usbioctl.h>
#include "usbccgp.h"
#include "security.h"
#include "debug.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, AppendInterfaceNumber)
#pragma alloc_text(PAGE, CopyDeviceRelations)
#pragma alloc_text(PAGE, GetFunctionInterfaceListBase)
#pragma alloc_text(PAGE, CallDriverSync)
#pragma alloc_text(PAGE, CallNextDriverSync)
#pragma alloc_text(PAGE, SetPdoRegistryParameter)
#pragma alloc_text(PAGE, GetPdoRegistryParameter)
#pragma alloc_text(PAGE, GetMsOsFeatureDescriptor)
#endif
#define USB_REQUEST_TIMEOUT 5000 // Timeout in ms (5 sec)
NTSTATUS CallNextDriverSync(PPARENT_FDO_EXT parentFdoExt, PIRP irp)
/*++
Routine Description:
Pass the IRP down to the next device object in the stack
synchronously, and bump the pendingActionCount around
the call to prevent the current device object from getting
removed before the IRP completes.
Arguments:
parentFdoExt - device extension of one of our device objects
irp - Io Request Packet
Return Value:
NT status code, indicates result returned by lower driver for this IRP.
--*/
{
NTSTATUS status;
PAGED_CODE();
IncrementPendingActionCount(parentFdoExt);
status = CallDriverSync(parentFdoExt->topDevObj, irp);
DecrementPendingActionCount(parentFdoExt);
return status;
}
VOID IncrementPendingActionCount(PPARENT_FDO_EXT parentFdoExt)
/*++
Routine Description:
Increment the pendingActionCount for a device object.
This keeps the device object from getting freed before
the action is completed.
Arguments:
devExt - device extension of device object
Return Value:
VOID
--*/
{
ASSERT(parentFdoExt->pendingActionCount >= 0);
InterlockedIncrement(&parentFdoExt->pendingActionCount);
}
VOID DecrementPendingActionCount(PPARENT_FDO_EXT parentFdoExt)
/*++
Routine Description:
Decrement the pendingActionCount for a device object.
This is called when an asynchronous action is completed
AND ALSO when we get the REMOVE_DEVICE IRP.
If the pendingActionCount goes to -1, that means that all
actions are completed and we've gotten the REMOVE_DEVICE IRP;
in this case, set the removeEvent event so we can finish
unloading.
Arguments:
devExt - device extension of device object
Return Value:
VOID
--*/
{
ASSERT(parentFdoExt->pendingActionCount >= 0);
InterlockedDecrement(&parentFdoExt->pendingActionCount);
if (parentFdoExt->pendingActionCount < 0){
/*
* All pending actions have completed and we've gotten
* the REMOVE_DEVICE IRP.
* Set the removeEvent so we'll stop waiting on REMOVE_DEVICE.
*/
ASSERT((parentFdoExt->state == STATE_REMOVING) ||
(parentFdoExt->state == STATE_REMOVED));
KeSetEvent(&parentFdoExt->removeEvent, 0, FALSE);
}
}
/*
********************************************************************************
* CallDriverSyncCompletion
********************************************************************************
*
*
*/
NTSTATUS CallDriverSyncCompletion(IN PDEVICE_OBJECT devObjOrNULL, IN PIRP irp, IN PVOID Context)
/*++
Routine Description:
Completion routine for CallDriverSync.
Arguments:
devObjOrNULL -
Usually, this is this driver's device object.
However, if this driver created the IRP,
there is no stack location in the IRP for this driver;
so the kernel has no place to store the device object;
** so devObj will be NULL in this case **.
irp - completed Io Request Packet
context - context passed to IoSetCompletionRoutine by CallDriverSync.
Return Value:
NT status code, indicates result returned by lower driver for this IRP.
--*/
{
PUSB_REQUEST_TIMEOUT_CONTEXT timeoutContext = Context;
PKEVENT event = timeoutContext->event;
PLONG lock = timeoutContext->lock;
ASSERT(irp->IoStatus.Status != STATUS_IO_TIMEOUT);
KeSetEvent(event, 0, FALSE);
InterlockedExchange(lock, 3);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS CallDriverSync(IN PDEVICE_OBJECT devObj, IN OUT PIRP irp)
/*++
Routine Description:
Call IoCallDriver to send the irp to the device object;
then, synchronize with the completion routine.
When CallDriverSync returns, the action has completed
and the irp again belongs to the current driver.
NOTE: In order to keep the device object from getting freed
while this IRP is pending, you should call
IncrementPendingActionCount() and
DecrementPendingActionCount()
around the CallDriverSync call.
Arguments:
devObj - targetted device object
irp - Io Request Packet
Return Value:
NT status code, indicates result returned by lower driver for this IRP.
--*/
{
PUSB_REQUEST_TIMEOUT_CONTEXT timeoutContext;
KEVENT event;
LONG lock;
LARGE_INTEGER dueTime;
PIO_STACK_LOCATION irpStack;
ULONG majorFunction;
ULONG minorFunction;
NTSTATUS status;
PAGED_CODE();
irpStack = IoGetNextIrpStackLocation(irp);
majorFunction = irpStack->MajorFunction;
minorFunction = irpStack->MinorFunction;
KeInitializeEvent(&event, NotificationEvent, FALSE);
lock = 0;
timeoutContext = ALLOCPOOL(NonPagedPool, sizeof(USB_REQUEST_TIMEOUT_CONTEXT));
if (timeoutContext) {
timeoutContext->event = &event;
timeoutContext->lock = &lock;
IoSetCompletionRoutine( irp,
CallDriverSyncCompletion, // context
timeoutContext,
TRUE, TRUE, TRUE);
status = IoCallDriver(devObj, irp);
if (status == STATUS_PENDING) {
dueTime.QuadPart = -10000 * USB_REQUEST_TIMEOUT;
status = KeWaitForSingleObject(
&event,
Executive, // wait reason
KernelMode,
FALSE, // not alertable
&dueTime);
if (status == STATUS_TIMEOUT) {
DBGWARN(("CallDriverSync timed out!\n"));
if (InterlockedExchange(&lock, 1) == 0) {
//
// We got it to the IRP before it was completed. We can cancel
// the IRP without fear of losing it, as the completion routine
// won't let go of the IRP until we say so.
//
IoCancelIrp(irp);
//
// Release the completion routine. If it already got there,
// then we need to complete it ourselves. Otherwise we got
// through IoCancelIrp before the IRP completed entirely.
//
if (InterlockedExchange(&lock, 2) == 3) {
//
// Mark it pending because we switched threads.
//
IoMarkIrpPending(irp);
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
}
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
// Return an error code because STATUS_TIMEOUT is a successful
// code.
irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
}
}
FREEPOOL(timeoutContext);
status = irp->IoStatus.Status;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS(status)){
DBGVERBOSE(("IRP 0x%02X/0x%02X failed in CallDriverSync w/ status %xh.",
majorFunction, minorFunction, status));
}
return status;
}
/*
********************************************************************************
* AppendInterfaceNumber
********************************************************************************
*
* oldIDs is a multi-String of hardware IDs.
* Return a new string with '&MI_xx' appended to each id,
* where 'xx' is the interface number of the first interface in that function.
*/
PWCHAR AppendInterfaceNumber(PWCHAR oldIDs, ULONG interfaceNum)
{
ULONG newIdLen;
PWCHAR id, newIDs;
WCHAR suffix[] = L"&MI_xx";
PAGED_CODE();
/*
* Calculate the length of the final multi-string.
*/
for (id = oldIDs, newIdLen = 0; *id; ){
ULONG thisIdLen = WStrLen(id);
newIdLen += thisIdLen + 1 + sizeof(suffix);
id += thisIdLen + 1;
}
/*
* Add one for the extra NULL at the end of the multi-string.
*/
newIdLen++;
newIDs = ALLOCPOOL(NonPagedPool, newIdLen*sizeof(WCHAR));
if (newIDs){
ULONG oldIdOff, newIdOff;
/*
* Copy each string in the multi-string, replacing the bus name.
*/
for (oldIdOff = newIdOff = 0; oldIDs[oldIdOff]; ){
ULONG thisIdLen = WStrLen(oldIDs+oldIdOff);
swprintf(suffix, L"&MI_%02x", interfaceNum);
/*
* Copy the new bus name to the new string.
*/
newIdOff += WStrCpy(newIDs+newIdOff, oldIDs+oldIdOff);
newIdOff += WStrCpy(newIDs+newIdOff, (PWSTR)suffix) + 1;
oldIdOff += thisIdLen + 1;
}
/*
* Add extra NULL to terminate multi-string.
*/
newIDs[newIdOff] = UNICODE_NULL;
}
return newIDs;
}
/*
********************************************************************************
* CopyDeviceRelations
********************************************************************************
*
*
*/
PDEVICE_RELATIONS CopyDeviceRelations(PDEVICE_RELATIONS deviceRelations)
{
PDEVICE_RELATIONS newDeviceRelations;
PAGED_CODE();
if (deviceRelations){
ULONG size = sizeof(DEVICE_RELATIONS) + (deviceRelations->Count*sizeof(PDEVICE_OBJECT));
newDeviceRelations = MemDup(deviceRelations, size);
}
else {
newDeviceRelations = NULL;
}
return newDeviceRelations;
}
PUSBD_INTERFACE_LIST_ENTRY GetFunctionInterfaceListBase(
PPARENT_FDO_EXT parentFdoExt,
ULONG functionIndex,
PULONG numFunctionInterfaces)
{
PUSBD_INTERFACE_LIST_ENTRY iface = NULL;
PUSB_CONFIGURATION_DESCRIPTOR configDesc;
ULONG i, func;
UCHAR ifaceClass;
ULONG audFuncBaseIndex = -1;
PAGED_CODE();
configDesc = parentFdoExt->selectedConfigDesc;
ASSERT(configDesc->bNumInterfaces);
for (func = 0, i = 0; i < (ULONG)configDesc->bNumInterfaces-1; i++){
ifaceClass = parentFdoExt->interfaceList[i].InterfaceDescriptor->bInterfaceClass;
if (ifaceClass == USB_DEVICE_CLASS_CONTENT_SECURITY){
/*
* We don't expose the CS interface(s).
*/
continue;
}
if (func == functionIndex){
break;
}
switch (ifaceClass){
case USB_DEVICE_CLASS_AUDIO:
/*
* For USB_DEVICE_CLASS_AUDIO, we return groups of interfaces
* with common class as functions.
*
* BUT, only while the interface subclass is different than the
* first one in this grouping. If the subclass is the same,
* then this is a different function.
* Note that it is conceivable that a device could be created
* where a second audio function starts with an interface with
* a different subclass than the previous audio interface, but
* this is how USBHUB's generic parent driver works and thus we
* are bug-compatible with the older driver.
*/
if (audFuncBaseIndex == -1){
audFuncBaseIndex = i;
}
if ((parentFdoExt->interfaceList[i+1].InterfaceDescriptor->bInterfaceClass !=
USB_DEVICE_CLASS_AUDIO) ||
(parentFdoExt->interfaceList[audFuncBaseIndex].InterfaceDescriptor->bInterfaceSubClass ==
parentFdoExt->interfaceList[i+1].InterfaceDescriptor->bInterfaceSubClass)) {
func++;
audFuncBaseIndex = -1; // Reset base index for next audio function.
}
break;
default:
audFuncBaseIndex = -1; // Reset base index for next audio function.
/*
* For other classes, each interface is a function.
* Count alternate interfaces as part of the same function.
*/
ASSERT(parentFdoExt->interfaceList[i+1].InterfaceDescriptor->bAlternateSetting == 0);
if (parentFdoExt->interfaceList[i+1].InterfaceDescriptor->bAlternateSetting == 0){
func++;
}
break;
}
}
// note: need this redundant check outside in case bNumInterfaces == 1
if (func == functionIndex){
iface = &parentFdoExt->interfaceList[i];
ifaceClass = iface->InterfaceDescriptor->bInterfaceClass;
*numFunctionInterfaces = 1;
if (ifaceClass == USB_DEVICE_CLASS_CONTENT_SECURITY){
/*
* The CS interface was the last interface on the device.
* Don't return it as a function.
*/
iface = NULL;
}
else if (ifaceClass == USB_DEVICE_CLASS_AUDIO){
for (i = i + 1; i < (ULONG)configDesc->bNumInterfaces; i++){
if ((parentFdoExt->interfaceList[i].InterfaceDescriptor->bInterfaceClass ==
iface->InterfaceDescriptor->bInterfaceClass) &&
(parentFdoExt->interfaceList[i].InterfaceDescriptor->bInterfaceSubClass !=
iface->InterfaceDescriptor->bInterfaceSubClass)){
(*numFunctionInterfaces)++;
}
else {
break;
}
}
}
}
else {
*numFunctionInterfaces = 0;
}
return iface;
}
/*
********************************************************************************
* GetStringDescriptor
********************************************************************************
*
*
*
*/
NTSTATUS GetStringDescriptor( PPARENT_FDO_EXT parentFdoExt,
UCHAR stringIndex,
LANGID langId,
PUSB_STRING_DESCRIPTOR stringDesc,
ULONG bufferLen)
{
NTSTATUS status;
URB urb;
UsbBuildGetDescriptorRequest(&urb,
(USHORT)sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_STRING_DESCRIPTOR_TYPE,
stringIndex,
langId,
stringDesc,
NULL,
bufferLen,
NULL);
status = SubmitUrb(parentFdoExt, &urb, TRUE, NULL, NULL);
return status;
}
/*
********************************************************************************
* SetPdoRegistryParameter
********************************************************************************
*
*
*
*/
NTSTATUS SetPdoRegistryParameter (
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PWCHAR KeyName,
IN PVOID Data,
IN ULONG DataLength,
IN ULONG KeyType,
IN ULONG DevInstKeyType
)
{
UNICODE_STRING keyNameUnicodeString;
HANDLE handle;
NTSTATUS ntStatus;
PAGED_CODE();
RtlInitUnicodeString(&keyNameUnicodeString, KeyName);
ntStatus = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
DevInstKeyType,
STANDARD_RIGHTS_ALL,
&handle);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwSetValueKey(handle,
&keyNameUnicodeString,
0,
KeyType,
Data,
DataLength);
ZwClose(handle);
}
DBGVERBOSE(("SetPdoRegistryParameter status 0x%x\n", ntStatus));
return ntStatus;
}
/*
********************************************************************************
* GetPdoRegistryParameter
********************************************************************************
*
*
*
*/
NTSTATUS GetPdoRegistryParameter (
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PWCHAR ValueName,
OUT PVOID Data,
IN ULONG DataLength,
OUT PULONG Type,
OUT PULONG ActualDataLength
)
/*++
Routine Description:
This routines queries the data for a registry value entry associated
with the device instance specific registry key for the PDO.
The registry value entry would be found under this registry key:
HKLM\System\CCS\Enum\<DeviceID>\<InstanceID>\Device Parameters
Arguments:
PhysicalDeviceObject - Yep, the PDO
ValueName - Name of the registry value entry for which the data is requested
Data - Buffer in which the requested data is returned
DataLength - Length of the data buffer
Type - (optional) The data type (e.g. REG_SZ, REG_DWORD) is returned here
ActualDataLength - (optional) The actual length of the data is returned here
If this is larger than DataLength then not all of the
value data has been returned.
Return Value:
--*/
{
HANDLE handle;
NTSTATUS ntStatus;
PAGED_CODE();
ntStatus = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_ALL,
&handle);
if (NT_SUCCESS(ntStatus))
{
PKEY_VALUE_PARTIAL_INFORMATION partialInfo;
UNICODE_STRING valueName;
ULONG length;
ULONG resultLength;
RtlInitUnicodeString(&valueName, ValueName);
// Size and allocate a KEY_VALUE_PARTIAL_INFORMATION structure,
// including room for the returned value data.
//
length = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
DataLength;
partialInfo = ALLOCPOOL(PagedPool, length);
if (partialInfo)
{
// Query the value data.
//
ntStatus = ZwQueryValueKey(handle,
&valueName,
KeyValuePartialInformation,
partialInfo,
length,
&resultLength);
// If we got any data that is good enough
//
if (ntStatus == STATUS_BUFFER_OVERFLOW)
{
ntStatus = STATUS_SUCCESS;
}
if (NT_SUCCESS(ntStatus))
{
// Only copy the smaller of the the requested data length or
// the actual data length.
//
RtlCopyMemory(Data,
partialInfo->Data,
DataLength < partialInfo->DataLength ?
DataLength :
partialInfo->DataLength);
// Return the value data type and actual length, if requested.
//
if (Type)
{
*Type = partialInfo->Type;
}
if (ActualDataLength)
{
*ActualDataLength = partialInfo->DataLength;
}
}
FREEPOOL(partialInfo);
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
ZwClose(handle);
}
return ntStatus;
}
/*
********************************************************************************
* GetMsOsFeatureDescriptor
********************************************************************************
*
*
*
*/
NTSTATUS GetMsOsFeatureDescriptor (
PPARENT_FDO_EXT ParentFdoExt,
UCHAR Recipient,
UCHAR InterfaceNumber,
USHORT Index,
PVOID DataBuffer,
ULONG DataBufferLength,
PULONG BytesReturned
)
{
struct _URB_OS_FEATURE_DESCRIPTOR_REQUEST *urb;
NTSTATUS ntStatus;
PAGED_CODE();
if (BytesReturned)
{
*BytesReturned = 0;
}
urb = ALLOCPOOL(NonPagedPool, sizeof(struct _URB_OS_FEATURE_DESCRIPTOR_REQUEST));
if (urb != NULL)
{
// Initialize the URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR request
//
RtlZeroMemory(urb, sizeof(struct _URB_OS_FEATURE_DESCRIPTOR_REQUEST));
urb->Hdr.Function = URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR;
urb->Hdr.Length = sizeof(struct _URB_OS_FEATURE_DESCRIPTOR_REQUEST);
urb->TransferBufferLength = DataBufferLength;
urb->TransferBuffer = DataBuffer;
urb->Recipient = Recipient;
urb->InterfaceNumber = InterfaceNumber;
urb->MS_FeatureDescriptorIndex = Index;
// Submit the URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR request
//
ntStatus = SubmitUrb(ParentFdoExt, (PURB)urb, TRUE, NULL, NULL);
if (NT_SUCCESS(ntStatus) &&
BytesReturned)
{
*BytesReturned = urb->TransferBufferLength;
}
FREEPOOL(urb);
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
return ntStatus;
}
/*
********************************************************************************
* GetMsExtendedConfigDescriptor
********************************************************************************
*
*
*
*/
NTSTATUS
GetMsExtendedConfigDescriptor (
IN PPARENT_FDO_EXT ParentFdoExt
)
/*++
Routine Description:
This routines queries a device for an Extended Configuration Descriptor.
Arguments:
ParentFdoExt - The device extension of the parent FDO
Return Value:
If successful, a pointer to the Extended Configuration Descriptor, which the
caller must free, else NULL.
--*/
{
MS_EXT_CONFIG_DESC_HEADER msExtConfigDescHeader;
PMS_EXT_CONFIG_DESC pMsExtConfigDesc;
ULONG bytesReturned;
NTSTATUS ntStatus;
PAGED_CODE();
ntStatus = STATUS_NOT_SUPPORTED;
pMsExtConfigDesc = NULL;
RtlZeroMemory(&msExtConfigDescHeader, sizeof(MS_EXT_CONFIG_DESC_HEADER));
// Request just the header of the MS Extended Configuration Descriptor
//
ntStatus = GetMsOsFeatureDescriptor(
ParentFdoExt,
0, // Recipient Device
0, // Interface
MS_EXT_CONFIG_DESCRIPTOR_INDEX,
&msExtConfigDescHeader,
sizeof(MS_EXT_CONFIG_DESC_HEADER),
&bytesReturned);
// Make sure the MS Extended Configuration Descriptor header looks ok
//
if (NT_SUCCESS(ntStatus) &&
bytesReturned == sizeof(MS_EXT_CONFIG_DESC_HEADER) &&
msExtConfigDescHeader.bcdVersion == MS_EXT_CONFIG_DESC_VER &&
msExtConfigDescHeader.wIndex == MS_EXT_CONFIG_DESCRIPTOR_INDEX &&
msExtConfigDescHeader.bCount > 0 &&
msExtConfigDescHeader.dwLength == sizeof(MS_EXT_CONFIG_DESC_HEADER) +
msExtConfigDescHeader.bCount * sizeof(MS_EXT_CONFIG_DESC_FUNCTION))
{
// Allocate a buffer large enough for the entire descriptor
//
pMsExtConfigDesc = ALLOCPOOL(NonPagedPool,
msExtConfigDescHeader.dwLength);
if (pMsExtConfigDesc)
{
RtlZeroMemory(pMsExtConfigDesc, msExtConfigDescHeader.dwLength);
// Request the entire MS Extended Configuration Descriptor
//
ntStatus = GetMsOsFeatureDescriptor(
ParentFdoExt,
0, // Recipient Device
0, // Interface
MS_EXT_CONFIG_DESCRIPTOR_INDEX,
pMsExtConfigDesc,
msExtConfigDescHeader.dwLength,
&bytesReturned);
if (!( NT_SUCCESS(ntStatus) &&
bytesReturned == msExtConfigDescHeader.dwLength &&
RtlCompareMemory(&msExtConfigDescHeader,
pMsExtConfigDesc,
sizeof(MS_EXT_CONFIG_DESC_HEADER)) ==
sizeof(MS_EXT_CONFIG_DESC_HEADER) &&
ValidateMsExtendedConfigDescriptor(
pMsExtConfigDesc,
ParentFdoExt->selectedConfigDesc) ))
{
// Something went wrong retrieving the MS Extended Configuration
// Descriptor, or it doesn't look valid. Free the buffer.
//
FREEPOOL(pMsExtConfigDesc);
pMsExtConfigDesc = NULL;
}
else
{
ntStatus = STATUS_SUCCESS;
}
}
else
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
ASSERT(!ISPTR(ParentFdoExt->msExtConfigDesc));
ParentFdoExt->msExtConfigDesc = pMsExtConfigDesc;
return ntStatus;
}
/*
********************************************************************************
* ValidateMsExtendedConfigDescriptor
********************************************************************************
*
*
*
*/
BOOLEAN
ValidateMsExtendedConfigDescriptor (
IN PMS_EXT_CONFIG_DESC MsExtConfigDesc,
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor
)
/*++
Routine Description:
This routines validates an Extended Configuration Descriptor.
Arguments:
MsExtConfigDesc - The Extended Configuration Descriptor to be validated.
It is assumed that the header of this descriptor has
already been validated.
ConfigurationDescriptor - Configuration Descriptor, assumed to already
validated.
Return Value:
TRUE if the Extended Configuration Descriptor appears to be valid,
else FALSE.
--*/
{
UCHAR interfacesRemaining;
ULONG i;
ULONG j;
UCHAR c;
BOOLEAN gotNull;
PAGED_CODE();
interfacesRemaining = ConfigurationDescriptor->bNumInterfaces;
for (i = 0; i < MsExtConfigDesc->Header.bCount; i++)
{
// Make sure that there is at least one interface in this function.
//
if (MsExtConfigDesc->Function[i].bInterfaceCount == 0)
{
return FALSE;
}
// Make sure that there are not too many interfaces in this function.
//
if (MsExtConfigDesc->Function[i].bInterfaceCount > interfacesRemaining)
{
return FALSE;
}
interfacesRemaining -= MsExtConfigDesc->Function[i].bInterfaceCount;
// Make sure the no interfaces were skipped between the interfaces
// of the previous function and the interfaces of this function.
//
if (i &&
MsExtConfigDesc->Function[i-1].bFirstInterfaceNumber +
MsExtConfigDesc->Function[i-1].bInterfaceCount !=
MsExtConfigDesc->Function[i].bFirstInterfaceNumber)
{
return FALSE;
}
// Make sure that the CompatibleID is valid.
// Valid characters are 'A' through 'Z', '0' through '9', and '_"
// and null padded to the the right end of the array, but not
// necessarily null terminated.
//
for (j = 0, gotNull = FALSE;
j < sizeof(MsExtConfigDesc->Function[i].CompatibleID);
j++)
{
c = MsExtConfigDesc->Function[i].CompatibleID[j];
if (c == 0)
{
gotNull = TRUE;
}
else
{
if (gotNull ||
!((c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
(c == '_')))
{
return FALSE;
}
}
}
// Make sure that the SubCompatibleID is valid.
// Valid characters are 'A' through 'Z', '0' through '9', and '_"
// and null padded to the the right end of the array, but not
// necessarily null terminated.
//
for (j = 0, gotNull = FALSE;
j < sizeof(MsExtConfigDesc->Function[i].SubCompatibleID);
j++)
{
c = MsExtConfigDesc->Function[i].SubCompatibleID[j];
if (c == 0)
{
gotNull = TRUE;
}
else
{
if (gotNull ||
!((c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
(c == '_')))
{
return FALSE;
}
}
}
// Make sure that if the SubCompatibleID is non-null then the
// CompatibleID is also non-null.
//
if (MsExtConfigDesc->Function[i].SubCompatibleID[0] != 0 &&
MsExtConfigDesc->Function[i].CompatibleID[0] == 0)
{
return FALSE;
}
}
// Make sure that all of the interfaces were consumed by functions.
//
if (interfacesRemaining > 0)
{
return FALSE;
}
return TRUE;
}
/*
********************************************************************************
* MemDup
********************************************************************************
*
* Return a fresh copy of the argument.
*
*/
PVOID MemDup(PVOID dataPtr, ULONG length)
{
PVOID newPtr;
newPtr = (PVOID)ALLOCPOOL(NonPagedPool, length);
if (newPtr){
RtlCopyMemory(newPtr, dataPtr, length);
}
else {
DBGWARN(("MemDup: Memory allocation (size %xh) failed -- not a bug if verifier pool failures enabled.", length));
}
return newPtr;
}
/*
********************************************************************************
* WStrLen
********************************************************************************
*
*/
ULONG WStrLen(PWCHAR str)
{
ULONG result = 0;
while (*str++ != UNICODE_NULL){
result++;
}
return result;
}
/*
********************************************************************************
* WStrCpy
********************************************************************************
*
*/
ULONG WStrCpy(PWCHAR dest, PWCHAR src)
{
ULONG result = 0;
while (*dest++ = *src++){
result++;
}
return result;
}
BOOLEAN WStrCompareN(PWCHAR str1, PWCHAR str2, ULONG maxChars)
{
while ((maxChars > 0) && *str1 && (*str1 == *str2)){
maxChars--;
str1++;
str2++;
}
return (BOOLEAN)((maxChars == 0) || (!*str1 && !*str2));
}