serialport.c

Go to the documentation of this file.
00001 /**
00002 \file    serialport.c
00003 \brief   serial port communications 'c' function library.
00004 \author  Glenn D. MacGougan (GDM)
00005 \date    2008-11-26
00006 \since   2008-11-26
00007 
00008 \b "LICENSE INFORMATION" \n
00009 Copyright (c) 2008, refer to 'author' doxygen tags \n
00010 All rights reserved. \n
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided the following conditions are met: \n
00014 
00015 - Redistributions of source code must retain the above copyright
00016   notice, this list of conditions and the following disclaimer. \n
00017 - Redistributions in binary form must reproduce the above copyright
00018   notice, this list of conditions and the following disclaimer in the
00019   documentation and/or other materials provided with the distribution. \n
00020 - The name(s) of the contributor(s) may not be used to endorse or promote 
00021   products derived from this software without specific prior written 
00022   permission. \n
00023 
00024 THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 
00025 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00026 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00027 DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00028 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00029 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
00030 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
00031 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00032 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00033 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
00034 SUCH DAMAGE.
00035 */
00036 
00037 #include <string.h>
00038 #include <stdlib.h>
00039 #include <stdio.h>
00040 #include "serialport.h"
00041 
00042 #define SERIALPORT_SETUPCOMM_INTERNAL_BUFFER_SIZE (8192)   //!< Internal buffer size used by the serial port. 8 KB should be plenty.
00043 
00044 
00045 /// \brief  A function to assist in error reporting.
00046 static BOOL SERIALPORT_static_ReportLastError();
00047 
00048 
00049 
00050 BOOL SERIALPORT_Initialize( SERIALPORT_structObject* serialport )
00051 {
00052   if( serialport == NULL )
00053   {
00054     SERIALPORT_ERROR_MSG( "if( serialport == NULL )" );
00055     return FALSE;
00056   }
00057   serialport->baudrate = SERIALPORT_BAUD_9600;
00058   serialport->parity   = SERIALPORT_PARITY_NONE;
00059   serialport->databits = SERIALPORT_DATABITS_8;
00060   serialport->stopbits = SERIALPORT_STOPBITS_1;
00061   serialport->flowcontrol = SERIALPORT_FLOWCONTROL_NONE;
00062   serialport->isPortOpen = FALSE;
00063 
00064   serialport->port = 0;      // not yet a valid port.
00065   serialport->timeout = 0; // 100 ms time-out default.
00066   serialport->handle = NULL; // invalid handle initially.
00067 
00068   serialport->behaviour = SERIALPORT_BEHAVIOUR_RETURN_IMMEDIATELY;
00069 
00070   memset( &serialport->control_setting, 0, sizeof(serialport->control_setting) );
00071 
00072   return TRUE;
00073 }
00074 
00075 
00076 
00077 
00078 BOOL SERIALPORT_Open( SERIALPORT_structObject* serialport )
00079 {
00080   char msg[128];
00081   char comName[8];
00082   COMMTIMEOUTS CommTimeouts;
00083   WCHAR comStr[8];
00084   unsigned i = 0;
00085 
00086   if( serialport == NULL )
00087   {
00088     SERIALPORT_ERROR_MSG( "if( serialport == NULL )" );
00089     return FALSE;
00090   }
00091   
00092   // Check if the port is already open
00093   if( serialport->isPortOpen )
00094   {
00095     sprintf( msg, "Serial port %d is already open.", serialport->port );
00096     SERIALPORT_ERROR_MSG( msg );
00097     return FALSE;
00098   }
00099 
00100   sprintf( comName, "COM%d", serialport->port );
00101 
00102   for( i = 0; i < 8; i++ )
00103   {
00104     comStr[i] = comName[i];
00105   }
00106 
00107   // open the port
00108   // CreateFile() refer http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
00109   serialport->handle = CreateFile( 
00110     comStr,            // e.g. "COM1"
00111     GENERIC_READ | GENERIC_WRITE, // access rights: read and write
00112     0,                            // exclusive access
00113     NULL,                         // no security attributes
00114     OPEN_EXISTING,                // the serial port must exist on the system.
00115     0,                            // no flags or attributes
00116     NULL                          // No template file is needed
00117     );
00118 
00119   // check the handle
00120   if( serialport->handle == INVALID_HANDLE_VALUE )
00121   {
00122     SERIALPORT_ERROR_MSG( "CreateFile returned an invalid handle.\nGetLastError() reports:\n" );
00123     SERIALPORT_static_ReportLastError();    
00124     return FALSE;
00125   }
00126 
00127   // set buffer sizes
00128   // SetupComm() refer http://msdn.microsoft.com/en-us/library/aa363439.aspx
00129   if( !SetupComm( serialport->handle, SERIALPORT_SETUPCOMM_INTERNAL_BUFFER_SIZE, SERIALPORT_SETUPCOMM_INTERNAL_BUFFER_SIZE ) )
00130   {
00131     SERIALPORT_ERROR_MSG( "SetupComm returned error condition.\nGetLastError() reports:\n" );   
00132     SERIALPORT_static_ReportLastError();    
00133     SERIALPORT_Close( serialport );
00134     return FALSE;
00135   }
00136 
00137   if( !GetCommState(serialport->handle, &serialport->control_setting) )
00138   {
00139     SERIALPORT_ERROR_MSG( "GetCommState returned error condition.\nGetLastError() reports:\n" );    
00140     SERIALPORT_static_ReportLastError();    
00141     SERIALPORT_Close( serialport );
00142     return FALSE;
00143   }
00144 
00145   // see http://msdn.microsoft.com/en-us/library/aa363214(VS.85).aspx
00146   serialport->control_setting.BaudRate = serialport->baudrate;
00147   serialport->control_setting.ByteSize = serialport->databits;
00148   serialport->control_setting.Parity   = serialport->parity;
00149   serialport->control_setting.StopBits = serialport->stopbits;
00150   if( serialport->parity != SERIALPORT_PARITY_NONE )
00151   {
00152     serialport->control_setting.fParity = TRUE;
00153   }
00154   else
00155   {
00156     serialport->control_setting.fParity = FALSE;
00157   }
00158 
00159   if( serialport->flowcontrol == SERIALPORT_FLOWCONTROL_NONE )
00160   {
00161     serialport->control_setting.fOutxCtsFlow = 0;
00162     serialport->control_setting.fOutxDsrFlow = 0;
00163     serialport->control_setting.fDtrControl  = DTR_CONTROL_DISABLE;
00164     serialport->control_setting.fOutX        = 0;
00165     serialport->control_setting.fInX         = 0;
00166     serialport->control_setting.fRtsControl  = RTS_CONTROL_DISABLE;
00167   }
00168   else if( serialport->flowcontrol == SERIALPORT_FLOWCONTROL_HARDWARE )
00169   {
00170     serialport->control_setting.fOutxCtsFlow  = 1;
00171     serialport->control_setting.fOutxDsrFlow  = 1;
00172     serialport->control_setting.fDtrControl  = DTR_CONTROL_HANDSHAKE;
00173     serialport->control_setting.fOutX        = 0;
00174     serialport->control_setting.fInX         = 0;
00175     serialport->control_setting.fRtsControl  = RTS_CONTROL_HANDSHAKE;
00176   }
00177   else if( serialport->flowcontrol == SERIALPORT_FLOWCONTROL_XONXOFF )
00178   {
00179     serialport->control_setting.fOutxCtsFlow  = 0;
00180     serialport->control_setting.fOutxDsrFlow  = 0;
00181     serialport->control_setting.fDtrControl  = DTR_CONTROL_ENABLE;
00182     serialport->control_setting.fOutX        = 1;
00183     serialport->control_setting.fInX         = 1;
00184     serialport->control_setting.fRtsControl  = RTS_CONTROL_ENABLE;
00185   }
00186   else
00187   {
00188     SERIALPORT_ERROR_MSG( "Unexpected" );   
00189     return FALSE;
00190   }
00191 
00192   // set the COM port settings
00193   // refer http://msdn.microsoft.com/en-us/library/aa363436.aspx
00194   if( !SetCommState( serialport->handle, &serialport->control_setting ) )
00195   {
00196     SERIALPORT_ERROR_MSG( "SetCommState returned error condition.\nGetLastError() reports:\n" );    
00197     SERIALPORT_static_ReportLastError();  
00198     SERIALPORT_Close( serialport );
00199     return FALSE;
00200   }
00201 
00202   // Retrieve the time-out parameters for all read and write operations on the port.  
00203   // http://msdn.microsoft.com/en-us/library/aa363261(VS.85).aspx
00204   if( !GetCommTimeouts( serialport->handle, &CommTimeouts ) )
00205   {
00206     SERIALPORT_ERROR_MSG( "GetCommTimeouts returned error condition.\nGetLastError() reports:\n" );   
00207     SERIALPORT_static_ReportLastError();  
00208     SERIALPORT_Close( serialport );
00209     return FALSE;
00210   }
00211 
00212   if( serialport->behaviour == SERIALPORT_BEHAVIOUR_WAIT_UNTIL_BYTES_DETECTED )
00213   {
00214     CommTimeouts.ReadIntervalTimeout = MAXDWORD;
00215     CommTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
00216     CommTimeouts.ReadTotalTimeoutConstant = serialport->timeout;
00217   }
00218   else if( serialport->behaviour == SERIALPORT_BEHAVIOUR_RETURN_IMMEDIATELY )
00219   {
00220     CommTimeouts.ReadIntervalTimeout = MAXDWORD;
00221     CommTimeouts.ReadTotalTimeoutMultiplier = 0;
00222     CommTimeouts.ReadTotalTimeoutConstant = 0;
00223     serialport->timeout = 0;
00224   }
00225   else
00226   {
00227     SERIALPORT_ERROR_MSG("Unexpected.");
00228     return FALSE;
00229   }
00230 
00231   if( !SetCommTimeouts( serialport->handle, &CommTimeouts ) )
00232   {
00233     SERIALPORT_ERROR_MSG( "SetCommTimeouts returned FALSE.\nGetLastError() reports:\n" );   
00234     SERIALPORT_static_ReportLastError();
00235     SERIALPORT_Close( serialport );
00236     return FALSE;
00237   }
00238 
00239   serialport->isPortOpen = TRUE;
00240 
00241   return TRUE;
00242 }
00243 
00244 BOOL SERIALPORT_Close( SERIALPORT_structObject* serialport )
00245 {
00246   if( serialport == NULL )
00247   {
00248     SERIALPORT_ERROR_MSG( "if( serialport == NULL )" );
00249     return FALSE;
00250   }
00251   if( serialport->isPortOpen )
00252   {
00253     if( !CloseHandle( serialport->handle ) )
00254     {
00255       SERIALPORT_ERROR_MSG( "CloseHandle returned FALSE.\nGetLastError() reports:\n" );   
00256       SERIALPORT_static_ReportLastError();        
00257       return FALSE;
00258     }
00259     serialport->isPortOpen = FALSE;
00260     serialport->handle = NULL;
00261     return TRUE;
00262   }
00263   else
00264   {
00265     SERIALPORT_ERROR_MSG( "The serial port is not open." );   
00266     return FALSE;
00267   }
00268 }
00269 
00270 
00271 /*
00272 BOOL SERIALPORT_IsDataAvailableToRead( SERIALPORT_structObject* serialport, BOOL* isAvailable )
00273 {
00274   BOOL result;
00275   if( serialport->isPortOpen )
00276   {
00277     unsigned long eventMask = 0;
00278 
00279     // refer http://msdn.microsoft.com/en-us/library/aa363435.aspx
00280 
00281     // Wait briefly on the event EV_RXCHAR (0x0001).
00282     // This means a character was received and placed in the input buffer.
00283 
00284     if( !SetCommMask( serialport->handle, EV_RXCHAR ) )
00285     {
00286       SERIALPORT_ERROR_MSG( "SetCommMask returned FALSE.\nGetLastError() reports:\n" );
00287       SERIALPORT_static_ReportLastError();
00288       SERIALPORT_Close( serialport );
00289       return FALSE;
00290     }
00291 
00292     if( !WaitCommEvent( serialport->handle, &eventMask, NULL ) )
00293     {
00294       SERIALPORT_ERROR_MSG( "WaitCommEvent returned FALSE.\nGetLastError() reports:\n" );
00295       SERIALPORT_static_ReportLastError();
00296       SERIALPORT_Close( serialport );
00297       return FALSE;
00298     }
00299 
00300     return TRUE;
00301   }
00302   else
00303   {
00304     SERIALPORT_ERROR_MSG( "The serial port is not open." );   
00305     return FALSE;
00306   }
00307 }
00308 */
00309 
00310 BOOL SERIALPORT_Read( 
00311   SERIALPORT_structObject* serialport,       //!< The serial port 'object'.
00312   unsigned char* buffer,                     //!< A valid buffer in which to place data.
00313   const unsigned int bytesAvailableInBuffer, //!< The maximum number of bytes avaiable in the buffer.
00314   unsigned int *bytesRead                     //!< The number of bytes read and stored in the buffer.
00315   )
00316 {
00317   BOOL result;
00318   DWORD dwbytesRead = 0;
00319   *bytesRead = 0;  
00320 
00321   if( serialport == NULL )
00322   {
00323     SERIALPORT_ERROR_MSG( "if( serialport == NULL )" );
00324     return FALSE;
00325   }
00326   if( serialport->isPortOpen )
00327   {
00328     result = ReadFile(
00329       serialport->handle, 
00330       buffer, 
00331       bytesAvailableInBuffer, 
00332       (LPDWORD)&dwbytesRead, 
00333       NULL );
00334 
00335     *bytesRead = dwbytesRead;
00336 
00337     if( GetLastError() == ERROR_IO_PENDING )
00338     {
00339       // The read operation is pending completion asynchronously. This is not an error.
00340       return TRUE;
00341     }
00342     if( !result )
00343     {
00344       SERIALPORT_ERROR_MSG( "ReadFile returned FALSE.\nGetLastError() reports:\n" );
00345       SERIALPORT_static_ReportLastError();        
00346       SERIALPORT_Close( serialport );
00347       return FALSE;
00348     }
00349     else
00350     {      
00351       return TRUE;
00352     }
00353   }
00354   else
00355   {
00356     SERIALPORT_ERROR_MSG( "The serial port is not open." );   
00357     return FALSE;
00358   }
00359 }
00360 
00361 BOOL SERIALPORT_Write( 
00362   SERIALPORT_structObject* serialport,     //!< The serial port 'object'.
00363   const unsigned char* buffer,             //!< A valid buffer containing the data to write.
00364   const unsigned int numberOfBytesToWrite  //!< The number of bytes to write.  
00365   )
00366 {  
00367 
00368   BOOL result;
00369   DWORD dwbytesWritten = 0;
00370   
00371   if( serialport == NULL )
00372   {
00373     SERIALPORT_ERROR_MSG( "if( serialport == NULL )" );
00374     return FALSE;
00375   }
00376   if( serialport->isPortOpen )
00377   {
00378     // write to file
00379     result = WriteFile( 
00380       serialport->handle,
00381       buffer,
00382       numberOfBytesToWrite,
00383       (LPDWORD)&dwbytesWritten,
00384       NULL 
00385       );
00386 
00387     if( GetLastError() == ERROR_IO_PENDING )
00388     {
00389       // The write operation is pending completion asynchronously. This is not an error.
00390       return TRUE;
00391     }
00392     if( !result )
00393     {
00394       SERIALPORT_ERROR_MSG( "WriteFile returned FALSE.\nGetLastError() reports:\n" );
00395       SERIALPORT_static_ReportLastError();        
00396       SERIALPORT_Close( serialport );
00397       return FALSE;
00398     }
00399     else
00400     {   
00401       if( dwbytesWritten != numberOfBytesToWrite )
00402       {
00403         SERIALPORT_ERROR_MSG( "if( dwbytesWritten != numberOfBytesToWrite )" );
00404         return FALSE;
00405       }      
00406       return TRUE;
00407     }
00408   }
00409   else
00410   {
00411     SERIALPORT_ERROR_MSG( "The serial port is not open." );   
00412     return FALSE;
00413   }
00414 }
00415 
00416 
00417 
00418 BOOL SERIALPORT_static_ReportLastError()
00419 {
00420   unsigned i = 0;
00421   char msg[8192];
00422   LPTSTR pszMessage;
00423   DWORD dwLastError = GetLastError(); 
00424   FormatMessage(
00425     FORMAT_MESSAGE_ALLOCATE_BUFFER | 
00426     FORMAT_MESSAGE_FROM_SYSTEM |
00427     FORMAT_MESSAGE_IGNORE_INSERTS,
00428     NULL,
00429     dwLastError,
00430     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
00431     (LPTSTR)&pszMessage,
00432     0, 
00433     NULL 
00434     );
00435   for( i = 0; i < 8192; i++ )
00436   {
00437     msg[i] = (char)pszMessage[i];
00438     if( pszMessage[i] == '\0' )
00439     {
00440       break;
00441     }
00442   }
00443   SERIALPORT_ERROR_MSG( msg );
00444 
00445   return TRUE;
00446 }
00447 
00448 
00449 BOOL SERIALPORT_WaitOnRingIndicatorEvent(
00450   SERIALPORT_structObject* serialport,     //!< The serial port 'object'.
00451   unsigned short timeout_ms,               //!< Wait for up to this timeout value [ms].
00452   BOOL *ring_detected                      //!< A boolean to indicate if a ring signal was detected.
00453   )
00454 {
00455   BOOL result;
00456   DWORD EvtMask;
00457   SERIALPORT_enumBehaviour prev_behaviour;
00458   SERIALPORT_enumBehaviour prev_timeout;
00459   COMMTIMEOUTS CommTimeouts;
00460 
00461   if( serialport == NULL )
00462   {
00463     SERIALPORT_ERROR_MSG( "if( serialport == NULL )" );
00464     return FALSE;
00465   }
00466   if( !serialport->isPortOpen )
00467   {
00468     SERIALPORT_ERROR_MSG( "if( !serialport->isPortOpen )" );
00469     return FALSE;
00470   }
00471 
00472   result = GetCommMask( serialport->handle, &EvtMask );
00473   if( !result )
00474   {
00475     SERIALPORT_ERROR_MSG( "GetCommMask returned FALSE.\nGetLastError() reports:\n" );
00476     SERIALPORT_static_ReportLastError();        
00477     SERIALPORT_Close( serialport );
00478     return FALSE;
00479   }
00480   EvtMask |= EV_RING;
00481 
00482   result = SetCommMask( serialport->handle, EvtMask );
00483   if( !result )
00484   {
00485     SERIALPORT_ERROR_MSG( "SetCommMask returned FALSE.\nGetLastError() reports:\n" );
00486     SERIALPORT_static_ReportLastError();        
00487     SERIALPORT_Close( serialport );
00488     return FALSE;
00489   }
00490 
00491   // Alter the serial port so that it has a timeout value.
00492   prev_behaviour = serialport->behaviour; // store the old behaviour  
00493   prev_timeout = serialport->timeout; // and old timout
00494   serialport->behaviour = SERIALPORT_BEHAVIOUR_WAIT_UNTIL_BYTES_DETECTED;
00495   
00496   // Retrieve the time-out parameters for all read and write operations on the port.
00497   // http://msdn.microsoft.com/en-us/library/aa363261(VS.85).aspx
00498   if( !GetCommTimeouts( serialport->handle, &CommTimeouts ) )
00499   {
00500     SERIALPORT_ERROR_MSG( "GetCommTimeouts returned error condition.\nGetLastError() reports:\n" );   
00501     SERIALPORT_static_ReportLastError();  
00502     SERIALPORT_Close( serialport );
00503     return FALSE;
00504   }
00505 
00506   CommTimeouts.ReadIntervalTimeout = MAXDWORD;
00507   CommTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
00508   CommTimeouts.ReadTotalTimeoutConstant = timeout_ms;
00509 
00510   if( !SetCommTimeouts( serialport->handle, &CommTimeouts ) )
00511   {
00512     SERIALPORT_ERROR_MSG( "SetCommTimeouts returned FALSE.\nGetLastError() reports:\n" );   
00513     SERIALPORT_static_ReportLastError();
00514     SERIALPORT_Close( serialport );
00515     return FALSE;
00516   }
00517 
00518   EvtMask = 0;
00519   result = WaitCommEvent(
00520     serialport->handle,
00521     &EvtMask,
00522     NULL
00523     );
00524   if( !result )
00525   {
00526     SERIALPORT_ERROR_MSG( "WaitCommEvent returned FALSE.\nGetLastError() reports:\n" );
00527     SERIALPORT_static_ReportLastError();        
00528     SERIALPORT_Close( serialport );
00529     return FALSE;
00530   }
00531 
00532   if( EvtMask == EV_RING )
00533   {
00534     *ring_detected = TRUE;
00535   }
00536 
00537   serialport->behaviour = prev_behaviour;
00538   serialport->timeout = prev_timeout;
00539 
00540   if( serialport->behaviour == SERIALPORT_BEHAVIOUR_WAIT_UNTIL_BYTES_DETECTED )
00541   {
00542     CommTimeouts.ReadIntervalTimeout = MAXDWORD;
00543     CommTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
00544     CommTimeouts.ReadTotalTimeoutConstant = serialport->timeout;
00545   }
00546   else if( serialport->behaviour == SERIALPORT_BEHAVIOUR_RETURN_IMMEDIATELY )
00547   {
00548     CommTimeouts.ReadIntervalTimeout = MAXDWORD;
00549     CommTimeouts.ReadTotalTimeoutMultiplier = 0;
00550     CommTimeouts.ReadTotalTimeoutConstant = 0;
00551     serialport->timeout = 0;
00552   }
00553   else
00554   {
00555     SERIALPORT_ERROR_MSG("Unexpected.");
00556     return FALSE;
00557   }
00558   if( !SetCommTimeouts( serialport->handle, &CommTimeouts ) )
00559   {
00560     SERIALPORT_ERROR_MSG( "SetCommTimeouts returned FALSE.\nGetLastError() reports:\n" );   
00561     SERIALPORT_static_ReportLastError();
00562     SERIALPORT_Close( serialport );
00563     return FALSE;
00564   }
00565 
00566   return TRUE;
00567 }
00568 
SourceForge.net Logo