void SerialSend( char *Buff, int BuffLength ) { if ( PortIsOpened ) { if ( ModbusSerialUseRtsToSend ) { SerialSetRTS( 1 ); } if ( ModbusDebugLevel>=2 ) printf("Serial writing...\n"); while(BuffLength > 0) { int r = write(fd,Buff,BuffLength); if(r < 0) { if ( ModbusDebugLevel>=1 ) printf("SerialSend failed: %s!\n", strerror(errno)); break; } Buff += r; BuffLength -= r; } if ( ModbusDebugLevel>=2 ) printf("Writing done!\n"); // wait until everything has been transmitted tcdrain( fd ); if ( ModbusSerialUseRtsToSend ) { SerialSetRTS( 0 ); } } }
BOOLEAN SerialTurnOnBreak( IN PVOID Context ) /*++ Routine Description: This routine will turn on break in the hardware and record the fact the break is on, in the extension variable that holds reasons that transmission is stopped. Arguments: Context - Really a pointer to the device extension. Return Value: This routine always returns FALSE. --*/ { PSERIAL_DEVICE_EXTENSION Extension = Context; UCHAR OldLineControl; if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { SerialSetRTS(Extension); } OldLineControl = READ_LINE_CONTROL(Extension->Controller); OldLineControl |= SERIAL_LCR_BREAK; WRITE_LINE_CONTROL( Extension->Controller, OldLineControl ); Extension->TXHolding |= SERIAL_TX_BREAK; return FALSE; }
BOOLEAN SerialSetupNewHandFlow( IN PSERIAL_DEVICE_EXTENSION Extension, IN PSERIAL_HANDFLOW NewHandFlow ) /*++ Routine Description: This routine adjusts the flow control based on new control flow. Arguments: Extension - A pointer to the serial device extension. NewHandFlow - A pointer to a serial handflow structure that is to become the new setup for flow control. Return Value: This routine always returns FALSE. --*/ { SERIAL_HANDFLOW New = *NewHandFlow; // // If the Extension->DeviceIsOpened is FALSE that means // we are entering this routine in response to an open request. // If that is so, then we always proceed with the work regardless // of whether things have changed. // // // First we take care of the DTR flow control. We only // do work if something has changed. // if ((!Extension->DeviceIsOpened) || ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK) != (New.ControlHandShake & SERIAL_DTR_MASK))) { SerialDump( SERFLOW, ("SERIAL: Processing DTR flow for %x\n", Extension->Controller) ); if (New.ControlHandShake & SERIAL_DTR_MASK) { // // Well we might want to set DTR. // // Before we do, we need to check whether we are doing // dtr flow control. If we are then we need to check // if then number of characters in the interrupt buffer // exceeds the XoffLimit. If it does then we don't // enable DTR AND we set the RXHolding to record that // we are holding because of the dtr. // if ((New.ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_HANDSHAKE) { if ((Extension->BufferSize - New.XoffLimit) > Extension->CharsInInterruptBuffer) { // // However if we are already holding we don't want // to turn it back on unless we exceed the Xon // limit. // if (Extension->RXHolding & SERIAL_RX_DTR) { // // We can assume that its DTR line is already low. // if (Extension->CharsInInterruptBuffer > (ULONG)New.XonLimit) { SerialDump( SERFLOW, ("SERIAL: Removing DTR block on reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_DTR; SerialSetDTR(Extension); } } else { SerialSetDTR(Extension); } } else { SerialDump( SERFLOW, ("SERIAL: Setting DTR block on reception for %x\n", Extension->Controller) ); Extension->RXHolding |= SERIAL_RX_DTR; SerialClrDTR(Extension); } } else { // // Note that if we aren't currently doing dtr flow control then // we MIGHT have been. So even if we aren't currently doing // DTR flow control, we should still check if RX is holding // because of DTR. If it is, then we should clear the holding // of this bit. // if (Extension->RXHolding & SERIAL_RX_DTR) { SerialDump( SERFLOW, ("SERIAL: Removing dtr block of reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_DTR; } SerialSetDTR(Extension); } } else { // // The end result here will be that DTR is cleared. // // We first need to check whether reception is being held // up because of previous DTR flow control. If it is then // we should clear that reason in the RXHolding mask. // if (Extension->RXHolding & SERIAL_RX_DTR) { SerialDump( SERFLOW, ("SERIAL: removing dtr block of reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_DTR; } SerialClrDTR(Extension); } } // // Time to take care of the RTS Flow control. // // First we only do work if something has changed. // if ((!Extension->DeviceIsOpened) || ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) != (New.FlowReplace & SERIAL_RTS_MASK))) { SerialDump( SERFLOW, ("SERIAL: Processing RTS flow\n", Extension->Controller) ); if ((New.FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_HANDSHAKE) { // // Well we might want to set RTS. // // Before we do, we need to check whether we are doing // rts flow control. If we are then we need to check // if then number of characters in the interrupt buffer // exceeds the XoffLimit. If it does then we don't // enable RTS AND we set the RXHolding to record that // we are holding because of the rts. // if ((Extension->BufferSize - New.XoffLimit) > Extension->CharsInInterruptBuffer) { // // However if we are already holding we don't want // to turn it back on unless we exceed the Xon // limit. // if (Extension->RXHolding & SERIAL_RX_RTS) { // // We can assume that its RTS line is already low. // if (Extension->CharsInInterruptBuffer > (ULONG)New.XonLimit) { SerialDump( SERFLOW, ("SERIAL: Removing rts block of reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_RTS; SerialSetRTS(Extension); } } else { SerialSetRTS(Extension); } } else { SerialDump( SERFLOW, ("SERIAL: Setting rts block of reception for %x\n", Extension->Controller) ); Extension->RXHolding |= SERIAL_RX_RTS; SerialClrRTS(Extension); } } else if ((New.FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_CONTROL) { // // Note that if we aren't currently doing rts flow control then // we MIGHT have been. So even if we aren't currently doing // RTS flow control, we should still check if RX is holding // because of RTS. If it is, then we should clear the holding // of this bit. // if (Extension->RXHolding & SERIAL_RX_RTS) { SerialDump( SERFLOW, ("SERIAL: Clearing rts block of reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_RTS; } SerialSetRTS(Extension); } else if ((New.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // // We first need to check whether reception is being held // up because of previous RTS flow control. If it is then // we should clear that reason in the RXHolding mask. // if (Extension->RXHolding & SERIAL_RX_RTS) { SerialDump( SERFLOW, ("SERIAL: TOGGLE Clearing rts block of reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_RTS; } // // We have to place the rts value into the Extension // now so that the code that tests whether the // rts line should be lowered will find that we // are "still" doing transmit toggling. The code // for lowering can be invoked later by a timer so // it has to test whether it still needs to do its // work. // Extension->HandFlow.FlowReplace &= ~SERIAL_RTS_MASK; Extension->HandFlow.FlowReplace |= SERIAL_TRANSMIT_TOGGLE; // // The order of the tests is very important below. // // If there is a break then we should turn on the RTS. // // If there isn't a break but there are characters in // the hardware, then turn on the RTS. // // If there are writes pending that aren't being held // up, then turn on the RTS. // if ((Extension->TXHolding & SERIAL_TX_BREAK) || ((SerialProcessLSR(Extension) & (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) != (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) || (Extension->CurrentWriteIrp || Extension->TransmitImmediate || (!IsListEmpty(&Extension->WriteQueue)) && (!Extension->TXHolding))) { SerialSetRTS(Extension); } else { // // This routine will check to see if it is time // to lower the RTS because of transmit toggle // being on. If it is ok to lower it, it will, // if it isn't ok, it will schedule things so // that it will get lowered later. // Extension->CountOfTryingToLowerRTS++; SerialPerhapsLowerRTS(Extension); } } else { // // The end result here will be that RTS is cleared. // // We first need to check whether reception is being held // up because of previous RTS flow control. If it is then // we should clear that reason in the RXHolding mask. // if (Extension->RXHolding & SERIAL_RX_RTS) { SerialDump( SERFLOW, ("SERIAL: Clearing rts block of reception for %x\n", Extension->Controller) ); Extension->RXHolding &= ~SERIAL_RX_RTS; } SerialClrRTS(Extension); } } // // We now take care of automatic receive flow control. // We only do work if things have changed. // if ((!Extension->DeviceIsOpened) || ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_RECEIVE) != (New.FlowReplace & SERIAL_AUTO_RECEIVE))) { if (New.FlowReplace & SERIAL_AUTO_RECEIVE) { // // We wouldn't be here if it had been on before. // // We should check to see whether we exceed the turn // off limits. // // Note that since we are following the OS/2 flow // control rules we will never send an xon if // when enabling xon/xoff flow control we discover that // we could receive characters but we are held up do // to a previous Xoff. // if ((Extension->BufferSize - New.XoffLimit) <= Extension->CharsInInterruptBuffer) { // // Cause the Xoff to be sent. // Extension->RXHolding |= SERIAL_RX_XOFF; SerialProdXonXoff( Extension, FALSE ); } } else { // // The app has disabled automatic receive flow control. // // If transmission was being held up because of // an automatic receive Xoff, then we should // cause an Xon to be sent. // if (Extension->RXHolding & SERIAL_RX_XOFF) { Extension->RXHolding &= ~SERIAL_RX_XOFF; // // Cause the Xon to be sent. // SerialProdXonXoff( Extension, TRUE ); } } } // // We now take care of automatic transmit flow control. // We only do work if things have changed. // if ((!Extension->DeviceIsOpened) || ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) != (New.FlowReplace & SERIAL_AUTO_TRANSMIT))) { if (New.FlowReplace & SERIAL_AUTO_TRANSMIT) { // // We wouldn't be here if it had been on before. // // BUG BUG ??? There is some belief that if autotransmit // was just enabled, I should go look in what we // already received, and if we find the xoff character // then we should stop transmitting. I think this // is an application bug. For now we just care about // what we see in the future. // ; } else { // // The app has disabled automatic transmit flow control. // // If transmission was being held up because of // an automatic transmit Xoff, then we should // cause an Xon to be sent. // if (Extension->TXHolding & SERIAL_TX_XOFF) { Extension->TXHolding &= ~SERIAL_TX_XOFF; // // Cause the Xon to be sent. // SerialProdXonXoff( Extension, TRUE ); } } } // // At this point we can simply make sure that entire // handflow structure in the extension is updated. // Extension->HandFlow = New; return FALSE; }
VOID SerialHandleReducedIntBuffer( IN PSERIAL_DEVICE_EXTENSION Extension ) /*++ Routine Description: This routine is called to handle a reduction in the number of characters in the interrupt (typeahead) buffer. It will check the current output flow control and re-enable transmission as needed. NOTE: This routine assumes that it is working at interrupt level. Arguments: Extension - A pointer to the device extension. Return Value: None. --*/ { // // If we are doing receive side flow control and we are // currently "holding" then because we've emptied out // some characters from the interrupt buffer we need to // see if we can "re-enable" reception. // if (Extension->RXHolding) { if (Extension->CharsInInterruptBuffer <= (ULONG)Extension->HandFlow.XonLimit) { if (Extension->RXHolding & SERIAL_RX_DTR) { Extension->RXHolding &= ~SERIAL_RX_DTR; SerialSetDTR(Extension); } if (Extension->RXHolding & SERIAL_RX_RTS) { Extension->RXHolding &= ~SERIAL_RX_RTS; SerialSetRTS(Extension); } if (Extension->RXHolding & SERIAL_RX_XOFF) { // // Prod the transmit code to send xon. // SerialProdXonXoff( Extension, TRUE ); } } } }
BOOLEAN SerialISR( IN WDFINTERRUPT Interrupt, IN ULONG MessageID ) /*++ Routine Description: This is the interrupt service routine for the serial port driver. It will determine whether the serial port is the source of this interrupt. If it is, then this routine will do the minimum of processing to quiet the interrupt. It will store any information necessary for later processing. Arguments: InterruptObject - Points to the interrupt object declared for this device. We *do not* use this parameter. Return Value: This function will return TRUE if the serial port is the source of this interrupt, FALSE otherwise. --*/ { // // Holds the information specific to handling this device. // PSERIAL_DEVICE_EXTENSION Extension = NULL; // // Holds the contents of the interrupt identification record. // A low bit of zero in this register indicates that there is // an interrupt pending on this device. // UCHAR InterruptIdReg; // // Will hold whether we've serviced any interrupt causes in this // routine. // BOOLEAN ServicedAnInterrupt; UCHAR tempLSR; PREQUEST_CONTEXT reqContext = NULL; UNREFERENCED_PARAMETER(MessageID); Extension = SerialGetDeviceExtension(WdfInterruptGetDevice(Interrupt)); // // Make sure we have an interrupt pending. If we do then // we need to make sure that the device is open. If the // device isn't open or powered down then quiet the device. Note that // if the device isn't opened when we enter this routine // it can't open while we're in it. // InterruptIdReg = READ_INTERRUPT_ID_REG(Extension, Extension->Controller); if ((InterruptIdReg & SERIAL_IIR_NO_INTERRUPT_PENDING)) { ServicedAnInterrupt = FALSE; } else if (!Extension->DeviceIsOpened/* || (Extension->PowerState != PowerDeviceD0)*/) { // // We got an interrupt with the device being closed or when the // device is supposed to be powered down. This // is not unlikely with a serial device. We just quietly // keep servicing the causes until it calms down. // ServicedAnInterrupt = TRUE; do { InterruptIdReg &= (~SERIAL_IIR_FIFOS_ENABLED); switch (InterruptIdReg) { case SERIAL_IIR_RLS: { READ_LINE_STATUS(Extension, Extension->Controller); break; } case SERIAL_IIR_RDA: case SERIAL_IIR_CTI: { READ_RECEIVE_BUFFER(Extension, Extension->Controller); break; } case SERIAL_IIR_THR: { // // Alread clear from reading the iir. // // We want to keep close track of whether // the holding register is empty. // Extension->HoldingEmpty = TRUE; break; } case SERIAL_IIR_MS: { READ_MODEM_STATUS(Extension, Extension->Controller); break; } default: { ASSERT(FALSE); break; } } } while (!((InterruptIdReg = READ_INTERRUPT_ID_REG(Extension, Extension->Controller)) & SERIAL_IIR_NO_INTERRUPT_PENDING)); } else { ServicedAnInterrupt = TRUE; do { // // We only care about bits that can denote an interrupt. // InterruptIdReg &= SERIAL_IIR_RLS | SERIAL_IIR_RDA | SERIAL_IIR_CTI | SERIAL_IIR_THR | SERIAL_IIR_MS; // // We have an interrupt. We look for interrupt causes // in priority order. The presence of a higher interrupt // will mask out causes of a lower priority. When we service // and quiet a higher priority interrupt we then need to check // the interrupt causes to see if a new interrupt cause is // present. // switch (InterruptIdReg) { case SERIAL_IIR_RLS: { SerialProcessLSR(Extension); break; } case SERIAL_IIR_RDA: case SERIAL_IIR_CTI: { // // Reading the receive buffer will quiet this interrupt. // // It may also reveal a new interrupt cause. // UCHAR ReceivedChar; do { ReceivedChar = READ_RECEIVE_BUFFER(Extension, Extension->Controller); Extension->PerfStats.ReceivedCount++; Extension->WmiPerfData.ReceivedCount++; ReceivedChar &= Extension->ValidDataMask; if (!ReceivedChar && (Extension->HandFlow.FlowReplace & SERIAL_NULL_STRIPPING)) { // // If what we got is a null character // and we're doing null stripping, then // we simply act as if we didn't see it. // goto ReceiveDoLineStatus; } if ((Extension->HandFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) && ((ReceivedChar == Extension->SpecialChars.XonChar) || (ReceivedChar == Extension->SpecialChars.XoffChar))) { // // No matter what happens this character // will never get seen by the app. // if (ReceivedChar == Extension->SpecialChars.XoffChar) { Extension->TXHolding |= SERIAL_TX_XOFF; if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { SerialInsertQueueDpc( Extension->StartTimerLowerRTSDpc )?Extension->CountOfTryingToLowerRTS++:0; } } else { if (Extension->TXHolding & SERIAL_TX_XOFF) { // // We got the xon char **AND*** we // were being held up on transmission // by xoff. Clear that we are holding // due to xoff. Transmission will // automatically restart because of // the code outside the main loop that // catches problems chips like the // SMC and the Winbond. // Extension->TXHolding &= ~SERIAL_TX_XOFF; } } goto ReceiveDoLineStatus; } // // Check to see if we should note // the receive character or special // character event. // if (Extension->IsrWaitMask) { if (Extension->IsrWaitMask & SERIAL_EV_RXCHAR) { Extension->HistoryMask |= SERIAL_EV_RXCHAR; } if ((Extension->IsrWaitMask & SERIAL_EV_RXFLAG) && (Extension->SpecialChars.EventChar == ReceivedChar)) { Extension->HistoryMask |= SERIAL_EV_RXFLAG; } if (Extension->IrpMaskLocation && Extension->HistoryMask) { *Extension->IrpMaskLocation = Extension->HistoryMask; Extension->IrpMaskLocation = NULL; Extension->HistoryMask = 0; reqContext = SerialGetRequestContext(Extension->CurrentWaitRequest); reqContext->Information = sizeof(ULONG); SerialInsertQueueDpc( Extension->CommWaitDpc ); } } SerialPutChar( Extension, ReceivedChar ); // // If we're doing line status and modem // status insertion then we need to insert // a zero following the character we just // placed into the buffer to mark that this // was reception of what we are using to // escape. // if (Extension->EscapeChar && (Extension->EscapeChar == ReceivedChar)) { SerialPutChar( Extension, SERIAL_LSRMST_ESCAPE ); } ReceiveDoLineStatus: ; // // This reads the interrupt ID register and detemines if bits are 0 // If either of the reserved bits are 1, we stop servicing interrupts // Since this detection method is not guarenteed this is enabled via // a registry entry "UartDetectRemoval" and intialized on DriverEntry. // This is disabled by default and will only be enabled on Stratus systems // that allow hot replacement of serial cards // if(Extension->UartRemovalDetect) { UCHAR DetectRemoval; DetectRemoval = READ_INTERRUPT_ID_REG(Extension, Extension->Controller); if(DetectRemoval & SERIAL_IIR_MUST_BE_ZERO) { // break out of this loop and stop processing interrupts break; } } if (!((tempLSR = SerialProcessLSR(Extension)) & SERIAL_LSR_DR)) { // // No more characters, get out of the // loop. // break; } if ((tempLSR & ~(SERIAL_LSR_THRE | SERIAL_LSR_TEMT | SERIAL_LSR_DR)) && Extension->EscapeChar) { // // An error was indicated and inserted into the // stream, get out of the loop. // break; } } WHILE (TRUE); break; } case SERIAL_IIR_THR: { doTrasmitStuff:; Extension->HoldingEmpty = TRUE; if (Extension->WriteLength || Extension->TransmitImmediate || Extension->SendXoffChar || Extension->SendXonChar) { // // 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. // Extension->EmptiedTransmit = TRUE; // // If we have output flow control based on // the modem status lines, then we have to do // all the modem work before we output each // character. (Otherwise we might miss a // status line change.) // if (Extension->HandFlow.ControlHandShake & SERIAL_OUT_HANDSHAKEMASK) { SerialHandleModemUpdate( Extension, TRUE ); } // // We can only send the xon character if // the only reason we are holding is because // of the xoff. (Hardware flow control or // sending break preclude putting a new character // on the wire.) // if (Extension->SendXonChar && !(Extension->TXHolding & ~SERIAL_TX_XOFF)) { if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // // We have to raise if we're sending // this character. // SerialSetRTS(Extension->WdfInterrupt, Extension); Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, Extension->SpecialChars.XonChar); SerialInsertQueueDpc( Extension->StartTimerLowerRTSDpc )?Extension->CountOfTryingToLowerRTS++:0; } else { Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, Extension->SpecialChars.XonChar); } Extension->SendXonChar = FALSE; Extension->HoldingEmpty = FALSE; // // If we send an xon, by definition we // can't be holding by Xoff. // Extension->TXHolding &= ~SERIAL_TX_XOFF; // // If we are sending an xon char then // by definition we can't be "holding" // up reception by Xoff. // Extension->RXHolding &= ~SERIAL_RX_XOFF; } else if (Extension->SendXoffChar && !Extension->TXHolding) { if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // // We have to raise if we're sending // this character. // SerialSetRTS(Extension->WdfInterrupt, Extension); Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, Extension->SpecialChars.XoffChar); SerialInsertQueueDpc( Extension->StartTimerLowerRTSDpc )?Extension->CountOfTryingToLowerRTS++:0; } else { Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, Extension->SpecialChars.XoffChar); } // // We can't be sending an Xoff character // if the transmission is already held // up because of Xoff. Therefore, if we // are holding then we can't send the char. // // // If the application has set xoff continue // mode then we don't actually stop sending // characters if we send an xoff to the other // side. // if (!(Extension->HandFlow.FlowReplace & SERIAL_XOFF_CONTINUE)) { Extension->TXHolding |= SERIAL_TX_XOFF; if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { SerialInsertQueueDpc( Extension->StartTimerLowerRTSDpc )?Extension->CountOfTryingToLowerRTS++:0; } } Extension->SendXoffChar = FALSE; Extension->HoldingEmpty = FALSE; // // Even if transmission is being held // up, we should still transmit an immediate // character if all that is holding us // up is xon/xoff (OS/2 rules). // } else if (Extension->TransmitImmediate && (!Extension->TXHolding || (Extension->TXHolding == SERIAL_TX_XOFF) )) { Extension->TransmitImmediate = FALSE; if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // // We have to raise if we're sending // this character. // SerialSetRTS(Extension->WdfInterrupt, Extension); Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, Extension->ImmediateChar); SerialInsertQueueDpc( Extension->StartTimerLowerRTSDpc )?Extension->CountOfTryingToLowerRTS++:0; } else { Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, Extension->ImmediateChar); } Extension->HoldingEmpty = FALSE; SerialInsertQueueDpc( Extension->CompleteImmediateDpc ); } else if (!Extension->TXHolding) { ULONG amountToWrite; if (Extension->FifoPresent) { amountToWrite = (Extension->TxFifoAmount < Extension->WriteLength)? Extension->TxFifoAmount: Extension->WriteLength; } else { amountToWrite = 1; } if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { // // We have to raise if we're sending // this character. // SerialSetRTS(Extension->WdfInterrupt, Extension); if (amountToWrite == 1) { Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, *(Extension->WriteCurrentChar)); } else { Extension->PerfStats.TransmittedCount += amountToWrite; Extension->WmiPerfData.TransmittedCount += amountToWrite; WRITE_TRANSMIT_FIFO_HOLDING(Extension, Extension->Controller, Extension->WriteCurrentChar, amountToWrite); } SerialInsertQueueDpc( Extension->StartTimerLowerRTSDpc )?Extension->CountOfTryingToLowerRTS++:0; } else { if (amountToWrite == 1) { Extension->PerfStats.TransmittedCount++; Extension->WmiPerfData.TransmittedCount++; WRITE_TRANSMIT_HOLDING(Extension, Extension->Controller, *(Extension->WriteCurrentChar)); } else { Extension->PerfStats.TransmittedCount += amountToWrite; Extension->WmiPerfData.TransmittedCount += amountToWrite; WRITE_TRANSMIT_FIFO_HOLDING(Extension, Extension->Controller, Extension->WriteCurrentChar, amountToWrite); } } Extension->HoldingEmpty = FALSE; Extension->WriteCurrentChar += amountToWrite; Extension->WriteLength -= amountToWrite; if (!Extension->WriteLength) { // // 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 request. // reqContext = SerialGetRequestContext(Extension->CurrentWriteRequest); reqContext->Information = (reqContext->MajorFunction == IRP_MJ_WRITE)? (reqContext->Length): (1); SerialInsertQueueDpc( Extension->CompleteWriteDpc ); } } } break; } case SERIAL_IIR_MS: { SerialHandleModemUpdate( Extension, FALSE ); break; } } } while (!((InterruptIdReg = READ_INTERRUPT_ID_REG(Extension, Extension->Controller)) & SERIAL_IIR_NO_INTERRUPT_PENDING)); // // Besides catching the WINBOND and SMC chip problems this // will also cause transmission to restart incase of an xon // char being received. Don't remove. // if (SerialProcessLSR(Extension) & SERIAL_LSR_THRE) { if (!Extension->TXHolding && (Extension->WriteLength || Extension->TransmitImmediate)) { goto doTrasmitStuff; } } } return ServicedAnInterrupt; }
/*++ Routine Description: Try to start off the write by slipping it in behind a transmit immediate char, or if that isn't available and the transmit holding register is empty, "tickle" the UART into interrupting with a transmit buffer empty. NOTE: This routine is called by WdfInterruptSynchronize. NOTE: This routine assumes that it is called with the cancel spin lock held. Arguments: Context - Really a pointer to the device extension. Return Value: This routine always returns FALSE. --*/ _Use_decl_annotations_ BOOLEAN SerialGiveWriteToIsr( WDFINTERRUPT Interrupt, PVOID Context ) { PSERIAL_DEVICE_EXTENSION extension = Context; // The current stack location. This contains all of the // information we need to process this particular request. PREQUEST_CONTEXT reqContext; UNREFERENCED_PARAMETER(Interrupt); reqContext = SerialGetRequestContext(extension->CurrentWriteRequest); // We might have a xoff counter request masquerading as a // write. The length of these requests will always be one // and we can get a pointer to the actual character from // the data supplied by the user. if (reqContext->MajorFunction == IRP_MJ_WRITE) { extension->WriteLength = reqContext->Length; extension->WriteCurrentChar = reqContext->SystemBuffer; } else { extension->WriteLength = 1; extension->WriteCurrentChar = ((PUCHAR)reqContext->SystemBuffer) + FIELD_OFFSET(SERIAL_XOFF_COUNTER, XoffChar); } // The isr now has a reference to the request. SERIAL_SET_REFERENCE(reqContext, SERIAL_REF_ISR); // Check first to see if an immediate char is transmitting. // If it is then we'll just slip in behind it when its done. if (!extension->TransmitImmediate) { // If there is no immediate char transmitting then we // will "re-enable" the transmit holding register empty // interrupt. The 16550 family of devices will always // signal a transmit holding register empty interrupt // *ANY* time this bit is set to one. By doing things // this way we can simply use the normal interrupt code // to start off this write. // // We've been keeping track of whether the transmit holding // register is empty so it we only need to do this // if the register is empty. if (extension->HoldingEmpty) { DISABLE_ALL_INTERRUPTS(extension, extension->Controller); ENABLE_ALL_INTERRUPTS(extension, extension->Controller); } } // The rts line may already be up from previous writes, // however, it won't take much additional time to turn // on the RTS line if we are doing transmit toggling. if ((extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { SerialSetRTS(extension->WdfInterrupt, extension); } return FALSE; }