mirror of https://github.com/tongzx/nt5src
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.
633 lines
18 KiB
633 lines
18 KiB
/*
|
|
*************************************************************************
|
|
* File: I1394.C
|
|
*
|
|
* Module: HID1394.SYS
|
|
* HID (Human Input Device) minidriver for IEEE 1394 devices.
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Author: ervinp
|
|
*
|
|
*************************************************************************
|
|
*/
|
|
|
|
#include <wdm.h>
|
|
#include <hidport.h>
|
|
#include <1394.h>
|
|
|
|
#include "hid1394.h"
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
ULONG resetGeneration = 0;
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_SubmitIRB
|
|
********************************************************************************
|
|
*
|
|
*
|
|
* Submit an IRB (IO Request Block) to the IEEE 1394 bus
|
|
* by sending the bus an IRP with IoControlCode==IOCTL_1394_CLASS.
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_SubmitIRB(PDEVICE_OBJECT devObj, PIRB irb)
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION devExt;
|
|
PIRP irp;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_1394_CLASS,
|
|
GET_NEXT_DEVICE_OBJECT(devObj),
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE, /* INTERNAL */
|
|
&event,
|
|
&ioStatus);
|
|
|
|
if (irp){
|
|
PIO_STACK_LOCATION nextSp;
|
|
|
|
nextSp = IoGetNextIrpStackLocation(irp);
|
|
nextSp->Parameters.Others.Argument1 = irb;
|
|
|
|
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(devObj), irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
NTSTATUS waitStatus;
|
|
|
|
/*
|
|
* Specify a timeout of 5 seconds for this call to complete.
|
|
* Negative timeout indicates time relative to now (in 100ns units).
|
|
*
|
|
* BUGBUG - timeout happens rarely for HumGetReportDescriptor
|
|
* when you plug in and out repeatedly very fast.
|
|
* Figure out why this call never completes.
|
|
*/
|
|
static LARGE_INTEGER timeout = {(ULONG) -50000000, 0xFFFFFFFF };
|
|
|
|
waitStatus = KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, &timeout);
|
|
if (waitStatus == STATUS_TIMEOUT){
|
|
/*
|
|
* Note - Return STATUS_IO_TIMEOUT, not STATUS_TIMEOUT.
|
|
* STATUS_IO_TIMEOUT is an NT error status, STATUS_TIMEOUT is not.
|
|
*/
|
|
ioStatus.Status = STATUS_IO_TIMEOUT;
|
|
|
|
// BUGBUG - test timeout with faulty nack-ing device from glens
|
|
// BUGBUG - also need to cancel read irps at HIDCLASS level
|
|
|
|
//
|
|
// Cancel the Irp we just sent.
|
|
//
|
|
IoCancelIrp(irp);
|
|
|
|
//
|
|
// Now wait for the Irp to be cancelled/completed below
|
|
//
|
|
waitStatus = KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
}
|
|
else {
|
|
status = STATUS_DATA_ERROR;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS BuildIRB_GetAddrFromDevObj(PIRB irb)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_GET_ADDR_FROM_DEVICE_OBJECT;
|
|
irb->Flags = 0;
|
|
irb->u.Get1394AddressFromDeviceObject.fulFlags = 0; // BUGBUG ?
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS BuildIRB_BusReset(PIRB irb)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_BUS_RESET;
|
|
irb->Flags = 0;
|
|
irb->u.BusReset.fulFlags = 0;
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS BuildIRB_AsyncRead( PIRB irb,
|
|
PIO_ADDRESS addr,
|
|
PMDL bufferMdl,
|
|
ULONG bufLen,
|
|
ULONG resetGeneration)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_ASYNC_READ;
|
|
irb->Flags = 0;
|
|
irb->u.AsyncRead.DestinationAddress = *addr;
|
|
irb->u.AsyncRead.nNumberOfBytesToRead = bufLen;
|
|
irb->u.AsyncRead.nBlockSize = 0;
|
|
irb->u.AsyncRead.fulFlags = 0;
|
|
irb->u.AsyncRead.Mdl = bufferMdl;
|
|
irb->u.AsyncRead.ulGeneration = resetGeneration;
|
|
// BUGBUG FINISH irb->u.AsyncRead.chPriority = ;
|
|
// irb->u.AsyncRead.nSpeed = ;
|
|
// irb->u.AsyncRead.tCode = ;
|
|
irb->u.AsyncRead.Reserved = 0;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS BuildIRB_AsyncWrite( PIRB irb,
|
|
PIO_ADDRESS addr,
|
|
PMDL bufferMdl,
|
|
ULONG bufLen,
|
|
ULONG resetGeneration)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_ASYNC_WRITE;
|
|
irb->Flags = 0;
|
|
irb->u.AsyncRead.DestinationAddress = *addr;
|
|
irb->u.AsyncRead.nNumberOfBytesToRead = bufLen;
|
|
irb->u.AsyncRead.nBlockSize = 0;
|
|
irb->u.AsyncRead.fulFlags = 0;
|
|
irb->u.AsyncRead.Mdl = bufferMdl;
|
|
irb->u.AsyncRead.ulGeneration = resetGeneration;
|
|
// BUGBUG FINISH irb->u.AsyncRead.chPriority = ;
|
|
// irb->u.AsyncRead.nSpeed = ;
|
|
// irb->u.AsyncRead.tCode = ;
|
|
irb->u.AsyncRead.Reserved = 0;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS BuildIRB_IsochAllocateChannel(PIRB irb, ULONG requestedChannel)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_CHANNEL;
|
|
irb->Flags = 0;
|
|
irb->u.IsochAllocateChannel.nRequestedChannel = requestedChannel;
|
|
|
|
// BUGBUG is there a reserved HID channel ?
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS BuildIRB_IsochFreeChannel(PIRB irb, ULONG channel)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_ISOCH_FREE_CHANNEL;
|
|
irb->Flags = 0;
|
|
irb->u.IsochFreeChannel.nChannel = channel;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS BuildIRB_GetLocalHostInfo(PIRB irb, ULONG level, PVOID infoPtr)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_GET_LOCAL_HOST_INFO;
|
|
irb->Flags = 0;
|
|
irb->u.GetLocalHostInformation.nLevel = level;
|
|
irb->u.GetLocalHostInformation.Information = infoPtr;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS BuildIRB_GetNodeAddress(PIRB irb)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_GET_ADDR_FROM_DEVICE_OBJECT;
|
|
irb->Flags = 0;
|
|
irb->u.Get1394AddressFromDeviceObject.fulFlags = 0;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS BuildIRB_Control(PIRB irb,
|
|
ULONG controlCode,
|
|
PMDL inBuffer,
|
|
ULONG inBufferLen,
|
|
PMDL outBuffer,
|
|
ULONG outBufferLen)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
irb->FunctionNumber = REQUEST_GET_ADDR_FROM_DEVICE_OBJECT;
|
|
irb->Flags = 0;
|
|
irb->u.Control.ulIoControlCode = controlCode;
|
|
irb->u.Control.pInBuffer = inBuffer;
|
|
irb->u.Control.ulInBufferLength = inBufferLen;
|
|
irb->u.Control.pOutBuffer = outBuffer;
|
|
irb->u.Control.ulOutBufferLength = outBufferLen;
|
|
irb->u.Control.BytesReturned = 0;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_ReadCompletion
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_ReadCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS result = STATUS_SUCCESS;
|
|
PIRB irb;
|
|
ULONG bytesRead;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
|
|
|
|
//
|
|
// We passed a pointer to the IRB as our context, get it now.
|
|
//
|
|
irb = (PIRB)context;
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
|
|
/*
|
|
* Free the IRB we allocated in HIDT_ReadReport.
|
|
*/
|
|
ExFreePool(irb);
|
|
|
|
/*
|
|
* If the lower driver returned PENDING, mark our stack location as
|
|
* pending also. This prevents the IRP's thread from being freed if
|
|
* the client's call returns pending.
|
|
*/
|
|
if (irp->PendingReturned){
|
|
IoMarkIrpPending(irp);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* HIDT_ReadReport
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_ReadReport(PDEVICE_OBJECT devObj, PIRP irp, OUT BOOLEAN *needsCompletion)
|
|
{
|
|
PIRB irb;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(irp->UserBuffer);
|
|
|
|
irb = ExAllocatePoolWithTag( NonPagedPool, sizeof(IRB), HID1394_TAG);
|
|
if (irb){
|
|
BOOLEAN sentIrb = FALSE;
|
|
PIO_STACK_LOCATION irpSp, nextSp;
|
|
ULONG bufLen;
|
|
PMDL bufferMdl = NULL; // BUGBUG
|
|
IO_ADDRESS addr; // BUGBUG
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
nextSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
bufLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
ASSERT(bufLen);
|
|
|
|
|
|
// BUGBUG init bufferMdl, addr
|
|
if (BuildIRB_AsyncRead(irb, &addr, bufferMdl, bufLen, resetGeneration)){
|
|
|
|
nextSp->Parameters.Others.Argument1 = irb;
|
|
nextSp->MajorFunction = irpSp->MajorFunction;
|
|
// BUGBUG ? nextSp->Parameters.DeviceIoControl.IoControlCode = xxx;
|
|
nextSp->DeviceObject = GET_NEXT_DEVICE_OBJECT(devObj); // BUGBUG ?
|
|
|
|
IoSetCompletionRoutine( irp,
|
|
HIDT_ReadCompletion,
|
|
irb, // context
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(devObj), irp);
|
|
|
|
*needsCompletion = FALSE;
|
|
sentIrb = TRUE;
|
|
}
|
|
else {
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
}
|
|
|
|
if (!sentIrb){
|
|
ExFreePool(irb);
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_ReadCompletion
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_WriteCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS result = STATUS_SUCCESS;
|
|
PIRB irb;
|
|
ULONG bytesRead;
|
|
PDEVICE_EXTENSION devExt;
|
|
|
|
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
|
|
|
|
//
|
|
// We passed a pointer to the IRB as our context, get it now.
|
|
//
|
|
irb = (PIRB)context;
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
|
|
/*
|
|
* Free the IRB we allocated in HIDT_ReadReport.
|
|
*/
|
|
ExFreePool(irb);
|
|
|
|
/*
|
|
* If the lower driver returned PENDING, mark our stack location as
|
|
* pending also. This prevents the IRP's thread from being freed if
|
|
* the client's call returns pending.
|
|
*/
|
|
if (irp->PendingReturned){
|
|
IoMarkIrpPending(irp);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* HIDT_ReadReport
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_WriteReport(PDEVICE_OBJECT devObj, PIRP irp, OUT BOOLEAN *needsCompletion)
|
|
{
|
|
PIRB irb;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(irp->UserBuffer);
|
|
|
|
irb = ExAllocatePoolWithTag( NonPagedPool, sizeof(IRB), HID1394_TAG);
|
|
if (irb){
|
|
BOOLEAN sentIrb = FALSE;
|
|
PIO_STACK_LOCATION irpSp, nextSp;
|
|
ULONG bufLen;
|
|
PMDL bufferMdl = NULL; // BUGBUG
|
|
IO_ADDRESS addr; // BUGBUG
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(irp);
|
|
nextSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
bufLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
ASSERT(bufLen);
|
|
|
|
|
|
// BUGBUG init bufferMdl, addr
|
|
if (BuildIRB_AsyncWrite(irb, &addr, bufferMdl, bufLen, resetGeneration)){
|
|
|
|
nextSp->Parameters.Others.Argument1 = irb;
|
|
nextSp->MajorFunction = irpSp->MajorFunction;
|
|
// BUGBUG ? nextSp->Parameters.DeviceIoControl.IoControlCode = xxx;
|
|
nextSp->DeviceObject = GET_NEXT_DEVICE_OBJECT(devObj); // BUGBUG ?
|
|
|
|
IoSetCompletionRoutine( irp,
|
|
HIDT_WriteCompletion,
|
|
irb, // context
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(devObj), irp);
|
|
|
|
*needsCompletion = FALSE;
|
|
sentIrb = TRUE;
|
|
}
|
|
else {
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
}
|
|
|
|
if (!sentIrb){
|
|
ExFreePool(irb);
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
#if 0
|
|
// BUGBUG: george says using host config rom is wrong;
|
|
// use GetConfigurationInfo
|
|
/*
|
|
* GetConfigROM
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS GetConfigROM(PDEVICE_OBJECT devObj)
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
IRB irb;
|
|
GET_LOCAL_HOST_INFO5 configRomInfo;
|
|
NTSTATUS status;
|
|
|
|
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
|
|
|
|
BuildIRB_GetLocalHostInfo(&irb, GET_HOST_CONFIG_ROM, &configRomInfo);
|
|
|
|
/*
|
|
* Make one call just to get the required length.
|
|
*/
|
|
configRomInfo.ConfigRom = NULL;
|
|
configRomInfo.ConfigRomLength = 0;
|
|
status = HIDT_SubmitIRB(devObj, &irb);
|
|
if (NT_SUCCESS(status)){
|
|
if (configRomInfo.ConfigRomLength > 0){
|
|
configRomInfo.ConfigRom = ExAllocatePoolWithTag(NonPagedPool, configRomInfo.ConfigRomLength, HID1394_TAG);
|
|
if (configRomInfo.ConfigRom){
|
|
status = HIDT_SubmitIRB(devObj, &irb);
|
|
if (NT_SUCCESS(status)){
|
|
devExt->configROM = configRomInfo.ConfigRom;
|
|
devExt->configROMlength = configRomInfo.ConfigRomLength;
|
|
}
|
|
else {
|
|
ExFreePool(configRomInfo.ConfigRom);
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(configRomInfo.ConfigRomLength > 0);
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
}
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
|
|
NTSTATUS GetConfigInfo(PDEVICE_OBJECT devObj)
|
|
{
|
|
PDEVICE_EXTENSION devExt;
|
|
IRB irb;
|
|
NTSTATUS status;
|
|
|
|
devExt = GET_MINIDRIVER_DEVICE_EXTENSION(devObj);
|
|
|
|
irb.FunctionNumber = REQUEST_GET_CONFIGURATION_INFO;
|
|
irb.Flags = 0;
|
|
|
|
/*
|
|
* Make one call just to get the required buffer lengths.
|
|
*/
|
|
irb.u.GetConfigurationInformation.UnitDirectory = NULL;
|
|
irb.u.GetConfigurationInformation.UnitDirectoryBufferSize = 0;
|
|
irb.u.GetConfigurationInformation.UnitDependentDirectory = NULL;
|
|
irb.u.GetConfigurationInformation.UnitDependentDirectoryBufferSize = 0;
|
|
irb.u.GetConfigurationInformation.VendorLeaf = NULL;
|
|
irb.u.GetConfigurationInformation.VendorLeafBufferSize = 0;
|
|
irb.u.GetConfigurationInformation.ModelLeaf = NULL;
|
|
irb.u.GetConfigurationInformation.ModelLeafBufferSize = 0;
|
|
|
|
status = HIDT_SubmitIRB(devObj, &irb);
|
|
if (NT_SUCCESS(status)){
|
|
if (irb.u.GetConfigurationInformation.UnitDirectoryBufferSize &&
|
|
irb.u.GetConfigurationInformation.UnitDependentDirectoryBufferSize &&
|
|
irb.u.GetConfigurationInformation.VendorLeafBufferSize &&
|
|
irb.u.GetConfigurationInformation.ModelLeafBufferSize){
|
|
|
|
/*
|
|
* Allocate the required buffers
|
|
*/
|
|
|
|
irb.u.GetConfigurationInformation.UnitDirectory =
|
|
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.UnitDirectoryBufferSize, HID1394_TAG);
|
|
irb.u.GetConfigurationInformation.UnitDependentDirectory =
|
|
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.UnitDependentDirectoryBufferSize, HID1394_TAG);
|
|
irb.u.GetConfigurationInformation.VendorLeaf =
|
|
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.VendorLeafBufferSize, HID1394_TAG);
|
|
irb.u.GetConfigurationInformation.ModelLeaf =
|
|
ExAllocatePoolWithTag(NonPagedPool, irb.u.GetConfigurationInformation.ModelLeafBufferSize, HID1394_TAG);
|
|
|
|
|
|
if (irb.u.GetConfigurationInformation.UnitDirectory &&
|
|
irb.u.GetConfigurationInformation.UnitDependentDirectory &&
|
|
irb.u.GetConfigurationInformation.VendorLeaf &&
|
|
irb.u.GetConfigurationInformation.ModelLeaf){
|
|
|
|
irb.u.GetConfigurationInformation.ConfigRom = &devExt->configROM;
|
|
|
|
status = HIDT_SubmitIRB(devObj, &irb);
|
|
|
|
// BUGBUG FINISH
|
|
// UnitDirectory contains sequence of keys.
|
|
// look for HID key ?
|
|
|
|
}
|
|
else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)){
|
|
/*
|
|
* Free any of the buffers which we were able to allocate
|
|
*/
|
|
if (irb.u.GetConfigurationInformation.UnitDirectory){
|
|
ExFreePool(irb.u.GetConfigurationInformation.UnitDirectory);
|
|
}
|
|
if (irb.u.GetConfigurationInformation.UnitDependentDirectory){
|
|
ExFreePool(irb.u.GetConfigurationInformation.UnitDependentDirectory);
|
|
}
|
|
if (irb.u.GetConfigurationInformation.VendorLeaf){
|
|
ExFreePool(irb.u.GetConfigurationInformation.VendorLeaf);
|
|
}
|
|
if (irb.u.GetConfigurationInformation.ModelLeaf){
|
|
ExFreePool(irb.u.GetConfigurationInformation.ModelLeaf);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_BAD_DEVICE_TYPE;
|
|
}
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
|