windows-nt/Source/XPSP1/NT/net/unimodem/sample/sys/readwrit.c
2020-09-26 16:20:57 +08:00

673 lines
16 KiB
C

/*
* UNIMODEM "Fakemodem" controllerless driver illustrative example
*
* (C) 2000 Microsoft Corporation
* All Rights Reserved
*
* The code in this module simply supports the very basic AT command parser.
* This code should be completely replaced with the actual code to support
* your controllerless modem.
*/
#include "fakemodem.h"
NTSTATUS
FakeModemRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
KIRQL CancelIrql;
Irp->IoStatus.Information = 0;
//
// make sure the device is ready for irp's
//
status=CheckStateAndAddReference( DeviceObject, Irp);
if (STATUS_SUCCESS != status) {
//
// not accepting irp's. The irp has already been completed
//
return status;
}
Irp->IoStatus.Status=STATUS_PENDING;
IoMarkIrpPending(Irp);
KeAcquireSpinLock(&deviceExtension->SpinLock, &OldIrql);
//
// make irp cancelable
//
IoAcquireCancelSpinLock(&CancelIrql);
IoSetCancelRoutine(Irp, ReadCancelRoutine);
IoReleaseCancelSpinLock(CancelIrql);
//
// put it on queue
//
InsertTailList(&deviceExtension->ReadQueue, &Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock(&deviceExtension->SpinLock, OldIrql);
//
// call the real work function to process the irps
//
ReadIrpWorker( DeviceObject);
RemoveReferenceForDispatch(DeviceObject);
return STATUS_PENDING;
}
NTSTATUS
FakeModemWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
KIRQL CancelIrql;
Irp->IoStatus.Information = 0;
// make sure the device is ready for irp's
status=CheckStateAndAddReference( DeviceObject, Irp);
if (STATUS_SUCCESS != status) {
// not accepting irp's. The irp has already been complted
return status;
}
Irp->IoStatus.Status=STATUS_PENDING;
IoMarkIrpPending(Irp);
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
// make irp cancelable
IoAcquireCancelSpinLock(&CancelIrql);
IoSetCancelRoutine(Irp, WriteCancelRoutine);
IoReleaseCancelSpinLock(CancelIrql);
// put it on queue
InsertTailList( &deviceExtension->WriteQueue, &Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock(&deviceExtension->SpinLock, OldIrql);
// call the real work function to process the irps
if (deviceExtension->Started)
{
WriteIrpWorker(DeviceObject);
}
RemoveReferenceForDispatch(DeviceObject);
return STATUS_PENDING;
}
VOID
WriteIrpWorker(
IN PDEVICE_OBJECT DeviceObject
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
if (deviceExtension->CurrentWriteIrp != NULL) {
// already in use
goto Exit;
}
while (!IsListEmpty(&deviceExtension->WriteQueue)) {
PLIST_ENTRY ListElement;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
KIRQL CancelIrql;
ListElement=RemoveHeadList( &deviceExtension->WriteQueue);
Irp=CONTAINING_RECORD(ListElement,IRP,Tail.Overlay.ListEntry);
IoAcquireCancelSpinLock(&CancelIrql);
if (Irp->Cancel) {
// this one has been canceled
Irp->IoStatus.Information=STATUS_CANCELLED;
IoReleaseCancelSpinLock(CancelIrql);
continue;
}
IoSetCancelRoutine(
Irp,
NULL
);
IoReleaseCancelSpinLock(CancelIrql);
deviceExtension->CurrentWriteIrp=Irp;
IrpSp=IoGetCurrentIrpStackLocation(Irp);
ProcessWriteBytes( deviceExtension, Irp->AssociatedIrp.SystemBuffer,
IrpSp->Parameters.Write.Length);
KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
RemoveReferenceAndCompleteRequest( DeviceObject, Irp, STATUS_SUCCESS);
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
deviceExtension->CurrentWriteIrp=NULL;
}
Exit:
KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
TryToSatisfyRead( deviceExtension);
ReadIrpWorker( DeviceObject);
ProcessConnectionStateChange( DeviceObject);
return;
}
VOID
ProcessWriteBytes(
PDEVICE_EXTENSION DeviceExtension,
PUCHAR Characters,
ULONG Length
)
{
UCHAR CurrentCharacter;
while (Length != 0) {
CurrentCharacter=*Characters++;
Length--;
PutCharInReadBuffer( DeviceExtension, CurrentCharacter);
switch (DeviceExtension->CommandMatchState) {
case COMMAND_MATCH_STATE_IDLE:
if ((CurrentCharacter == 'a') || (CurrentCharacter == 'A')) {
// got an A
DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_GOT_A;
DeviceExtension->ConnectCommand=FALSE;
DeviceExtension->IgnoreNextChar=FALSE;
}
break;
case COMMAND_MATCH_STATE_GOT_A:
if ((CurrentCharacter == 't') || (CurrentCharacter == 'T')) {
// got an T
DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_GOT_T;
} else {
if (CurrentCharacter == '\r') {
DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_IDLE;
}
}
break;
case COMMAND_MATCH_STATE_GOT_T:
if (!DeviceExtension->IgnoreNextChar) {
// the last char was not a special char
// check for CONNECT command
if ((CurrentCharacter == 'A') || (CurrentCharacter == 'a')) {
DeviceExtension->ConnectCommand=TRUE;
}
if ((CurrentCharacter == 'D') || (CurrentCharacter == 'd')) {
DeviceExtension->ConnectCommand=TRUE;
}
}
DeviceExtension->IgnoreNextChar=FALSE;
if ((CurrentCharacter == '&')
||
(CurrentCharacter == '/')
||
(CurrentCharacter == '\\')
||
(CurrentCharacter == '+')
||
(CurrentCharacter == '%')) {
// these characters are part of are used in init
// strings and may be proceeding an A or D
// which we don't want to misinterpret as a dial or answer
DeviceExtension->IgnoreNextChar=TRUE;
}
if (CurrentCharacter == '\r') {
//
// got a CR, send a response to the command
//
DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_IDLE;
if (DeviceExtension->ConnectCommand) {
//
// place <cr><lf>CONNECT<cr><lf> in the buffer
//
PutCharInReadBuffer(DeviceExtension,'\r');
PutCharInReadBuffer(DeviceExtension,'\n');
PutCharInReadBuffer(DeviceExtension,'C');
PutCharInReadBuffer(DeviceExtension,'O');
PutCharInReadBuffer(DeviceExtension,'N');
PutCharInReadBuffer(DeviceExtension,'N');
PutCharInReadBuffer(DeviceExtension,'E');
PutCharInReadBuffer(DeviceExtension,'C');
PutCharInReadBuffer(DeviceExtension,'T');
PutCharInReadBuffer(DeviceExtension,'\r');
PutCharInReadBuffer(DeviceExtension,'\n');
//
// connected now raise CD
//
DeviceExtension->CurrentlyConnected=TRUE;
DeviceExtension->ConnectionStateChanged=TRUE;
} else {
// place <cr><lf>OK<cr><lf> in the buffer
PutCharInReadBuffer(DeviceExtension,'\r');
PutCharInReadBuffer(DeviceExtension,'\n');
PutCharInReadBuffer(DeviceExtension,'O');
PutCharInReadBuffer(DeviceExtension,'K');
PutCharInReadBuffer(DeviceExtension,'\r');
PutCharInReadBuffer(DeviceExtension,'\n');
}
}
break;
default:
break;
}
}
return;
}
VOID
PutCharInReadBuffer(
PDEVICE_EXTENSION DeviceExtension,
UCHAR Character
)
{
if (DeviceExtension->BytesInReadBuffer < READ_BUFFER_SIZE) {
// room in buffer
DeviceExtension->ReadBuffer[DeviceExtension->ReadBufferEnd]=Character;
DeviceExtension->ReadBufferEnd++;
DeviceExtension->ReadBufferEnd %= READ_BUFFER_SIZE;
DeviceExtension->BytesInReadBuffer++;
}
return;
}
VOID
ReadIrpWorker(
PDEVICE_OBJECT DeviceObject
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
while ((deviceExtension->CurrentReadIrp == NULL)
&& !IsListEmpty(&deviceExtension->ReadQueue)) {
PLIST_ENTRY ListElement;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
KIRQL CancelIrql;
ListElement=RemoveHeadList( &deviceExtension->ReadQueue);
Irp=CONTAINING_RECORD(ListElement,IRP,Tail.Overlay.ListEntry);
IoAcquireCancelSpinLock(&CancelIrql);
if (Irp->Cancel) {
// this one has been canceled
Irp->IoStatus.Information=STATUS_CANCELLED;
IoReleaseCancelSpinLock(CancelIrql);
continue;
}
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(CancelIrql);
deviceExtension->CurrentReadIrp=Irp;
KeReleaseSpinLock(&deviceExtension->SpinLock, OldIrql);
TryToSatisfyRead( deviceExtension);
KeAcquireSpinLock(&deviceExtension->SpinLock, &OldIrql);
}
KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
return;
}
VOID
TryToSatisfyRead(
PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
PIRP Irp=NULL;
PIO_STACK_LOCATION IrpSp;
ULONG BytesToMove;
ULONG FirstHalf;
ULONG SecondHalf;
KeAcquireSpinLock(
&DeviceExtension->SpinLock,
&OldIrql
);
if ((DeviceExtension->CurrentReadIrp != NULL) && (DeviceExtension->BytesInReadBuffer > 0)) {
//
// there is an IRP and there are characters waiting
//
Irp=DeviceExtension->CurrentReadIrp;
IrpSp=IoGetCurrentIrpStackLocation(Irp);
BytesToMove=IrpSp->Parameters.Read.Length < DeviceExtension->BytesInReadBuffer ?
IrpSp->Parameters.Read.Length : DeviceExtension->BytesInReadBuffer;
if (DeviceExtension->ReadBufferBegin+BytesToMove > READ_BUFFER_SIZE) {
//
// the buffer is wrapped around, have move in two pieces
//
FirstHalf=READ_BUFFER_SIZE-DeviceExtension->ReadBufferBegin;
SecondHalf=BytesToMove-FirstHalf;
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&DeviceExtension->ReadBuffer[DeviceExtension->ReadBufferBegin],
FirstHalf);
RtlCopyMemory(
(PUCHAR)Irp->AssociatedIrp.SystemBuffer+FirstHalf,
&DeviceExtension->ReadBuffer[0], SecondHalf);
} else {
//
// can do it all at once
//
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer,
&DeviceExtension->ReadBuffer[DeviceExtension->ReadBufferBegin],
BytesToMove);
}
//
// fix up queue pointers
//
DeviceExtension->BytesInReadBuffer-=BytesToMove;
DeviceExtension->ReadBufferBegin+= BytesToMove;
DeviceExtension->ReadBufferBegin %= READ_BUFFER_SIZE;
Irp->IoStatus.Information=BytesToMove;
}
KeReleaseSpinLock( &DeviceExtension->SpinLock, OldIrql);
if (Irp != NULL) {
//
// if irp isn't null, then we handled one
//
RemoveReferenceAndCompleteRequest(
DeviceExtension->DeviceObject, Irp, STATUS_SUCCESS);
DeviceExtension->CurrentReadIrp=NULL;
}
return;
}
VOID
WriteCancelRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
//
// release the cancel spinlock to avaoid deadlocks with deviceextension spinlock
//
IoReleaseCancelSpinLock(Irp->CancelIrql);
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
if (Irp->IoStatus.Information != STATUS_CANCELLED) {
//
// the irp is still in the queue, remove it
//
RemoveEntryList( &Irp->Tail.Overlay.ListEntry);
}
KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
Irp->IoStatus.Information = 0;
RemoveReferenceAndCompleteRequest( DeviceObject, Irp, STATUS_CANCELLED);
return;
}
VOID
ReadCancelRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status=STATUS_UNSUCCESSFUL;
KIRQL OldIrql;
// release the cancel spinlock to avoid deadlocks with deviceextension spinlock
IoReleaseCancelSpinLock(Irp->CancelIrql);
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
if (Irp->IoStatus.Information != STATUS_CANCELLED) {
// the irp is still in the queue, remove it
RemoveEntryList( &Irp->Tail.Overlay.ListEntry);
}
KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
Irp->IoStatus.Information = 0;
RemoveReferenceAndCompleteRequest( DeviceObject, Irp, STATUS_CANCELLED);
return;
}
VOID
ProcessConnectionStateChange(
IN PDEVICE_OBJECT DeviceObject
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
PIRP CurrentWaitIrp=NULL;
KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
if (deviceExtension->ConnectionStateChanged) {
//
// state changed
//
deviceExtension->ConnectionStateChanged=FALSE;
if (deviceExtension->CurrentlyConnected) {
//
// now it is connected, raise CD
//
deviceExtension->ModemStatus |= SERIAL_DCD_STATE;
} else {
//
// not connected any more, clear CD
//
deviceExtension->ModemStatus &= ~(SERIAL_DCD_STATE);
}
if (deviceExtension->CurrentMask & SERIAL_EV_RLSD) {
//
// app want's to know about these changes, tell it
//
CurrentWaitIrp=deviceExtension->CurrentMaskIrp;
deviceExtension->CurrentMaskIrp=NULL;
}
}
KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
if (CurrentWaitIrp != NULL) {
D_TRACE(DbgPrint("FAKEMODEM: ProcessConectionState\n");)
*((PULONG)CurrentWaitIrp->AssociatedIrp.SystemBuffer)=SERIAL_EV_RLSD;
CurrentWaitIrp->IoStatus.Information=sizeof(ULONG);
RemoveReferenceAndCompleteRequest(
deviceExtension->DeviceObject, CurrentWaitIrp, STATUS_SUCCESS);
}
return;
}