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.
4109 lines
146 KiB
4109 lines
146 KiB
/***************************************************************************
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
UTILS.C
|
|
|
|
Abstract:
|
|
|
|
Routines that don't fit anywhere else.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Copyright (c) 1998 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
12/23/97 : created
|
|
|
|
Authors:
|
|
|
|
Tom Green
|
|
|
|
|
|
****************************************************************************/
|
|
|
|
#include <wdm.h>
|
|
#include <ntddser.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <usb.h>
|
|
#include <usbdrivr.h>
|
|
#include <usbdlib.h>
|
|
#include <usbcomm.h>
|
|
|
|
#ifdef WMI_SUPPORT
|
|
#include <wmilib.h>
|
|
#include <wmidata.h>
|
|
#include <wmistr.h>
|
|
#endif
|
|
|
|
#include "usbser.h"
|
|
#include "usbserpw.h"
|
|
#include "serioctl.h"
|
|
#include "utils.h"
|
|
#include "debugwdm.h"
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#if DBG
|
|
#pragma alloc_text(PAGEUBS0, UsbSerLockPagableCodeSection)
|
|
#endif
|
|
|
|
#pragma alloc_text(PAGEUBS0, UsbSerGetRegistryKeyValue)
|
|
#pragma alloc_text(PAGEUBS0, UsbSerUndoExternalNaming)
|
|
#pragma alloc_text(PAGEUBS0, UsbSerDoExternalNaming)
|
|
|
|
#pragma alloc_text(PAGEUBS0, StopDevice)
|
|
#pragma alloc_text(PAGEUBS0, StartPerfTimer)
|
|
#pragma alloc_text(PAGEUBS0, StopPerfTimer)
|
|
#pragma alloc_text(PAGEUBS0, BytesPerSecond)
|
|
#pragma alloc_text(PAGEUBS0, CallUSBD)
|
|
#pragma alloc_text(PAGEUBS0, ConfigureDevice)
|
|
#pragma alloc_text(PAGEUBS0, BuildRequest)
|
|
// #pragma alloc_text(PAGEUBS0, BuildReadRequest) -- called from restartnotify
|
|
#pragma alloc_text(PAGEUBS0, ClassVendorCommand)
|
|
#pragma alloc_text(PAGEUBS0, StartRead)
|
|
#pragma alloc_text(PAGEUBS0, StartNotifyRead)
|
|
#pragma alloc_text(PAGEUBS0, UsbSerRestoreModemSettings)
|
|
#pragma alloc_text(PAGEUBS0, StartDevice)
|
|
#pragma alloc_text(PAGEUBS0, DeleteObjectAndLink)
|
|
#pragma alloc_text(PAGEUBS0, RemoveDevice)
|
|
|
|
// #pragma alloc_text(PAGEUSBS, CancelPendingWaitMasks) -- called from STOP
|
|
#pragma alloc_text(PAGEUSBS, UsbSerTryToCompleteCurrent)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerGetNextIrp)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerStartOrQueue)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerCancelQueued)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerKillAllReadsOrWrites)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerKillPendingIrps)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerCompletePendingWaitMasks)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerProcessEmptyTransmit)
|
|
#pragma alloc_text(PAGEUSBS, UsbSerCancelWaitOnMask)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
// we will support 256 devices, keep track of open slots here
|
|
#define NUM_DEVICE_SLOTS 256
|
|
|
|
|
|
LOCAL BOOLEAN Slots[NUM_DEVICE_SLOTS];
|
|
LOCAL ULONG NumDevices;
|
|
LOCAL PDEVICE_OBJECT GlobDeviceObject;
|
|
|
|
USHORT RxBuffSize = RX_BUFF_SIZE;
|
|
|
|
|
|
/************************************************************************/
|
|
/* UsbSerGetRegistryValues */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Gets values from the registry */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* Handle Handle to the opened registry key */
|
|
/* */
|
|
/* PKeyNameString ANSI string to the desired key */
|
|
/* */
|
|
/* KeyNameStringLength Length of the KeyNameString */
|
|
/* */
|
|
/* PData Buffer to place the key value in */
|
|
/* */
|
|
/* DataLength Length of the data buffer */
|
|
/* */
|
|
/* PDevExt - pointer to the device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* STATUS_SUCCESS if all works, otherwise status of system call that */
|
|
/* went wrong. */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
UsbSerGetRegistryKeyValue(IN HANDLE Handle, IN PWCHAR PKeyNameString,
|
|
IN ULONG KeyNameStringLength, IN PVOID PData,
|
|
IN ULONG DataLength)
|
|
{
|
|
UNICODE_STRING keyName;
|
|
ULONG length;
|
|
PKEY_VALUE_FULL_INFORMATION pFullInfo;
|
|
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter UsbSerGetRegistryKeyValue");
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerGetRegistryKeyValue\n"));
|
|
|
|
RtlInitUnicodeString(&keyName, PKeyNameString);
|
|
|
|
length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength
|
|
+ DataLength;
|
|
|
|
pFullInfo = DEBUG_MEMALLOC(PagedPool, length);
|
|
|
|
if (pFullInfo) {
|
|
status = ZwQueryValueKey(Handle, &keyName,
|
|
KeyValueFullInformation, pFullInfo,
|
|
length, &length);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// If there is enough room in the data buffer,
|
|
// copy the output
|
|
//
|
|
|
|
if (DataLength >= pFullInfo->DataLength) {
|
|
RtlCopyMemory(PData, ((PUCHAR)pFullInfo)
|
|
+ pFullInfo->DataOffset,
|
|
pFullInfo->DataLength);
|
|
}
|
|
}
|
|
|
|
DEBUG_MEMFREE(pFullInfo);
|
|
}
|
|
|
|
DEBUG_LOG_ERROR(status);
|
|
DEBUG_LOG_PATH("exit UsbSerGetRegistryKeyValue");
|
|
DEBUG_TRACE3(("status (%08X)\n", status));
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerGetRegistryKeyValue %08X\n",
|
|
status));
|
|
|
|
return status;
|
|
} // UsbSerGetRegistryKeyValue
|
|
|
|
|
|
/************************************************************************/
|
|
/* UsbSerUndoExternalNaming */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Remove any and all external namespace interfaces we exposed */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* PDevExt - pointer to the device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
UsbSerUndoExternalNaming(IN PDEVICE_EXTENSION PDevExt)
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter UsbSerUndoExternalNaming");
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerUndoExternalNaming\n"));
|
|
|
|
if (PDevExt->SymbolicLinkName.Buffer && PDevExt->CreatedSymbolicLink) {
|
|
IoDeleteSymbolicLink(&PDevExt->SymbolicLinkName);
|
|
}
|
|
|
|
if (PDevExt->SymbolicLinkName.Buffer != NULL) {
|
|
DEBUG_MEMFREE(PDevExt->SymbolicLinkName.Buffer);
|
|
RtlInitUnicodeString(&PDevExt->SymbolicLinkName, NULL);
|
|
}
|
|
|
|
if (PDevExt->DosName.Buffer != NULL) {
|
|
DEBUG_MEMFREE(PDevExt->DosName.Buffer);
|
|
RtlInitUnicodeString(&PDevExt->DosName, NULL);
|
|
}
|
|
|
|
if (PDevExt->DeviceName.Buffer != NULL) {
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
|
|
PDevExt->DeviceName.Buffer);
|
|
DEBUG_MEMFREE(PDevExt->DeviceName.Buffer);
|
|
RtlInitUnicodeString(&PDevExt->DeviceName, NULL);
|
|
}
|
|
|
|
#ifdef WMI_SUPPORT
|
|
if (PDevExt->WmiIdentifier.Buffer)
|
|
{
|
|
DEBUG_MEMFREE(PDevExt->WmiIdentifier.Buffer);
|
|
PDevExt->WmiIdentifier.MaximumLength
|
|
= PDevExt->WmiIdentifier.Length = 0;
|
|
PDevExt->WmiIdentifier.Buffer = NULL;
|
|
}
|
|
#endif
|
|
|
|
DEBUG_LOG_PATH("exit UsbSerUndoExternalNaming");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerUndoExternalNaming\n"));
|
|
} // UsbSerUndoExternalNaming
|
|
|
|
|
|
/************************************************************************/
|
|
/* UsbSerDoExternalNaming */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Exposes interfaces in external namespace */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* PDevExt - pointer to the device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
UsbSerDoExternalNaming(IN PDEVICE_EXTENSION PDevExt)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE keyHandle;
|
|
WCHAR *pRegName = NULL;
|
|
UNICODE_STRING linkName;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter UsbSerDoExternalNaming");
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerDoExternalNaming\n"));
|
|
|
|
RtlZeroMemory(&linkName, sizeof(UNICODE_STRING));
|
|
linkName.MaximumLength = SYMBOLIC_NAME_LENGTH * sizeof(WCHAR);
|
|
linkName.Buffer = DEBUG_MEMALLOC(PagedPool, linkName.MaximumLength
|
|
+ sizeof(WCHAR));
|
|
|
|
if (linkName.Buffer == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
RtlZeroMemory(linkName.Buffer, linkName.MaximumLength + sizeof(WCHAR));
|
|
|
|
pRegName = DEBUG_MEMALLOC(PagedPool, SYMBOLIC_NAME_LENGTH * sizeof(WCHAR)
|
|
+ sizeof(WCHAR));
|
|
|
|
if (pRegName == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
status = IoOpenDeviceRegistryKey(PDevExt->PhysDeviceObject,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
STANDARD_RIGHTS_READ, &keyHandle);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
status = UsbSerGetRegistryKeyValue(keyHandle, L"PortName", sizeof(L"PortName"),
|
|
pRegName, SYMBOLIC_NAME_LENGTH * sizeof(WCHAR));
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
status = UsbSerGetRegistryKeyValue(keyHandle, L"Identifier",
|
|
sizeof(L"Identifier"), pRegName,
|
|
SYMBOLIC_NAME_LENGTH * sizeof(WCHAR));
|
|
if (status != STATUS_SUCCESS) {
|
|
ZwClose(keyHandle);
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
}
|
|
|
|
ZwClose(keyHandle);
|
|
|
|
#ifdef WMI_SUPPORT
|
|
{
|
|
ULONG bufLen;
|
|
|
|
bufLen = wcslen(pRegName) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
|
|
|
|
PDevExt->WmiIdentifier.Buffer = DEBUG_MEMALLOC(PagedPool, bufLen);
|
|
|
|
if (PDevExt->WmiIdentifier.Buffer == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
|
|
RtlZeroMemory(PDevExt->WmiIdentifier.Buffer, bufLen);
|
|
|
|
PDevExt->WmiIdentifier.Length = 0;
|
|
PDevExt->WmiIdentifier.MaximumLength = (USHORT)bufLen - sizeof(WCHAR);
|
|
RtlAppendUnicodeToString(&PDevExt->WmiIdentifier, pRegName);
|
|
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Create the "\\DosDevices\\<symbolicname>" string
|
|
//
|
|
|
|
RtlAppendUnicodeToString(&linkName, L"\\");
|
|
RtlAppendUnicodeToString(&linkName, DEFAULT_DIRECTORY);
|
|
RtlAppendUnicodeToString(&linkName, L"\\");
|
|
RtlAppendUnicodeToString(&linkName, pRegName);
|
|
|
|
//
|
|
// Allocate pool and save the symbolic link name in the device extension
|
|
//
|
|
PDevExt->SymbolicLinkName.MaximumLength = linkName.Length + sizeof(WCHAR);
|
|
PDevExt->SymbolicLinkName.Buffer
|
|
= DEBUG_MEMALLOC(PagedPool, PDevExt->SymbolicLinkName.MaximumLength);
|
|
|
|
if (PDevExt->SymbolicLinkName.Buffer == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
RtlZeroMemory(PDevExt->SymbolicLinkName.Buffer,
|
|
PDevExt->SymbolicLinkName.MaximumLength);
|
|
|
|
RtlAppendUnicodeStringToString(&PDevExt->SymbolicLinkName, &linkName);
|
|
|
|
status = IoCreateSymbolicLink(&PDevExt->SymbolicLinkName,
|
|
&PDevExt->DeviceName);
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
PDevExt->CreatedSymbolicLink = TRUE;
|
|
|
|
PDevExt->DosName.Buffer = DEBUG_MEMALLOC(PagedPool, 64 + sizeof(WCHAR));
|
|
|
|
if (PDevExt->DosName.Buffer == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
PDevExt->DosName.MaximumLength = 64 + sizeof(WCHAR);
|
|
PDevExt->DosName.Length = 0;
|
|
|
|
RtlZeroMemory(PDevExt->DosName.Buffer, PDevExt->DosName.MaximumLength);
|
|
|
|
RtlAppendUnicodeToString(&PDevExt->DosName, pRegName);
|
|
RtlZeroMemory(((PUCHAR)(&PDevExt->DosName.Buffer[0]))
|
|
+ PDevExt->DosName.Length, sizeof(WCHAR));
|
|
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM",
|
|
PDevExt->DeviceName.Buffer, REG_SZ,
|
|
PDevExt->DosName.Buffer,
|
|
PDevExt->DosName.Length + sizeof(WCHAR));
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
goto UsbSerDoExternalNamingError;
|
|
}
|
|
|
|
UsbSerDoExternalNamingError:;
|
|
|
|
//
|
|
// Clean up error conditions
|
|
//
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
if (PDevExt->DosName.Buffer != NULL) {
|
|
DEBUG_MEMFREE(PDevExt->DosName.Buffer);
|
|
PDevExt->DosName.Buffer = NULL;
|
|
}
|
|
|
|
if (PDevExt->CreatedSymbolicLink == TRUE) {
|
|
IoDeleteSymbolicLink(&PDevExt->SymbolicLinkName);
|
|
PDevExt->CreatedSymbolicLink = FALSE;
|
|
}
|
|
|
|
if (PDevExt->SymbolicLinkName.Buffer != NULL) {
|
|
DEBUG_MEMFREE(PDevExt->SymbolicLinkName.Buffer);
|
|
PDevExt->SymbolicLinkName.Buffer = NULL;
|
|
}
|
|
|
|
if (PDevExt->DeviceName.Buffer != NULL) {
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
|
|
PDevExt->DeviceName.Buffer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Always clean up our temp buffers.
|
|
//
|
|
|
|
if (linkName.Buffer != NULL) {
|
|
DEBUG_MEMFREE(linkName.Buffer);
|
|
}
|
|
|
|
if (pRegName != NULL) {
|
|
DEBUG_MEMFREE(pRegName);
|
|
}
|
|
|
|
DEBUG_LOG_ERROR(status);
|
|
DEBUG_LOG_PATH("exit UsbSerDoExternalNaming");
|
|
DEBUG_TRACE3(("status (%08X)\n", status));
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerDoExternalNaming %08X\n", status));
|
|
|
|
return status;
|
|
|
|
} // UsbSerDoExternalNaming
|
|
|
|
|
|
|
|
NTSTATUS
|
|
UsbSerAbortPipes(IN PDEVICE_OBJECT PDevObj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called as part of sudden device removal handling.
|
|
Cancels any pending transfers for all open pipes.
|
|
|
|
Arguments:
|
|
|
|
Ptrs to our FDO
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PURB pUrb;
|
|
PDEVICE_EXTENSION pDevExt;
|
|
ULONG pendingIrps;
|
|
|
|
DEBUG_TRACE1(("UsbSerAbortPipes\n"));
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD,
|
|
(">UsbSerAbortPipes (%08X)\n", PDevObj));
|
|
|
|
pDevExt = PDevObj->DeviceExtension;
|
|
pUrb = DEBUG_MEMALLOC(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST));
|
|
|
|
if (pUrb != NULL)
|
|
{
|
|
|
|
pUrb->UrbHeader.Length = (USHORT)sizeof(struct _URB_PIPE_REQUEST);
|
|
pUrb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
|
|
pUrb->UrbPipeRequest.PipeHandle = pDevExt->DataInPipe;
|
|
|
|
ntStatus = CallUSBD(PDevObj, pUrb);
|
|
|
|
if (ntStatus != STATUS_SUCCESS) {
|
|
goto UsbSerAbortPipesErr;
|
|
}
|
|
|
|
//
|
|
// Wait for all the read IRPS to drain
|
|
//
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("DataInCountw %08X @ %08X\n",
|
|
pDevExt->PendingDataInCount,
|
|
&pDevExt->PendingDataInCount));
|
|
|
|
//
|
|
// Decrement for initial value
|
|
//
|
|
|
|
pendingIrps = InterlockedDecrement(&pDevExt->PendingDataInCount);
|
|
|
|
if (pendingIrps) {
|
|
DEBUG_TRACE1(("Abort DataIn Pipe\n"));
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("Waiting for DataIn Pipe\n"));
|
|
KeWaitForSingleObject(&pDevExt->PendingDataInEvent, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// Reset counter
|
|
//
|
|
|
|
InterlockedIncrement(&pDevExt->PendingDataInCount);
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("DataInCountx %08X @ %08X\n",
|
|
pDevExt->PendingDataInCount,
|
|
&pDevExt->PendingDataInCount));
|
|
|
|
pUrb->UrbHeader.Length = (USHORT)sizeof(struct _URB_PIPE_REQUEST);
|
|
pUrb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
|
|
pUrb->UrbPipeRequest.PipeHandle = pDevExt->DataOutPipe;
|
|
|
|
ntStatus = CallUSBD(PDevObj, pUrb);
|
|
|
|
if (ntStatus != STATUS_SUCCESS) {
|
|
goto UsbSerAbortPipesErr;
|
|
}
|
|
|
|
//
|
|
// Wait for all the write irps to drain
|
|
//
|
|
|
|
//
|
|
// Decrement for initial value
|
|
//
|
|
|
|
pendingIrps = InterlockedDecrement(&pDevExt->PendingDataOutCount);
|
|
|
|
if (pendingIrps) {
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("Waiting for DataOut Pipe\n"));
|
|
KeWaitForSingleObject(&pDevExt->PendingDataOutEvent, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// Reset counter
|
|
//
|
|
|
|
InterlockedIncrement(&pDevExt->PendingDataOutCount);
|
|
|
|
|
|
pUrb->UrbHeader.Length = (USHORT)sizeof(struct _URB_PIPE_REQUEST);
|
|
pUrb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
|
|
pUrb->UrbPipeRequest.PipeHandle = pDevExt->NotificationPipe;
|
|
|
|
ntStatus = CallUSBD(PDevObj, pUrb);
|
|
|
|
//
|
|
// Wait for all the notify irps to drain
|
|
//
|
|
|
|
//
|
|
// Decrement for initial value
|
|
//
|
|
|
|
pendingIrps = InterlockedDecrement(&pDevExt->PendingNotifyCount);
|
|
|
|
if (pendingIrps) {
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("Waiting for Notify Pipe\n"));
|
|
KeWaitForSingleObject(&pDevExt->PendingNotifyEvent, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
// //
|
|
// Die my darling, die.
|
|
//
|
|
|
|
// IoCancelIrp(pDevExt->NotifyIrp);
|
|
|
|
|
|
|
|
// Reset counter
|
|
//
|
|
|
|
InterlockedIncrement(&pDevExt->PendingNotifyCount);
|
|
|
|
UsbSerAbortPipesErr:;
|
|
|
|
DEBUG_MEMFREE(pUrb);
|
|
|
|
} else {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD,
|
|
("<UsbSerAbortPipes %08X\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* StartDevice */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Take care of processing needed to start device. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Irp - pointer to an I/O Request Packet */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
StartDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
KEVENT Event;
|
|
PVOID pPagingHandle;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter StartDevice");
|
|
|
|
DEBUG_TRACE1(("StartDevice\n"));
|
|
|
|
// pass this down to the USB stack first
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Initialize our DPC's
|
|
//
|
|
|
|
KeInitializeDpc(&DeviceExtension->TotalReadTimeoutDpc,
|
|
UsbSerReadTimeout, DeviceExtension);
|
|
KeInitializeDpc(&DeviceExtension->IntervalReadTimeoutDpc,
|
|
UsbSerIntervalReadTimeout, DeviceExtension);
|
|
KeInitializeDpc(&DeviceExtension->TotalWriteTimeoutDpc,
|
|
UsbSerWriteTimeout, DeviceExtension);
|
|
|
|
//
|
|
// Initialize timers
|
|
//
|
|
|
|
KeInitializeTimer(&DeviceExtension->WriteRequestTotalTimer);
|
|
KeInitializeTimer(&DeviceExtension->ReadRequestTotalTimer);
|
|
KeInitializeTimer(&DeviceExtension->ReadRequestIntervalTimer);
|
|
|
|
//
|
|
// Store values into the extension for interval timing.
|
|
//
|
|
|
|
//
|
|
// If the interval timer is less than a second then come
|
|
// in with a short "polling" loop.
|
|
//
|
|
// For large (> then 2 seconds) use a 1 second poller.
|
|
//
|
|
|
|
DeviceExtension->ShortIntervalAmount.QuadPart = -1;
|
|
DeviceExtension->LongIntervalAmount.QuadPart = -10000000;
|
|
DeviceExtension->CutOverAmount.QuadPart = 200000000;
|
|
|
|
|
|
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp, UsbSerSyncCompletion, &Event, TRUE, TRUE,
|
|
TRUE);
|
|
|
|
NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, Irp);
|
|
|
|
// wait for Irp to complete if status is pending
|
|
if(NtStatus == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE,
|
|
NULL);
|
|
}
|
|
|
|
NtStatus = Irp->IoStatus.Status;
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
goto ExitStartDevice;
|
|
}
|
|
|
|
NtStatus = GetDeviceDescriptor(DeviceObject);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
goto ExitStartDevice;
|
|
}
|
|
|
|
NtStatus = ConfigureDevice(DeviceObject);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
goto ExitStartDevice;
|
|
}
|
|
|
|
//
|
|
// Page in and lock necessary code
|
|
//
|
|
pPagingHandle = UsbSerLockPagableCodeSection(PAGEUSBSER_Function);
|
|
|
|
// reset device
|
|
ResetDevice(NULL, DeviceObject);
|
|
|
|
// init stuff in device extension
|
|
|
|
DeviceExtension->HandFlow.ControlHandShake = 0;
|
|
DeviceExtension->HandFlow.FlowReplace = SERIAL_RTS_CONTROL;
|
|
DeviceExtension->AcceptingRequests = TRUE;
|
|
|
|
InitializeListHead(&DeviceExtension->ReadQueue);
|
|
InitializeListHead(&DeviceExtension->ImmediateReadQueue);
|
|
|
|
UsbSerDoExternalNaming(DeviceExtension);
|
|
|
|
// clear DTR and RTS
|
|
SetClrDtr(DeviceObject, FALSE);
|
|
ClrRts(NULL, DeviceExtension);
|
|
|
|
// kick off a read
|
|
StartRead(DeviceExtension);
|
|
|
|
// kick off a notification read
|
|
StartNotifyRead(DeviceExtension);
|
|
|
|
UsbSerUnlockPagableImageSection(pPagingHandle);
|
|
|
|
ExitStartDevice:;
|
|
|
|
if(NT_SUCCESS(NtStatus))
|
|
{
|
|
DeviceExtension->DeviceState = DEVICE_STATE_STARTED;
|
|
|
|
// try and idle the modem
|
|
// UsbSerFdoSubmitIdleRequestIrp(DeviceExtension);
|
|
}
|
|
|
|
Irp->IoStatus.Status = NtStatus;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
DEBUG_LOG_PATH("exit StartDevice");
|
|
|
|
return NtStatus;
|
|
} // StartDevice
|
|
|
|
|
|
/************************************************************************/
|
|
/* StopDevice */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Take care of processing needed to stop device. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Irp - pointer to an I/O Request Packet */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
StopDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
ULONG Size;
|
|
PURB Urb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter StopDevice");
|
|
|
|
DEBUG_TRACE1(("StopDevice\n"));
|
|
|
|
UsbSerFetchBooleanLocked(&DeviceExtension->AcceptingRequests,
|
|
FALSE, &DeviceExtension->ControlLock);
|
|
|
|
CancelPendingWaitMasks(DeviceExtension);
|
|
|
|
if(DeviceExtension->DeviceState == DEVICE_STATE_STARTED)
|
|
{
|
|
DEBUG_TRACE1(("AbortPipes\n"));
|
|
UsbSerAbortPipes(DeviceObject);
|
|
}
|
|
|
|
DeviceExtension->DeviceState = DEVICE_STATE_STOPPED;
|
|
|
|
if(DeviceExtension->PendingIdleIrp)
|
|
{
|
|
IoCancelIrp(DeviceExtension->PendingIdleIrp);
|
|
}
|
|
|
|
Size = sizeof(struct _URB_SELECT_CONFIGURATION);
|
|
|
|
Urb = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if(Urb)
|
|
{
|
|
|
|
UsbBuildSelectConfigurationRequest(Urb, (USHORT) Size, NULL);
|
|
|
|
NtStatus = CallUSBD(DeviceObject, Urb);
|
|
|
|
DEBUG_TRACE3(("Device Configuration Closed status = (%08X) "
|
|
"USB status = (%08X)\n", NtStatus,
|
|
Urb->UrbHeader.Status));
|
|
|
|
DEBUG_MEMFREE(Urb);
|
|
}
|
|
else
|
|
{
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit StopDevice");
|
|
|
|
return NtStatus;
|
|
} // StopDevice
|
|
|
|
|
|
/************************************************************************/
|
|
/* RemoveDevice */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Take care of processing needed to remove device. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Irp - pointer to an I/O Request Packet */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
RemoveDevice(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PVOID pPagingHandle;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter RemoveDevice");
|
|
|
|
DEBUG_TRACE1(("RemoveDevice\n"));
|
|
|
|
//
|
|
// Page in and lock necessary code
|
|
//
|
|
|
|
pPagingHandle = UsbSerLockPagableCodeSection(PAGEUSBSER_Function);
|
|
|
|
UsbSerFetchBooleanLocked(&DeviceExtension->AcceptingRequests,
|
|
FALSE, &DeviceExtension->ControlLock);
|
|
|
|
CancelPendingWaitMasks(DeviceExtension);
|
|
|
|
//
|
|
// Cancel all pending USB transactions
|
|
//
|
|
|
|
if(DeviceExtension->DeviceState == DEVICE_STATE_STARTED)
|
|
{
|
|
DEBUG_TRACE1(("AbortPipes\n"));
|
|
UsbSerAbortPipes(DeviceObject);
|
|
}
|
|
|
|
//
|
|
// Once we set accepting requests to false, we shouldn't
|
|
// have any more contention here -- if we do, we're dead
|
|
// because we're freeing memory out from under our feet.
|
|
//
|
|
|
|
DEBUG_TRACE1(("Freeing Allocated Memory\n"));
|
|
|
|
// free allocated notify URB
|
|
if(DeviceExtension->NotifyUrb)
|
|
{
|
|
DEBUG_MEMFREE(DeviceExtension->NotifyUrb);
|
|
DeviceExtension->NotifyUrb = NULL;
|
|
}
|
|
|
|
// free allocated Read URB
|
|
if(DeviceExtension->ReadUrb)
|
|
{
|
|
DEBUG_MEMFREE(DeviceExtension->ReadUrb);
|
|
DeviceExtension->ReadUrb = NULL;
|
|
}
|
|
|
|
// free allocated device descriptor
|
|
if(DeviceExtension->DeviceDescriptor)
|
|
{
|
|
DEBUG_MEMFREE(DeviceExtension->DeviceDescriptor);
|
|
DeviceExtension->DeviceDescriptor = NULL;
|
|
}
|
|
|
|
// free up read buffer
|
|
if(DeviceExtension->ReadBuff)
|
|
{
|
|
DEBUG_MEMFREE(DeviceExtension->ReadBuff);
|
|
DeviceExtension->ReadBuff = NULL;
|
|
}
|
|
|
|
if(DeviceExtension->USBReadBuff)
|
|
{
|
|
DEBUG_MEMFREE(DeviceExtension->USBReadBuff);
|
|
DeviceExtension->USBReadBuff = NULL;
|
|
}
|
|
|
|
// free up notification buffer
|
|
if(DeviceExtension->NotificationBuff)
|
|
{
|
|
DEBUG_MEMFREE(DeviceExtension->NotificationBuff);
|
|
DeviceExtension->NotificationBuff = NULL;
|
|
}
|
|
|
|
DEBUG_TRACE1(("Undo Serial Name\n"));
|
|
|
|
UsbSerUndoExternalNaming(DeviceExtension);
|
|
|
|
//
|
|
// Pass this down to the next driver
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, Irp);
|
|
|
|
DEBUG_TRACE1(("Detach Device\n"));
|
|
|
|
// detach device from stack
|
|
IoDetachDevice(DeviceExtension->StackDeviceObject);
|
|
|
|
DEBUG_TRACE1(("DevExt (%08X) DevExt Size (%08X)\n", DeviceExtension, sizeof(DEVICE_EXTENSION)));
|
|
|
|
DEBUG_TRACE1(("Delete Object and Link\n"));
|
|
|
|
// delete device object and symbolic link
|
|
DeleteObjectAndLink(DeviceObject);
|
|
|
|
DEBUG_TRACE1(("Done Removing Device\n"));
|
|
|
|
DEBUG_LOG_PATH("exit RemoveDevice");
|
|
|
|
UsbSerUnlockPagableImageSection(pPagingHandle);
|
|
|
|
DeviceExtension->DeviceState = DEVICE_STATE_STOPPED;
|
|
|
|
return NtStatus;
|
|
} // RemoveDevice
|
|
|
|
|
|
/************************************************************************/
|
|
/* CreateDeviceObject */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Take care of processing needed to create device obeject for */
|
|
/* device. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DriverObject - pointer to a driver object */
|
|
/* DeviceObject - pointer to a device object pointer */
|
|
/* DeviceName - pointer to a base name of device */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
CreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT *DeviceObject,
|
|
IN PCHAR DeviceName)
|
|
{
|
|
ANSI_STRING DevName;
|
|
ANSI_STRING LinkName;
|
|
NTSTATUS NtStatus;
|
|
UNICODE_STRING DeviceNameUnicodeString;
|
|
UNICODE_STRING LinkNameUnicodeString;
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
CHAR DeviceLinkBuffer[NAME_MAX];
|
|
CHAR DeviceNameBuffer[NAME_MAX];
|
|
ULONG DeviceInstance;
|
|
ULONG bufferLen;
|
|
KIRQL OldIrql;
|
|
|
|
DEBUG_LOG_PATH("enter CreateDeviceObject");
|
|
|
|
DEBUG_TRACE1(("CreateDeviceObject\n"));
|
|
|
|
KeAcquireSpinLock(&GlobalSpinLock, &OldIrql);
|
|
|
|
// let's get an instance
|
|
for (DeviceInstance = 0; DeviceInstance < NUM_DEVICE_SLOTS;
|
|
DeviceInstance++) {
|
|
if (Slots[DeviceInstance] == FALSE)
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock(&GlobalSpinLock, OldIrql);
|
|
|
|
// check if we didn't have any empty slots
|
|
if (DeviceInstance == NUM_DEVICE_SLOTS)
|
|
NtStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
else {
|
|
// complete names of links and devices
|
|
sprintf(DeviceLinkBuffer, "%s%s%03d", "\\DosDevices\\", DeviceName,
|
|
DeviceInstance);
|
|
sprintf(DeviceNameBuffer, "%s%s%03d", "\\Device\\", DeviceName,
|
|
DeviceInstance);
|
|
|
|
// init ANSI string with our link and device names
|
|
RtlInitAnsiString(&DevName, DeviceNameBuffer);
|
|
RtlInitAnsiString(&LinkName, DeviceLinkBuffer);
|
|
|
|
DeviceNameUnicodeString.Length = 0;
|
|
DeviceNameUnicodeString.Buffer = NULL;
|
|
|
|
LinkNameUnicodeString.Length = 0;
|
|
LinkNameUnicodeString.Buffer = NULL;
|
|
|
|
*DeviceObject = NULL;
|
|
|
|
// convert to UNICODE string
|
|
NtStatus = RtlAnsiStringToUnicodeString(&DeviceNameUnicodeString,
|
|
&DevName, TRUE);
|
|
|
|
if(NT_SUCCESS(NtStatus))
|
|
{
|
|
NtStatus = RtlAnsiStringToUnicodeString(&LinkNameUnicodeString,
|
|
&LinkName, TRUE);
|
|
|
|
if(NT_SUCCESS(NtStatus))
|
|
{
|
|
DEBUG_TRACE3(("Create Device (%s)\n", DeviceNameBuffer));
|
|
|
|
// create the device object
|
|
NtStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
|
|
&DeviceNameUnicodeString,
|
|
FILE_DEVICE_MODEM, 0, TRUE,
|
|
DeviceObject);
|
|
} else {
|
|
goto CreateDeviceObjectError;
|
|
}
|
|
} else {
|
|
goto CreateDeviceObjectError;
|
|
}
|
|
|
|
// created the device object O.K., create symbolic links,
|
|
// attach device object, and fill in the device extension
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
// create symbolic links
|
|
|
|
DEBUG_TRACE3(("Create SymLink (%s)\n", DeviceLinkBuffer));
|
|
|
|
|
|
NtStatus = IoCreateUnprotectedSymbolicLink(&LinkNameUnicodeString,
|
|
&DeviceNameUnicodeString);
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
goto CreateDeviceObjectError;
|
|
}
|
|
|
|
// get pointer to device extension
|
|
DeviceExtension = (PDEVICE_EXTENSION) (*DeviceObject)->DeviceExtension;
|
|
|
|
// let's zero out device extension
|
|
RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION));
|
|
|
|
// save our strings
|
|
|
|
// save link name
|
|
strcpy(DeviceExtension->LinkName, DeviceLinkBuffer);
|
|
|
|
bufferLen = RtlAnsiStringToUnicodeSize(&DevName);
|
|
|
|
DeviceExtension->DeviceName.Length = 0;
|
|
DeviceExtension->DeviceName.MaximumLength = (USHORT)bufferLen;
|
|
|
|
DeviceExtension->DeviceName.Buffer = DEBUG_MEMALLOC(PagedPool,
|
|
bufferLen);
|
|
|
|
if (DeviceExtension->DeviceName.Buffer == NULL) {
|
|
//
|
|
// Skip out. We have worse problems than missing
|
|
// the name.
|
|
//
|
|
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto CreateDeviceObjectError;
|
|
} else {
|
|
RtlAnsiStringToUnicodeString(&DeviceExtension->DeviceName, &DevName,
|
|
FALSE);
|
|
|
|
|
|
// save physical device object
|
|
DeviceExtension->PhysDeviceObject = *DeviceObject;
|
|
DeviceExtension->Instance = DeviceInstance;
|
|
|
|
// initialize spinlocks
|
|
KeInitializeSpinLock(&DeviceExtension->ControlLock);
|
|
|
|
// mark this device slot as in use and increment number
|
|
// of devices
|
|
KeAcquireSpinLock(&GlobalSpinLock, &OldIrql);
|
|
|
|
Slots[DeviceInstance] = TRUE;
|
|
NumDevices++;
|
|
|
|
KeReleaseSpinLock(&GlobalSpinLock, OldIrql);
|
|
|
|
DeviceExtension->IsDevice = TRUE;
|
|
|
|
KeInitializeEvent(&DeviceExtension->PendingDataInEvent,
|
|
SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&DeviceExtension->PendingDataOutEvent,
|
|
SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&DeviceExtension->PendingNotifyEvent,
|
|
SynchronizationEvent, FALSE);
|
|
KeInitializeEvent(&DeviceExtension->PendingFlushEvent,
|
|
SynchronizationEvent, FALSE);
|
|
|
|
DeviceExtension->PendingDataInCount = 1;
|
|
DeviceExtension->PendingDataOutCount = 1;
|
|
DeviceExtension->PendingNotifyCount = 1;
|
|
DeviceExtension->SanityCheck = SANITY_CHECK;
|
|
|
|
}
|
|
}
|
|
|
|
CreateDeviceObjectError:;
|
|
// free Unicode strings
|
|
RtlFreeUnicodeString(&DeviceNameUnicodeString);
|
|
RtlFreeUnicodeString(&LinkNameUnicodeString);
|
|
|
|
//
|
|
// Delete the devobj if there was an error
|
|
//
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
if (*DeviceObject) {
|
|
IoDeleteDevice(*DeviceObject);
|
|
*DeviceObject = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// log an error if we got one
|
|
DEBUG_LOG_ERROR(NtStatus);
|
|
DEBUG_LOG_PATH("exit CreateDeviceObject");
|
|
DEBUG_TRACE3(("status (%08X)\n", NtStatus));
|
|
|
|
return NtStatus;
|
|
} // CreateDeviceObject
|
|
|
|
|
|
/************************************************************************/
|
|
/* CompleteIO */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Complete IO request and log IRP */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to device object. */
|
|
/* Irp - pointer to IRP. */
|
|
/* MajorFunction - major function of IRP. */
|
|
/* IoBuffer - buffer for data passed in and out of driver. */
|
|
/* BufferLen - length of buffer */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
CompleteIO(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG MajorFunction,
|
|
IN PVOID IoBuffer, IN ULONG_PTR BufferLen)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
|
|
DEBUG_LOG_PATH("enter CompleteIO");
|
|
|
|
// get pointer to device extension
|
|
DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
// log IRP count and bytes processed in device extension
|
|
DeviceExtension->IRPCount++;
|
|
DeviceExtension->ByteCount
|
|
= RtlLargeIntegerAdd(DeviceExtension->ByteCount,
|
|
RtlConvertUlongToLargeInteger((ULONG)Irp->IoStatus
|
|
.Information));
|
|
|
|
// make entry in IRP history table
|
|
DEBUG_LOG_IRP_HIST(DeviceObject, Irp, MajorFunction, IoBuffer,
|
|
(ULONG)BufferLen);
|
|
|
|
// if we got here, must want to complete request on IRP
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
DEBUG_LOG_PATH("exit CompleteIO");
|
|
} // CompleteIO
|
|
|
|
|
|
/************************************************************************/
|
|
/* DeleteObjectAndLink */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Deletes a device object and associated symbolic link */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to device object. */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
DeleteObjectAndLink(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension;
|
|
UNICODE_STRING DeviceLinkUnicodeString;
|
|
ANSI_STRING DeviceLinkAnsiString;
|
|
NTSTATUS NtStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter DeleteObjectAndLink");
|
|
|
|
DEBUG_TRACE1(("DeleteObjectAndLink\n"));
|
|
|
|
// get pointer to device extension, we will get the symbolic link name
|
|
// here
|
|
DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
// get rid of the symbolic link
|
|
RtlInitAnsiString(&DeviceLinkAnsiString, DeviceExtension->LinkName);
|
|
NtStatus = RtlAnsiStringToUnicodeString(&DeviceLinkUnicodeString,
|
|
&DeviceLinkAnsiString, TRUE);
|
|
|
|
DEBUG_TRACE1(("Delete Symbolic Link\n"));
|
|
|
|
IoDeleteSymbolicLink(&DeviceLinkUnicodeString);
|
|
|
|
// clear out slot and decrement number of devices
|
|
if(DeviceExtension->Instance < NUM_DEVICE_SLOTS)
|
|
{
|
|
UsbSerFetchBooleanLocked(&Slots[DeviceExtension->Instance],
|
|
FALSE, &GlobalSpinLock);
|
|
NumDevices--;
|
|
|
|
if(!NumDevices)
|
|
DEBUG_CHECKMEM();
|
|
}
|
|
|
|
DEBUG_TRACE1(("Delete Device Object\n"));
|
|
|
|
if(DeviceExtension->SanityCheck != SANITY_CHECK)
|
|
{
|
|
DEBUG_TRACE1(("Device Extension Scrozzled\n"));
|
|
}
|
|
|
|
// wait to do this till here as this triggers unload routine
|
|
IoDeleteDevice(DeviceObject);
|
|
|
|
DEBUG_TRACE1(("Done Deleting Device Object and Link\n"));
|
|
|
|
DEBUG_LOG_PATH("exit DeleteObjectAndLink");
|
|
|
|
return NtStatus;
|
|
} // DeleteObjectAndLink
|
|
|
|
|
|
/************************************************************************/
|
|
/* StartPerfTimer */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Start perf timer for measuring bytes/second throughput */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to device extension for device */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
StartPerfTimer(IN OUT PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// set up perf stuff if perf timing enabled
|
|
if(DeviceExtension && DeviceExtension->PerfTimerEnabled)
|
|
{
|
|
// get current perf counter
|
|
DeviceExtension->TimerStart = KeQueryPerformanceCounter(NULL);
|
|
}
|
|
} // StartPerfTimer
|
|
|
|
|
|
/************************************************************************/
|
|
/* StopPerfTimer */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Stop perf timer for measuring bytes/second throughput */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to device extension for device */
|
|
/* BytesXfered - number of bytes tranferred this iteration */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
StopPerfTimer(IN OUT PDEVICE_EXTENSION DeviceExtension,
|
|
IN ULONG BytesXfered)
|
|
{
|
|
LARGE_INTEGER BytesThisTransfer;
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER TimeThisTransfer;
|
|
|
|
PAGED_CODE();
|
|
|
|
if(DeviceExtension && DeviceExtension->PerfTimerEnabled)
|
|
{
|
|
// get updated time
|
|
CurrentTime = KeQueryPerformanceCounter(NULL);
|
|
|
|
// stop perf timing with system timer
|
|
BytesThisTransfer = RtlConvertUlongToLargeInteger(BytesXfered);
|
|
|
|
DeviceExtension->BytesXfered
|
|
= RtlLargeIntegerAdd(DeviceExtension->BytesXfered,
|
|
BytesThisTransfer);
|
|
|
|
// now add the time it took to elapsed time
|
|
TimeThisTransfer
|
|
= RtlLargeIntegerSubtract(CurrentTime,
|
|
DeviceExtension->TimerStart);
|
|
|
|
DeviceExtension->ElapsedTime
|
|
= RtlLargeIntegerAdd(DeviceExtension->ElapsedTime,
|
|
TimeThisTransfer);
|
|
}
|
|
|
|
} // StopPerfTimer
|
|
|
|
|
|
/************************************************************************/
|
|
/* BytesPerSecond */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Start perf timer for measuring bytes/second throughput */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to device extension for device */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* ULONG - bytes/second for device */
|
|
/* */
|
|
/************************************************************************/
|
|
ULONG
|
|
BytesPerSecond(IN OUT PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
ULONG Remainder;
|
|
LARGE_INTEGER Result;
|
|
LARGE_INTEGER TicksPerSecond;
|
|
|
|
PAGED_CODE();
|
|
|
|
// get ticks per second from perf counter
|
|
KeQueryPerformanceCounter(&TicksPerSecond);
|
|
|
|
// scale the bytes xfered
|
|
Result = RtlExtendedIntegerMultiply(DeviceExtension->BytesXfered,
|
|
TicksPerSecond.LowPart);
|
|
|
|
// Don't divide by 0
|
|
DeviceExtension->ElapsedTime.LowPart
|
|
= (DeviceExtension->ElapsedTime.LowPart == 0L) ? 1 :
|
|
DeviceExtension->ElapsedTime.LowPart;
|
|
|
|
// lets get stats here
|
|
Result
|
|
= RtlExtendedLargeIntegerDivide(Result,
|
|
DeviceExtension->ElapsedTime.LowPart,
|
|
&Remainder);
|
|
|
|
return Result.LowPart;
|
|
} // BytesPerSecond
|
|
|
|
|
|
/************************************************************************/
|
|
/* CallUSBD */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Call USB bus driver. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Urb - pointer to URB */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
CallUSBD(IN PDEVICE_OBJECT DeviceObject, IN PURB Urb)
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
PIO_STACK_LOCATION NextStack;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter CallUSBD");
|
|
|
|
// issue a synchronous request
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
Irp = IoAllocateIrp(DeviceExtension->StackDeviceObject->StackSize, FALSE);
|
|
|
|
if (Irp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// Set the Irp parameters
|
|
NextStack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
|
|
NextStack->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
NextStack->Parameters.Others.Argument1 = Urb;
|
|
|
|
// Set the completion routine, which will signal the event
|
|
IoSetCompletionRoutine(Irp,
|
|
CallUSBD_SyncCompletionRoutine,
|
|
&Event,
|
|
TRUE, // InvokeOnSuccess
|
|
TRUE, // InvokeOnError
|
|
TRUE); // InvokeOnCancel
|
|
|
|
DEBUG_LOG_PATH("Calling USB driver stack");
|
|
|
|
NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, Irp);
|
|
|
|
DEBUG_LOG_PATH("Returned from calling USB driver stack");
|
|
|
|
// block on pending request
|
|
if(NtStatus == STATUS_PENDING)
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
|
|
// Specify a timeout of 30 seconds to wait for this call to complete.
|
|
//
|
|
timeout.QuadPart = -10000 * 30000;
|
|
|
|
NtStatus = KeWaitForSingleObject(&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&timeout);
|
|
|
|
if(NtStatus == STATUS_TIMEOUT)
|
|
{
|
|
NtStatus = STATUS_IO_TIMEOUT;
|
|
|
|
// Cancel the Irp we just sent.
|
|
//
|
|
IoCancelIrp(Irp);
|
|
|
|
// And wait until the cancel completes
|
|
//
|
|
KeWaitForSingleObject(&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
NtStatus = Irp->IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
DEBUG_LOG_PATH("exit CallUSBD");
|
|
|
|
return NtStatus;
|
|
} // CallUSBD
|
|
|
|
/************************************************************************/
|
|
/* CallUSBD_SyncCompletionRoutine */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Completion routine for USB sync request. */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
CallUSBD_SyncCompletionRoutine(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context)
|
|
{
|
|
PKEVENT kevent;
|
|
|
|
kevent = (PKEVENT) Context;
|
|
|
|
KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} // CallUSBD_SyncCompletionRoutine
|
|
|
|
|
|
/************************************************************************/
|
|
/* GetDeviceDescriptor */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Get device descriptor for USB device. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
GetDeviceDescriptor(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS NtStatus;
|
|
PUSB_DEVICE_DESCRIPTOR DeviceDescriptor;
|
|
PURB Urb;
|
|
ULONG Size;
|
|
ULONG UrbCDRSize;
|
|
KIRQL OldIrql;
|
|
|
|
DEBUG_LOG_PATH("enter GetDeviceDescriptor");
|
|
|
|
UrbCDRSize = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST);
|
|
|
|
Urb = DEBUG_MEMALLOC(NonPagedPool, UrbCDRSize);
|
|
|
|
if (Urb) {
|
|
Size = sizeof(USB_DEVICE_DESCRIPTOR);
|
|
|
|
DeviceDescriptor = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if (DeviceDescriptor) {
|
|
|
|
UsbBuildGetDescriptorRequest(Urb, (USHORT)UrbCDRSize,
|
|
USB_DEVICE_DESCRIPTOR_TYPE, 0, 0,
|
|
DeviceDescriptor, NULL, Size, NULL);
|
|
|
|
NtStatus = CallUSBD(DeviceObject, Urb);
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
DEBUG_TRACE3(("Device Descriptor (%08X)\n", DeviceDescriptor));
|
|
DEBUG_TRACE3(("Length (%08X)\n",
|
|
Urb->UrbControlDescriptorRequest
|
|
.TransferBufferLength));
|
|
DEBUG_TRACE3(("Device Descriptor:\n"));
|
|
DEBUG_TRACE3(("-------------------------\n"));
|
|
DEBUG_TRACE3(("bLength (%08X)\n",
|
|
DeviceDescriptor->bLength));
|
|
DEBUG_TRACE3(("bDescriptorType (%08X)\n",
|
|
DeviceDescriptor->bDescriptorType));
|
|
DEBUG_TRACE3(("bcdUSB (%08X)\n",
|
|
DeviceDescriptor->bcdUSB));
|
|
DEBUG_TRACE3(("bDeviceClass (%08X)\n",
|
|
DeviceDescriptor->bDeviceClass));
|
|
DEBUG_TRACE3(("bDeviceSubClass (%08X)\n",
|
|
DeviceDescriptor->bDeviceSubClass));
|
|
DEBUG_TRACE3(("bDeviceProtocol (%08X)\n",
|
|
DeviceDescriptor->bDeviceProtocol));
|
|
DEBUG_TRACE3(("bMaxPacketSize0 (%08X)\n",
|
|
DeviceDescriptor->bMaxPacketSize0));
|
|
DEBUG_TRACE3(("idVendor (%08X)\n",
|
|
DeviceDescriptor->idVendor));
|
|
DEBUG_TRACE3(("idProduct (%08X)\n",
|
|
DeviceDescriptor->idProduct));
|
|
DEBUG_TRACE3(("bcdDevice (%08X)\n",
|
|
DeviceDescriptor->bcdDevice));
|
|
DEBUG_TRACE3(("iManufacturer (%08X)\n",
|
|
DeviceDescriptor->iManufacturer));
|
|
DEBUG_TRACE3(("iProduct (%08X)\n",
|
|
DeviceDescriptor->iProduct));
|
|
DEBUG_TRACE3(("iSerialNumber (%08X)\n",
|
|
DeviceDescriptor->iSerialNumber));
|
|
DEBUG_TRACE3(("bNumConfigurations (%08X)\n",
|
|
DeviceDescriptor->bNumConfigurations));
|
|
}
|
|
} else {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// save the device descriptor
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
PVOID pOldDesc = NULL;
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
if (DeviceExtension->DeviceDescriptor) {
|
|
pOldDesc = DeviceExtension->DeviceDescriptor;
|
|
}
|
|
DeviceExtension->DeviceDescriptor = DeviceDescriptor;
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
if (pOldDesc != NULL) {
|
|
DEBUG_MEMFREE(pOldDesc);
|
|
}
|
|
} else if (DeviceDescriptor) {
|
|
DEBUG_MEMFREE(DeviceDescriptor);
|
|
}
|
|
|
|
DEBUG_MEMFREE(Urb);
|
|
|
|
} else {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit GetDeviceDescriptor");
|
|
|
|
return NtStatus;
|
|
} // GetDeviceDescriptor
|
|
|
|
|
|
/************************************************************************/
|
|
/* ConfigureDevice */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Initializes USB device and selects configuration. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
ConfigureDevice(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
= DeviceObject->DeviceExtension;
|
|
NTSTATUS NtStatus;
|
|
PURB Urb;
|
|
ULONG Size;
|
|
ULONG UrbCDRSize;
|
|
PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor;
|
|
ULONG NumConfigs;
|
|
UCHAR Config;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter ConfigureDevice");
|
|
|
|
UrbCDRSize = sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST);
|
|
|
|
// first configure the device
|
|
Urb = DEBUG_MEMALLOC(NonPagedPool, UrbCDRSize);
|
|
|
|
if (Urb) {
|
|
|
|
// there may be problems with the 82930 chip, so make this buffer bigger
|
|
// to prevent choking
|
|
Size = sizeof(USB_CONFIGURATION_DESCRIPTOR) + 256;
|
|
|
|
// get the number of configurations
|
|
NumConfigs = DeviceExtension->DeviceDescriptor->bNumConfigurations;
|
|
|
|
// run through all of the configurations looking for a CDC device
|
|
for (Config = 0; Config < NumConfigs; Config++) {
|
|
|
|
// we will probably only do this once, maybe twice
|
|
while (TRUE) {
|
|
|
|
ConfigurationDescriptor = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if (ConfigurationDescriptor) {
|
|
UsbBuildGetDescriptorRequest(Urb, (USHORT)UrbCDRSize,
|
|
USB_CONFIGURATION_DESCRIPTOR_TYPE,
|
|
Config, 0, ConfigurationDescriptor,
|
|
NULL, Size, NULL);
|
|
|
|
NtStatus = CallUSBD(DeviceObject, Urb);
|
|
|
|
DEBUG_TRACE3(("Configuration Descriptor (%08X) "
|
|
"Length (%08X)\n", ConfigurationDescriptor,
|
|
Urb->UrbControlDescriptorRequest
|
|
.TransferBufferLength));
|
|
} else {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
// see if we got enough data, we may get an error in URB because of
|
|
// buffer overrun
|
|
if (Urb->UrbControlDescriptorRequest.TransferBufferLength>0 &&
|
|
ConfigurationDescriptor->wTotalLength > Size) {
|
|
// size of data exceeds current buffer size, so allocate correct
|
|
// size
|
|
Size = ConfigurationDescriptor->wTotalLength;
|
|
DEBUG_MEMFREE(ConfigurationDescriptor);
|
|
ConfigurationDescriptor = NULL;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
NtStatus = SelectInterface(DeviceObject, ConfigurationDescriptor);
|
|
DEBUG_MEMFREE(ConfigurationDescriptor);
|
|
ConfigurationDescriptor = NULL;
|
|
}
|
|
else
|
|
{
|
|
DEBUG_MEMFREE(ConfigurationDescriptor);
|
|
ConfigurationDescriptor = NULL;
|
|
}
|
|
|
|
|
|
// found a config we like
|
|
if (NT_SUCCESS(NtStatus))
|
|
break;
|
|
}
|
|
|
|
DEBUG_MEMFREE(Urb);
|
|
} else {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
DEBUG_LOG_PATH("exit ConfigureDevice");
|
|
|
|
return NtStatus;
|
|
} // ConfigureDevice
|
|
|
|
|
|
/************************************************************************/
|
|
/* SelectInterface */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Select interface for USB device. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* ConfigurationDescriptor - pointer to config descriptor */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
SelectInterface(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
= DeviceObject->DeviceExtension;
|
|
NTSTATUS NtStatus;
|
|
PURB Urb;
|
|
USHORT Size;
|
|
ULONG Index;
|
|
PUSBD_INTERFACE_INFORMATION Interfaces[2];
|
|
PUSBD_INTERFACE_INFORMATION Interface;
|
|
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor[2];
|
|
UCHAR AlternateSetting, InterfaceNumber;
|
|
ULONG Pipe;
|
|
KIRQL OldIrql;
|
|
PUCHAR Temp;
|
|
BOOLEAN FoundCommDevice = FALSE;
|
|
|
|
DEBUG_LOG_PATH("enter SelectInterface");
|
|
|
|
Urb = USBD_CreateConfigurationRequest(ConfigurationDescriptor, &Size);
|
|
|
|
if (Urb) {
|
|
Temp = (PUCHAR) &Urb->UrbSelectConfiguration.Interface;
|
|
|
|
for (InterfaceNumber = 0;
|
|
InterfaceNumber < ConfigurationDescriptor->bNumInterfaces;
|
|
InterfaceNumber++) {
|
|
AlternateSetting = 0;
|
|
|
|
InterfaceDescriptor[InterfaceNumber] =
|
|
USBD_ParseConfigurationDescriptor(ConfigurationDescriptor,
|
|
InterfaceNumber,
|
|
AlternateSetting);
|
|
|
|
Interfaces[InterfaceNumber] = (PUSBD_INTERFACE_INFORMATION) Temp;
|
|
|
|
Interfaces[InterfaceNumber]->Length
|
|
= GET_USBD_INTERFACE_SIZE(InterfaceDescriptor[InterfaceNumber]
|
|
->bNumEndpoints);
|
|
Interfaces[InterfaceNumber]->InterfaceNumber
|
|
= InterfaceDescriptor[InterfaceNumber]->bInterfaceNumber;
|
|
Interfaces[InterfaceNumber]->AlternateSetting
|
|
= InterfaceDescriptor[InterfaceNumber]->bAlternateSetting;
|
|
|
|
for (Index = 0; Index < Interfaces[InterfaceNumber]->NumberOfPipes;
|
|
Index++)
|
|
{
|
|
PUSBD_PIPE_INFORMATION PipeInformation;
|
|
|
|
PipeInformation = &Interfaces[InterfaceNumber]->Pipes[Index];
|
|
|
|
if (USB_ENDPOINT_DIRECTION_IN(PipeInformation->EndpointAddress))
|
|
{
|
|
// check for data in pipe
|
|
if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK)
|
|
{
|
|
// set bulk pipe in max transfer size
|
|
PipeInformation->MaximumTransferSize
|
|
= USB_RX_BUFF_SIZE;
|
|
}
|
|
}
|
|
else if (USB_ENDPOINT_DIRECTION_OUT(PipeInformation->EndpointAddress))
|
|
{
|
|
// check for data out pipe
|
|
if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK)
|
|
{
|
|
|
|
// set bulk pipe out max transfer size
|
|
PipeInformation->MaximumTransferSize
|
|
= MAXIMUM_TRANSFER_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
Temp += Interfaces[InterfaceNumber]->Length;
|
|
}
|
|
|
|
|
|
UsbBuildSelectConfigurationRequest(Urb, Size, ConfigurationDescriptor);
|
|
|
|
NtStatus = CallUSBD(DeviceObject, Urb);
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
ExFreePool(Urb);
|
|
goto ExitSelectInterface;
|
|
}
|
|
|
|
DEBUG_TRACE3(("Select Config Status (%08X)\n", NtStatus));
|
|
|
|
DeviceExtension->ConfigurationHandle
|
|
= Urb->UrbSelectConfiguration.ConfigurationHandle;
|
|
|
|
for (InterfaceNumber = 0;
|
|
InterfaceNumber < ConfigurationDescriptor->bNumInterfaces;
|
|
InterfaceNumber++) {
|
|
|
|
Interface = Interfaces[InterfaceNumber];
|
|
|
|
DEBUG_TRACE3(("---------\n"));
|
|
DEBUG_TRACE3(("NumberOfPipes (%08X)\n", Interface->NumberOfPipes));
|
|
DEBUG_TRACE3(("Length (%08X)\n", Interface->Length));
|
|
DEBUG_TRACE3(("Alt Setting (%08X)\n",
|
|
Interface->AlternateSetting));
|
|
DEBUG_TRACE3(("Interface Number (%08X)\n",
|
|
Interface->InterfaceNumber));
|
|
DEBUG_TRACE3(("Class (%08X) SubClass (%08X) Protocol (%08X)\n",
|
|
Interface->Class,
|
|
Interface->SubClass,
|
|
Interface->Protocol));
|
|
|
|
if (Interface->Class == USB_COMM_COMMUNICATION_CLASS_CODE) {
|
|
FoundCommDevice = TRUE;
|
|
DeviceExtension->CommInterface = Interface->InterfaceNumber;
|
|
}
|
|
|
|
for (Pipe = 0; Pipe < Interface->NumberOfPipes; Pipe++) {
|
|
PUSBD_PIPE_INFORMATION PipeInformation;
|
|
|
|
PipeInformation = &Interface->Pipes[Pipe];
|
|
|
|
DEBUG_TRACE3(("---------\n"));
|
|
DEBUG_TRACE3(("PipeType (%08X)\n",
|
|
PipeInformation->PipeType));
|
|
DEBUG_TRACE3(("EndpointAddress (%08X)\n",
|
|
PipeInformation->EndpointAddress));
|
|
DEBUG_TRACE3(("MaxPacketSize (%08X)\n",
|
|
PipeInformation->MaximumPacketSize));
|
|
DEBUG_TRACE3(("Interval (%08X)\n",
|
|
PipeInformation->Interval));
|
|
DEBUG_TRACE3(("Handle (%08X)\n",
|
|
PipeInformation->PipeHandle));
|
|
DEBUG_TRACE3(("MaximumTransferSize (%08X)\n",
|
|
PipeInformation->MaximumTransferSize));
|
|
|
|
// now lets save pipe handles in device extension
|
|
if (USB_ENDPOINT_DIRECTION_IN(PipeInformation->EndpointAddress)) {
|
|
// check for data in pipe
|
|
if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK) {
|
|
PVOID pOldNotBuff = NULL;
|
|
PVOID pOldReadBuff = NULL;
|
|
PVOID pOldUSBReadBuff = NULL;
|
|
PVOID pNewNotBuff = NULL;
|
|
PVOID pNewReadBuff = NULL;
|
|
PVOID pNewUSBReadBuff = NULL;
|
|
|
|
DeviceExtension->RxMaxPacketSize = RxBuffSize;
|
|
|
|
if (DeviceExtension->RxMaxPacketSize != 0) {
|
|
pNewReadBuff = DEBUG_MEMALLOC(NonPagedPool,
|
|
DeviceExtension->RxMaxPacketSize);
|
|
}
|
|
|
|
pNewNotBuff = DEBUG_MEMALLOC(NonPagedPool,
|
|
NOTIFICATION_BUFF_SIZE);
|
|
|
|
pNewUSBReadBuff = DEBUG_MEMALLOC(NonPagedPool,
|
|
USB_RX_BUFF_SIZE);
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
DeviceExtension->DataInPipe = PipeInformation->PipeHandle;
|
|
|
|
if (DeviceExtension->NotificationBuff)
|
|
pOldNotBuff = DeviceExtension->NotificationBuff;
|
|
|
|
if (DeviceExtension->ReadBuff)
|
|
pOldReadBuff = DeviceExtension->ReadBuff;
|
|
|
|
if (DeviceExtension->USBReadBuff)
|
|
pOldUSBReadBuff = DeviceExtension->USBReadBuff;
|
|
|
|
DeviceExtension->RxQueueSize
|
|
= DeviceExtension->RxMaxPacketSize;
|
|
DeviceExtension->CharsInReadBuff = 0;
|
|
DeviceExtension->CurrentReadBuffPtr = 0;
|
|
|
|
DeviceExtension->ReadBuff = pNewReadBuff;
|
|
|
|
DeviceExtension->USBReadBuff = pNewUSBReadBuff;
|
|
|
|
DeviceExtension->NotificationBuff = pNewNotBuff;
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
if (pOldNotBuff != NULL) {
|
|
DEBUG_MEMFREE(pOldNotBuff);
|
|
}
|
|
|
|
if (pOldReadBuff != NULL) {
|
|
DEBUG_MEMFREE(pOldReadBuff);
|
|
}
|
|
|
|
if (pOldUSBReadBuff != NULL) {
|
|
DEBUG_MEMFREE(pOldUSBReadBuff);
|
|
}
|
|
}
|
|
// check for notification pipe
|
|
else if (PipeInformation->PipeType
|
|
== USB_ENDPOINT_TYPE_INTERRUPT)
|
|
DeviceExtension->NotificationPipe
|
|
= PipeInformation->PipeHandle;
|
|
} else {
|
|
// check for data out pipe
|
|
if (PipeInformation->PipeType == USB_ENDPOINT_TYPE_BULK)
|
|
DeviceExtension->DataOutPipe = PipeInformation->PipeHandle;
|
|
}
|
|
}
|
|
|
|
DEBUG_TRACE3(("Data Out (%08X) Data In (%08X) Notification (%08X)\n",
|
|
DeviceExtension->DataOutPipe,
|
|
DeviceExtension->DataInPipe,
|
|
DeviceExtension->NotificationPipe));
|
|
|
|
}
|
|
ExFreePool(Urb);
|
|
} else {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (!FoundCommDevice)
|
|
NtStatus = STATUS_NO_SUCH_DEVICE;
|
|
|
|
ExitSelectInterface:;
|
|
|
|
DEBUG_LOG_PATH("exit SelectInterface");
|
|
|
|
return NtStatus;
|
|
} // SelectInterface
|
|
|
|
|
|
/************************************************************************/
|
|
/* BuildRequest */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Build a Urb for a USB request */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Irp - pointer to Irp */
|
|
/* PipeHandle - USB pipe handle */
|
|
/* Read - transfer direction */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* Pointer to URB */
|
|
/* */
|
|
/************************************************************************/
|
|
PURB
|
|
BuildRequest(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
|
|
IN USBD_PIPE_HANDLE PipeHandle, IN BOOLEAN Read)
|
|
{
|
|
ULONG Size;
|
|
ULONG Length;
|
|
PURB Urb;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter BuildRequest");
|
|
|
|
// length of buffer
|
|
Length = MmGetMdlByteCount(Irp->MdlAddress);
|
|
|
|
// allocate and zero Urb
|
|
Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
|
Urb = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if (Urb) {
|
|
RtlZeroMemory(Urb, Size);
|
|
|
|
Urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) Size;
|
|
Urb->UrbBulkOrInterruptTransfer.Hdr.Function =
|
|
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
|
|
Urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
|
|
Urb->UrbBulkOrInterruptTransfer.TransferFlags =
|
|
Read ? USBD_TRANSFER_DIRECTION_IN : 0;
|
|
|
|
// use an MDL
|
|
Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = Irp->MdlAddress;
|
|
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = Length;
|
|
|
|
// short packet is not treated as an error.
|
|
Urb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK;
|
|
|
|
// no linkage for now
|
|
Urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit BuildRequest");
|
|
|
|
return Urb;
|
|
} // BuildRequest
|
|
|
|
|
|
/************************************************************************/
|
|
/* BuildReadRequest */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Build a Urb for a USB read request */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* Urb - pointer to URB */
|
|
/* Buffer - pointer to data buffer */
|
|
/* Length - length of data buffer */
|
|
/* PipeHandle - USB pipe handle */
|
|
/* Read - transfer direction */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
BuildReadRequest(PURB Urb, PUCHAR Buffer, ULONG Length,
|
|
IN USBD_PIPE_HANDLE PipeHandle, IN BOOLEAN Read)
|
|
{
|
|
ULONG Size;
|
|
|
|
// PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter BuildReadRequest");
|
|
|
|
Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
|
|
|
// zero Urb
|
|
RtlZeroMemory(Urb, Size);
|
|
|
|
Urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) Size;
|
|
Urb->UrbBulkOrInterruptTransfer.Hdr.Function =
|
|
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
|
|
Urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
|
|
Urb->UrbBulkOrInterruptTransfer.TransferFlags =
|
|
Read ? USBD_TRANSFER_DIRECTION_IN : 0;
|
|
|
|
// we are using a tranfsfer buffer instead of an MDL
|
|
Urb->UrbBulkOrInterruptTransfer.TransferBuffer = Buffer;
|
|
Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = Length;
|
|
Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
|
|
|
|
// short packet is not treated as an error.
|
|
Urb->UrbBulkOrInterruptTransfer.TransferFlags |= USBD_SHORT_TRANSFER_OK;
|
|
|
|
// no linkage for now
|
|
Urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
|
|
|
|
DEBUG_LOG_PATH("exit BuildReadRequest");
|
|
|
|
} // BuildReadRequest
|
|
|
|
|
|
/************************************************************************/
|
|
/* ClassVendorCommand */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Issue class or vendor specific command */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Request - request field of class/vendor specific command */
|
|
/* Value - value field of class/vendor specific command */
|
|
/* Index - index field of class/vendor specific command */
|
|
/* Buffer - pointer to data buffer */
|
|
/* BufferLen - data buffer length */
|
|
/* Read - data direction flag */
|
|
/* Class - True if Class Command, else vendor command */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
ClassVendorCommand(IN PDEVICE_OBJECT DeviceObject, IN UCHAR Request,
|
|
IN USHORT Value, IN USHORT Index, IN PVOID Buffer,
|
|
IN OUT PULONG BufferLen, IN BOOLEAN Read, IN ULONG ComType)
|
|
{
|
|
NTSTATUS NtStatus;
|
|
PURB Urb;
|
|
ULONG Size;
|
|
ULONG Length;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter VendorCommand");
|
|
|
|
// length of buffer passed in
|
|
Length = BufferLen ? *BufferLen : 0;
|
|
|
|
Size = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
|
|
|
|
// allocate memory for the Urb
|
|
Urb = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if (Urb) {
|
|
UsbBuildVendorRequest(Urb, ComType == USBSER_CLASS_COMMAND
|
|
? URB_FUNCTION_CLASS_INTERFACE
|
|
: URB_FUNCTION_VENDOR_DEVICE, (USHORT) Size,
|
|
Read ? USBD_TRANSFER_DIRECTION_IN
|
|
: USBD_TRANSFER_DIRECTION_OUT, 0, Request, Value,
|
|
Index, Buffer, NULL, Length, NULL);
|
|
|
|
NtStatus = CallUSBD(DeviceObject, Urb);
|
|
|
|
// get length of buffer
|
|
if (BufferLen)
|
|
*BufferLen = Urb->UrbControlVendorClassRequest.TransferBufferLength;
|
|
|
|
DEBUG_MEMFREE(Urb);
|
|
} else {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
DEBUG_LOG_PATH("exit VendorCommand");
|
|
|
|
return NtStatus;
|
|
} // ClassVendorCommand
|
|
|
|
|
|
/************************************************************************/
|
|
/* CancelPendingWaitMasks */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Cancels any wait masks in progress. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to a device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
CancelPendingWaitMasks(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
KIRQL OldIrql;
|
|
PIRP CurrentMaskIrp;
|
|
|
|
DEBUG_LOG_PATH("enter CancelPendingWaitMasks");
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">CancelPendingWaitMasks\n"));
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
CurrentMaskIrp = DeviceExtension->CurrentMaskIrp;
|
|
|
|
// mark current pending wait mask as cancelled
|
|
if(CurrentMaskIrp)
|
|
{
|
|
DeviceExtension->CurrentMaskIrp = NULL;
|
|
|
|
|
|
CurrentMaskIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
CurrentMaskIrp->IoStatus.Information = 0;
|
|
IoSetCancelRoutine(CurrentMaskIrp, NULL);
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
IoCompleteRequest(CurrentMaskIrp, IO_NO_INCREMENT);
|
|
DEBUG_TRACE1(("CancelPendingWaitMask\n"));
|
|
} else {
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit CancelPendingWaitMasks");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<CancelPendingWaitMasks\n"));
|
|
|
|
} // CancelPendingWaitMasks
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* StartRead */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Kick off a read. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to a device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
StartRead(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
PIRP ReadIrp;
|
|
PURB ReadUrb;
|
|
CCHAR StackSize;
|
|
ULONG Size;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter StartRead");
|
|
UsbSerSerialDump(USBSERTRACERD, (">StartRead\n"));
|
|
|
|
// get stack size for Irp and allocate one that we will use to keep
|
|
// read requests going
|
|
StackSize = (CCHAR)(DeviceExtension->StackDeviceObject->StackSize + 1);
|
|
|
|
ReadIrp = IoAllocateIrp(StackSize, FALSE);
|
|
|
|
if (ReadIrp) {
|
|
// get size of Urb and allocate
|
|
Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
|
|
|
ReadUrb = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if (ReadUrb) {
|
|
|
|
KeInitializeEvent(&DeviceExtension->ReadEvent, NotificationEvent,
|
|
FALSE);
|
|
|
|
// save these to be freed when not needed
|
|
|
|
UsbSerFetchPVoidLocked(&DeviceExtension->ReadIrp, ReadIrp,
|
|
&DeviceExtension->ControlLock);
|
|
|
|
UsbSerFetchPVoidLocked(&DeviceExtension->ReadUrb, ReadUrb,
|
|
&DeviceExtension->ControlLock);
|
|
|
|
RestartRead(DeviceExtension);
|
|
}
|
|
}
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("<StartRead\n"));
|
|
DEBUG_LOG_PATH("exit StartRead");
|
|
} // StartRead
|
|
|
|
|
|
/************************************************************************/
|
|
/* RestartRead */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Restart read request. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to a device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
RestartRead(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
PIRP ReadIrp;
|
|
PURB ReadUrb;
|
|
PIO_STACK_LOCATION NextStack;
|
|
BOOLEAN StartAnotherRead;
|
|
KIRQL OldIrql;
|
|
NTSTATUS NtStatus;
|
|
|
|
DEBUG_LOG_PATH("enter RestartRead");
|
|
UsbSerSerialDump(USBSERTRACERD, (">RestartRead\n"));
|
|
|
|
do
|
|
{
|
|
StartAnotherRead = FALSE;
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
if(!DeviceExtension->ReadInProgress && DeviceExtension->CharsInReadBuff <= LOW_WATER_MARK
|
|
&& DeviceExtension->AcceptingRequests)
|
|
{
|
|
StartAnotherRead = TRUE;
|
|
DeviceExtension->ReadInProgress = TRUE;
|
|
DeviceExtension->ReadInterlock = START_READ;
|
|
}
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
if(StartAnotherRead)
|
|
{
|
|
ReadIrp = DeviceExtension->ReadIrp;
|
|
ReadUrb = DeviceExtension->ReadUrb;
|
|
|
|
BuildReadRequest(ReadUrb, DeviceExtension->USBReadBuff,
|
|
USB_RX_BUFF_SIZE,
|
|
DeviceExtension->DataInPipe, TRUE);
|
|
|
|
// set Irp up for a submit Urb IOCTL
|
|
NextStack = IoGetNextIrpStackLocation(ReadIrp);
|
|
NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextStack->Parameters.Others.Argument1 = ReadUrb;
|
|
NextStack->Parameters.DeviceIoControl.IoControlCode
|
|
= IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
// completion routine will take care of updating buffers and counters
|
|
IoSetCompletionRoutine(ReadIrp,ReadCompletion, DeviceExtension, TRUE,
|
|
TRUE, TRUE);
|
|
|
|
DEBUG_TRACE1(("StartRead\n"));
|
|
|
|
InterlockedIncrement(&DeviceExtension->PendingDataInCount);
|
|
UsbSerSerialDump(USBSERTRACERD, ("DataInCounty %08X @ %08X\n",
|
|
DeviceExtension->PendingDataInCount,
|
|
&DeviceExtension->PendingDataInCount));
|
|
|
|
NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, ReadIrp);
|
|
|
|
DEBUG_TRACE1(("Read Status (%08X)\n", NtStatus));
|
|
|
|
if(!NT_SUCCESS(NtStatus))
|
|
{
|
|
if(InterlockedDecrement(&DeviceExtension->PendingDataInCount) == 0)
|
|
{
|
|
KeSetEvent(&DeviceExtension->PendingDataInEvent, IO_NO_INCREMENT,
|
|
FALSE);
|
|
UsbSerSerialDump(USBSERTRACERD, ("DataInCountz %08X @ %08X\n",
|
|
DeviceExtension->PendingDataInCount,
|
|
&DeviceExtension->PendingDataInCount));
|
|
}
|
|
}
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
if(DeviceExtension->ReadInterlock == IMMEDIATE_READ)
|
|
{
|
|
StartAnotherRead = TRUE;
|
|
}
|
|
else
|
|
{
|
|
StartAnotherRead = FALSE;
|
|
}
|
|
|
|
DeviceExtension->ReadInterlock = END_READ;
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
}
|
|
}while(StartAnotherRead)
|
|
|
|
DEBUG_LOG_PATH("exit RestartRead");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<RestartRead\n"));
|
|
} // RestartRead
|
|
|
|
|
|
/************************************************************************/
|
|
/* StartNotifyRead */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Kick off a notify read. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to a device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
StartNotifyRead(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
PIRP NotifyIrp;
|
|
PURB NotifyUrb;
|
|
CCHAR StackSize;
|
|
ULONG Size;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter StartNotifyRead");
|
|
UsbSerSerialDump(USBSERTRACERD, (">StartNotifyRead\n"));
|
|
|
|
// get stack size for Irp and allocate one that we will use to keep
|
|
// notification requests going
|
|
StackSize = (CCHAR)(DeviceExtension->StackDeviceObject->StackSize + 1);
|
|
|
|
NotifyIrp = IoAllocateIrp(StackSize, FALSE);
|
|
|
|
if (NotifyIrp) {
|
|
// get size of Urb and allocate
|
|
Size = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
|
|
|
|
NotifyUrb = DEBUG_MEMALLOC(NonPagedPool, Size);
|
|
|
|
if (NotifyUrb) {
|
|
// save these to be freed when not needed
|
|
UsbSerFetchPVoidLocked(&DeviceExtension->NotifyIrp, NotifyIrp,
|
|
&DeviceExtension->ControlLock);
|
|
UsbSerFetchPVoidLocked(&DeviceExtension->NotifyUrb, NotifyUrb,
|
|
&DeviceExtension->ControlLock);
|
|
|
|
RestartNotifyRead(DeviceExtension);
|
|
}
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit StartNotifyRead");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<StartNotifyRead\n"));
|
|
} // StartNotifyRead
|
|
|
|
|
|
/************************************************************************/
|
|
/* RestartNotifyRead */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Kick off a notify read. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to a device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
RestartNotifyRead(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
PIRP NotifyIrp;
|
|
PURB NotifyUrb;
|
|
PIO_STACK_LOCATION NextStack;
|
|
NTSTATUS NtStatus;
|
|
|
|
|
|
DEBUG_LOG_PATH("enter RestartNotifyRead");
|
|
UsbSerSerialDump(USBSERTRACERD, (">RestartNotifyRead\n"));
|
|
|
|
NotifyUrb = DeviceExtension->NotifyUrb;
|
|
NotifyIrp = DeviceExtension->NotifyIrp;
|
|
|
|
if(DeviceExtension->AcceptingRequests)
|
|
{
|
|
BuildReadRequest(NotifyUrb, DeviceExtension->NotificationBuff,
|
|
NOTIFICATION_BUFF_SIZE,
|
|
DeviceExtension->NotificationPipe, TRUE);
|
|
|
|
// set Irp up for a submit Urb IOCTL
|
|
NextStack = IoGetNextIrpStackLocation(NotifyIrp);
|
|
NextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextStack->Parameters.Others.Argument1 = NotifyUrb;
|
|
NextStack->Parameters.DeviceIoControl.IoControlCode
|
|
= IOCTL_INTERNAL_USB_SUBMIT_URB;
|
|
|
|
// completion routine will take care of updating buffers and counters
|
|
IoSetCompletionRoutine(NotifyIrp, NotifyCompletion, DeviceExtension,
|
|
TRUE, TRUE, TRUE);
|
|
|
|
DEBUG_TRACE1(("Start NotifyRead\n"));
|
|
|
|
InterlockedIncrement(&DeviceExtension->PendingNotifyCount);
|
|
|
|
NtStatus = IoCallDriver(DeviceExtension->StackDeviceObject, NotifyIrp);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
if (InterlockedDecrement(&DeviceExtension->PendingNotifyCount) == 0)
|
|
{
|
|
KeSetEvent(&DeviceExtension->PendingNotifyEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
DEBUG_TRACE1(("Status (%08X)\n", NtStatus));
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit RestartNotifyRead");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<RestartNotifyRead\n"));
|
|
} // RestartNotifyRead
|
|
|
|
|
|
/************************************************************************/
|
|
/* ReadCompletion */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Read completion routine. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceObject - pointer to a device object */
|
|
/* Irp - pointer to Irp */
|
|
/* Context - pointer to driver defined context */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* NTSTATUS */
|
|
/* */
|
|
/************************************************************************/
|
|
NTSTATUS
|
|
ReadCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
|
|
{
|
|
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) Context;
|
|
PURB Urb;
|
|
ULONG Count;
|
|
KIRQL OldIrql;
|
|
|
|
DEBUG_LOG_PATH("enter ReadCompletion");
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, (">ReadCompletion(%08X)\n", Irp));
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
Urb = DeviceExtension->ReadUrb;
|
|
|
|
Count = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)
|
|
&& (DeviceExtension->CurrentDevicePowerState == PowerDeviceD0))
|
|
{
|
|
|
|
DeviceExtension->HistoryMask |= SERIAL_EV_RXCHAR | SERIAL_EV_RX80FULL;
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
//
|
|
// Scan for RXFLAG char if needed
|
|
//
|
|
|
|
if(DeviceExtension->IsrWaitMask & SERIAL_EV_RXFLAG)
|
|
{
|
|
ULONG i;
|
|
|
|
for(i = 0; i < Count; i++)
|
|
{
|
|
if(*((PUCHAR)(DeviceExtension->USBReadBuff + i))
|
|
== DeviceExtension->SpecialChars.EventChar)
|
|
{
|
|
DeviceExtension->HistoryMask |= SERIAL_EV_RXFLAG;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
PutData(DeviceExtension, Count);
|
|
|
|
// got some data, let's see if we can satisfy any queued reads
|
|
CheckForQueuedReads(DeviceExtension);
|
|
|
|
DEBUG_TRACE1(("ReadCompletion (%08X)\n", DeviceExtension->CharsInReadBuff));
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
DeviceExtension->ReadInProgress = FALSE;
|
|
|
|
if(DeviceExtension->ReadInterlock == END_READ)
|
|
{
|
|
|
|
DeviceExtension->ReadInterlock = IMMEDIATE_READ;
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
RestartRead(DeviceExtension);
|
|
}
|
|
else
|
|
{
|
|
DeviceExtension->ReadInterlock = IMMEDIATE_READ;
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// the device is not accepting requests, so signal anyone who
|
|
// cancelled this or is waiting for it to stop
|
|
//
|
|
DeviceExtension->ReadInterlock = IMMEDIATE_READ;
|
|
|
|
DeviceExtension->ReadInProgress = FALSE;
|
|
|
|
DeviceExtension->AcceptingRequests = FALSE;
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
KeSetEvent(&DeviceExtension->ReadEvent, 1, FALSE);
|
|
|
|
DEBUG_TRACE1(("RC Irp Status (%08X)\n", Irp->IoStatus.Status));
|
|
}
|
|
|
|
|
|
//
|
|
// Notify everyone if this is the last IRP
|
|
//
|
|
|
|
if (InterlockedDecrement(&DeviceExtension->PendingDataInCount) == 0)
|
|
{
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("DataIn pipe is empty\n"));
|
|
KeSetEvent(&DeviceExtension->PendingDataInEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("DataInCount %08X @ %08X\n",
|
|
DeviceExtension->PendingDataInCount,
|
|
&DeviceExtension->PendingDataInCount));
|
|
|
|
|
|
DEBUG_LOG_PATH("exit ReadCompletion");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<ReadCompletion\n"));
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} // ReadCompletion
|
|
|
|
|
|
/************************************************************************/
|
|
/* GetData */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Get data from circular buffer. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to device extension */
|
|
/* Buffer - pointer to buffer */
|
|
/* BufferLen - size of buffer */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* ULONG */
|
|
/* */
|
|
/************************************************************************/
|
|
ULONG
|
|
GetData(IN PDEVICE_EXTENSION DeviceExtension, IN PCHAR Buffer,
|
|
IN ULONG BufferLen, IN OUT PULONG_PTR NewCount)
|
|
{
|
|
ULONG count;
|
|
KIRQL OldIrql;
|
|
|
|
DEBUG_LOG_PATH("enter GetData");
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, (">GetData\n"));
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
BufferLen = min(DeviceExtension->CharsInReadBuff, BufferLen);
|
|
|
|
if(BufferLen)
|
|
{
|
|
|
|
count = min(BufferLen, (DeviceExtension->RxMaxPacketSize - DeviceExtension->CurrentReadBuffPtr));
|
|
|
|
memcpy(Buffer,
|
|
&DeviceExtension->ReadBuff[DeviceExtension->CurrentReadBuffPtr],
|
|
count);
|
|
|
|
Buffer += count;
|
|
DeviceExtension->CurrentReadBuffPtr += count;
|
|
DeviceExtension->CharsInReadBuff -= count;
|
|
DeviceExtension->NumberNeededForRead -= count;
|
|
BufferLen -= count;
|
|
*NewCount += count;
|
|
|
|
// if there is still something left in the buffer, then we wrapped
|
|
if(BufferLen)
|
|
{
|
|
memcpy(Buffer, DeviceExtension->ReadBuff, BufferLen);
|
|
DeviceExtension->CurrentReadBuffPtr = BufferLen;
|
|
DeviceExtension->CharsInReadBuff -= BufferLen;
|
|
DeviceExtension->NumberNeededForRead -= BufferLen;
|
|
*NewCount += BufferLen;
|
|
}
|
|
|
|
}
|
|
|
|
DEBUG_TRACE2(("Count (%08X) CharsInReadBuff (%08X)\n", count, DeviceExtension->CharsInReadBuff));
|
|
|
|
#if DBG
|
|
if (UsbSerSerialDebugLevel & USBSERDUMPRD) {
|
|
ULONG i;
|
|
|
|
DbgPrint("RD: ");
|
|
|
|
for (i = 0; i < count; i++) {
|
|
DbgPrint("%02x ", Buffer[i] & 0xFF);
|
|
}
|
|
|
|
DbgPrint("\n\n");
|
|
}
|
|
#endif
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
|
|
RestartRead(DeviceExtension);
|
|
|
|
DEBUG_LOG_PATH("exit GetData");
|
|
UsbSerSerialDump(USBSERTRACERD, ("<GetData\n"));
|
|
|
|
return count;
|
|
} // GetData
|
|
|
|
/************************************************************************/
|
|
/* PutData */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Put data in circular buffer. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to device extension */
|
|
/* BufferLen - size of buffer */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
PutData(IN PDEVICE_EXTENSION DeviceExtension, IN ULONG BufferLen)
|
|
{
|
|
KIRQL OldIrql;
|
|
ULONG count;
|
|
ULONG BuffPtr;
|
|
|
|
if(BufferLen)
|
|
{
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
// get current pointer into circular buffer
|
|
BuffPtr = (DeviceExtension->CharsInReadBuff + DeviceExtension->CurrentReadBuffPtr) %
|
|
DeviceExtension->RxMaxPacketSize;
|
|
|
|
// figure out amount to copy into read buffer, in case we would right past end of buffer
|
|
count = min(BufferLen, (DeviceExtension->RxMaxPacketSize - BuffPtr));
|
|
|
|
memcpy(&DeviceExtension->ReadBuff[BuffPtr],
|
|
DeviceExtension->USBReadBuff, count);
|
|
|
|
// update counters
|
|
BufferLen -= count;
|
|
DeviceExtension->CharsInReadBuff += count;
|
|
DeviceExtension->ReadByIsr += count;
|
|
|
|
|
|
// if there is still something left in the buffer, then we wrapped
|
|
if(BufferLen)
|
|
{
|
|
// count still holds the amount copied from buffer on first copy
|
|
// and BufferLen holds the amount remaining to copy
|
|
memcpy(DeviceExtension->ReadBuff,
|
|
&DeviceExtension->USBReadBuff[count], BufferLen);
|
|
|
|
DeviceExtension->CharsInReadBuff += BufferLen;
|
|
DeviceExtension->ReadByIsr += BufferLen;
|
|
}
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
}
|
|
} // PutData
|
|
|
|
|
|
|
|
VOID
|
|
UsbSerRundownIrpRefs(IN PIRP *PpCurrentOpIrp, IN PKTIMER IntervalTimer OPTIONAL,
|
|
IN PKTIMER TotalTimer OPTIONAL,
|
|
IN PDEVICE_EXTENSION PDevExt)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs through the various items that *could*
|
|
have a reference to the current read/write. It try's to kill
|
|
the reason. If it does succeed in killing the reason it
|
|
will decrement the reference count on the irp.
|
|
|
|
NOTE: This routine assumes that it is called with the cancel
|
|
spin lock held.
|
|
|
|
Arguments:
|
|
|
|
PpCurrentOpIrp - Pointer to a pointer to current irp for the
|
|
particular operation.
|
|
|
|
IntervalTimer - Pointer to the interval timer for the operation.
|
|
NOTE: This could be null.
|
|
|
|
TotalTimer - Pointer to the total timer for the operation.
|
|
NOTE: This could be null.
|
|
|
|
PDevExt - Pointer to device extension
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
// PAGED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerRundownIrpRefs(%08X)\n",
|
|
*PpCurrentOpIrp));
|
|
|
|
//
|
|
// This routine is called with the cancel spin lock held
|
|
// so we know only one thread of execution can be in here
|
|
// at one time.
|
|
//
|
|
|
|
//
|
|
// First we see if there is still a cancel routine. If
|
|
// so then we can decrement the count by one.
|
|
//
|
|
|
|
if ((*PpCurrentOpIrp)->CancelRoutine) {
|
|
|
|
USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_CANCEL);
|
|
|
|
IoSetCancelRoutine(*PpCurrentOpIrp, NULL);
|
|
|
|
}
|
|
|
|
if (IntervalTimer) {
|
|
|
|
//
|
|
// Try to cancel the operations interval timer. If the operation
|
|
// returns true then the timer did have a reference to the
|
|
// irp. Since we've canceled this timer that reference is
|
|
// no longer valid and we can decrement the reference count.
|
|
//
|
|
// If the cancel returns false then this means either of two things:
|
|
//
|
|
// a) The timer has already fired.
|
|
//
|
|
// b) There never was an interval timer.
|
|
//
|
|
// In the case of "b" there is no need to decrement the reference
|
|
// count since the "timer" never had a reference to it.
|
|
//
|
|
// In the case of "a", then the timer itself will be coming
|
|
// along and decrement it's reference. Note that the caller
|
|
// of this routine might actually be the this timer, but it
|
|
// has already decremented the reference.
|
|
//
|
|
|
|
if (KeCancelTimer(IntervalTimer)) {
|
|
USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_INT_TIMER);
|
|
}
|
|
}
|
|
|
|
if (TotalTimer) {
|
|
|
|
//
|
|
// Try to cancel the operations total timer. If the operation
|
|
// returns true then the timer did have a reference to the
|
|
// irp. Since we've canceled this timer that reference is
|
|
// no longer valid and we can decrement the reference count.
|
|
//
|
|
// If the cancel returns false then this means either of two things:
|
|
//
|
|
// a) The timer has already fired.
|
|
//
|
|
// b) There never was an total timer.
|
|
//
|
|
// In the case of "b" there is no need to decrement the reference
|
|
// count since the "timer" never had a reference to it.
|
|
// //
|
|
// If we have an escape char event pending, we can't overstuff,
|
|
// so subtract one from the length
|
|
//
|
|
|
|
|
|
// In the case of "a", then the timer itself will be coming
|
|
// along and decrement it's reference. Note that the caller
|
|
// of this routine might actually be the this timer, but it
|
|
// has already decremented the reference.
|
|
//
|
|
|
|
if (KeCancelTimer(TotalTimer)) {
|
|
USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_TOTAL_TIMER);
|
|
}
|
|
}
|
|
|
|
// USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, USBSER_REF_RXBUFFER);
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerRundownIrpRefs\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerTryToCompleteCurrent(IN PDEVICE_EXTENSION PDevExt,
|
|
IN KIRQL IrqlForRelease, IN NTSTATUS StatusToUse,
|
|
IN PIRP *PpCurrentOpIrp,
|
|
IN PLIST_ENTRY PQueue OPTIONAL,
|
|
IN PKTIMER PIntervalTimer OPTIONAL,
|
|
IN PKTIMER PTotalTimer OPTIONAL,
|
|
IN PUSBSER_START_ROUTINE Starter OPTIONAL,
|
|
IN PUSBSER_GET_NEXT_ROUTINE PGetNextIrp OPTIONAL,
|
|
IN LONG RefType,
|
|
IN BOOLEAN Complete)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to kill all of the reasons there are
|
|
references on the current read/write. If everything can be killed
|
|
it will complete this read/write and try to start another.
|
|
|
|
NOTE: This routine assumes that it is called with the cancel
|
|
spinlock held.
|
|
|
|
Arguments:
|
|
|
|
Extension - Simply a pointer to the device extension.
|
|
|
|
SynchRoutine - A routine that will synchronize with the isr
|
|
and attempt to remove the knowledge of the
|
|
current irp from the isr. NOTE: This pointer
|
|
can be null.
|
|
|
|
IrqlForRelease - This routine is called with the cancel spinlock held.
|
|
This is the irql that was current when the cancel
|
|
spinlock was acquired.
|
|
|
|
StatusToUse - The irp's status field will be set to this value, if
|
|
this routine can complete the irp.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR,
|
|
(">UsbSerTryToCompleteCurrent(%08X)\n", *PpCurrentOpIrp));
|
|
//
|
|
// We can decrement the reference to "remove" the fact
|
|
// that the caller no longer will be accessing this irp.
|
|
//
|
|
|
|
USBSER_CLEAR_REFERENCE(*PpCurrentOpIrp, RefType);
|
|
|
|
//
|
|
// Try to run down all other references to this irp.
|
|
//
|
|
|
|
UsbSerRundownIrpRefs(PpCurrentOpIrp, PIntervalTimer, PTotalTimer, PDevExt);
|
|
|
|
//
|
|
// See if the ref count is zero after trying to kill everybody else.
|
|
//
|
|
|
|
if (!USBSER_REFERENCE_COUNT(*PpCurrentOpIrp)) {
|
|
|
|
PIRP pNewIrp;
|
|
|
|
|
|
//
|
|
// The ref count was zero so we should complete this
|
|
// request.
|
|
//
|
|
// The following call will also cause the current irp to be
|
|
// completed.
|
|
//
|
|
|
|
(*PpCurrentOpIrp)->IoStatus.Status = StatusToUse;
|
|
|
|
if (StatusToUse == STATUS_CANCELLED) {
|
|
|
|
(*PpCurrentOpIrp)->IoStatus.Information = 0;
|
|
|
|
}
|
|
|
|
if (PGetNextIrp) {
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, IrqlForRelease);
|
|
|
|
(*PGetNextIrp)(PpCurrentOpIrp, PQueue, &pNewIrp, Complete, PDevExt);
|
|
|
|
|
|
if (pNewIrp) {
|
|
|
|
Starter(PDevExt);
|
|
}
|
|
|
|
} else {
|
|
|
|
PIRP pOldIrp = *PpCurrentOpIrp;
|
|
|
|
//
|
|
// There was no get next routine. We will simply complete
|
|
// the irp. We should make sure that we null out the
|
|
// pointer to the pointer to this irp.
|
|
//
|
|
|
|
*PpCurrentOpIrp = NULL;
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, IrqlForRelease);
|
|
|
|
if (Complete) {
|
|
IoCompleteRequest(pOldIrp, IO_SERIAL_INCREMENT);
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, IrqlForRelease);
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR,
|
|
("Current IRP still has reference of %08X\n",
|
|
((UINT_PTR)((IoGetCurrentIrpStackLocation((*PpCurrentOpIrp))->
|
|
Parameters.Others.Argument4)))));
|
|
}
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR,
|
|
("<UsbSerTryToCompleteCurrent\n"));
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CheckForQueuedReads */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* See if we have any queued reads that we can satisfy. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
CheckForQueuedReads(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
ULONG charsRead = 0;
|
|
PULONG pWaitMask;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// May be paged if we do counter
|
|
//
|
|
|
|
DEBUG_LOG_PATH("enter CheckForQueuedReads");
|
|
UsbSerSerialDump(USBSERTRACERD, (">CheckForQueuedReads\n"));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &oldIrql);
|
|
|
|
if ((DeviceExtension->CurrentReadIrp != NULL)
|
|
&& (USBSER_REFERENCE_COUNT(DeviceExtension->CurrentReadIrp)
|
|
& USBSER_REF_RXBUFFER))
|
|
{
|
|
|
|
RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql);
|
|
|
|
DEBUG_TRACE3(("Reading 0x%x\n", DeviceExtension->NumberNeededForRead));
|
|
|
|
charsRead
|
|
= GetData(DeviceExtension,
|
|
((PUCHAR)(DeviceExtension->CurrentReadIrp
|
|
->AssociatedIrp.SystemBuffer))
|
|
+ (IoGetCurrentIrpStackLocation(DeviceExtension
|
|
->CurrentReadIrp))
|
|
->Parameters.Read.Length
|
|
- DeviceExtension->NumberNeededForRead,
|
|
DeviceExtension->NumberNeededForRead,
|
|
&DeviceExtension->CurrentReadIrp->IoStatus.Information);
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &oldIrql);
|
|
|
|
//
|
|
// See if this read is complete
|
|
//
|
|
|
|
if (DeviceExtension->NumberNeededForRead == 0) {
|
|
DEBUG_TRACE3(("USBSER: Completing read\n"));
|
|
|
|
if(DeviceExtension->CurrentReadIrp)
|
|
{
|
|
DeviceExtension->CurrentReadIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Mark the read as completed and try to service the next one
|
|
//
|
|
|
|
|
|
DeviceExtension->CountOnLastRead = SERIAL_COMPLETE_READ_COMPLETE;
|
|
|
|
#if DBG
|
|
if (UsbSerSerialDebugLevel & USBSERDUMPRD) {
|
|
ULONG i;
|
|
ULONG count;
|
|
|
|
if (DeviceExtension->CurrentReadIrp->IoStatus.Status == STATUS_SUCCESS) {
|
|
count = (ULONG)DeviceExtension->CurrentReadIrp->IoStatus.Information;
|
|
} else {
|
|
count = 0;
|
|
|
|
}
|
|
DbgPrint("RD2: A(%08X) G(%08X) I(%08X)\n",
|
|
IoGetCurrentIrpStackLocation(DeviceExtension->CurrentReadIrp)
|
|
->Parameters.Read.Length, count, DeviceExtension->CurrentReadIrp);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
DbgPrint("%02x ", *(((PUCHAR)DeviceExtension->CurrentReadIrp
|
|
->AssociatedIrp.SystemBuffer) + i) & 0xFF);
|
|
}
|
|
|
|
if (i == 0) {
|
|
DbgPrint("NULL (%08X)\n", DeviceExtension->CurrentReadIrp
|
|
->IoStatus.Status);
|
|
}
|
|
|
|
DbgPrint("\n\n");
|
|
}
|
|
#endif
|
|
|
|
|
|
UsbSerTryToCompleteCurrent(DeviceExtension, oldIrql, STATUS_SUCCESS,
|
|
&DeviceExtension->CurrentReadIrp,
|
|
&DeviceExtension->ReadQueue,
|
|
&DeviceExtension->ReadRequestIntervalTimer,
|
|
&DeviceExtension->ReadRequestTotalTimer,
|
|
UsbSerStartRead, UsbSerGetNextIrp,
|
|
USBSER_REF_RXBUFFER,
|
|
TRUE);
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &oldIrql);
|
|
}
|
|
}
|
|
|
|
if (DeviceExtension->IsrWaitMask & SERIAL_EV_RXCHAR) {
|
|
DeviceExtension->HistoryMask |= SERIAL_EV_RXCHAR;
|
|
}
|
|
|
|
if (DeviceExtension->CurrentMaskIrp != NULL) {
|
|
pWaitMask = (PULONG)DeviceExtension->CurrentMaskIrp->
|
|
AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Process events
|
|
//
|
|
|
|
if (DeviceExtension->IsrWaitMask & DeviceExtension->HistoryMask) {
|
|
PIRP pMaskIrp;
|
|
|
|
DEBUG_TRACE3(("Completing events\n"));
|
|
|
|
*pWaitMask = DeviceExtension->HistoryMask;
|
|
DeviceExtension->HistoryMask = 0;
|
|
pMaskIrp = DeviceExtension->CurrentMaskIrp;
|
|
|
|
pMaskIrp->IoStatus.Information = sizeof(ULONG);
|
|
pMaskIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
DeviceExtension->CurrentMaskIrp = NULL;
|
|
IoSetCancelRoutine(pMaskIrp, NULL);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql);
|
|
|
|
|
|
IoCompleteRequest(pMaskIrp, IO_SERIAL_INCREMENT);
|
|
|
|
}
|
|
else
|
|
{
|
|
RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RELEASE_CANCEL_SPINLOCK(DeviceExtension, oldIrql);
|
|
}
|
|
|
|
|
|
DEBUG_LOG_PATH("exit CheckForQueuedReads");
|
|
|
|
UsbSerSerialDump(USBSERTRACERD, ("<CheckForQueuedReads\n"));
|
|
} // CheckForQueuedReads
|
|
|
|
|
|
VOID
|
|
UsbSerGetNextIrp(IN PIRP *PpCurrentOpIrp, IN PLIST_ENTRY PQueueToProcess,
|
|
OUT PIRP *PpNextIrp, IN BOOLEAN CompleteCurrent,
|
|
IN PDEVICE_EXTENSION PDevExt)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets the next IRP off a queue, marks it as current,
|
|
and possibly completes the current IRP.
|
|
|
|
Arguments:
|
|
|
|
PpCurrentOpIrp - A pointer to the pointer to the current IRP.
|
|
PQueueToProcess - A pointer to the queue to get the next IRP from.
|
|
PpNextIrp - A pointer to the pointer to the next IRP to process.
|
|
CompleteCurrent - TRUE if we should complete the IRP that is current at
|
|
the time we are called.
|
|
PDevExt - A pointer to the device extension.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP pOldIrp;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
DEBUG_LOG_PATH("Enter UsbSerGetNextIrp");
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR,
|
|
(">UsbSerGetNextIrp(%08)\n", *PpCurrentOpIrp));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql);
|
|
|
|
pOldIrp = *PpCurrentOpIrp;
|
|
|
|
#if DBG
|
|
if (pOldIrp != NULL) {
|
|
if (CompleteCurrent) {
|
|
ASSERT(pOldIrp->CancelRoutine == NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Check to see if there is a new irp to start up
|
|
//
|
|
|
|
if (!IsListEmpty(PQueueToProcess)) {
|
|
PLIST_ENTRY pHeadOfList;
|
|
|
|
pHeadOfList = RemoveHeadList(PQueueToProcess);
|
|
|
|
*PpCurrentOpIrp = CONTAINING_RECORD(pHeadOfList, IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
IoSetCancelRoutine(*PpCurrentOpIrp, NULL);
|
|
} else {
|
|
*PpCurrentOpIrp = NULL;
|
|
}
|
|
|
|
*PpNextIrp = *PpCurrentOpIrp;
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
//
|
|
// Complete the current one if so requested
|
|
//
|
|
|
|
if (CompleteCurrent) {
|
|
if (pOldIrp != NULL) {
|
|
IoCompleteRequest(pOldIrp, IO_SERIAL_INCREMENT);
|
|
}
|
|
}
|
|
|
|
DEBUG_LOG_PATH("Exit UsbSerGetNextIrp");
|
|
UsbSerSerialDump(USBSERTRACEOTH | USBSERTRACERD | USBSERTRACEWR,
|
|
("<UsbSerGetNextIrp\n"));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UsbSerStartOrQueue(IN PDEVICE_EXTENSION PDevExt, IN PIRP PIrp,
|
|
IN PLIST_ENTRY PQueue, IN PIRP *PPCurrentIrp,
|
|
IN PUSBSER_START_ROUTINE Starter)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to either start processing an I/O request or to
|
|
queue it on the appropriate queue if a request is already pending or
|
|
requests may not be started.
|
|
|
|
Arguments:
|
|
|
|
PDevExt - A pointer to the DeviceExtension.
|
|
PIrp - A pointer to the IRP that is being started or queued.
|
|
PQueue - A pointer to the queue to place the IRP on if necessary.
|
|
PPCurrentIrp - A pointer to the pointer to the currently active I/O IRP.
|
|
Starter - Function to call if we decide to start this IRP.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
NTSTATUS status;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
DEBUG_LOG_PATH("Enter UsbSerStartOrQueue");
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerStartOrQueue(%08X)\n", PIrp));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql);
|
|
|
|
if (IsListEmpty(PQueue) && (*PPCurrentIrp == NULL)) {
|
|
//
|
|
// Nothing pending -- start the new irp
|
|
//
|
|
|
|
*PPCurrentIrp = PIrp;
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
status = Starter(PDevExt);
|
|
|
|
DEBUG_LOG_PATH("Exit UsbSerStartOrQueue(1)");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerStartOrQueue(1) %08X\n",
|
|
status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// We're queueing the irp, so we need a cancel routine -- make sure
|
|
// the irp hasn't already been cancelled.
|
|
//
|
|
|
|
if (PIrp->Cancel) {
|
|
//
|
|
// The IRP was apparently cancelled. Complete it.
|
|
//
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
PIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
|
|
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
|
|
|
|
DEBUG_LOG_PATH("Exit UsbSerStartOrQueue(2)");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerStartOrQueue(2) %08X\n",
|
|
STATUS_CANCELLED));
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
//
|
|
// Mark as pending, attach our cancel routine
|
|
//
|
|
|
|
PIrp->IoStatus.Status = STATUS_PENDING;
|
|
IoMarkIrpPending(PIrp);
|
|
|
|
InsertTailList(PQueue, &PIrp->Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(PIrp, UsbSerCancelQueued);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
DEBUG_LOG_PATH("Exit UsbSerStartOrQueue(3)");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerStartOrQueue(3) %08X\n",
|
|
STATUS_PENDING));
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerCancelQueued(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used as a cancel routine for queued irps. Basically
|
|
for us this means read IRPs.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - A pointer to the serial device object.
|
|
|
|
PIrp - A pointer to the IRP that is being cancelled
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
DEBUG_LOG_PATH("Enter UsbSerCancelQueued");
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerCancelQueued(%08X)\n", PIrp));
|
|
|
|
//
|
|
// The irp was cancelled -- remove it from the queue
|
|
//
|
|
|
|
PIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
PIrp->IoStatus.Information = 0;
|
|
|
|
RemoveEntryList(&PIrp->Tail.Overlay.ListEntry);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, PIrp->CancelIrql);
|
|
|
|
IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT);
|
|
|
|
DEBUG_LOG_PATH("Exit UsbSerCancelQueued");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerCancelQueued\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerKillAllReadsOrWrites(IN PDEVICE_OBJECT PDevObj,
|
|
IN PLIST_ENTRY PQueueToClean,
|
|
IN PIRP *PpCurrentOpIrp)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to cancel all queued and the current irps
|
|
for reads or for writes.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - A pointer to the serial device object.
|
|
|
|
PQueueToClean - A pointer to the queue which we're going to clean out.
|
|
|
|
PpCurrentOpIrp - Pointer to a pointer to the current irp.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL cancelIrql;
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACERD | USBSERTRACEWR,
|
|
(">UsbSerKillAllReadsOrWrites(%08X)\n", *PpCurrentOpIrp));
|
|
//
|
|
// We acquire the cancel spin lock. This will prevent the
|
|
// irps from moving around.
|
|
//
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql);
|
|
|
|
//
|
|
// Clean the list from back to front.
|
|
//
|
|
|
|
while (!IsListEmpty(PQueueToClean)) {
|
|
|
|
PIRP pCurrentLastIrp = CONTAINING_RECORD(PQueueToClean->Blink, IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
RemoveEntryList(PQueueToClean->Blink);
|
|
|
|
cancelRoutine = pCurrentLastIrp->CancelRoutine;
|
|
pCurrentLastIrp->CancelIrql = cancelIrql;
|
|
pCurrentLastIrp->CancelRoutine = NULL;
|
|
pCurrentLastIrp->Cancel = TRUE;
|
|
|
|
cancelRoutine(PDevObj, pCurrentLastIrp);
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql);
|
|
|
|
}
|
|
|
|
//
|
|
// The queue is clean. Now go after the current if
|
|
// it's there.
|
|
//
|
|
|
|
if (*PpCurrentOpIrp) {
|
|
|
|
|
|
cancelRoutine = (*PpCurrentOpIrp)->CancelRoutine;
|
|
(*PpCurrentOpIrp)->Cancel = TRUE;
|
|
|
|
//
|
|
// If the current irp is not in a cancelable state
|
|
// then it *will* try to enter one and the above
|
|
// assignment will kill it. If it already is in
|
|
// a cancelable state then the following will kill it.
|
|
//
|
|
|
|
if (cancelRoutine) {
|
|
|
|
(*PpCurrentOpIrp)->CancelRoutine = NULL;
|
|
(*PpCurrentOpIrp)->CancelIrql = cancelIrql;
|
|
|
|
//
|
|
// This irp is already in a cancelable state. We simply
|
|
// mark it as canceled and call the cancel routine for
|
|
// it.
|
|
//
|
|
|
|
cancelRoutine(PDevObj, *PpCurrentOpIrp);
|
|
|
|
} else {
|
|
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql);
|
|
|
|
}
|
|
|
|
UsbSerSerialDump(USBSERTRACERD | USBSERTRACEWR,
|
|
("<UsbSerKillAllReadsOrWrites\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerKillPendingIrps(PDEVICE_OBJECT PDevObj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Kill all IRPs queued in our driver
|
|
|
|
Arguments:
|
|
|
|
PDevObj - a pointer to the device object
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
|
|
KIRQL cancelIrql;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerKillPendingIrps\n"));
|
|
|
|
//
|
|
// Kill all reads; we do not queue writes
|
|
//
|
|
|
|
UsbSerKillAllReadsOrWrites(PDevObj, &pDevExt->ReadQueue,
|
|
&pDevExt->CurrentReadIrp);
|
|
|
|
//
|
|
// Get rid of any pending waitmasks
|
|
//
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql);
|
|
|
|
if (pDevExt->CurrentMaskIrp != NULL) {
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
|
|
cancelRoutine = pDevExt->CurrentMaskIrp->CancelRoutine;
|
|
pDevExt->CurrentMaskIrp->Cancel = TRUE;
|
|
|
|
ASSERT(cancelRoutine);
|
|
|
|
if (cancelRoutine) {
|
|
pDevExt->CurrentMaskIrp->CancelRoutine = NULL;
|
|
pDevExt->CurrentMaskIrp->CancelIrql = cancelIrql;
|
|
|
|
cancelRoutine(PDevObj, pDevExt->CurrentMaskIrp);
|
|
} else {
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql);
|
|
}
|
|
|
|
}else {
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, cancelIrql);
|
|
}
|
|
|
|
//
|
|
// Cancel any pending wait-wake irps
|
|
//
|
|
|
|
if (pDevExt->PendingWakeIrp != NULL) {
|
|
IoCancelIrp(pDevExt->PendingWakeIrp);
|
|
pDevExt->PendingWakeIrp = NULL;
|
|
}
|
|
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerKillPendingIrps\n"));
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CompletePendingWaitMasks */
|
|
/************************************************************************/
|
|
/* */
|
|
/* Routine Description: */
|
|
/* */
|
|
/* Completes any wait masks in progress with no events set. */
|
|
/* */
|
|
/* Arguments: */
|
|
/* */
|
|
/* DeviceExtension - pointer to a device extension */
|
|
/* */
|
|
/* Return Value: */
|
|
/* */
|
|
/* VOID */
|
|
/* */
|
|
/************************************************************************/
|
|
VOID
|
|
UsbSerCompletePendingWaitMasks(IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
KIRQL OldIrql;
|
|
PIRP CurrentMaskIrp;
|
|
KIRQL cancelIrql;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
DEBUG_LOG_PATH("enter CompletePendingWaitMasks");
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">CompletePendingWaitMasks\n"));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(DeviceExtension, &cancelIrql);
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
|
|
|
|
CurrentMaskIrp = DeviceExtension->CurrentMaskIrp;
|
|
|
|
if (CurrentMaskIrp) {
|
|
|
|
CurrentMaskIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
CurrentMaskIrp->IoStatus.Information = sizeof(ULONG);
|
|
*((PULONG)CurrentMaskIrp->AssociatedIrp.SystemBuffer) = 0;
|
|
|
|
DeviceExtension->CurrentMaskIrp = NULL;
|
|
|
|
IoSetCancelRoutine(CurrentMaskIrp, NULL);
|
|
|
|
}
|
|
|
|
// complete the queued IRP if needed
|
|
if (CurrentMaskIrp) {
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
RELEASE_CANCEL_SPINLOCK(DeviceExtension, cancelIrql);
|
|
|
|
IoCompleteRequest(CurrentMaskIrp, IO_NO_INCREMENT);
|
|
DEBUG_TRACE1(("CompletePendingWaitMask\n"));
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
|
|
RELEASE_CANCEL_SPINLOCK(DeviceExtension, cancelIrql);
|
|
}
|
|
|
|
DEBUG_LOG_PATH("exit CompletePendingWaitMasks");
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<CompletePendingWaitMasks\n"));
|
|
} // CancelPendingWaitMasks
|
|
|
|
|
|
VOID
|
|
UsbSerRestoreModemSettings(PDEVICE_OBJECT PDevObj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Restores the modem's settings upon a powerup.
|
|
|
|
Arguments:
|
|
|
|
PDevExt - a pointer to the device extension
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
(void)SetLineControlAndBaud(PDevObj);
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerProcessEmptyTransmit(IN PDEVICE_EXTENSION PDevExt)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called whenever our tx queue is empty in order
|
|
to set the proper events, etc.
|
|
|
|
Arguments:
|
|
|
|
PDevExt - Pointer to DeviceExtension for the device
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
PULONG pWaitMask;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
//
|
|
// Set the event if needed
|
|
//
|
|
|
|
PDevExt->HistoryMask |= SERIAL_EV_TXEMPTY;
|
|
|
|
if (PDevExt->IsrWaitMask & SERIAL_EV_TXEMPTY) {
|
|
PIRP pMaskIrp;
|
|
|
|
DEBUG_TRACE3(("Completing events\n"));
|
|
|
|
ACQUIRE_CANCEL_SPINLOCK(PDevExt, &oldIrql);
|
|
|
|
if (PDevExt->CurrentMaskIrp != NULL) {
|
|
pWaitMask = (PULONG)PDevExt->CurrentMaskIrp->
|
|
AssociatedIrp.SystemBuffer;
|
|
|
|
*pWaitMask = PDevExt->HistoryMask;
|
|
PDevExt->HistoryMask = 0;
|
|
pMaskIrp = PDevExt->CurrentMaskIrp;
|
|
|
|
pMaskIrp->IoStatus.Information = sizeof(ULONG);
|
|
pMaskIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
PDevExt->CurrentMaskIrp = NULL;
|
|
|
|
IoSetCancelRoutine(pMaskIrp, NULL);
|
|
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
|
|
IoCompleteRequest(pMaskIrp, IO_SERIAL_INCREMENT);
|
|
} else {
|
|
RELEASE_CANCEL_SPINLOCK(PDevExt, oldIrql);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerCancelWaitOnMask(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used as a cancel routine for WaitOnMask IRPs.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to Device Object
|
|
PIrp - Pointer to IRP that is being canceled; must be the same as
|
|
the current mask IRP.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
|
|
|
|
USBSER_ALWAYS_LOCKED_CODE();
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, (">UsbSerCancelWaitOnMask(%08X)\n", PIrp));
|
|
|
|
ASSERT(pDevExt->CurrentMaskIrp == PIrp);
|
|
|
|
PIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
PIrp->IoStatus.Information = 0;
|
|
|
|
pDevExt->CurrentMaskIrp = NULL;
|
|
RELEASE_CANCEL_SPINLOCK(pDevExt, PIrp->CancelIrql);
|
|
IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT);
|
|
|
|
UsbSerSerialDump(USBSERTRACEOTH, ("<UsbSerCancelWaitOnMask(%08X)"));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UsbSerSyncCompletion(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp,
|
|
IN PKEVENT PUsbSerSyncEvent)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to signal an event. It is used as a completion
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - Pointer to Device Object
|
|
PIrp - Pointer to IRP that is being completed
|
|
PUsbSerSyncEvent - Pointer to event that we should set
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
{
|
|
KeSetEvent(PUsbSerSyncEvent, IO_NO_INCREMENT, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
PVOID
|
|
UsbSerLockPagableCodeSection(PVOID SecFunc)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to lockdown code pages and increment a lock counter
|
|
for debugging.
|
|
|
|
Arguments:
|
|
|
|
SecFunc - Function in code section to be locked down.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Handle for locked down section.
|
|
|
|
--*/
|
|
{ PVOID handle;
|
|
|
|
PAGED_CODE();
|
|
|
|
handle = MmLockPagableCodeSection(SecFunc);
|
|
|
|
// can this be paged?
|
|
InterlockedIncrement(&PAGEUSBSER_Count);
|
|
|
|
return handle;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
VOID
|
|
UsbSerFetchBooleanLocked(PBOOLEAN PDest, BOOLEAN Src, PKSPIN_LOCK PSpinLock)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to assign a BOOLEAN value with spinlock protection.
|
|
|
|
Arguments:
|
|
|
|
PDest - A pointer to Lval.
|
|
|
|
Src - Rval.
|
|
|
|
PSpinLock - Pointer to the spin lock we should hold.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL tmpIrql;
|
|
|
|
KeAcquireSpinLock(PSpinLock, &tmpIrql);
|
|
*PDest = Src;
|
|
KeReleaseSpinLock(PSpinLock, tmpIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
UsbSerFetchPVoidLocked(PVOID *PDest, PVOID Src, PKSPIN_LOCK PSpinLock)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to assign a PVOID value with spinlock protection.
|
|
|
|
Arguments:
|
|
|
|
PDest - A pointer to Lval.
|
|
|
|
Src - Rval.
|
|
|
|
PSpinLock - Pointer to the spin lock we should hold.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL tmpIrql;
|
|
|
|
KeAcquireSpinLock(PSpinLock, &tmpIrql);
|
|
*PDest = Src;
|
|
KeReleaseSpinLock(PSpinLock, tmpIrql);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Work item to kick off another notify read
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to the device object
|
|
|
|
DeviceExtension - context for this call
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
VOID
|
|
USBSER_RestartNotifyReadWorkItem(IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_EXTENSION DeviceExtension)
|
|
{
|
|
KIRQL oldIrql;
|
|
PIO_WORKITEM ioWorkItem;
|
|
|
|
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &oldIrql);
|
|
|
|
ioWorkItem = DeviceExtension->IoWorkItem;
|
|
|
|
DeviceExtension->IoWorkItem = NULL;
|
|
|
|
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, oldIrql);
|
|
|
|
IoFreeWorkItem(ioWorkItem);
|
|
|
|
RestartNotifyRead(DeviceExtension);
|
|
} // USBER_RestartNotifyReadWorkItem
|
|
|
|
|
|
|