/* * UNIMODEM "Fakemodem" controllerless driver illustrative example * * (C) 2000 Microsoft Corporation * All Rights Reserved * */ #include "fakemodem.h" NTSTATUS FakeModemIoControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status=STATUS_UNSUCCESSFUL; KIRQL OldIrql; PIO_STACK_LOCATION IrpSp; 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; } IrpSp=IoGetCurrentIrpStackLocation(Irp); switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SERIAL_GET_WAIT_MASK: { if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } *((PULONG)Irp->AssociatedIrp.SystemBuffer)= deviceExtension->CurrentMask; Irp->IoStatus.Information=sizeof(ULONG); break; } case IOCTL_SERIAL_SET_WAIT_MASK: { PIRP CurrentWaitIrp=NULL; ULONG NewMask; if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } else { KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql); // get rid of the current wait CurrentWaitIrp=deviceExtension->CurrentMaskIrp; deviceExtension->CurrentMaskIrp=NULL; Irp->IoStatus.Information=sizeof(ULONG); // save the new mask NewMask = *((ULONG *)Irp->AssociatedIrp.SystemBuffer); deviceExtension->CurrentMask=NewMask; D_TRACE(DbgPrint("FAKEMODEM: set wait mask, %08lx\n",NewMask);) KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql); if (CurrentWaitIrp != NULL) { D_TRACE(DbgPrint("FAKEMODEM: set wait mask- complete wait\n");) *((PULONG)CurrentWaitIrp->AssociatedIrp.SystemBuffer)=0; CurrentWaitIrp->IoStatus.Information=sizeof(ULONG); RemoveReferenceAndCompleteRequest( deviceExtension->DeviceObject, CurrentWaitIrp, STATUS_SUCCESS); } } break; } case IOCTL_SERIAL_WAIT_ON_MASK: { PIRP CurrentWaitIrp=NULL; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } D_TRACE(DbgPrint("FAKEMODEM: wait on mask\n");) KeAcquireSpinLock(&deviceExtension->SpinLock, &OldIrql); // // capture the current irp if any // CurrentWaitIrp=deviceExtension->CurrentMaskIrp; deviceExtension->CurrentMaskIrp=NULL; if (deviceExtension->CurrentMask == 0) { // // can only set if mask is not zero // status=STATUS_UNSUCCESSFUL; } else { deviceExtension->CurrentMaskIrp=Irp; Irp->IoStatus.Status=STATUS_PENDING; IoMarkIrpPending(Irp); #if DBG Irp=NULL; #endif status=STATUS_PENDING; } KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql ); if (CurrentWaitIrp != NULL) { D_TRACE(DbgPrint("FAKEMODEM: wait on mask- complete wait\n");) *((PULONG)CurrentWaitIrp->AssociatedIrp.SystemBuffer)=0; CurrentWaitIrp->IoStatus.Information=sizeof(ULONG); RemoveReferenceAndCompleteRequest( deviceExtension->DeviceObject, CurrentWaitIrp, STATUS_SUCCESS); } break; } case IOCTL_SERIAL_PURGE: { ULONG Mask=*((PULONG)Irp->AssociatedIrp.SystemBuffer); if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } if (Mask & SERIAL_PURGE_RXABORT) { PIRP Irp; KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql ); while ( !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); KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql); Irp->IoStatus.Information=0; RemoveReferenceAndCompleteRequest( deviceExtension->DeviceObject, Irp, STATUS_CANCELLED); KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql); } Irp=NULL; if (deviceExtension->CurrentReadIrp != NULL) { // // get the current one // Irp=deviceExtension->CurrentReadIrp; deviceExtension->CurrentReadIrp=NULL; } KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql); if (Irp != NULL) { Irp->IoStatus.Information=0; RemoveReferenceAndCompleteRequest( deviceExtension->DeviceObject, Irp, STATUS_CANCELLED); } } break; } case IOCTL_SERIAL_GET_MODEMSTATUS: { if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } Irp->IoStatus.Information=sizeof(ULONG); *((PULONG)Irp->AssociatedIrp.SystemBuffer)= deviceExtension->ModemStatus; break; } case IOCTL_SERIAL_SET_TIMEOUTS: { PSERIAL_TIMEOUTS NewTimeouts = ((PSERIAL_TIMEOUTS)(Irp->AssociatedIrp.SystemBuffer)); if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SERIAL_TIMEOUTS)) { status = STATUS_BUFFER_TOO_SMALL; break; } RtlCopyMemory( &deviceExtension->CurrentTimeouts, NewTimeouts, sizeof(PSERIAL_TIMEOUTS)); Irp->IoStatus.Information = sizeof(PSERIAL_TIMEOUTS); break; } case IOCTL_SERIAL_GET_TIMEOUTS: { if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_TIMEOUTS)) { status = STATUS_BUFFER_TOO_SMALL; break; } RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer, &deviceExtension->CurrentTimeouts, sizeof(PSERIAL_TIMEOUTS)); Irp->IoStatus.Information = sizeof(PSERIAL_TIMEOUTS); break; } case IOCTL_SERIAL_GET_COMMSTATUS: { PSERIAL_STATUS SerialStatus=(PSERIAL_STATUS)Irp->AssociatedIrp.SystemBuffer; if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_STATUS)) { status = STATUS_BUFFER_TOO_SMALL; break; } RtlZeroMemory( SerialStatus, sizeof(SERIAL_STATUS)); KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql); SerialStatus->AmountInInQueue=deviceExtension->BytesInReadBuffer; KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql); Irp->IoStatus.Information = sizeof(SERIAL_STATUS); break; } case IOCTL_SERIAL_SET_DTR: case IOCTL_SERIAL_CLR_DTR: { KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql); if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_SET_DTR) { // // raising DTR // deviceExtension->ModemStatus=SERIAL_DTR_STATE | SERIAL_DSR_STATE; D_TRACE(DbgPrint("FAKEMODEM: Set DTR\n");) } else { // // dropping DTR, drop connection if there is one // D_TRACE(DbgPrint("FAKEMODEM: Clear DTR\n");) if (deviceExtension->CurrentlyConnected == TRUE) { // // not connected any more // deviceExtension->CurrentlyConnected=FALSE; deviceExtension->ConnectionStateChanged=TRUE; } } KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql); ProcessConnectionStateChange( DeviceObject); break; } default: status=STATUS_SUCCESS; } if (status != STATUS_PENDING) { // // complete now if not pending // RemoveReferenceAndCompleteRequest( DeviceObject, Irp, status); } RemoveReferenceForDispatch(DeviceObject); return status; }