Ciao vinper,
ti passo una classe che funzionava, però è un pò incasinata, nel senso che è scritta con delle compilazioni condizionali per funzionare sia sotto Windows che Qnx. Se tagli via tutte le parti per Qnx potrebbe servirti almeno come spunto.:
class SerialPort: public Object
{
public:
	SerialPort();
	~SerialPort();
	void Close();
	bool Open(UInt32 id, UInt32 baudRate);
	bool Open(const char* name, UInt32 baudRate);
	bool Read(UInt8&);
	bool ReadWithTimeout(UInt8& result, UInt32 timeoutMs);
	bool Write(UInt8);
	bool Write(UInt8* buffer, UInt32 bufferSize);
	UInt32 m_baudRate;
private:
	#ifdef __POSIX__
		 int m_serial;
	#endif
    #ifdef WIN32
		 void* m_serial;
	#endif
};
// SerialPort
SerialPort::SerialPort()
{
	m_baudRate = 0;
	#ifdef WIN32
		 m_serial = INVALID_HANDLE_VALUE;
	#endif
	#ifdef __POSIX__
		 m_serial = 0;
	#endif
}
SerialPort::~SerialPort()
{
}
void SerialPort::Close()
{
	#ifdef WIN32
		if( m_serial == INVALID_HANDLE_VALUE)
			return;
		CloseHandle(m_serial);
		 m_serial = INVALID_HANDLE_VALUE;
	#endif
	#ifdef __POSIX__
		 close(m_serial);
		 m_serial = 0;
	#endif
}
bool SerialPort::Open(UInt32 id, UInt32 baudRate)
{
	 // sotto QNX il nome è "/dev/ser1" per COM1 ecc.
	 // sotto Linux il nome è: "/dev/ttyS0" (per la prima COM1, ttyS1 per la COM2)
	 // sotto Windows il nome è "COM1" per COM1 ecc.
	string name;
	#ifdef __QNX__
		name = string("/dev/ser") + Utility::ToString(id);
	#else
		#ifdef __POSIX__
			name = string("/dev/ttyS") + Utility::ToString(id - 1);
		#endif
	#endif
	#ifdef WIN32
		name = string("COM") + Utility::ToString(id);
	#endif
		return Open(name.c_str(), baudRate);
}
bool SerialPort::Open(const char* name, UInt32 baudRate)
{
	#ifdef __QNX__
		 m_serial = open(name, O_RDWR| O_NONBLOCK); 
		 if (m_serial == -1)
			 return false;
		 struct termios options;
    	 speed_t speed = baudRate;
		 tcgetattr( m_serial, &options);
  		 cfsetispeed(&options, speed); 
		 cfsetospeed(&options, speed); 
     	 tcsetattr( m_serial, TCSAFLUSH, &options);
	#endif
	#ifdef WIN32
		m_serial = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
		if( m_serial == INVALID_HANDLE_VALUE)
			return false;
	#endif
	return true;
}
bool SerialPort::Read(UInt8& result)
{
	#ifdef __QNX__
	int bytesRead = read (m_serial, &result, 1);
	if (bytesRead != 1)
		return false;
	#endif
	#ifdef WIN32
		DWORD bytesRead = 0;
		if( ! ReadFile(m_serial, &result, 1, &bytesRead, NULL) )
			return false;
		if (bytesRead != 1)
			return false;
	#endif
	return true;
}
bool SerialPort::ReadWithTimeout(UInt8& result, UInt32 timeoutMs)
{
	const int retry = 10;
	int wait = timeoutMs / retry;
	if (wait == 0)
		wait = 1;
	for(int i = 0; i < retry; ++i)
	{
		if (Read(result))
			return true;
		DelayMs(wait);
	}
	return false;
}
bool SerialPort::Write(UInt8 writeValue)
{
	#ifdef __QNX__
		int bytesWritten = write(m_serial, &writeValue, 1 );
		if (bytesWritten != 1)
			return false;
	#endif
	#ifdef WIN32
		DWORD bytesWritten = 0;
		if( ! WriteFile(m_serial, &writeValue, 1, &bytesWritten, NULL) )
			return false;
	#endif
	return true;
}
bool SerialPort::Write(UInt8* buffer, UInt32 bufferSize)
{
	#ifdef __QNX__
		int bytesWritten = write(m_serial, buffer, bufferSize );
		if ((UInt32)bytesWritten != bufferSize)
			return false;
	#endif
	#ifdef WIN32
		DWORD bytesWritten = 0;
		if( ! WriteFile(m_serial, buffer, bufferSize, &bytesWritten, NULL) )
			return false;
		return bytesWritten == bufferSize;
	#endif
	return true;
}
Alla fine vedrai che la parte Windows è piuttosto semplice.
Se cerci un poco in rete troverai degli esempi migliori.