/*++ Module Name: waitmask.c Environment: Kernel mode Revision History : --*/ #include "precomp.h" NTSTATUS MoxaStartMask( IN PMOXA_DEVICE_EXTENSION Extension ) { PIO_STACK_LOCATION irpSp; PIRP newIrp; BOOLEAN setFirstStatus = FALSE; NTSTATUS firstStatus; do { irpSp = IoGetCurrentIrpStackLocation(Extension->CurrentMaskIrp); if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_SET_WAIT_MASK) { KeSynchronizeExecution( Extension->Interrupt, MoxaFinishOldWait, Extension ); Extension->CurrentMaskIrp->IoStatus.Status = STATUS_SUCCESS; if (!setFirstStatus) { firstStatus = STATUS_SUCCESS; setFirstStatus = TRUE; } MoxaGetNextIrp( &Extension->CurrentMaskIrp, &Extension->MaskQueue, &newIrp, TRUE, Extension ); } else { // // IOCTL_SERIAL_WAIT_ON_MASK // // First make sure that we have a non-zero mask. // If the app queues a wait on a zero mask it can't // be statisfied so it makes no sense to start it. // if ((!Extension->IsrWaitMask) || (Extension->CurrentWaitIrp)) { Extension->CurrentMaskIrp->IoStatus.Status = STATUS_INVALID_PARAMETER; if (!setFirstStatus) { firstStatus = STATUS_INVALID_PARAMETER; setFirstStatus = TRUE; } MoxaGetNextIrp( &Extension->CurrentMaskIrp, &Extension->MaskQueue, &newIrp, TRUE, Extension ); } else { KIRQL oldIrql; IoAcquireCancelSpinLock(&oldIrql); if (Extension->CurrentMaskIrp->Cancel) { Extension->CurrentMaskIrp->IoStatus.Status = STATUS_CANCELLED; IoReleaseCancelSpinLock(oldIrql); if (!setFirstStatus) { firstStatus = STATUS_CANCELLED; setFirstStatus = TRUE; } MoxaGetNextIrp( &Extension->CurrentMaskIrp, &Extension->MaskQueue, &newIrp, TRUE, Extension ); } else { if (!setFirstStatus) { firstStatus = STATUS_PENDING; setFirstStatus = TRUE; IoMarkIrpPending(Extension->CurrentMaskIrp); } Extension->CurrentWaitIrp = Extension->CurrentMaskIrp; MOXA_INIT_REFERENCE(Extension->CurrentWaitIrp); IoSetCancelRoutine( Extension->CurrentWaitIrp, MoxaCancelWait ); // // Since the cancel routine has a reference to // the irp we need to update the reference // count. // MOXA_INC_REFERENCE(Extension->CurrentWaitIrp); KeSynchronizeExecution( Extension->Interrupt, MoxaGiveWaitToIsr, Extension ); IoReleaseCancelSpinLock(oldIrql); MoxaGetNextIrp( &Extension->CurrentMaskIrp, &Extension->MaskQueue, &newIrp, FALSE, Extension ); } } } } while (newIrp); return firstStatus; } BOOLEAN MoxaFinishOldWait( IN PVOID Context ) { PMOXA_DEVICE_EXTENSION extension = Context; PUCHAR ofs; if (extension->IrpMaskLocation) { // // The isr still "owns" the irp. // *extension->IrpMaskLocation = 0; extension->IrpMaskLocation = NULL; extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG); // // We don't decrement the reference since the completion routine // will do that. // MoxaInsertQueueDpc( &extension->CommWaitDpc, NULL, NULL, extension ); } // // Don't wipe out any historical data we are still interested in. // extension->HistoryMask &= *((ULONG *)extension->CurrentMaskIrp-> AssociatedIrp.SystemBuffer); extension->IsrWaitMask = *((ULONG *)extension->CurrentMaskIrp-> AssociatedIrp.SystemBuffer); ofs = extension->PortOfs; if (extension->IsrWaitMask & SERIAL_EV_RXCHAR) *(PUSHORT)(ofs + HostStat) |= WakeupRx; else *(PUSHORT)(ofs + HostStat) &= ~WakeupRx; if (extension->IsrWaitMask & SERIAL_EV_RXFLAG) *(PUSHORT)(ofs + HostStat) |= WakeupEvent; else *(PUSHORT)(ofs + HostStat) &= ~WakeupEvent; if (extension->IsrWaitMask & SERIAL_EV_RX80FULL) *(PUSHORT)(ofs + HostStat) |= WakeupRx80Full; else *(PUSHORT)(ofs + HostStat) &= ~WakeupRx80Full; if (extension->IsrWaitMask & SERIAL_EV_ERR) { *(PUSHORT)(ofs + HostStat) |= WakeupError; } else { *(PUSHORT)(ofs + HostStat) &= ~WakeupError; } if (extension->IsrWaitMask & SERIAL_EV_BREAK) { *(PUSHORT)(ofs + HostStat) |= WakeupBreak; } else { *(PUSHORT)(ofs + HostStat) &= ~WakeupBreak; } return FALSE; } VOID MoxaCancelWait( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PMOXA_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension; MoxaTryToCompleteCurrent( extension, MoxaGrabWaitFromIsr, Irp->CancelIrql, STATUS_CANCELLED, &extension->CurrentWaitIrp, NULL, NULL, NULL, NULL, NULL ); } BOOLEAN MoxaGrabWaitFromIsr( IN PVOID Context ) { PMOXA_DEVICE_EXTENSION extension = Context; if (extension->IrpMaskLocation) { // // The isr still "owns" the irp. // *extension->IrpMaskLocation = 0; extension->IrpMaskLocation = NULL; extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG); // // Since the isr no longer references the irp we need to // decrement the reference count. // MOXA_DEC_REFERENCE(extension->CurrentWaitIrp); } return FALSE; } BOOLEAN MoxaGiveWaitToIsr( IN PVOID Context ) { PMOXA_DEVICE_EXTENSION extension = Context; MOXA_INC_REFERENCE(extension->CurrentWaitIrp); if (!extension->HistoryMask) extension->IrpMaskLocation = extension->CurrentWaitIrp->AssociatedIrp.SystemBuffer; else { *((ULONG *)extension->CurrentWaitIrp->AssociatedIrp.SystemBuffer) = extension->HistoryMask; extension->HistoryMask = 0; extension->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG); extension->CurrentWaitIrp->IoStatus.Status = STATUS_SUCCESS; MoxaInsertQueueDpc( &extension->CommWaitDpc, NULL, NULL, extension ); } return FALSE; } VOID MoxaCompleteWait( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemContext1, IN PVOID SystemContext2 ) { PMOXA_DEVICE_EXTENSION extension = DeferredContext; KIRQL oldIrql; IoAcquireCancelSpinLock(&oldIrql); MoxaTryToCompleteCurrent( extension, NULL, oldIrql, STATUS_SUCCESS, &extension->CurrentWaitIrp, NULL, NULL, NULL, NULL, NULL ); MoxaDpcEpilogue(extension, Dpc); }