📄 read.c
字号:
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
read.c
Abstract: ESC/POS (serial) interface for USB Point-of-Sale devices
Author:
ervinp
Environment:
Kernel mode
Revision History:
--*/
#include <WDM.H>
#include <usbdi.h>
#include "usbdlib.h"
#include <usbioctl.h>
#include "escpos.h"
#include "debug.h"
NTSTATUS ReadComPort(POSPDOEXT *pdoExt, PIRP irp)
{
NTSTATUS status;
PIO_STACK_LOCATION currentIrpSp;
/*
* In order to support ODD ENDPOINTs, we check
* whether this COM port has a read endpoint or not.
*/
if(!pdoExt->inputEndpointInfo.pipeHandle) {
DBGVERBOSE(("This PORT does not have an IN endpoint - Read request Rejected."));
return STATUS_NOT_SUPPORTED;
}
DBGVERBOSE(("ReadComPort"));
currentIrpSp = IoGetCurrentIrpStackLocation(irp);
ASSERT(currentIrpSp->Parameters.Read.Length);
ASSERT(!irp->MdlAddress);
/*
* Because this device object uses buffering method METHOD_NEITHER,
* the read buffer is irp->UserBuffer, which is potentially an application
* read buffer. If the read completes on a different thread than this calling
* thread, then the completion routine will not have the read buffer addressed
* correctly.
* Therefore, we have to map the UserBuffer using an MDL.
*/
irp->MdlAddress = MmCreateMdl(NULL, irp->UserBuffer, currentIrpSp->Parameters.Read.Length);
if (irp->MdlAddress){
status = STATUS_SUCCESS;
__try {
/*
* We're writing the read data to the buffer, so probe for WriteAccess.
*/
MmProbeAndLockPages(irp->MdlAddress, UserMode, IoWriteAccess);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
DBGERR(("MmProbeAndLockPages triggered exception status %xh.", status));
}
if (NT_SUCCESS(status)){
status = EnqueueReadIrp(pdoExt, irp, FALSE, FALSE);
if (status == STATUS_PENDING){
BOOLEAN doReadNow;
KIRQL oldIrql;
/*
* Atomically test-and-set the endpointIsBusy flag.
* If the endpoint was not busy, issue a read after dropping the lock.
*/
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
if (pdoExt->inputEndpointInfo.endpointIsBusy){
doReadNow = FALSE;
}
else {
pdoExt->inputEndpointInfo.endpointIsBusy = TRUE;
doReadNow = TRUE;
}
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
if (doReadNow){
IssueReadForClient(pdoExt);
}
}
}
}
else {
DBGERR(("MmCreateMdl failed"));
status = STATUS_DATA_ERROR;
}
return status;
}
VOID SatisfyPendingReads(POSPDOEXT *pdoExt)
{
LIST_ENTRY irpsToCompleteList, readPacketsToFree;
PLIST_ENTRY listEntry;
PIRP irp;
READPACKET *readPacket;
KIRQL oldIrql;
DBGVERBOSE(("SatisfyPendingReads"));
/*
* Accumulate the complete-ready IRPs on a private queue before completing.
* This is so we don't loop forever if they get re-queued on the same thread.
*/
InitializeListHead(&irpsToCompleteList);
InitializeListHead(&readPacketsToFree);
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
while (irp = DequeueReadIrp(pdoExt, TRUE)){
PIO_STACK_LOCATION currentIrpSp = IoGetCurrentIrpStackLocation(irp);
BOOLEAN canSatisfyOneIrp;
/*
* Do we have enough bytes to satisfy this IRP ?
*/
#if PARTIAL_READ_BUFFERS_OK
canSatisfyOneIrp = (pdoExt->totalQueuedReadDataLength > 0);
#else
canSatisfyOneIrp = (pdoExt->totalQueuedReadDataLength >= currentIrpSp->Parameters.Read.Length);
#endif
if (canSatisfyOneIrp){
ULONG userBufferOffset = 0;
BOOLEAN satisfiedThisIrp = FALSE;
PUCHAR mappedUserBuffer;
ASSERT(irp->MdlAddress);
ASSERT(!IsListEmpty(&pdoExt->completedReadPacketsList));
/*
* We may be completing this IRP on a different thread than the calling thread.
* So we cannot dereference UserBuffer directly.
* Use the MDL we created at call time instead.
*/
mappedUserBuffer = PosMmGetSystemAddressForMdlSafe(irp->MdlAddress);
if (mappedUserBuffer){
while (!IsListEmpty(&pdoExt->completedReadPacketsList) &&
(userBufferOffset < currentIrpSp->Parameters.Read.Length)){
ULONG bytesToCopy;
BOOLEAN thisIrpFull;
listEntry = RemoveHeadList(&pdoExt->completedReadPacketsList);
ASSERT(listEntry);
readPacket = CONTAINING_RECORD(listEntry, READPACKET, listEntry);
ASSERT(readPacket->signature == READPACKET_SIG);
bytesToCopy = MIN(currentIrpSp->Parameters.Read.Length-userBufferOffset,
readPacket->length-readPacket->offset);
ASSERT(bytesToCopy <= pdoExt->totalQueuedReadDataLength);
DBGVERBOSE(("SatisfyPendingReads: transferring %xh bytes to read irp", bytesToCopy));
/*
* Since we may be completing this IRP on a different thread than
* the one we got it on, we cannot write into the UserBuffer.
* We have to write into the MDL we allocated when we queued this IRP.
*/
RtlCopyMemory(mappedUserBuffer+userBufferOffset,
readPacket->data+readPacket->offset,
bytesToCopy);
userBufferOffset += bytesToCopy;
readPacket->offset += bytesToCopy;
pdoExt->totalQueuedReadDataLength -= bytesToCopy;
ASSERT(userBufferOffset <= currentIrpSp->Parameters.Read.Length);
ASSERT(readPacket->offset <= readPacket->length);
#if PARTIAL_READ_BUFFERS_OK
thisIrpFull = (userBufferOffset > 0);
#else
thisIrpFull = (userBufferOffset >= currentIrpSp->Parameters.Read.Length);
#endif
if (thisIrpFull){
/*
* We've satisfied this IRP.
* Break out of the inner loop so we get a new IRP.
* Put the readPacket back in its queue in case there
* are more bytes left in it.
*/
irp->IoStatus.Information = userBufferOffset;
irp->IoStatus.Status = STATUS_SUCCESS;
InsertTailList(&irpsToCompleteList, &irp->Tail.Overlay.ListEntry);
InsertHeadList(&pdoExt->completedReadPacketsList, &readPacket->listEntry);
satisfiedThisIrp = TRUE;
break;
}
else if (readPacket->offset == readPacket->length){
/*
* We've depleted this readPacket buffer.
*/
InsertTailList(&readPacketsToFree, &readPacket->listEntry);
ASSERT(!IsListEmpty(&pdoExt->completedReadPacketsList));
}
else {
DBGERR(("SatisfyPendingReads - data error"));
break;
}
}
ASSERT(satisfiedThisIrp);
}
else {
DBGERR(("PosMmGetSystemAddressForMdlSafe failed"));
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
InsertTailList(&irpsToCompleteList, &irp->Tail.Overlay.ListEntry);
}
}
else {
/*
* We can't satisfy this IRP, so put it back at the head of the list.
*/
NTSTATUS status;
DBGVERBOSE(("SatisfyPendingReads: not enough bytes to satisfy irp (%xh/%xh)", pdoExt->totalQueuedReadDataLength, currentIrpSp->Parameters.Read.Length));
status = EnqueueReadIrp(pdoExt, irp, TRUE, TRUE);
if (status == STATUS_CANCELLED){
/*
* The IRP was cancelled and the cancel routine was not called,
* so complete the IRP here.
*/
irp->IoStatus.Information = 0;
irp->IoStatus.Status = status;
InsertTailList(&irpsToCompleteList, &irp->Tail.Overlay.ListEntry);
}
break;
}
}
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -