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