389 lines
12 KiB
C
389 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
waitmask.c
|
|
|
|
Abstract: POS (serial) interface for USB Point-of-Sale devices
|
|
|
|
Author:
|
|
|
|
Karan Mehra [t-karanm]
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include <WDM.H>
|
|
|
|
#include <usbdi.h>
|
|
#include <usbdlib.h>
|
|
#include <usbioctl.h>
|
|
|
|
#include "escpos.h"
|
|
#include "debug.h"
|
|
|
|
|
|
NTSTATUS QuerySpecialFeature(PARENTFDOEXT *parentFdoExt)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE hRegDevice;
|
|
parentFdoExt->posFlag = 0;
|
|
|
|
status = IoOpenDeviceRegistryKey(parentFdoExt->physicalDevObj,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
KEY_READ,
|
|
&hRegDevice);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
UNICODE_STRING keyName;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInfo;
|
|
ULONG keyValueTotalSize, actualLength, value;
|
|
WCHAR oddKeyName[] = L"OddEndpointFlag";
|
|
|
|
#if STATUS_ENDPOINT
|
|
|
|
WCHAR serialKeyName[] = L"SerialEmulationFlag";
|
|
RtlInitUnicodeString(&keyName, serialKeyName);
|
|
|
|
keyValueTotalSize = sizeof(KEY_VALUE_FULL_INFORMATION) + keyName.Length*sizeof(WCHAR) + sizeof(ULONG);
|
|
|
|
keyValueInfo = ALLOCPOOL(PagedPool, keyValueTotalSize);
|
|
if (keyValueInfo) {
|
|
status = ZwQueryValueKey(hRegDevice,
|
|
&keyName,
|
|
KeyValueFullInformation,
|
|
keyValueInfo,
|
|
keyValueTotalSize,
|
|
&actualLength);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ASSERT(keyValueInfo->Type == REG_DWORD);
|
|
ASSERT(keyValueInfo->DataLength == sizeof(ULONG));
|
|
|
|
value = *((PULONG)(((PCHAR)keyValueInfo)+keyValueInfo->DataOffset));
|
|
if (value == 1)
|
|
parentFdoExt->posFlag |= SERIAL_EMULATION;
|
|
}
|
|
else
|
|
DBGVERBOSE(("QuerySpecialFeature: Flag not found. ZwQueryValueKey failed with %xh.", status));
|
|
|
|
FREEPOOL(keyValueInfo);
|
|
}
|
|
else
|
|
ASSERT(keyValueInfo);
|
|
|
|
#endif
|
|
|
|
RtlInitUnicodeString(&keyName, oddKeyName);
|
|
|
|
keyValueTotalSize = sizeof(KEY_VALUE_FULL_INFORMATION) + keyName.Length*sizeof(WCHAR) + sizeof(ULONG);
|
|
|
|
keyValueInfo = ALLOCPOOL(PagedPool, keyValueTotalSize);
|
|
if (keyValueInfo) {
|
|
status = ZwQueryValueKey(hRegDevice,
|
|
&keyName,
|
|
KeyValueFullInformation,
|
|
keyValueInfo,
|
|
keyValueTotalSize,
|
|
&actualLength);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ASSERT(keyValueInfo->Type == REG_DWORD);
|
|
ASSERT(keyValueInfo->DataLength == sizeof(ULONG));
|
|
|
|
value = *((PULONG)(((PCHAR)keyValueInfo)+keyValueInfo->DataOffset));
|
|
if (value == 1)
|
|
parentFdoExt->posFlag |= ODD_ENDPOINT;
|
|
}
|
|
else
|
|
DBGVERBOSE(("QuerySpecialFeature: Flag not found. ZwQueryValueKey failed with %xh.", status));
|
|
|
|
FREEPOOL(keyValueInfo);
|
|
}
|
|
else
|
|
ASSERT(keyValueInfo);
|
|
|
|
ZwClose(hRegDevice);
|
|
|
|
/*
|
|
* We do not wish to permit the Serial Emulation and
|
|
* Odd Endpoint features together on the same device.
|
|
*/
|
|
if((parentFdoExt->posFlag & SERIAL_EMULATION) && (parentFdoExt->posFlag & ODD_ENDPOINT)) {
|
|
DBGVERBOSE(("More than one special feature NOT supported on the same device."));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
}
|
|
else
|
|
DBGERR(("QuerySpecialFeature: IoOpenDeviceRegistryKey failed with %xh.", status));
|
|
|
|
DBGVERBOSE(("QuerySpecialFeature: posFlag is now: %xh.", parentFdoExt->posFlag));
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID InitializeSerEmulVariables(POSPDOEXT *pdoExt)
|
|
{
|
|
InitializeListHead(&pdoExt->pendingWaitIrpsList);
|
|
pdoExt->baudRate = SERIAL_BAUD_115200;
|
|
pdoExt->waitMask = 0;
|
|
pdoExt->supportedBauds = SERIAL_BAUD_300 | SERIAL_BAUD_600 | SERIAL_BAUD_1200
|
|
| SERIAL_BAUD_2400 | SERIAL_BAUD_4800 | SERIAL_BAUD_9600
|
|
| SERIAL_BAUD_19200 | SERIAL_BAUD_38400 | SERIAL_BAUD_57600
|
|
| SERIAL_BAUD_115200;
|
|
pdoExt->currentMask = 0;
|
|
pdoExt->fakeRxSize = 100;
|
|
pdoExt->fakeLineControl.WordLength = 8;
|
|
pdoExt->fakeLineControl.Parity = NO_PARITY;
|
|
pdoExt->fakeLineControl.StopBits = STOP_BITS_2;
|
|
pdoExt->fakeModemStatus = SERIAL_MSR_DCD | SERIAL_MSR_RI
|
|
| SERIAL_MSR_DSR | SERIAL_MSR_CTS;
|
|
}
|
|
|
|
|
|
NTSTATUS StatusPipe(POSPDOEXT *pdoExt, USBD_PIPE_HANDLE pipeHandle)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
#if STATUS_ENDPOINT
|
|
UsbBuildInterruptOrBulkTransferRequest(&pdoExt->statusUrb,
|
|
(USHORT) sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
|
|
pipeHandle,
|
|
(PVOID) &pdoExt->statusPacket,
|
|
NULL,
|
|
sizeof(pdoExt->statusPacket),
|
|
USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN,
|
|
NULL);
|
|
#else
|
|
UsbBuildGetDescriptorRequest(&pdoExt->statusUrb,
|
|
(USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
|
|
USB_DEVICE_DESCRIPTOR_TYPE,
|
|
0,
|
|
0,
|
|
(PVOID) &pdoExt->dummyPacket,
|
|
NULL,
|
|
sizeof(pdoExt->dummyPacket),
|
|
NULL);
|
|
#endif
|
|
|
|
IncrementPendingActionCount(pdoExt->parentFdoExt);
|
|
status = SubmitUrb(pdoExt->parentFdoExt->topDevObj,
|
|
&pdoExt->statusUrb,
|
|
FALSE,
|
|
StatusPipeCompletion,
|
|
pdoExt);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS StatusPipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
|
|
{
|
|
NTSTATUS status = irp->IoStatus.Status;
|
|
POSPDOEXT *pdoExt = (POSPDOEXT *)context;
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
UpdateMask(pdoExt);
|
|
StatusPipe(pdoExt, pdoExt->statusEndpointInfo.pipeHandle);
|
|
#if STATUS_ENDPOINT
|
|
DBGVERBOSE(("Mask Updated and URB sent down to retrieve Status Packet"));
|
|
#endif
|
|
}
|
|
|
|
IoFreeIrp(irp);
|
|
DecrementPendingActionCount(pdoExt->parentFdoExt);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
VOID UpdateMask(POSPDOEXT *pdoExt)
|
|
{
|
|
KIRQL oldIrql;
|
|
PIRP irp;
|
|
ULONG mask;
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
|
|
/*
|
|
* Modify the emulated variables based on statusPacket
|
|
*/
|
|
#if STATUS_ENDPOINT
|
|
/*
|
|
* Fill in the values of DCTS, DDSR, TERI, DDCD by EX-ORing the current
|
|
* modem bits CTS, DSR, RI and DCD with those at the STATUS Endpoint.
|
|
*/
|
|
pdoExt->fakeModemStatus = (pdoExt->fakeModemStatus & ~MSR_DELTA_MASK) | (((pdoExt->fakeModemStatus ^ pdoExt->statusPacket) & MSR_GLOBAL_MSK) >> MSR_DELTA_SHFT);
|
|
|
|
/*
|
|
* Replace the bits for CTS, DSR, RI and DCD with those at the STATUS Endpoint.
|
|
*/
|
|
pdoExt->fakeModemStatus = (pdoExt->fakeModemStatus & ~MSR_GLOBAL_MSK) | (pdoExt->statusPacket & MSR_GLOBAL_MSK);
|
|
|
|
if(pdoExt->statusPacket & EMULSER_OE)
|
|
pdoExt->fakeLineStatus |= SERIAL_LSR_OE;
|
|
|
|
if(pdoExt->statusPacket & EMULSER_PE)
|
|
pdoExt->fakeLineStatus |= SERIAL_LSR_PE;
|
|
|
|
if(pdoExt->statusPacket & EMULSER_FE)
|
|
pdoExt->fakeLineStatus |= SERIAL_LSR_FE;
|
|
|
|
if(pdoExt->statusPacket & EMULSER_BI)
|
|
pdoExt->fakeLineStatus |= SERIAL_LSR_BI;
|
|
|
|
if(pdoExt->statusPacket & EMULSER_DTR)
|
|
pdoExt->fakeDTRRTS |= SERIAL_DTR_STATE;
|
|
|
|
if(pdoExt->statusPacket & EMULSER_RTS)
|
|
pdoExt->fakeDTRRTS |= SERIAL_RTS_STATE;
|
|
#endif
|
|
|
|
if(pdoExt->waitMask) {
|
|
if((pdoExt->waitMask & SERIAL_EV_CTS) && (pdoExt->fakeModemStatus & SERIAL_MSR_DCTS))
|
|
pdoExt->currentMask |= SERIAL_EV_CTS;
|
|
|
|
if((pdoExt->waitMask & SERIAL_EV_DSR) && (pdoExt->fakeModemStatus & SERIAL_MSR_DDSR))
|
|
pdoExt->currentMask |= SERIAL_EV_DSR;
|
|
|
|
if((pdoExt->waitMask & SERIAL_EV_RING) && (pdoExt->fakeModemStatus & SERIAL_MSR_TERI))
|
|
pdoExt->currentMask |= SERIAL_EV_RING;
|
|
|
|
if((pdoExt->waitMask & SERIAL_EV_RLSD) && (pdoExt->fakeModemStatus & SERIAL_MSR_DDCD))
|
|
pdoExt->currentMask |= SERIAL_EV_RLSD;
|
|
|
|
if((pdoExt->waitMask & SERIAL_EV_ERR) && (pdoExt->fakeLineStatus & (SERIAL_LSR_OE
|
|
| SERIAL_LSR_PE | SERIAL_LSR_FE)))
|
|
pdoExt->currentMask |= SERIAL_EV_ERR;
|
|
|
|
if((pdoExt->waitMask & SERIAL_EV_BREAK) && (pdoExt->fakeLineStatus & SERIAL_LSR_BI))
|
|
pdoExt->currentMask |= SERIAL_EV_BREAK;
|
|
|
|
/*
|
|
* These are required to be filled by the read and write pipes.
|
|
* Currently, we return TRUE always.
|
|
*/
|
|
if(pdoExt->waitMask & SERIAL_EV_RXCHAR)
|
|
pdoExt->currentMask |= SERIAL_EV_RXCHAR;
|
|
|
|
if(pdoExt->waitMask & SERIAL_EV_RXFLAG)
|
|
pdoExt->currentMask |= SERIAL_EV_RXFLAG;
|
|
|
|
if(pdoExt->waitMask & SERIAL_EV_TXEMPTY)
|
|
pdoExt->currentMask |= SERIAL_EV_TXEMPTY;
|
|
}
|
|
mask = pdoExt->currentMask;
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
if(mask) {
|
|
CompletePendingWaitIrps(pdoExt, mask);
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
pdoExt->currentMask = 0;
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
}
|
|
}
|
|
|
|
|
|
VOID CompletePendingWaitIrps(POSPDOEXT *pdoExt, ULONG mask)
|
|
{
|
|
PIRP irp;
|
|
|
|
while((irp = DequeueWaitIrp(pdoExt))) {
|
|
irp->IoStatus.Information = sizeof(ULONG);
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
*(PULONG)irp->AssociatedIrp.SystemBuffer = mask;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS EnqueueWaitIrp(POSPDOEXT *pdoExt, PIRP irp)
|
|
{
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
NTSTATUS status = STATUS_PENDING;
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
|
|
if(IsListEmpty(&pdoExt->pendingWaitIrpsList))
|
|
{
|
|
InsertTailList(&pdoExt->pendingWaitIrpsList, &irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending(irp);
|
|
|
|
oldCancelRoutine = IoSetCancelRoutine(irp, WaitMaskCancelRoutine);
|
|
ASSERT(!oldCancelRoutine);
|
|
|
|
if (irp->Cancel) {
|
|
oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
|
|
if (oldCancelRoutine) {
|
|
RemoveEntryList(&irp->Tail.Overlay.ListEntry);
|
|
status = irp->IoStatus.Status = STATUS_CANCELLED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
status = irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
if (status != STATUS_PENDING)
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
PIRP DequeueWaitIrp(POSPDOEXT *pdoExt)
|
|
{
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
PLIST_ENTRY listEntry;
|
|
KIRQL oldIrql;
|
|
PIRP nextIrp = NULL;
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
|
|
while (!nextIrp && !IsListEmpty(&pdoExt->pendingWaitIrpsList)) {
|
|
listEntry = RemoveHeadList(&pdoExt->pendingWaitIrpsList);
|
|
nextIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
|
|
oldCancelRoutine = IoSetCancelRoutine(nextIrp, NULL);
|
|
|
|
if(oldCancelRoutine) {
|
|
ASSERT(oldCancelRoutine == WaitMaskCancelRoutine);
|
|
}
|
|
else {
|
|
ASSERT(nextIrp->Cancel);
|
|
InitializeListHead(&nextIrp->Tail.Overlay.ListEntry);
|
|
nextIrp = NULL;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
return nextIrp;
|
|
}
|
|
|
|
|
|
VOID WaitMaskCancelRoutine(PDEVICE_OBJECT devObj, PIRP irp)
|
|
{
|
|
DEVEXT *devExt;
|
|
POSPDOEXT *pdoExt;
|
|
KIRQL oldIrql;
|
|
|
|
devExt = devObj->DeviceExtension;
|
|
pdoExt = &devExt->pdoExt;
|
|
|
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|
|
|
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
|
|
RemoveEntryList(&irp->Tail.Overlay.ListEntry);
|
|
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
|
|
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
} |