You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1131 lines
33 KiB
1131 lines
33 KiB
/*
|
|
*************************************************************************
|
|
* 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);
|
|
|
|
InterlockedExchange(lock, 3);
|
|
KeSetEvent(event, 0, FALSE);
|
|
|
|
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));
|
|
}
|
|
|
|
|