4109 lines
142 KiB
C
4109 lines
142 KiB
C
/***************************************************************************
|
||
|
||
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 - 1;
|
||
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
|
||
|
||
|