414 lines
10 KiB
C
414 lines
10 KiB
C
/*
|
|
* 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;
|
|
|
|
|
|
}
|