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.
676 lines
18 KiB
676 lines
18 KiB
/*
|
|
*************************************************************************
|
|
* File: HID.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"
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, HIDT_GetHidDescriptor)
|
|
#pragma alloc_text(PAGE, HIDT_GetReportDescriptor)
|
|
#pragma alloc_text(PAGE, HIDT_GetStringDescriptor)
|
|
#pragma alloc_text(PAGE, HIDT_GetPhysicalDescriptor)
|
|
#pragma alloc_text(PAGE, HIDT_GetFeature)
|
|
#pragma alloc_text(PAGE, HIDT_SetFeature)
|
|
#pragma alloc_text(PAGE, HIDT_GetDeviceAttributes)
|
|
#endif
|
|
|
|
|
|
resetWorkItemContext *resetWorkItemsList = NULL;
|
|
KSPIN_LOCK resetWorkItemsListSpinLock;
|
|
|
|
|
|
/*
|
|
************************************************************
|
|
* EnqueueResetWorkItem
|
|
************************************************************
|
|
*
|
|
* This function must be called with resetWorkItemsListSpinLock held.
|
|
*/
|
|
VOID EnqueueResetWorkItem(resetWorkItemContext *workItem)
|
|
{
|
|
workItem->next = resetWorkItemsList;
|
|
resetWorkItemsList = workItem;
|
|
}
|
|
|
|
|
|
/*
|
|
************************************************************
|
|
* DequeueResetWorkItemWithIrp
|
|
************************************************************
|
|
*
|
|
* Dequeue the workItem with the given IRP
|
|
* AND mark it as cancelled in case the work item didn't fire yet.
|
|
*
|
|
*
|
|
*/
|
|
BOOLEAN DequeueResetWorkItemWithIrp(PIRP Irp, BOOLEAN irpWasCancelled)
|
|
{
|
|
BOOLEAN didDequeue = FALSE;
|
|
|
|
if (resetWorkItemsList){
|
|
resetWorkItemContext *removedItem = NULL;
|
|
|
|
if (resetWorkItemsList->irpToComplete == Irp){
|
|
removedItem = resetWorkItemsList;
|
|
resetWorkItemsList = resetWorkItemsList->next;
|
|
}
|
|
else {
|
|
resetWorkItemContext *thisWorkItem = resetWorkItemsList;
|
|
while (thisWorkItem->next && (thisWorkItem->next->irpToComplete != Irp)){
|
|
thisWorkItem = thisWorkItem->next;
|
|
}
|
|
removedItem = thisWorkItem->next;
|
|
if (removedItem){
|
|
thisWorkItem->next = removedItem->next;
|
|
}
|
|
}
|
|
|
|
if (removedItem){
|
|
removedItem->next = BAD_POINTER;
|
|
|
|
/*
|
|
* Mark this workItem as cancelled so we won't touch
|
|
* it's cancelled IRP when the workItem fires.
|
|
*/
|
|
if (irpWasCancelled){
|
|
removedItem->irpWasCancelled = TRUE;
|
|
removedItem->irpToComplete = BAD_POINTER;
|
|
}
|
|
|
|
didDequeue = TRUE;
|
|
}
|
|
}
|
|
|
|
return didDequeue;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetHidDescriptor
|
|
********************************************************************************
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Free all the allocated resources, etc.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* DeviceObject - pointer to a device object.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NT status code.
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetHidDescriptor(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
|
|
PAGED_CODE();
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
|
|
|
|
// BUGBUG FINISH
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetDeviceAttributes
|
|
********************************************************************************
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Fill in the given struct _HID_DEVICE_ATTRIBUTES
|
|
*
|
|
* Arguments:
|
|
*
|
|
* DeviceObject - pointer to a device object.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NT status code.
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetDeviceAttributes(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, BOOLEAN *NeedsCompletion)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PHID_DEVICE_ATTRIBUTES deviceAttributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
|
|
deviceAttributes = (PHID_DEVICE_ATTRIBUTES) Irp->UserBuffer;
|
|
|
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
|
|
sizeof (HID_DEVICE_ATTRIBUTES)){
|
|
|
|
//
|
|
// Report how many bytes were copied
|
|
//
|
|
Irp->IoStatus.Information = sizeof (HID_DEVICE_ATTRIBUTES);
|
|
|
|
deviceAttributes->Size = sizeof (HID_DEVICE_ATTRIBUTES);
|
|
|
|
// BUGBUG FINISH
|
|
// deviceAttributes->VendorID = deviceExtension->DeviceDescriptor->idVendor;
|
|
// deviceAttributes->ProductID = deviceExtension->DeviceDescriptor->idProduct;
|
|
// deviceAttributes->VersionNumber = deviceExtension->DeviceDescriptor->bcdDevice;
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
ntStatus = STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
return ntStatus;
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetReportDescriptor
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetReportDescriptor(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_IncrementPendingRequestCount
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_IncrementPendingRequestCount(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
LONG newRequestCount;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
newRequestCount = InterlockedIncrement(&DeviceExtension->NumPendingRequests);
|
|
|
|
//
|
|
// Make sure that the device is capable of receiving new requests.
|
|
//
|
|
if (DeviceExtension->DeviceState != DEVICE_STATE_RUNNING) {
|
|
|
|
//
|
|
// Device cannot receive any more IOs, decrement back, fail the increment
|
|
//
|
|
HIDT_DecrementPendingRequestCount(DeviceExtension);
|
|
ntStatus = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_DecrementPendingRequestCount
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID HIDT_DecrementPendingRequestCount(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
LONG PendingCount;
|
|
|
|
ASSERT(DeviceExtension->NumPendingRequests >= 0);
|
|
|
|
PendingCount = InterlockedDecrement(&DeviceExtension->NumPendingRequests);
|
|
if (PendingCount < 0){
|
|
|
|
ASSERT(DeviceExtension->DeviceState != DEVICE_STATE_RUNNING);
|
|
|
|
/*
|
|
* The device state is stopping, and the last outstanding request
|
|
* has just completed.
|
|
*
|
|
* Note: RemoveDevice does an extra decrement, so we complete
|
|
* the REMOVE IRP on the transition to -1, whether this
|
|
* happens in RemoveDevice itself or subsequently while
|
|
* RemoveDevice is waiting for this event to fire.
|
|
*/
|
|
|
|
KeSetEvent(&DeviceExtension->AllRequestsCompleteEvent, 0, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetPortStatus
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetPortStatus(IN PDEVICE_OBJECT DeviceObject, IN PULONG PortStatus)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_EnableParentPort
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_EnableParentPort(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_ResetWorkItem
|
|
********************************************************************************
|
|
*
|
|
* Resets the interrupt pipe after a read error is encountered.
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_ResetWorkItem(IN PVOID Context)
|
|
{
|
|
resetWorkItemContext *resetWorkItemObj;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
NTSTATUS ntStatus;
|
|
ULONG portStatus;
|
|
BOOLEAN didDequeue;
|
|
KIRQL oldIrql;
|
|
|
|
/*
|
|
* Get the information out of the resetWorkItemContext and free it.
|
|
*/
|
|
resetWorkItemObj = (resetWorkItemContext *)Context;
|
|
ASSERT(resetWorkItemObj);
|
|
ASSERT(resetWorkItemObj->sig == RESET_WORK_ITEM_CONTEXT_SIG);
|
|
|
|
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(resetWorkItemObj->deviceObject);
|
|
|
|
if (DEVICE_STATE_RUNNING == DeviceExtension->DeviceState) {
|
|
|
|
//
|
|
// Check the port state, if it is disabled we will need
|
|
// to re-enable it
|
|
//
|
|
ntStatus = HIDT_GetPortStatus(resetWorkItemObj->deviceObject, &portStatus);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
//
|
|
// port is disabled, attempt reset
|
|
//
|
|
HIDT_AbortPendingRequests(resetWorkItemObj->deviceObject);
|
|
|
|
HIDT_EnableParentPort(resetWorkItemObj->deviceObject);
|
|
}
|
|
|
|
//
|
|
// now attempt to reset the stalled pipe, this will clear the stall
|
|
// on the device as well.
|
|
//
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
// BUGBUG ntStatus = HIDT_ResetInterruptPipe(resetWorkItemObj->deviceObject);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Clear the ResetWorkItem ptr in the device extension
|
|
* AFTER resetting the pipe so we don't end up with
|
|
* two threads resetting the same pipe at the same time.
|
|
*/
|
|
(VOID)InterlockedExchange ((PVOID) &DeviceExtension->ResetWorkItem, 0);
|
|
|
|
/*
|
|
* The IRP that returned the error which prompted us to do this reset
|
|
* is still owned by this driver because we returned
|
|
* STATUS_MORE_PROCESSING_REQUIRED in the completion routine.
|
|
* Now that the hub is reset, complete this failed IRP.
|
|
*
|
|
* Note: we check the irpWasCancelled as well as checking
|
|
* if the IRP is in the queue to deal with the pathological
|
|
* case of the IRP being cancelled, re-allocated, and
|
|
* re-queued.
|
|
*/
|
|
KeAcquireSpinLock(&resetWorkItemsListSpinLock, &oldIrql);
|
|
if (resetWorkItemObj->irpWasCancelled){
|
|
didDequeue = FALSE;
|
|
}
|
|
else {
|
|
IoSetCancelRoutine(resetWorkItemObj->irpToComplete, NULL);
|
|
didDequeue = DequeueResetWorkItemWithIrp(resetWorkItemObj->irpToComplete, FALSE);
|
|
}
|
|
KeReleaseSpinLock(&resetWorkItemsListSpinLock, oldIrql);
|
|
|
|
/*
|
|
* If we found a waiting IRP, complete it _after_ releasing the spinlock.
|
|
*/
|
|
if (didDequeue){
|
|
IoCompleteRequest(resetWorkItemObj->irpToComplete, IO_NO_INCREMENT);
|
|
}
|
|
|
|
ExFreePool(resetWorkItemObj);
|
|
|
|
HIDT_DecrementPendingRequestCount(DeviceExtension);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* ResetCancelRoutine
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
VOID ResetCancelRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
BOOLEAN didDequeue;
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&resetWorkItemsListSpinLock, &oldIrql);
|
|
didDequeue = DequeueResetWorkItemWithIrp(Irp, TRUE);
|
|
KeReleaseSpinLock(&resetWorkItemsListSpinLock, oldIrql);
|
|
|
|
/*
|
|
* We must release the CancelSpinLock whether or not
|
|
* we are completing the IRP here.
|
|
*/
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
/*
|
|
* Complete this Irp only if we found it in our queue.
|
|
*/
|
|
if (didDequeue){
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetPhysicalDescriptor
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetPhysicalDescriptor( IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
BOOLEAN *NeedsCompletion)
|
|
{
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
// BUGBUG FINISH ? REMOVE ?
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetStringDescriptor
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetStringDescriptor( IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_PENDING;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PVOID buffer;
|
|
ULONG bufferSize;
|
|
BOOLEAN isIndexedString;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (IrpStack->Parameters.DeviceIoControl.IoControlCode){
|
|
case IOCTL_HID_GET_INDEXED_STRING:
|
|
/*
|
|
* IOCTL_HID_GET_INDEXED_STRING uses buffering method
|
|
* METHOD_OUT_DIRECT, which passes the buffer in the MDL.
|
|
*/
|
|
buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
|
|
isIndexedString = TRUE;
|
|
break;
|
|
|
|
case IOCTL_HID_GET_STRING:
|
|
/*
|
|
* IOCTL_HID_GET_STRING uses buffering method
|
|
* METHOD_NEITHER, which passes the buffer in Irp->UserBuffer.
|
|
*/
|
|
buffer = Irp->UserBuffer;
|
|
isIndexedString = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
buffer = NULL;
|
|
break;
|
|
}
|
|
|
|
bufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (buffer && bufferSize){
|
|
|
|
/*
|
|
* BUGBUG - hack
|
|
* String id and language id are in Type3InputBuffer field
|
|
* of IRP stack location.
|
|
*
|
|
* Note: the string ID should be identical to the string's
|
|
* field offset given in Chapter 9 of the USB spec.
|
|
*/
|
|
ULONG languageId = ((ULONG)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer) >> 16;
|
|
ULONG stringIndex;
|
|
|
|
if (isIndexedString){
|
|
stringIndex = ((ULONG)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer) & 0x0ffff;
|
|
}
|
|
else {
|
|
ULONG stringId = ((ULONG)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer) & 0x0ffff;
|
|
|
|
switch (stringId){
|
|
case HID_STRING_ID_IMANUFACTURER:
|
|
// BUGBUG FINISH stringIndex = DeviceExtension->DeviceDescriptor->iManufacturer;
|
|
break;
|
|
case HID_STRING_ID_IPRODUCT:
|
|
// BUGBUG FINISH stringIndex = DeviceExtension->DeviceDescriptor->iProduct;
|
|
break;
|
|
case HID_STRING_ID_ISERIALNUMBER:
|
|
// BUGBUG FINISH stringIndex = DeviceExtension->DeviceDescriptor->iSerialNumber;
|
|
break;
|
|
default:
|
|
stringIndex = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stringIndex == -1){
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else {
|
|
|
|
// BUGBUG FINISH
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
else {
|
|
ntStatus = STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetFeatureCompletion
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetFeatureCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
|
|
HIDT_DecrementPendingRequestCount(deviceExtension);
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)){
|
|
/*
|
|
* Record the number of bytes written.
|
|
*/
|
|
// BUGBUG FINISH Irp->IoStatus.Information =
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_GetFeature
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_GetFeature(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, BOOLEAN *NeedsCompletion)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_SetFeatureCompletion
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_SetFeatureCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
|
|
HIDT_DecrementPendingRequestCount(deviceExtension);
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)){
|
|
/*
|
|
* Record the number of bytes written.
|
|
*/
|
|
// BUGBUG FINISH Irp->IoStatus.Information =
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
********************************************************************************
|
|
* HIDT_SetFeature
|
|
********************************************************************************
|
|
*
|
|
*
|
|
*/
|
|
NTSTATUS HIDT_SetFeature(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, BOOLEAN *NeedsCompletion)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
// BUGBUG FINISH
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|