mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
8.3 KiB
303 lines
8.3 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
write.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 WriteComPort(POSPDOEXT *pdoExt, PIRP irp)
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION currentIrpSp;
|
|
|
|
/*
|
|
* In order to support ODD ENDPOINTs, we check
|
|
* whether this COM port has a write endpoint or not.
|
|
*/
|
|
if(!pdoExt->outputEndpointInfo.pipeHandle) {
|
|
DBGVERBOSE(("This PORT does not have an OUT endpoint - Write request Rejected."));
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
currentIrpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
/*
|
|
* Because this pdo's buffering method is METHOD_NEITHER,
|
|
* the buffer pointer is irp->UserBuffer, which may be an application address.
|
|
* Since we may not be able to send this buffer synchronously on this thread,
|
|
* we have to allocate an MDL for it.
|
|
*/
|
|
ASSERT(!irp->MdlAddress);
|
|
ASSERT(currentIrpSp->Parameters.Write.Length);
|
|
irp->MdlAddress = MmCreateMdl(NULL, irp->UserBuffer, currentIrpSp->Parameters.Write.Length);
|
|
if (irp->MdlAddress){
|
|
status = STATUS_SUCCESS;
|
|
__try {
|
|
/*
|
|
* We're reading the write data from the buffer, so probe for ReadAccess.
|
|
*/
|
|
MmProbeAndLockPages(irp->MdlAddress, UserMode, IoReadAccess);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
DBGERR(("MmProbeAndLockPages triggered exception status %xh.", status));
|
|
}
|
|
if (NT_SUCCESS(status)){
|
|
status = TryWrite(pdoExt, irp);
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("MmCreateMdl failed"));
|
|
status = STATUS_DATA_ERROR;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS TryWrite(POSPDOEXT *pdoExt, PIRP irp)
|
|
{
|
|
NTSTATUS status = STATUS_PENDING;
|
|
BOOLEAN isBusy;
|
|
KIRQL oldIrql;
|
|
BOOLEAN irpWasCancelled = FALSE;
|
|
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
|
|
if (pdoExt->outputEndpointInfo.endpointIsBusy){
|
|
/*
|
|
* Another thread is writing to this endpoint now.
|
|
* Queue the IRP.
|
|
*/
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
|
|
DBGWARN(("WriteComPort: endpoint is busy so queuing irp"));
|
|
oldCancelRoutine = IoSetCancelRoutine(irp, WriteCancelRoutine);
|
|
ASSERT(!oldCancelRoutine);
|
|
if (irp->Cancel){
|
|
DBGWARN(("WriteComPort: irp %ph was cancelled.", irp));
|
|
irpWasCancelled = TRUE;
|
|
oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
|
|
if (oldCancelRoutine){
|
|
/*
|
|
* Cancel routine was not called, so complete the IRP here.
|
|
*/
|
|
ASSERT(oldCancelRoutine == ReadCancelRoutine);
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else {
|
|
/*
|
|
* Cancel routine was called and it will complete the IRP
|
|
* as soon as we drop the spinlock. So don't touch this IRP.
|
|
* Return PENDING so dispatch routine won't complete the IRP.
|
|
*/
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
InsertTailList(&pdoExt->pendingWriteIrpsList, &irp->Tail.Overlay.ListEntry);
|
|
}
|
|
isBusy = TRUE;
|
|
}
|
|
else {
|
|
pdoExt->outputEndpointInfo.endpointIsBusy = TRUE;
|
|
isBusy = FALSE;
|
|
}
|
|
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
|
|
if (!isBusy){
|
|
|
|
PUCHAR mappedUserBuffer;
|
|
ULONG dataLen;
|
|
ULONG dataWritten = 0;
|
|
PIO_STACK_LOCATION currentIrpSp;
|
|
BOOLEAN callWriteWorkItem = FALSE;
|
|
|
|
currentIrpSp = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
status = STATUS_SUCCESS; // in case dataLen = 0
|
|
|
|
/*
|
|
* This function is called from the dispatch routine and also
|
|
* from a workItem callback. So we may not be on the calling thread.
|
|
* Therefore, we cannot use irp->UserBuffer because it may not be
|
|
* mapped in this context. Use the MDL we created at call time instead.
|
|
*/
|
|
mappedUserBuffer = PosMmGetSystemAddressForMdlSafe(irp->MdlAddress);
|
|
|
|
if(mappedUserBuffer) {
|
|
dataLen = currentIrpSp->Parameters.Write.Length;
|
|
|
|
while (dataLen){
|
|
ULONG len = MIN(dataLen, pdoExt->outputEndpointInfo.pipeLen);
|
|
|
|
DBGVERBOSE(("Writing %xh bytes to pipe.", len));
|
|
status = WritePipe( pdoExt->parentFdoExt,
|
|
pdoExt->outputEndpointInfo.pipeHandle,
|
|
mappedUserBuffer,
|
|
len);
|
|
if (NT_SUCCESS(status)){
|
|
dataLen -= len;
|
|
dataWritten += len;
|
|
mappedUserBuffer += len;
|
|
}
|
|
else {
|
|
DBGERR(("Write failed with status %xh.", status));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free the MDL we created for the UserBuffer
|
|
*/
|
|
ASSERT(irp->MdlAddress);
|
|
MmUnlockPages(irp->MdlAddress);
|
|
FREEPOOL(irp->MdlAddress);
|
|
irp->MdlAddress = NULL;
|
|
|
|
irp->IoStatus.Information = dataWritten;
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
pdoExt->outputEndpointInfo.endpointIsBusy = FALSE;
|
|
if (!IsListEmpty(&pdoExt->pendingWriteIrpsList)){
|
|
callWriteWorkItem = TRUE;;
|
|
}
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
/*
|
|
* If there are some writes waiting, schedule a workItem to process them.
|
|
*/
|
|
if (callWriteWorkItem){
|
|
ExQueueWorkItem(&pdoExt->writeWorkItem, DelayedWorkQueue);
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* Return STATUS_UNSUCCESSFUL and free the MDL we created for the UserBuffer.
|
|
*/
|
|
DBGERR(("PosMmGetSystemAddressForMdlSafe failed"));
|
|
irp->IoStatus.Information = 0;
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
ASSERT(irp->MdlAddress);
|
|
MmUnlockPages(irp->MdlAddress);
|
|
FREEPOOL(irp->MdlAddress);
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID WorkItemCallback_Write(PVOID context)
|
|
{
|
|
POSPDOEXT *pdoExt = (POSPDOEXT *)context;
|
|
KIRQL oldIrql;
|
|
PIRP irp = NULL;
|
|
|
|
DBGVERBOSE(("WorkItemCallback_Write: pdoExt=%ph ", pdoExt));
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
|
|
if (IsListEmpty(&pdoExt->pendingWriteIrpsList)){
|
|
DBGERR(("WorkItemCallback_Write: list is empty ?!"));
|
|
}
|
|
else {
|
|
while (!irp && !IsListEmpty(&pdoExt->pendingWriteIrpsList)){
|
|
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
PLIST_ENTRY listEntry = RemoveHeadList(&pdoExt->pendingWriteIrpsList);
|
|
|
|
ASSERT(listEntry);
|
|
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
cancelRoutine = IoSetCancelRoutine(irp, NULL);
|
|
|
|
if (cancelRoutine){
|
|
ASSERT(cancelRoutine == WriteCancelRoutine);
|
|
}
|
|
else {
|
|
/*
|
|
* This IRP was cancelled and the cancel routine was called.
|
|
* The cancel routine will complete this IRP as soon as we drop
|
|
* the spinlock, so don't touch the IRP.
|
|
*/
|
|
ASSERT(irp->Cancel);
|
|
DBGWARN(("WorkItemCallback_Write: irp was cancelled"));
|
|
irp = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
if (irp){
|
|
NTSTATUS status = TryWrite(pdoExt, irp);
|
|
if (status != STATUS_PENDING){
|
|
|
|
/*
|
|
* Set the SL_PENDING in the IRP
|
|
* to indicate that the IRP is completing on a different thread.
|
|
*/
|
|
IoMarkIrpPending(irp);
|
|
|
|
irp->IoStatus.Status = status;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID WriteCancelRoutine(PDEVICE_OBJECT devObj, PIRP irp)
|
|
{
|
|
DEVEXT *devExt;
|
|
POSPDOEXT *pdoExt;
|
|
KIRQL oldIrql;
|
|
|
|
DBGWARN(("WriteCancelRoutine: devObj=%ph, irp=%ph.", devObj, irp));
|
|
|
|
devExt = devObj->DeviceExtension;
|
|
ASSERT(devExt->signature == DEVICE_EXTENSION_SIGNATURE);
|
|
ASSERT(devExt->isPdo);
|
|
pdoExt = &devExt->pdoExt;
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
RemoveEntryList(&irp->Tail.Overlay.ListEntry);
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|