📄 win32port.cpp
字号:
INFINITE );
//
// We can return from the wait call with one of four possible
// results. 0-3 mean that one of the event handles in the
// array defined above was set by some process. The other is
// a timeout. If you are nervous about waiting forever, you can
// put a 10 or 20 second timeout on this wait, and print a TRACE
// message every time the wait times out. This gives you a little
// bit of a warm feeling that lets you know things are really
// working.
//
switch ( result ) {
// If the first event handle in the array was set, it means
// the main thread of the program wants to close the port.
// It sets the kill event, and we in turn set the done flag.
// We will then exit from the bottom of the loop.
case 0 : // kill thread event
done = true;
break;
// This case is selected if an overlapped read of data has
// signalled that it is complete. If the ReadFile() call
// completed because it passed in some data, we stuff that
// data into the RX queue and call the notification function
// to alert the user's process.
case 1 :
if ( GetOverlappedResult( port->m_hPort,
&AsyncReadInfo,
&dwBytesRead,
FALSE ) )
{ // read completed successfully
if ( dwBytesRead ) {
port->m_RxQueue.Insert( read_buffer, dwBytesRead );
port->RxNotify( dwBytesRead );
}
}
// Since the last ReadFile() operation completed, we are
// no longer reading data. We set this flag to false so
// that we can kick off a new read at the top of the loop.
port->m_bInputThreadReading = false;
break;
// When we reach case 2, it means that the WaitCommEvent()
// call completed, which means that one of the line status
// or modem status events has been triggered. We don't
// know which one it is, so we call the handler for both
// possibilities, allowing them to decide if something has
// happened, and who to notify about it.
case 2 : { /* Status event */
DWORD dwOverlappedResult;
if ( GetOverlappedResult( port->m_hPort,
&AsyncStatusInfo,
&dwOverlappedResult,
FALSE ) )
{
port->check_modem_status( false, dwCommEvent );
port->clear_error();
}
// Clear this flag so that a new call to WaitCommEvent()
// can be made at the top of the loop.
waiting_on_status = false;
break;
}
// When we reach case 3, it means that another thread read
// some data from the RX queue, opening up some room, and
// noticed that we weren't actively reading data. When this
// happens, we need to wake up anc start a new call to
// ReadFile() so that we can fill up all that empty
// space in the RX queue.
case 3 :
break;
default :
assert( false );
}
}
}
//
// When the input thread has decided to exit, it first kills the
// two events it created, then exits via a return statement.
CloseHandle( AsyncReadInfo.hEvent );
CloseHandle( AsyncStatusInfo.hEvent );
}
//
// The output thread is very similar in structure to the input thread,
// with a few notable differences. Instead of including the wait
// for output data as part of its main loop, the output thread calls
// a subroutine to actually perform the data output. And that output
// function runs to completion, so we will never wait for output to
// be complete in the main body of the thread shown here. It would
// probably be a useful modification of this thread to change it so
// that pending output is waited for in the main thread.
//
void Win32Port::OutputThread(void * arglist)
{
//
// As was the case for the input thread, we get a pointer to the
// Win32Port object passed to us from the calling routine. We just
// have to cast the pointer, then we have full access to all members
// ofthe port's structure.
//
Win32Port *port = (Win32Port *) arglist;
// The array of handles I wait for in the main loop has one
// fewer element than the same array in the input thread.
// We're only waiting for three things here: a request to send
// more data via WriteFile(), a request to kill the thread, or
// a request to send a break.
HANDLE handles[ 3 ] = { port->m_hKillOutputThreadEvent,
port->m_hWriteRequestEvent,
port->m_hBreakRequestEvent };
port->TxNotify();
for ( bool done = false ; !done ; ) {
switch ( WaitForMultipleObjects( 3, handles, FALSE, INFINITE ) ) {
//
// There are three possible returns from the call that waits
// for something to happens: the three events and a timeout.
// The first case means that a thread has attempted to close
// the port, which results in the kill thread event being
// set. When that's the case, we modify the control flag for
// the loop so that we'll all out of the loop when we reach
// the bottom.
//
case 0 : //m_hKillOutputThreadEvent
done = true;
break;
//
// Much of the time this loop will be sitting here doing nothing.
// When an output request comes through, the appropriate event
// is set and we end up here. We then call the output worker
// routine to actually dump the output through the serial port.
//
case 1 : //m_hWriteRequestEvent
done = port->output_worker();
break;
//
// If the break event is set, it means we are being requested
// to send a break out through the given port. The duration
// of the break has already been set in a member of the object,
// so all we have to do here is make it happen. Since this is
// a background thread with no GUI, it's safe to just sit in
// a sleep call for the duration of the break.
//
case 2 : //m_hBreakRequestEvent
SetCommBreak( port->m_hPort );
SleepEx( port->m_iBreakDuration, FALSE );
ClearCommBreak( port->m_hPort );
break;
//
// This can only be bad!
//
default :
assert( false );
break;
}
}
}
//
// When a request comes in to transmit some data, this routine is called.
// It starts things up by calling WriteFile(), then waits for the
// asynchronous I/O to complete. Note that while it is waiting for
// WriteFile() to complete it can also be notified of a kill thread
// event, in which case it returns immediately.
//
bool Win32Port::output_worker()
{
OVERLAPPED AsyncWriteInfo = { 0 };
AsyncWriteInfo.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
assert( AsyncWriteInfo.hEvent );
HANDLE handles[ 2 ] = { m_hKillOutputThreadEvent,
AsyncWriteInfo.hEvent };
bool killed = false;
for ( bool done = false ; ; !done ) {
//
// First get as much data from the output buffer as we can
//
char data[ 500 ];
int count = m_TxQueue.Extract( data, 500 );
if ( count == 0 ) {
TxNotify();
break;
}
//
// Now we enter the transmit loop, where the data will actually
// be sent out the serial port.
//
DWORD result_count;
if ( !WriteFile( m_hPort,
data,
count,
&result_count,
&AsyncWriteInfo ) ) {
//
// WriteFile() returned an error. If the error tells us that the I/O
// operation didn't complete, that's okay.
//
if ( GetLastError() == ERROR_IO_PENDING ) {
switch ( WaitForMultipleObjects( 2, handles, FALSE, INFINITE ) ) {
//
// Case 0 is the thread kill event, we need to give this up. Since
// the event is cleared when I detect it, I have to reset it
//
case 0 : //m_hKillOutputThreadEvent
done = true;
killed = true;
PurgeComm( m_hPort, PURGE_TXABORT );
break;
//
// Case 1 means the WriteFile routine signaled completion.
// We're not out of the woods completely, there are a
// few errors we have to check.
//
case 1 : //AsyncWriteInfo.hEvent
// The overlapped result can show that the write
// event completed, or it stopped for some other
// reason. If it is some other reason we exit the
// loop immediately. Otherwise we will pass through
// the loop and kick off another write. Note that
// a TXFlush() call creates an error here, which
// we ignore.
if ( !GetOverlappedResult( m_hPort,
&AsyncWriteInfo,
&result_count,
FALSE ) ||
result_count != count ) {
if ( GetLastError() == ERROR_IO_PENDING )
clear_error();
else
translate_last_error();
done = true;
}
break;
//
// We had better not ever land here
//
default :
assert( false );
}
} else {
translate_last_error();
done = true;
}
} else {
//
// If we fall through to this point, it means that WriteFile()
// returned immediately. If it did, it means that we were able
// to send all of the characters requested in the call. If
// we get here and all the characters weren't send,
// something bad happened.
//
if ( result_count != count ) {
translate_last_error();
done = true;
}
}
}
//
// On the way out, close the event handle
//
CloseHandle( AsyncWriteInfo.hEvent );
return killed;
}
//
// When an asynchronous event is processed from the call to
// WaitCommEvent(), we have to call this guy to determine what
// happened. We check each of the modemstatus lines to see who
// changed, then call the notification functions to let the
// calling process know what happened.
//
void Win32Port::check_modem_status(bool first_time, DWORD event_mask )
{
//
// There shouldn't be anything to prevent us from reading
// the input lines. If an error occurs, it is bad.
///
if ( !GetCommModemStatus( m_hPort, &m_dwModemStatus ) )
assert( false );
//
// The first_time flag is set one time when this guy is
// called to force the notification functions to be called.
// Forcing this to happen means that an application can be
// sure that it will can use the notification functions to
// determine status.
//
if ( first_time ) //report everything
{
CtsNotify( ( MS_CTS_ON & m_dwModemStatus ) != 0 );
DsrNotify( ( MS_DSR_ON & m_dwModemStatus ) != 0 );
CdNotify( ( MS_RLSD_ON & m_dwModemStatus ) != 0 );
RiNotify( 0 );
} else { //Only report events
//
// If it isn't the first time, we only send notification for
// events that actually occured in the event that caused
// this function to be invoked/
//
if ( event_mask & EV_CTS )
CtsNotify( ( MS_CTS_ON & m_dwModemStatus ) != 0 );
if ( event_mask & EV_DSR )
DsrNotify( ( MS_DSR_ON & m_dwModemStatus ) != 0 );
if ( event_mask & EV_RLSD )
CdNotify( ( MS_RLSD_ON & m_dwModemStatus ) != 0 );
//
// Win95/98 *always* reports an RI event if RI is high when any
// other line changes. This is not really a good thing. All
// I'm interested is seeing EV_RINGTE, so I only report an
// event if RI is low
//
if ( event_mask & ( EV_RING | EV_RINGTE ) )
if ( ( MS_RING_ON & m_dwModemStatus ) == 0 )
RiNotify( 0 );
}
}
// EOF Win32Port.cpp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -