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.
672 lines
17 KiB
672 lines
17 KiB
/*
|
|
* 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;
|
|
|
|
}
|