|
|
/*
* 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;
}
|