Как я могу исправить пользовательскую ошибку многопортовой последовательной связи? (Драйвер режима ядра Windows)
Проблема : вывод полученных данных на порт выбрасывает повторяющиеся символы
Правильные данные TX : быстрая бурая лиса перепрыгивает через хвост ленивой собаки и обратно 1234567890
Нет никаких ошибок в данных rx, если комм. находится между только одним UART,но как только прерывания вступают в картину (комм. с 6 UARTs) ,появляются следующие ошибки и вызывают сбой. Это повторяющиеся символы, которых следует избегать.
Быстрая Коричневая Лиса Прыгает Через Ленивую Дж Собакам Хвост И Снова 1234567890
Быстрая Коричневая Лиса Перепрыгивает Через Хвост Ленивой Собаки И Обратно 1234567890
Быстрая Бурая Лиса Перепрыгивает Через Хвост Ленивой Собаки И Обратно 1234567890
Быстрая коричневая Лиса прыгает через ленивую Н собак хвост и снова 1234567890
Быстрая Бурая Лиса Перепрыгивает Через Хвост Ленивой Собаки И Обратно 1 1234567890
#include "SerialPort.h" #include "SuppFunc.h" /* * Some handy constants. Better an enum than preprocessor macros or global constant. */ enum SerialPortConstants { SerialDefXON = 0x11, SerialDefXOFF = 0x13, MaximumBaudRate = 38400, BaseMode1Value = 0x00, BaseMode2Value = 0x00, InitialQueueSize = 8192, BlockAReceiveInterrupt = 0x02, BlockBReceiveInterrupt = 0x20, BlockATransmitInterrupt = 0x01, BlockBTransmitInterrupt = 0x10, FIFOErrorFlags = 0x70, FrameStatusError = 0x40, ParityStatusError = 0x20, OverrunStatusError = 0x10, RxRDYStatus = 0x01, FFULLStatus = 0x02, TxRDYStatus = 0x04, BreakOnCommand = 0x60, BreakOffCommand = 0x70, DisableRxCommand = 0x02, EnableRxCommand = 0x01, MillisecondsTo100Nanoseconds = 10000, }; /* * Private SerialPort function declarations. */ /* * Validates the provided flags against all the valid flags. * * @param providedFlags, the flags to test. * @param allValidFlags, the set of valid flags. * @returns 0 if the flags are invalid, anyother value indicates that the flags are valid. */ unsigned char SerialPortValidateFlags( ULONG providedFlags, ULONG allValidFlags ); /* * Resets the serial port connection settings to the default values. * * @param [in] serialport the serial port to initialise the attributes of. */ void SerialPortResetConnectionSettings( SerialPort* serialPort ); /* * Increases the size of the suppied queue to the required size. If the queue is * already big enough no action is taken. * * @param [in/out] queue the queue to update. * @param [in] newSize the new size of the queue. */ void UpdateQueue( Queue* queue, unsigned int newSize ); /* * Transmits the next byte from the transmit queue to the Tx register of the supplied serial port. * * @param [in/out] serialPort the serial port to transmit data to. */ void SerialPortTransmitByte( SerialPort* serialPort ); /* * Receives a byte from the serial port and adds it to the receive queue. * * @param [in/out] serialPort the serial port to receive data from. */ void SerialPortReceiveByte( SerialPort* serialPort ); /* * This function will cancel a read request for the suppiled serial port if one is pending. * * @param [in] serialPort the serial port to cancel any pending reads on. */ void TryCancellingRead( SerialPort* serialPort ); /* * Provides access to the read transfer data and then resets those values in the serial port. * * @param [in] serialPort the serial port to get the read transfer data from. * @param [in/out] bufferLength the length of the client supplied buffer in the IRP. * @returns a pointer to the IRP for the read. */ PIRP GetAndResetPendingRead( SerialPort* serialPort, unsigned int* bufferLength ); /* * This function will cancel a write request for the suppiled serial port if one is pending. * * @param [in] serialPort the serial port to cancel any pending write on. */ void TryCancellingWrite( SerialPort* serialPort ); /* * Provides access to the write transfer data and then resets those values in the serial port. * * @param [in] serialPort the serial port to get the write transfer data from. * @param [in/out] bufferLength the length of the client supplied buffer in the IRP. * @returns a pointer to the IRP for the write. */ PIRP GetAndResetPendingWrite( SerialPort* serialPort, unsigned int* bufferLength ); /* * Processes the interrupts of a UART. * * @param [in] serialPort the serial port to process. * @param [in] interruptStatusValue the value of the interrupt status register for the DUART that the serial port is part of. * @param [in] transmitInterruptMask the tranmit interrupt mask for this type of serial port. * @param [in] receiveInterruptMask the receive interrupt mask for this type of serial port. */ void ProcessUARTInterrupts( SerialPort* serialPort, UARTRegisterValue interruptStatusValue, UARTRegisterValue transmitInterruptMask, UARTRegisterValue receiveInterruptMask ); /*++ Routine Description: This routine is used to cancel the current read. Arguments: Device - Wdf device handle Request - Pointer to the WDFREQUEST to be canceled. Return Value: None. --*/ DRIVER_CANCEL SerialCancelCurrentRead; void SerialCancelCurrentRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); /* * Reads from the receive buffer until either the supplied numberOfBytes is read or the buffer is empty. * * @param [in] serialPort the serial port buffer to read from. * @param [in] numberOfBytes the number of bytes in the supplied buffer. * @param [out] buffer the buffer to put the received data into. * @param [out] numberOfBytesRead gets set to the number of bytes actually read. */ #define SerialPortCopyFromReceiveBuffer( serialPort, numberOfBytes, buffer, numberOfBytesRead ) ( *numberOfBytesRead = FillBufferFromQueue( &serialPort->receiveQueue, buffer, numberOfBytes ) ) /* * Writes to the transmit queue until either the supplied numberOfBytes is written or the queue is full. * * @param [in] serialPort the serial port buffer to write to. * @param [in] numberOfBytes the number of bytes in the supplied buffer. * @param [out] buffer the buffer to get the data to transmit from. * @param [out] numberOfBytesWritten gets set to the number of bytes actually written. */ #define SerialPortCopyToWriteBuffer( serialPort, numberOfBytes, buffer, numberOfBytesWritten ) ( *numberOfBytesWritten = FillQueueFromBuffer( &serialPort->transmitQueue, buffer, numberOfBytes ) ) /* * Resets the mode register pointer of the supplied UART. * * @param [in] _serialPort the serial port to write to. * @returns 1 if successful or 0 if an error occured. */ #define ResetSerialPortModeRegisterPointer( _serialPort ) ResetUARTModeRegisterPointer( _serialPort->baseAddress, _serialPort->uartType ) /* * Reset the receiver, this resets the FIFO pointer to the begginning and disables the receiver. * * @param [in] _serialPort the serial port to reset. * @returns 1 if successful or 0 if an error occured. */ #define SerialPortResetReceiver( _serialPort ) ResetReceiver( _serialPort->baseAddress, _serialPort->uartType ) /* * Write to a serial port register. * * @param [in] _serialPort the serial port to write to. * @param [in] _uartRegister the register to write to. * @param [in] _value the value to write to the register. * @returns 1 if successful or 0 if an error occured. */ #define WriteSerialPortRegister( _serialPort, _uartRegister, _value ) WriteUARTRegister( _serialPort->baseAddress, _uartRegister, _serialPort->uartType, _value ) /* * Read from a serial port register. * * @param [in] _serialPort the serial port to read from. * @param [in] _uartRegister the register to read from. * @param [in] _value the address of the value to write the register value to. * @returns 1 if successful or 0 if an error occured. */ #define ReadSerialPortRegister( _serialPort, _uartRegister, _value ) ReadUARTRegister( _serialPort->baseAddress, _uartRegister, _serialPort->uartType, _value ) NTSTATUS SerialPortInitialise( SerialPort* serialPort, unsigned char deviceNumber ) { NTSTATUS status = STATUS_SUCCESS; unsigned long baseAddress = 0; UARTType uartType = UARTDummyUnit; unsigned char uartId = 255; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } else { if ( deviceNumber != 0 ) { baseAddress = ( ( deviceNumber - 1 ) / 2 ) * UARTPairRegistersSize; if ( ( ( deviceNumber - 1 ) % 2 ) == 0 ) { uartType = UARTUnitA; } else { uartType = UARTUnitB; } uartId = deviceNumber - 1; } DebugPrintf(( "SerialPortInitialise: device id: %u, base address: %u, UART type: %s\n", deviceNumber, baseAddress, ( ( uartType == UARTUnitA ) ? "UARTUnitA" : ( ( uartType == UARTUnitB ) ? "UARTUnitB" : "UARTDummyUnit" ) ) )); serialPort->uartId = uartId; serialPort->baseAddress = baseAddress; serialPort->uartType = uartType; serialPort->open = 0; serialPort->waitMask.currentWaitMask = 0; serialPort->waitMask.waitOnMaskPending = 0; serialPort->waitMask.pIrp = NULL; serialPort->waitMask.waitEvents = NULL; serialPort->pendingRead.pIrp = NULL; serialPort->pendingRead.bufferLength = 0; KeInitializeSpinLock( &serialPort->pendingRead.lock ); SerialPortResetConnectionSettings( serialPort ); InitialiseQueueLock( &serialPort->receiveQueue ); InitialiseQueueLock( &serialPort->transmitQueue ); InitializeListHead( &serialPort->readCompletionWorkerList ); KeInitializeSpinLock( &serialPort->readCompletionWorkerListLock ); serialPort->readCompletionWorkerActive = 0; SerialPortClearStats( serialPort ); serialPort->numberOfQueuedWriteBytes = 0; // initialise the queue, it is important that the buffer pointer is initialised to NULL before the first call. LockQueue( &serialPort->receiveQueue ); // as the queues of this port are not yet in operation it's safe to initialise the buffer without locking. serialPort->receiveQueue.buffer = NULL; InitialiseQueue( &serialPort->receiveQueue, InitialQueueSize ); UnlockQueue( &serialPort->receiveQueue ); LockQueue( &serialPort->transmitQueue ); serialPort->transmitQueue.buffer = NULL; InitialiseQueue( &serialPort->transmitQueue, InitialQueueSize ); UnlockQueue( &serialPort->transmitQueue ); } return status; } NTSTATUS SerialPortOpen( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; UARTRegisterValue dataElement; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } else { if ( serialPort->open == 1 ) { status = STATUS_ACCESS_DENIED; } else { // set the open flag. serialPort->open = 1; // initialise the queue LockQueue( &serialPort->receiveQueue ); InitialiseQueue( &serialPort->receiveQueue, InitialQueueSize ); UnlockQueue( &serialPort->receiveQueue ); LockQueue( &serialPort->transmitQueue ); InitialiseQueue( &serialPort->transmitQueue, InitialQueueSize ); UnlockQueue( &serialPort->transmitQueue ); serialPort->numberOfQueuedWriteBytes = 0; SerialPortClearStats( serialPort ); if ( OpenUART( serialPort->baseAddress, serialPort->uartType ) == 0 ) { DebugPrintf(( "Failed to Open the UART.\n" )); } } } return status; } NTSTATUS SerialPortClose( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; unsigned int debugStat = 0; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } else { if ( serialPort->waitMask.pIrp != NULL ) { PlxCompleteIrpWithInformation( serialPort->waitMask.pIrp, STATUS_SUCCESS, 0 ); serialPort->waitMask.pIrp = NULL; } serialPort->open = 0; serialPort->waitMask.currentWaitMask = 0; serialPort->waitMask.waitOnMaskPending = 0; serialPort->waitMask.waitEvents = NULL; TryCancellingRead( serialPort ); TryCancellingWrite( serialPort ); // TODO: get this to cancel all pending write requests. SerialPortResetConnectionSettings( serialPort ); // ensure that the queue is freed. LockQueue( &serialPort->receiveQueue ); FreeQueue( &serialPort->receiveQueue ); UnlockQueue( &serialPort->receiveQueue ); LockQueue( &serialPort->transmitQueue ); FreeQueue( &serialPort->transmitQueue ); UnlockQueue( &serialPort->transmitQueue ); if ( CloseUART( serialPort->baseAddress, serialPort->uartType ) == 0 ) { DebugPrintf(( "Failed to Close the UART.\n" )); } } return status; } NTSTATUS SerialPortSetQueueSize( SerialPort* serialPort, PSERIAL_QUEUE_SIZE serialQueueSize ) { NTSTATUS status = STATUS_SUCCESS; if ( ( serialQueueSize == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { DebugPrintf(( "SerialPortSetQueueSize InSize %d, OutSize %d\n", serialQueueSize->InSize, serialQueueSize->OutSize )); UpdateQueue( &serialPort->receiveQueue, serialQueueSize->InSize ); UpdateQueue( &serialPort->transmitQueue, serialQueueSize->OutSize ); } return status; } NTSTATUS SerialPortConfigSize( SerialPort* serialPort, PULONG configSize ) { NTSTATUS status = STATUS_SUCCESS; if ( ( configSize == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { *configSize = 0; // According to MSDN this IO control code is obsolete, so we set the size to 0. DebugPrintf(( "SerialPortConfigSize %d\n", *configSize )); } return status; } NTSTATUS SerialPortGetBaudRate( SerialPort* serialPort, PSERIAL_BAUD_RATE baudRate ) { NTSTATUS status = STATUS_SUCCESS; if ( ( baudRate == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { baudRate->BaudRate = serialPort->baudRate; DebugPrintf(( "SerialPortGetBaudRate %d\n", baudRate->BaudRate )); } return status; } NTSTATUS SerialPortSetBaudRate( SerialPort* serialPort, PSERIAL_BAUD_RATE baudRate ) { NTSTATUS status = STATUS_SUCCESS; UARTRegisterValue csrData; if ( ( baudRate == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { DebugPrintf(( "SerialPortSetBaudRate %u\n", baudRate->BaudRate )); if ( GetBAUDRate( baudRate->BaudRate, &csrData ) == 0 ) { // The baud rate is not valid. status = STATUS_INVALID_PARAMETER; DebugPrintf(( "Invalid baud rate %u\n", baudRate->BaudRate )); } else { serialPort->baudRate = baudRate->BaudRate; WriteSerialPortRegister( serialPort, StatusAndClockSelect, csrData ); } } return status; } NTSTATUS SerialPortGetLineControl( SerialPort* serialPort, PSERIAL_LINE_CONTROL lineControl ) { NTSTATUS status = STATUS_SUCCESS; if ( ( lineControl == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { lineControl->WordLength = serialPort->lineControl.WordLength; lineControl->Parity = serialPort->lineControl.Parity; lineControl->StopBits = serialPort->lineControl.StopBits; DebugPrintf(( "SerialPortGetLineControl -> WordLength: %d, Parity: %d, StopBits: %d\n", lineControl->WordLength, lineControl->Parity, lineControl->StopBits )); } return status; } NTSTATUS SerialPortSetLineControl( SerialPort* serialPort, PSERIAL_LINE_CONTROL lineControl ) { NTSTATUS status = STATUS_SUCCESS; UARTRegisterValue dataBits; UARTRegisterValue parity; UARTRegisterValue stopBits; unsigned char fiveStopBits = 0; if ( ( lineControl == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { if ( ResetSerialPortModeRegisterPointer( serialPort ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetLineControl -> FAILED to Reset the Mode register pointer\n" )); } else { if ( GetDataBits( lineControl->WordLength, &dataBits ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetLineControl -> FAILED to GetDataBits WordLength: %d\n", lineControl->WordLength )); } else { if ( GetParity( lineControl->Parity, &parity ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetLineControl -> FAILED to GetParity Parity: %d\n", lineControl->Parity )); } else { if ( lineControl->WordLength == 5 ) { fiveStopBits = 1; } if ( GetStopBits( lineControl->StopBits, &stopBits, fiveStopBits ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetLineControl -> FAILED to GetStopBits StopBits: %d\n", lineControl->StopBits )); } else { if ( WriteSerialPortRegister( serialPort, Mode, parity | dataBits ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetLineControl -> FAILED to Write Mode 1 register\n" )); } else { serialPort->lineControl.WordLength = lineControl->WordLength; serialPort->lineControl.Parity = lineControl->Parity; if ( WriteSerialPortRegister( serialPort, Mode, stopBits ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetLineControl -> FAILED to Write Mode 2 register\n" )); } else { serialPort->lineControl.StopBits = lineControl->StopBits; } } } } } } DebugPrintf(( "SerialPortSetLineControl -> WordLength: %d, Parity: %d, StopBits: %d\n", lineControl->WordLength, lineControl->Parity, lineControl->StopBits )); } return status; } NTSTATUS SerialPortGetChars( SerialPort* serialPort, PSERIAL_CHARS chars ) { NTSTATUS status = STATUS_SUCCESS; if ( ( chars == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { chars->EofChar = serialPort->serialChars.EofChar; chars->ErrorChar = serialPort->serialChars.ErrorChar; chars->BreakChar = serialPort->serialChars.BreakChar; chars->EventChar = serialPort->serialChars.EventChar; chars->XonChar = serialPort->serialChars.XonChar; chars->XoffChar = serialPort->serialChars.XoffChar; DebugPrintf(( "SerialPortGetChars -> EofChar: %d, ErrorChar: %d, BreakChar: %d, EventChar: %d, XonChar: %d, XoffChar: %d\n", chars->EofChar, chars->ErrorChar, chars->BreakChar, chars->EventChar, chars->XonChar, chars->XoffChar )); } return status; } NTSTATUS SerialPortSetChars( SerialPort* serialPort, PSERIAL_CHARS chars ) { NTSTATUS status = STATUS_SUCCESS; if ( ( chars == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { serialPort->serialChars.EofChar = chars->EofChar; serialPort->serialChars.ErrorChar = chars->ErrorChar; serialPort->serialChars.BreakChar = chars->BreakChar; serialPort->serialChars.EventChar = chars->EventChar; serialPort->serialChars.XonChar = chars->XonChar; serialPort->serialChars.XoffChar = chars->XoffChar; DebugPrintf(( "SerialPortSetChars -> EofChar: %d, ErrorChar: %d, BreakChar: %d, EventChar: %d, XonChar: %d, XoffChar: %d\n", chars->EofChar, chars->ErrorChar, chars->BreakChar, chars->EventChar, chars->XonChar, chars->XoffChar )); } return status; } NTSTATUS SerialPortGetHandflow( SerialPort* serialPort, PSERIAL_HANDFLOW handflow ) { NTSTATUS status = STATUS_SUCCESS; if ( ( handflow == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { handflow->ControlHandShake = serialPort->handflow.ControlHandShake; handflow->FlowReplace = serialPort->handflow.FlowReplace; handflow->XonLimit = serialPort->handflow.XonLimit; handflow->XoffLimit = serialPort->handflow.XoffLimit; DebugPrintf(( "SerialPortGetHandflow -> ControlHandShake: %X, FlowReplace: %X, XonLimit: %d, XoffLimit: %d\n", handflow->ControlHandShake, handflow->FlowReplace, handflow->XonLimit, handflow->XoffLimit )); } return status; } NTSTATUS SerialPortSetHandflow( SerialPort* serialPort, PSERIAL_HANDFLOW handflow ) { NTSTATUS status = STATUS_SUCCESS; if ( ( handflow == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { serialPort->handflow.ControlHandShake = handflow->ControlHandShake; serialPort->handflow.FlowReplace = handflow->FlowReplace; serialPort->handflow.XonLimit = handflow->XonLimit; serialPort->handflow.XoffLimit = handflow->XoffLimit; DebugPrintf(( "SerialPortSetHandflow -> ControlHandShake: %X, FlowReplace: %X, XonLimit: %d, XoffLimit: %d\n", handflow->ControlHandShake, handflow->FlowReplace, handflow->XonLimit, handflow->XoffLimit )); } return status; } NTSTATUS SerialPortSetRTS( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } // A status of STATUS_INVALID_PARAMETER indicates that the handshake flow control of the device is set to automatically use RTS. //status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetRTS\n" )); return status; } NTSTATUS SerialPortClearRTS( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } // A status of STATUS_INVALID_PARAMETER indicates that the handshake flow control of the device is set to automatically use RTS. //status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortClearRTS\n" )); return status; } NTSTATUS SerialPortSetDTR( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } // A status of STATUS_INVALID_PARAMETER indicates that the handshake flow control of the device is set to automatically use DTR. //status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortSetDTR\n" )); return status; } NTSTATUS SerialPortClearDTR( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } // A status of STATUS_INVALID_PARAMETER indicates that the handshake flow control of the device is set to automatically use DTR. //status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortClearDTR\n" )); return status; } NTSTATUS SerialPortSetTimeouts( SerialPort* serialPort, PSERIAL_TIMEOUTS timeouts ) { NTSTATUS status = STATUS_SUCCESS; if ( ( timeouts == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { // this condition is invalid as specified by the MSDN documentation. if ( ( timeouts->ReadIntervalTimeout == MAXULONG ) && ( timeouts->ReadTotalTimeoutConstant == MAXULONG ) ) { status = STATUS_INVALID_PARAMETER; } else { serialPort->timeouts.ReadIntervalTimeout = timeouts->ReadIntervalTimeout; serialPort->timeouts.ReadTotalTimeoutMultiplier = timeouts->ReadTotalTimeoutMultiplier; serialPort->timeouts.ReadTotalTimeoutConstant = timeouts->ReadTotalTimeoutConstant; serialPort->timeouts.WriteTotalTimeoutMultiplier = timeouts->WriteTotalTimeoutMultiplier; serialPort->timeouts.WriteTotalTimeoutConstant = timeouts->WriteTotalTimeoutConstant; } DebugPrintf(( "SerialPortSetTimeouts -> ReadIntervalTimeout: %u, ReadTotalTimeoutMultiplier: %u, ReadTotalTimeoutConstant: %u, WriteTotalTimeoutMultiplier: %u, WriteTotalTimeoutConstant: %u\n", timeouts->ReadIntervalTimeout, timeouts->ReadTotalTimeoutMultiplier, timeouts->ReadTotalTimeoutConstant, timeouts->WriteTotalTimeoutMultiplier, timeouts->WriteTotalTimeoutConstant )); } return status; } NTSTATUS SerialPortGetTimeouts( SerialPort* serialPort, PSERIAL_TIMEOUTS timeouts ) { NTSTATUS status = STATUS_SUCCESS; if ( ( timeouts == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { timeouts->ReadIntervalTimeout = serialPort->timeouts.ReadIntervalTimeout; timeouts->ReadTotalTimeoutMultiplier = serialPort->timeouts.ReadTotalTimeoutMultiplier; timeouts->ReadTotalTimeoutConstant = serialPort->timeouts.ReadTotalTimeoutConstant; timeouts->WriteTotalTimeoutMultiplier = serialPort->timeouts.WriteTotalTimeoutMultiplier; timeouts->WriteTotalTimeoutConstant = serialPort->timeouts.WriteTotalTimeoutConstant; DebugPrintf(( "SerialPortGetTimeouts -> ReadIntervalTimeout: %u, ReadTotalTimeoutMultiplier: %u, ReadTotalTimeoutConstant: %u, WriteTotalTimeoutMultiplier: %u, WriteTotalTimeoutConstant: %u\n", timeouts->ReadIntervalTimeout, timeouts->ReadTotalTimeoutMultiplier, timeouts->ReadTotalTimeoutConstant, timeouts->WriteTotalTimeoutMultiplier, timeouts->WriteTotalTimeoutConstant )); } return status; } NTSTATUS SerialPortWaitOnMask( SerialPort* serialPort, PULONG waitEvents, PIRP pIrp ) { NTSTATUS status = STATUS_SUCCESS; if ( ( waitEvents == NULL ) || ( pIrp == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { // A client can wait for the wait events represented by flag bits SERIAL_EV_RXCHAR through SERIAL_EV_EVENT2. For more information about these event flags, see SERIAL_EV_XXX. // A client sends an IOCTL_SERIAL_WAIT_ON_MASK request to wait for the occurrence of an event that was specified in the wait mask supplied by the most recent IOCTL_SERIAL_SET_WAIT_MASK request. // If one or more events in the current wait mask occur before the IOCTL_SERIAL_WAIT_ON_MASK request is sent, this request is immediately completed with a status of STATUS_SUCCESS and an output // mask value that identifies the events. If no event in the wait mask occurs before the IOCTL_SERIAL_WAIT_ON_MASK request is sent, this request is marked as pending, and it waits in the serial // controller queue for the next occurrence of an event in the current wait mask. // After a client's IOCTL_SERIAL_WAIT_ON_MASK request is completed with a status of STATUS_SUCCESS and a nonzero output mask value, the client can send a new IOCTL_SERIAL_WAIT_ON_MASK request to // wait for another event in the current wait mask. Only a new event that occurs after the previous IOCTL_SERIAL_WAIT_ON_MASK request was completed will cause the new IOCTL_SERIAL_WAIT_ON_MASK // request to be completed with a status of STATUS_SUCCESS and a nonzero output mask value. // A status of STATUS_INVALID_PARAMETER indicates that no wait events are set, or a wait-on-mask request is already pending. if ( ( serialPort->waitMask.currentWaitMask == 0 ) || ( serialPort->waitMask.waitOnMaskPending == 1 ) ) { status = STATUS_INVALID_PARAMETER; *waitEvents = 0; } else { serialPort->waitMask.waitOnMaskPending = 1; status = STATUS_PENDING; serialPort->waitMask.pIrp = pIrp; serialPort->waitMask.waitEvents = waitEvents; *waitEvents = 0; } DebugPrintf(( "SerialPortWaitOnMask 0x%X\n", *waitEvents )); } return status; } NTSTATUS SerialPortSetWaitMask( SerialPort* serialPort, PULONG newWaitMask ) { NTSTATUS status = STATUS_SUCCESS; if ( ( newWaitMask == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { // Logically or together all the valid flags, invert that so that we have all the invalid flags set and // 'and' this with the provided flags to see if any of the invalid flags are set in the provided flags... if ( SerialPortValidateFlags( *newWaitMask, SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR | SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING | SERIAL_EV_PERR | SERIAL_EV_RX80FULL | SERIAL_EV_EVENT1 | SERIAL_EV_EVENT2 ) == 0 ) { status = STATUS_INVALID_PARAMETER; } if ( status == STATUS_SUCCESS ) { // TODO: If a wait-on-mask request is already pending when a set-wait-mask request is processed, // the pending wait-on-event request is completed with a status of STATUS_SUCCESS and the // output wait event mask is set to zero. if ( serialPort->waitMask.waitOnMaskPending == 1 ) { PlxCompleteIrpWithInformation( serialPort->waitMask.pIrp, STATUS_SUCCESS, 0 ); serialPort->waitMask.waitOnMaskPending = 0; serialPort->waitMask.pIrp = NULL; } // save the wait mask. serialPort->waitMask.currentWaitMask = *newWaitMask; } DebugPrintf(( "SerialPortSetWaitMask 0x%X\n", serialPort->waitMask.currentWaitMask )); } return status; } NTSTATUS SerialPortGetWaitMask( SerialPort* serialPort, PULONG waitMask ) { NTSTATUS status = STATUS_SUCCESS; if ( ( waitMask == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { // get the wait mask. *waitMask = serialPort->waitMask.currentWaitMask; DebugPrintf(( "SerialPortGetWaitMask 0x%X\n", serialPort->waitMask.currentWaitMask )); } return status; } NTSTATUS SerialPortPurge( SerialPort* serialPort, PULONG purgeMask ) { NTSTATUS status = STATUS_SUCCESS; if ( ( purgeMask == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { // A status of STATUS_INVALID_PARAMETER indicates that the purge mask is not valid. if ( SerialPortValidateFlags( *purgeMask, SERIAL_PURGE_RXABORT | SERIAL_PURGE_RXCLEAR | SERIAL_PURGE_TXABORT | SERIAL_PURGE_TXCLEAR ) == 0 ) { status = STATUS_INVALID_PARAMETER; DebugPrintf(( "SerialPortPurge invalid flag set %d\n", *purgeMask )); } else { if ( *purgeMask & SERIAL_PURGE_RXABORT ) { TryCancellingRead( serialPort ); } if ( *purgeMask & SERIAL_PURGE_RXCLEAR ) { TryCancellingRead( serialPort ); LockQueue( &serialPort->receiveQueue ); InitialiseQueue( &serialPort->receiveQueue, serialPort->receiveQueue.size ); UnlockQueue( &serialPort->receiveQueue ); } if ( *purgeMask & ( SERIAL_PURGE_TXABORT | SERIAL_PURGE_TXCLEAR ) ) { TryCancellingWrite( serialPort ); } DebugPrintf(( "SerialPortPurge 0x%.1X\n", *purgeMask )); } } return status; } NTSTATUS SerialPortGetCommStatus( SerialPort* serialPort, PSERIAL_STATUS serialStatus ) { NTSTATUS status = STATUS_SUCCESS; ULONG currentTxQueueUsed = 0; ULONG currentRxQueueUsed = 0; if ( ( serialStatus == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { LockQueue( &serialPort->receiveQueue ); currentRxQueueUsed = serialPort->receiveQueue.used; UnlockQueue( &serialPort->receiveQueue ); LockQueue( &serialPort->transmitQueue ); currentTxQueueUsed = serialPort->transmitQueue.used; UnlockQueue( &serialPort->transmitQueue ); serialStatus->Errors = 0; serialStatus->HoldReasons = 0; serialStatus->AmountInInQueue = currentRxQueueUsed; serialStatus->AmountInOutQueue = currentTxQueueUsed; serialStatus->EofReceived = 0; serialStatus->WaitForImmediate = 0; DebugPrintf(( "SerialPortGetCommStatus\n")); //DebugPrintf(( "SerialGetCommStatus -> Errors: %d, HoldReasons: %d, AmountInInQueue: %d, AmountInOutQueue: %d, EofReceived: %d, WaitForImmediate: %d\n", // serialStatus->Errors, serialStatus->HoldReasons, serialStatus->AmountInInQueue, serialStatus->AmountInOutQueue, serialStatus->EofReceived, serialStatus->WaitForImmediate )); } return status; } NTSTATUS SerialPortGetProperties( SerialPort* serialPort, PSERIAL_COMMPROP commProperties ) { NTSTATUS status = STATUS_SUCCESS; ULONG currentTxQueueSize = 0; ULONG currentRxQueueSize = 0; if ( ( commProperties == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { commProperties->PacketLength = sizeof( SERIAL_COMMPROP ); commProperties->PacketVersion = 2; commProperties->ServiceMask = SERIAL_SP_SERIALCOMM; commProperties->MaxTxQueue = 0; commProperties->MaxRxQueue = 0; commProperties->MaxBaud = SERIAL_BAUD_38400; commProperties->ProvSubType = SERIAL_SP_RS232; commProperties->ProvCapabilities = //SERIAL_PCF_DTRDSR | // DTR data terminal ready) and DSR (data set ready) are supported. //SERIAL_PCF_RTSCTS | // RTS (request to send) and CTS (clear to send) are supported. //SERIAL_PCF_CD | // CD (carrier detect) is supported. SERIAL_PCF_PARITY_CHECK | // Parity checking is supported. //SERIAL_PCF_XONXOFF | // XON (transmit on) and XOFF (transmit off) flow control are supported. SERIAL_PCF_SETXCHAR | // The XON and XOFF characters are settable. SERIAL_PCF_TOTALTIMEOUTS | // Total-elapsed-time time-outs are supported. SERIAL_PCF_INTTIMEOUTS | // Interval time-outs are supported. SERIAL_PCF_SPECIALCHARS; // Special characters are supported. commProperties->SettableParams = SERIAL_SP_PARITY | // Parity type (even or odd) SERIAL_SP_BAUD | // Baud rate SERIAL_SP_DATABITS | // Data bits SERIAL_SP_STOPBITS | // Stop bits //SERIAL_SP_HANDSHAKING; // Handshaking (flow control) NO FLOW CONTROL!!! This is hardwired on the T48 DMX cards. SERIAL_SP_PARITY_CHECK; // other supported baud rates are: 50, 200, 1050, 2000. commProperties->SettableBaud = SERIAL_BAUD_075 | // 75 bps SERIAL_BAUD_110 | // 110 bps SERIAL_BAUD_150 | // 150 bps SERIAL_BAUD_300 | // 300 bps SERIAL_BAUD_600 | // 600 bps SERIAL_BAUD_1200 | // 1,200 bps SERIAL_BAUD_1800 | SERIAL_BAUD_2400 | // 2,400 bps SERIAL_BAUD_4800 | // 4,800 bps SERIAL_BAUD_9600 | // 9,600 bps SERIAL_BAUD_19200 | // 19,200 bps SERIAL_BAUD_38400; // 38,400 bps commProperties->SettableData = SERIAL_DATABITS_5 | //5 data bits SERIAL_DATABITS_6 | //6 data bits SERIAL_DATABITS_7 | //7 data bits SERIAL_DATABITS_8; commProperties->SettableStopParity = SERIAL_STOPBITS_10 | // One stop bit. SERIAL_STOPBITS_15 | // One and a half stop bits. SERIAL_STOPBITS_20 | // Two stop bits. SERIAL_PARITY_NONE | // No parity bit is used. SERIAL_PARITY_ODD | // Odd parity. The parity bit is 1 if the number of 1s in the character value is even. Otherwise, the parity bit is 0. SERIAL_PARITY_EVEN | // Even parity. The parity bit is 1 if the number of 1s in the character value is odd. Otherwise, the parity bit is 0. SERIAL_PARITY_MARK | // The parity bit is always set to 1. SERIAL_PARITY_SPACE; LockQueue( &serialPort->receiveQueue ); currentRxQueueSize = serialPort->receiveQueue.size; UnlockQueue( &serialPort->receiveQueue ); commProperties->CurrentTxQueue = currentTxQueueSize; commProperties->CurrentRxQueue = currentRxQueueSize; commProperties->ProvSpec1 = 0; commProperties->ProvSpec2 = 0; commProperties->ProvChar[0] = 0; DebugPrintf(( "SerialPortGetProperties\n" )); } return status; } NTSTATUS SerialPortWrite( SerialPort* serialPort, unsigned int numberOfBytes, void* buffer, unsigned int* numberOfBytesWritten, PIRP pIrp ) { NTSTATUS status = STATUS_SUCCESS; LARGE_INTEGER dueTime = { 0 }; //DebugPrintf_Cont(( "(Length: %u) ...", numberOfBytes )); if ( ( numberOfBytesWritten == NULL ) || ( buffer == NULL ) || ( serialPort == NULL ) ) { if ( numberOfBytesWritten != NULL ) { *numberOfBytesWritten = 0; } status = STATUS_INVALID_PARAMETER; } else { if ( numberOfBytes == 0 ) { *numberOfBytesWritten = 0; } else { SerialPortCopyToWriteBuffer( serialPort, numberOfBytes, buffer, numberOfBytesWritten ); dueTime.QuadPart = -((LONGLONG)( ( serialPort->timeouts.WriteTotalTimeoutMultiplier * numberOfBytes ) + serialPort->timeouts.WriteTotalTimeoutConstant ) * MillisecondsTo100Nanoseconds ); if ( dueTime.QuadPart != 0 ) { KeSetTimer( &serialPort->writeTimeout.timer, dueTime, &serialPort->writeTimeout.timeoutDpc ); } //if ( serialPort->transmitQueue.used > 0 ) { SetWriteInterrupt( serialPort->baseAddress, serialPort->uartType ); } } } return status; } NTSTATUS SerialPortRead( SerialPort* serialPort, unsigned int numberOfBytes, void* buffer, unsigned int* numberOfBytesRead, PIRP pIrp ) { KIRQL cancelIRQL; NTSTATUS status = STATUS_SUCCESS; unsigned char pendRequest = 0; LARGE_INTEGER dueTime = { 0 }; unsigned int receiveQueueUsed = 0; if ( numberOfBytesRead != NULL ) { *numberOfBytesRead = 0; } if ( ( numberOfBytesRead == NULL ) || ( buffer == NULL ) || ( serialPort == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { if ( ( serialPort->timeouts.ReadIntervalTimeout == MAXULONG ) && ( serialPort->timeouts.ReadTotalTimeoutConstant == 0 ) && ( serialPort->timeouts.ReadTotalTimeoutMultiplier == 0 ) ) { } else { LockQueue( &serialPort->receiveQueue ); receiveQueueUsed = serialPort->receiveQueue.used; UnlockQueue( &serialPort->receiveQueue ); if ( ( serialPort->timeouts.ReadIntervalTimeout == MAXULONG ) && ( serialPort->timeouts.ReadTotalTimeoutMultiplier == MAXULONG ) && ( serialPort->timeouts.ReadTotalTimeoutConstant < MAXULONG ) ) { if ( receiveQueueUsed == 0 ) { pendRequest = 1; numberOfBytes = 1; } } else if ( receiveQueueUsed < numberOfBytes ) { pendRequest = 1; } } if ( pendRequest == 1 ) { status = STATUS_PENDING; KeAcquireSpinLock( &serialPort->pendingRead.lock, &serialPort->pendingRead.irqlOriginal ); serialPort->pendingRead.pIrp = pIrp; serialPort->pendingRead.bufferLength = numberOfBytes; IoAcquireCancelSpinLock( &cancelIRQL ); IoSetCancelRoutine( pIrp, SerialCancelCurrentRead ); IoReleaseCancelSpinLock( cancelIRQL ); KeReleaseSpinLock( &serialPort->pendingRead.lock, serialPort->pendingRead.irqlOriginal ); IoMarkIrpPending( pIrp ); dueTime.QuadPart = -((LONGLONG)( ( serialPort->timeouts.ReadTotalTimeoutMultiplier * numberOfBytes ) + serialPort->timeouts.ReadTotalTimeoutConstant ) * MillisecondsTo100Nanoseconds ); if ( dueTime.QuadPart != 0 ) { KeSetTimer( &serialPort->readTimeout.timer, dueTime, &serialPort->readTimeout.timeoutDpc ); } } else { SerialPortCopyFromReceiveBuffer( serialPort, numberOfBytes, buffer, numberOfBytesRead ); } } return status; } NTSTATUS SerialPortClearStats( SerialPort* serialPort ) { NTSTATUS status = STATUS_SUCCESS; if ( serialPort == NULL ) { status = STATUS_INVALID_PARAMETER; } else { serialPort->performanceStats.ReceivedCount = 0; serialPort->performanceStats.TransmittedCount = 0; serialPort->performanceStats.FrameErrorCount = 0; serialPort->performanceStats.SerialOverrunErrorCount = 0; serialPort->performanceStats.BufferOverrunErrorCount = 0; serialPort->performanceStats.ParityErrorCount = 0; DebugPrintf(( "SerialClearStats\n" )); } return status; } NTSTATUS SerialPortGetStats( SerialPort* serialPort, SERIALPERF_STATS* performanceStats ) { NTSTATUS status = STATUS_SUCCESS; if ( ( serialPort == NULL ) || ( performanceStats == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { performanceStats->ReceivedCount = serialPort->performanceStats.ReceivedCount; performanceStats->TransmittedCount = serialPort->performanceStats.TransmittedCount; performanceStats->FrameErrorCount = serialPort->performanceStats.FrameErrorCount; performanceStats->SerialOverrunErrorCount = serialPort->performanceStats.SerialOverrunErrorCount; performanceStats->BufferOverrunErrorCount = serialPort->performanceStats.BufferOverrunErrorCount; performanceStats->ParityErrorCount = serialPort->performanceStats.ParityErrorCount; DebugPrintf(( "SerialGetStats: ReceivedCount %d, TransmittedCount %d, FrameErrorCount %d, SerialOverrunErrorCount %d, BufferOverrunErrorCount %d, ParityErrorCount %d\n", serialPort->performanceStats.ReceivedCount, serialPort->performanceStats.TransmittedCount, serialPort->performanceStats.FrameErrorCount, serialPort->performanceStats.SerialOverrunErrorCount, serialPort->performanceStats.BufferOverrunErrorCount, serialPort->performanceStats.ParityErrorCount )); } return status; } NTSTATUS SerialPortGetDTRRTS( SerialPort* serialPort, PULONG dtrRts ) { //SERIAL_DTR_STATE | SERIAL_RTS_STATE NTSTATUS status = STATUS_SUCCESS; if ( ( serialPort == NULL ) || ( dtrRts == NULL ) ) { status = STATUS_INVALID_PARAMETER; } else { *dtrRts = 0; DebugPrintf(( "SerialPortGetDTRRTS\n" )); } return status; }
Что я уже пробовал:
1.отключил keAquireSpinlock и keReleasespinlock - что снизило частоту ошибок, но основная проблема повторения символов сохраняется.
Richard MacCutchan
Пожалуйста, отредактируйте свой вопрос и удалите весь этот неформатированный дамп кода. Если у вас есть конкретный вопрос, пожалуйста, напишите его. Но не стоит просто сбрасывать весь свой код и ожидать, что кто-то отладит его для вас.
Gerry Schmitz
Похоже на проблему с указателем буфера.
Rick York
- Лучше перечисление, чем макросы препроцессора или глобальная константа."
Чем перечисление лучше глобальной константы? По сути, это одно и то же. Одно из отличий заключается в том, что вы можете легко столкнуться с проблемами типа переменной, используя перечисляемое значение вместо константы с конкретным типом, для которого оно используется. На мой взгляд, лучше иметь их в качестве констант в пространстве имен, чтобы все типы были правильными. FWIW, класс со всеми статическими членами и методами фактически является тем же самым, что и пространство имен.
Кроме того, эти макросы функций могут и должны быть встроенными функциями. Макросов следует избегать, если только не существует жизнеспособной альтернативы.
И последнее - для блокировки ресурсов мне очень не нравятся явные вызовы блокировки и разблокировки. Я предпочитаю использовать экскурсионный класс, который является формой RAII. Класс выполнял бы блокировку при строительстве и разблокировку при разрушении. Конечным результатом является блокировка ресурса на время существования объекта. Затем возникает вопрос о предоставлении соответствующей области видимости для определения правильного времени жизни объекта.