/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Copyright (c) 1991, 1992, 1993 Microsoft Corporation Module Name: isr.c Abstract: This module contains the interrupt service routine for the serial driver. Author: Anthony V. Ercolano 26-Sep-1991 Environment: Kernel mode Revision History : -----------------------------------------------------------------------------*/ #include "precomp.h" BOOLEAN SerialISR(IN PKINTERRUPT InterruptObject, IN PVOID Context) { // Holds the information specific to handling this device. PCARD_DEVICE_EXTENSION pCard = Context; PPORT_DEVICE_EXTENSION pPort; BOOLEAN ServicedAnInterrupt = FALSE; PUART_OBJECT pUart = pCard->pFirstUart; DWORD IntsPending = 0; UNREFERENCED_PARAMETER(InterruptObject); #ifndef BUILD_SPXMINIPORT // If the card is not powered, delay interrupt service until it is. if(!(pCard->PnpPowerFlags & PPF_POWERED) && (pCard->PnpPowerFlags & PPF_STARTED)) return ServicedAnInterrupt; // Most likely the interrupt is not ours anyway. #endif switch(pCard->CardType) { case Fast4_Isa: case Fast4_Pci: case RAS4_Pci: { if((READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_0_TO_7_INTS_REG) & FAST_UARTS_0_TO_3_INT_PENDING) == 0) return ServicedAnInterrupt; // If no Uarts have interrupts pending then return. break; } case Fast8_Isa: case Fast8_Pci: case RAS8_Pci: { if(READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_0_TO_7_INTS_REG) == 0) return ServicedAnInterrupt; // If no Uarts have interrupts pending then return. break; } case Fast16_Isa: case Fast16_Pci: case Fast16FMC_Pci: { if((READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_0_TO_7_INTS_REG) == 0) && (READ_PORT_UCHAR(pCard->Controller + FAST_UARTS_9_TO_16_INTS_REG) == 0)) return ServicedAnInterrupt; // If no Uarts have interrupts pending then return. break; } break; case Speed2_Pci: case Speed2P_Pci: case Speed4_Pci: case Speed4P_Pci: { if((READ_REGISTER_ULONG( (PULONG)(pCard->LocalConfigRegisters + SPEED_GIS_REG)) & INTERNAL_UART_INT_PENDING) == 0) return ServicedAnInterrupt; // If no Uarts have interrupts pending then return. break; } case Speed2and4_Pci_8BitBus: case Speed2P_Pci_8BitBus: case Speed4P_Pci_8BitBus: return ServicedAnInterrupt; // No UARTs therefore NO interrupts that are ours - we hope. break; default: break; } if(pUart) { while((IntsPending = pCard->UartLib.UL_IntsPending_XXXX(&pUart))) { pPort = (PPORT_DEVICE_EXTENSION) pCard->UartLib.UL_GetAppBackPtr_XXXX(pUart); // Get Port Extension for UART. SpxDbgMsg(ISRINFO, ("%s: Int on 0x%lX", PRODUCT_NAME, IntsPending)); // Service receive status interrupts if(IntsPending & UL_IP_RX_STAT) { BYTE LineStatus = 0; DWORD RxStatus; pPort->pUartLib->UL_GetStatus_XXXX(pUart, &RxStatus, UL_GS_OP_LINESTATUS); // If OVERRUN/PARITY/FRAMING/DATA/BREAK error if(RxStatus & (UL_US_OVERRUN_ERROR | UL_US_PARITY_ERROR | UL_US_FRAMING_ERROR | UL_US_DATA_ERROR | UL_US_BREAK_ERROR)) { BYTE TmpByte; // If the application has requested it, abort all the reads and writes on an error. if(pPort->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) KeInsertQueueDpc(&pPort->CommErrorDpc, NULL, NULL); /* if(pPort->EscapeChar) { TmpByte = pPort->EscapeChar; pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE); if(RxStatus & UL_US_DATA_ERROR) { TmpByte = SERIAL_LSRMST_LSR_DATA pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE); } else { TmpByte = SERIAL_LSRMST_LSR_NODATA pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE); } } */ if(RxStatus & UL_US_OVERRUN_ERROR) { pPort->ErrorWord |= SERIAL_ERROR_OVERRUN; LineStatus |= SERIAL_LSR_OE; pPort->PerfStats.SerialOverrunErrorCount++; #ifdef WMI_SUPPORT pPort->WmiPerfData.SerialOverrunErrorCount++; #endif } if(RxStatus & UL_US_PARITY_ERROR) { pPort->ErrorWord |= SERIAL_ERROR_PARITY; LineStatus |= SERIAL_LSR_PE; pPort->PerfStats.ParityErrorCount++; #ifdef WMI_SUPPORT pPort->WmiPerfData.ParityErrorCount++; #endif } if(RxStatus & UL_US_FRAMING_ERROR) { pPort->ErrorWord |= SERIAL_ERROR_FRAMING; LineStatus |= SERIAL_LSR_FE; pPort->PerfStats.FrameErrorCount++; #ifdef WMI_SUPPORT pPort->WmiPerfData.FrameErrorCount++; #endif } if(RxStatus & UL_US_DATA_ERROR) { LineStatus |= SERIAL_LSR_DR; } if(RxStatus & UL_US_BREAK_ERROR) { pPort->ErrorWord |= SERIAL_ERROR_BREAK; LineStatus |= SERIAL_LSR_BI; } /* if(pPort->EscapeChar) { TmpByte = LineStatus; pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE); } if(RxStatus & (UL_US_OVERRUN_ERROR | UL_US_PARITY_ERROR | UL_US_FRAMING_ERROR | UL_US_DATA_ERROR)) { if(pPort->HandFlow.FlowReplace & SERIAL_ERROR_CHAR) { TmpByte = pPort->SpecialChars.ErrorChar; pPort->pUartLib->UL_ImmediateByte_XXXX(pPort->pUart, &TmpByte, UL_IM_OP_WRITE); } } */ } if(pPort->IsrWaitMask) { if((pPort->IsrWaitMask & SERIAL_EV_ERR) && (RxStatus & (UL_US_OVERRUN_ERROR | UL_US_PARITY_ERROR | UL_US_FRAMING_ERROR | UL_US_DATA_ERROR))) { // if we detected a overrun/parity/framing/data error pPort->HistoryMask |= SERIAL_EV_ERR; } // if we detected a break error if((pPort->IsrWaitMask & SERIAL_EV_BREAK) && (RxStatus & UL_US_BREAK_ERROR)) pPort->HistoryMask |= SERIAL_EV_BREAK; #ifdef USE_HW_TO_DETECT_CHAR // if we detected the special char if((pPort->IsrWaitMask & SERIAL_EV_RXFLAG) && (RxStatus & UL_RS_SPECIAL_CHAR_DETECTED)) pPort->HistoryMask |= SERIAL_EV_RXFLAG; #endif if(pPort->IrpMaskLocation && pPort->HistoryMask) { *pPort->IrpMaskLocation = pPort->HistoryMask; pPort->IrpMaskLocation = NULL; pPort->HistoryMask = 0; pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG); // Mark IRP as about to complete normally to prevent cancel & timer DPCs // from doing so before DPC is allowed to run. //SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING); KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL); } } } // Service receive and receive timeout interrupts. if((IntsPending & UL_IP_RX) || (IntsPending & UL_IP_RXTO)) { DWORD StatusFlags = 0; int BytesReceived = pPort->pUartLib->UL_InputData_XXXX(pUart, &StatusFlags); if(StatusFlags & UL_RS_BUFFER_OVERRUN) { // We have a new character but no room for it. pPort->ErrorWord |= SERIAL_ERROR_QUEUEOVERRUN; pPort->PerfStats.BufferOverrunErrorCount++; #ifdef WMI_SUPPORT pPort->WmiPerfData.BufferOverrunErrorCount++; #endif } if(BytesReceived) { ULONG AmountInBuffer = 0; GET_BUFFER_STATE BufferState; pPort->ReadByIsr += BytesReceived; pPort->PerfStats.ReceivedCount += BytesReceived; // Increment Rx Counter #ifdef WMI_SUPPORT pPort->WmiPerfData.ReceivedCount += BytesReceived; #endif pPort->pUartLib->UL_BufferControl_XXXX(pUart, &BufferState, UL_BC_OP_GET, UL_BC_BUFFER | UL_BC_IN); AmountInBuffer = BufferState.BytesInINBuffer; if(pPort->IsrWaitMask) { // Check to see if we should note the receive character if(pPort->IsrWaitMask & SERIAL_EV_RXCHAR) pPort->HistoryMask |= SERIAL_EV_RXCHAR; // If we've become 80% full on this character and this is an interesting event, note it. if((pPort->IsrWaitMask & SERIAL_EV_RX80FULL) && (AmountInBuffer >= pPort->BufferSizePt8)) pPort->HistoryMask |= SERIAL_EV_RX80FULL; #ifndef USE_HW_TO_DETECT_CHAR // if we detected the special char if((pPort->IsrWaitMask & SERIAL_EV_RXFLAG) && (StatusFlags & UL_RS_SPECIAL_CHAR_DETECTED)) pPort->HistoryMask |= SERIAL_EV_RXFLAG; #endif if(pPort->IrpMaskLocation && pPort->HistoryMask) { *pPort->IrpMaskLocation = pPort->HistoryMask; pPort->IrpMaskLocation = NULL; pPort->HistoryMask = 0; pPort->CurrentWaitIrp->IoStatus.Information = sizeof(ULONG); // Mark IRP as about to complete normally to prevent cancel & timer DPCs // from doing so before DPC is allowed to run. //SERIAL_SET_REFERENCE(pPort->CurrentWaitIrp, SERIAL_REF_COMPLETING); KeInsertQueueDpc(&pPort->CommWaitDpc, NULL, NULL); } } // If we have a current Read IRP. if(pPort->CurrentReadIrp && pPort->NumberNeededForRead) { // If our ISR currently owns the IRP the we are allowed to do something with it, // But we only need to do something if we need to make room in the buffer // or we have enough bytes in the buffer to complete the current read IRP. if((SERIAL_REFERENCE_COUNT(pPort->CurrentReadIrp) & SERIAL_REF_ISR) && ((AmountInBuffer >= pPort->BufferSizePt8) || (AmountInBuffer >= pPort->NumberNeededForRead))) { ULONG NumberOfBytes = 0; NumberOfBytes = pPort->pUartLib->UL_ReadData_XXXX(pPort->pUart, (PUCHAR)(pPort->CurrentReadIrp->AssociatedIrp.SystemBuffer) + pPort->CurrentReadIrp->IoStatus.Information, IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->Parameters.Read.Length - pPort->CurrentReadIrp->IoStatus.Information); if(NumberOfBytes > pPort->NumberNeededForRead) pPort->NumberNeededForRead = 0; else pPort->NumberNeededForRead -= NumberOfBytes; pPort->CurrentReadIrp->IoStatus.Information += NumberOfBytes; if(pPort->NumberNeededForRead == 0) { ASSERT(pPort->CurrentReadIrp->IoStatus.Information == IoGetCurrentIrpStackLocation(pPort->CurrentReadIrp)->Parameters.Read.Length); // Mark IRP as about to complete normally to prevent cancel & timer DPCs // from doing so before DPC is allowed to run. SERIAL_SET_REFERENCE(pPort->CurrentReadIrp, SERIAL_REF_COMPLETING); KeInsertQueueDpc(&pPort->CompleteReadDpc, NULL, NULL); } } } } } // Service transmitt and transmitt empty interrupts. if((IntsPending & UL_IP_TX) || (IntsPending & UL_IP_TX_EMPTY)) { // No need to clear the INT it was already cleared by reading the IIR. DWORD BytesRemaining = pPort->pUartLib->UL_OutputData_XXXX(pUart); // Output some bytes // If we have a current Write Immediate IRP. if(pPort->CurrentImmediateIrp) { if(SERIAL_REFERENCE_COUNT(pPort->CurrentImmediateIrp) & SERIAL_REF_ISR) { if(pPort->TransmitImmediate == TRUE) { // Check if the byte has been sent. if(pPort->pUartLib->UL_ImmediateByte_XXXX(pUart, &pPort->ImmediateIndex, UL_IM_OP_STATUS) == UL_IM_NO_BYTE_TO_SEND) { pPort->TransmitImmediate = FALSE; pPort->EmptiedTransmit = TRUE; pPort->PerfStats.TransmittedCount++; // Increment Tx Counter #ifdef WMI_SUPPORT pPort->WmiPerfData.TransmittedCount++; #endif // Mark IRP as about to complete normally to prevent cancel & timer DPCs // from doing so before DPC is allowed to run. SERIAL_SET_REFERENCE(pPort->CurrentImmediateIrp, SERIAL_REF_COMPLETING); // Ask to complete the IRP. KeInsertQueueDpc(&pPort->CompleteImmediateDpc, NULL, NULL); } } } } // If we have a current Write IRP. if(pPort->CurrentWriteIrp && pPort->WriteLength) { // // Even though all of the characters being // sent haven't all been sent, this variable // will be checked when the transmit queue is // empty. If it is still true and there is a // wait on the transmit queue being empty then // we know we finished transmitting all characters // following the initiation of the wait since // the code that initiates the wait will set // this variable to false. // // One reason it could be false is that // the writes were cancelled before they // actually started, or that the writes // failed due to timeouts. This variable // basically says a character was written // by the isr at some point following the // initiation of the wait. // if(SERIAL_REFERENCE_COUNT(pPort->CurrentWriteIrp) & SERIAL_REF_ISR) { if(pPort->WriteLength > BytesRemaining) { pPort->PerfStats.TransmittedCount += (pPort->WriteLength - BytesRemaining); // Increment Tx Counter #ifdef WMI_SUPPORT pPort->WmiPerfData.TransmittedCount += (pPort->WriteLength - BytesRemaining); #endif } else { pPort->PerfStats.TransmittedCount += pPort->WriteLength; // Increment Tx Counter #ifdef WMI_SUPPORT pPort->WmiPerfData.TransmittedCount += pPort->WriteLength; #endif } pPort->WriteLength = BytesRemaining; pPort->EmptiedTransmit = TRUE; if(pPort->WriteLength == 0) // If write is complete - lets complete the IRP { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(pPort->CurrentWriteIrp); // No More characters left. This write is complete. Take care when // updating the information field, we could have an xoff // counter masquerading as a write irp. pPort->CurrentWriteIrp->IoStatus.Information = (IrpSp->MajorFunction == IRP_MJ_WRITE) ? (IrpSp->Parameters.Write.Length) : (1); // Mark IRP as about to complete normally to prevent cancel & timer DPCs // from doing so before DPC is allowed to run. SERIAL_SET_REFERENCE(pPort->CurrentWriteIrp, SERIAL_REF_COMPLETING); KeInsertQueueDpc(&pPort->CompleteWriteDpc, NULL, NULL); } } } } // Service modem interrupts. if(IntsPending & UL_IP_MODEM) { SerialHandleModemUpdate(pPort, FALSE); } // Save a pointer to the UART serviced so it can be the first UART serviced // in the list the next time the ISR is called. //pCard->pFirstUart = pUart; ServicedAnInterrupt = TRUE; } } return ServicedAnInterrupt; }