Esempio n. 1
0
/*! \brief Wait for data to arrive or a timeout to occur.
	\details The function waits until \c maxLength data is available or until a timeout occurs.
	The function returns success if data is available or XsResultValue::TIMEOUT if a
	timeout occurred. A timeout value of 0 indicates that the default timeout stored
	in the class should be used.
	\param maxLength The maximum number of bytes to wait for
	\param data A buffer that will be filled with the read data. It must be able to contain at
			least \a maxLength bytes.
	\param length An optional pointer to storage for the actual number of bytes read.
	\returns XRV_OK if the requested data was read
*/
XsResultValue UsbInterface::waitForData(const XsSize maxLength, void* data, XsSize* length)
{
	JLTRACE(gJournal, "timeout=" << d->m_timeout << ", data=" << data << ", length=" << length);
	uint32_t timeout = d->m_timeout;

	char *bdata = (char *)data;

	XsSize ln;
	if (length == NULL)
		length = &ln;
	uint32_t eTime = XsTime::getTimeOfDay() + timeout;
	XsSize newLength = 0;

	*length = 0;
	while ((*length < maxLength) && (XsTime::getTimeOfDay() <= eTime))
	{
		if (readData(maxLength - *length, bdata + *length, &newLength))
			return d->m_lastResult;
		*length += newLength;
	}
	JLTRACE(gJournal, "read " << length[0] << " of " << maxLength << " bytes");

	if (length[0] < maxLength)
		return (d->m_lastResult = XRV_TIMEOUT);
	else
		return (d->m_lastResult = XRV_OK);
}
Esempio n. 2
0
/*! \brief Wait for data to arrive or a timeout to occur.
	\details The function waits until \c maxLength data is available or until a timeout occurs.
	The function returns success if data is available or XsResultValue::TIMEOUT if a
	timeout occurred. A timeout value of 0 indicates that the default timeout stored
	in the class should be used.
	\param maxLength The maximum number of bytes to read before returning
	\param data The buffer to put the read data in.
	\returns XRV_OK if \a maxLength bytes were read, XRV_TIMEOUT if less was read, XRV_TIMEOUTNODATA if nothing was read
*/
XsResultValue SerialInterface::waitForData(XsSize maxLength, XsByteArray& data)
{
	data.clear();
	data.reserve(maxLength);

	//char *data = (char *)&_data[0];
	JLTRACE(gJournal, "timeout=" << m_timeout << ", maxLength=" << maxLength);
	uint32_t timeout = m_timeout;

	uint32_t eTime = XsTime_getTimeOfDay(NULL, NULL) + timeout;
//	uint32_t newLength = 0;

	while ((data.size() < maxLength) && (XsTime_getTimeOfDay(NULL, NULL) <= eTime))
	{
		XsByteArray raw;

		if (readData(maxLength - data.size(), raw) != XRV_OK)
			return m_lastResult;
		data.append(raw);
	}
	JLTRACE(gJournal, "Read " << data.size() << " of " << maxLength << " bytes");

	if (data.size() < maxLength)
		return (m_lastResult = XRV_TIMEOUT);
	else
		return (m_lastResult = XRV_OK);
}
Esempio n. 3
0
/*! \brief Read data from the serial port and put it into the data buffer.
	\details This function reads up to \a maxLength bytes from the USB port (non-blocking) and
	puts it into the \a data buffer.
	\param maxLength	The maximum number of bytes to read.
	\param data			Pointer to a buffer that will store the received data.
	\param length		The number of bytes placed into \c data.
	\returns XRV_OK if no error occurred. It can be that no data is available and XRV_OK will be
			returned. Check *length for the number of bytes that were read.
*/
XsResultValue UsbInterface::readData (const XsSize maxLength, void *data, XsSize* length)
{
	JLTRACE(gJournal, "maxLength=" << maxLength << ", data=0x" << data << ", length=0x" << length);
	XsSize ln;
	if (length == NULL)
		length = &ln;

	if (!isOpen())
		return (d->m_lastResult = XRV_NOPORTOPEN);

#ifdef USE_WINUSB
	XsSize remaining = 0;
	::EnterCriticalSection(&d->m_mutex);
	remaining = *length = d->m_varBuffer.size();
	if (*length > maxLength)
		*length = maxLength;
	if (*length)
	{
		memcpy(data, d->m_varBuffer.data(), *length);
		d->m_varBuffer.erase(0, *length);
		remaining = d->m_varBuffer.size();
	}
	::LeaveCriticalSection(&d->m_mutex);
	JLTRACE(gJournal, "returned success, read " << *length << " of " << maxLength << " bytes, first: " << JLHEXLOG(((char*)data)[0]) << ", " << remaining << " remaining in buffer");
#else
	int actual = 0;
	JLTRACE(gJournal, "starting bulk read, timeout = " << d->m_timeout);
	int res = UsbInterfacePrivate::getContextManager().m_libUsb.bulk_transfer(d->m_deviceHandle, (d->m_dataInEndPoint|LIBUSB_ENDPOINT_IN), (unsigned char *)data, maxLength, &actual, d->m_timeout);
	JLTRACE(gJournal, "bulk read returned: " << d->libusbErrorToString(res) << ". " << actual << " bytes received");
	if ((res != LIBUSB_SUCCESS && res != LIBUSB_ERROR_TIMEOUT) || (res == LIBUSB_ERROR_TIMEOUT && actual <= 0))
		return d->m_lastResult = d->libusbErrorToXrv(res);

	*length = actual;
#endif

#ifdef LOG_RX_TX
	if (*length > 0)
	{
		if (d->rx_log == NULL)
		{
			char fname[XS_MAX_FILENAME_LENGTH];
			sprintf(fname,"rx_USB%03u_%03u.log", usbBus(), usbAddress());
			d->rx_log = fopen(fname,"wb");
		}
		fwrite(data,1,*length,d->rx_log);
#ifdef LOG_RX_TX_FLUSH
		fflush(d->rx_log);
#endif
	}
#endif

	return (d->m_lastResult = XRV_OK);
}
Esempio n. 4
0
/*! \copydoc IProtocolHandler::findMessage
	\todo Since the assumption is that we receive a stream of valid messages without garbage, the scan
	is implemented in a rather naive and simple way. If we can expect lots of garbage in the data
	stream, this should probably be looked into.
*/
MessageLocation ProtocolHandler::findMessage(XsMessage& rcv, const XsByteArray& raw) const
{
	JLTRACE(gJournal, "Entry");
	MessageLocation rv(-1,0);
	rcv.clear();

	int bufferSize = (int) raw.size();
	if (bufferSize == 0)
		return rv;
	
	const unsigned char* buffer = raw.data();

	// loop through the buffer to find a preamble
	for (int pre = 0; pre < bufferSize; ++pre)
	{
		if (buffer[pre] == XS_PREAMBLE)
		{
			JLTRACE(gJournal, "Preamble found at " << pre);
			// we found a preamble, see if we can read a message from here
			if (rv.m_startPos == -1)
				rv.m_startPos = (int32_t) pre;
			int remaining = bufferSize-pre;	// remaining bytes in buffer INCLUDING preamble

			if (remaining < XS_LEN_MSGHEADERCS)
			{
				JLTRACE(gJournal, "Not enough header data read");
				if (rv.m_startPos != pre)
					continue;
				rv.m_size = -expectedMessageSize(&buffer[pre], remaining);
				return rv;
			}

			// read header
			const uint8_t* msgStart = &(buffer[pre]);
			const XsMessageHeader* hdr = (const XsMessageHeader*) msgStart;
			if (hdr->m_length == XS_EXTLENCODE)
			{
				if (remaining < XS_LEN_MSGEXTHEADERCS)
				{
					JLTRACE(gJournal, "Not enough extended header data read");
					if (rv.m_startPos != pre)
						continue;
					rv.m_size = -expectedMessageSize(&buffer[pre], remaining);
					return rv;
				}
			}
			else if (hdr->m_busId == 0 && hdr->m_messageId == 0)
			{
				// found 'valid' message that isn't actually valid... happens inside GPS raw data
				// skip to next preamble
				continue;
			}

			// check the reported size
			int target = expectedMessageSize(&buffer[pre], remaining);

			JLTRACE(gJournal, "Bytes in buffer=" << remaining << ", full target = " << target);
			if (target > (XS_LEN_MSGEXTHEADERCS + XS_MAXDATALEN))
			{
				// skip current preamble
				JLALERT(gJournal, "Invalid message length: " << target);
				rv.m_startPos = -1;
				continue;
			}

			if (remaining < target)
			{
				// not enough data read, skip current preamble
				JLTRACE(gJournal, "Not enough data read: " << remaining << " / " << target);
				if (rv.m_size == 0)
					rv.m_size = -target;
				continue;
			}

			// we have read enough data to fulfill our target so we'll try to parse the message
			// and check the checksum
			//if (rcv->loadFromString(msgStart, (uint16_t) target) == XRV_OK)
			if (rcv.loadFromString(msgStart, (uint16_t)target))
			{
				JLTRACE(gJournal,
						"OK, size = " << (int) rcv.getTotalMessageSize()
						<< std::hex << std::setfill('0')
						<< " First bytes " << std::setw(2) << (int) msgStart[0]
						<< " " << std::setw(2) << (int) msgStart[1]
						<< " " << std::setw(2) << (int) msgStart[2]
						<< " " << std::setw(2) << (int) msgStart[3]
						<< " " << std::setw(2) << (int) msgStart[4]
						<< std::dec << std::setfill(' '));
				rv.m_size = (int) rcv.getTotalMessageSize();
				rv.m_startPos = pre;	// we do this again here because this may not be the first preamble encountered (the check for -1 at the start of the loop is necessary)
				return rv;
			}

			// we could not read the message, clear message and try next preamble
			rcv.clear();
			if (rv.m_startPos == pre)
			{
				rv.m_startPos = -1;
				JLALERT(gJournal,
					"Invalid checksum"
					<< std::hex << std::setfill('0')
					<< " First bytes " << std::setw(2) << (int) msgStart[0]
					<< " " << std::setw(2) << (int) msgStart[1]
					<< " " << std::setw(2) << (int) msgStart[2]
					<< " " << std::setw(2) << (int) msgStart[3]
					<< " " << std::setw(2) << (int) msgStart[4]
					<< std::dec << std::setfill(' '));
			}
		}
	}
	JLTRACE(gJournal, "Exit");
	return rv;
}
Esempio n. 5
0
void UsbInterfacePrivate::threadFunc()
{
	HANDLE handles[1+m_oCount];
	handles[0] = m_quitEvent;
	handles[m_oCount] = m_waitEvents[m_oCount-1];
	//= { m_quitEvent, m_waitEvents[0], m_waitEvents[1] };

	// start first read operation
	for (m_readIdx = 0 ; m_readIdx < (m_oCount-1); ++m_readIdx)
	{
		handles[m_readIdx+1] = m_waitEvents[m_readIdx];
		//m_readIdx = 0;
		m_overlapped[m_readIdx] = OVERLAPPED();
		::ResetEvent(m_waitEvents[m_readIdx]);		//lint !e534
		m_overlapped[m_readIdx].hEvent = m_waitEvents[m_readIdx];
		m_winUsb.ReadPipe(m_usbHandle[1],
			m_bulkInPipe,
			m_fixedBuffer[m_readIdx],
			(ULONG)m_fixedBufferSize,
			0,
			&m_overlapped[m_readIdx]);	//lint !e534
	}
	int fastCount = 0;
	//m_readIdx = 1;
	bool policyFast = false;
	bool run = true;
	while (run)
	{
		// start follow-up read operation
		m_overlapped[m_readIdx] = OVERLAPPED();
		::ResetEvent(m_waitEvents[m_readIdx]);		//lint !e534
		m_overlapped[m_readIdx].hEvent = m_waitEvents[m_readIdx];
		m_winUsb.ReadPipe(m_usbHandle[1],
			m_bulkInPipe,
			m_fixedBuffer[m_readIdx],
			(ULONG)m_fixedBufferSize,
			0,
			&m_overlapped[m_readIdx]);	//lint !e534
		m_readIdx = (m_readIdx + 1) % m_oCount;
		int64_t tBegin = XsTime_timeStampNow(0);
		DWORD waitResult = ::WaitForMultipleObjects(1+m_oCount, handles, FALSE, INFINITE);
#if 0	// not sure if this causes problems, but it should help in catching up
		int64_t tEnd = XsTime_timeStampNow(0);
		switch (tEnd - tBegin)
		{
		case 0:
			if (++fastCount > m_fastPolicyThreshold && !policyFast)
			{
				policyFast = true;
				// set fast policy
				UCHAR enable = TRUE;
				m_winUsb.SetPipePolicy(m_usbHandle[1], m_bulkInPipe, IGNORE_SHORT_PACKETS, sizeof(UCHAR), &enable);	//lint !e534
			}
			break;

		case 1:
			if (fastCount)
				--fastCount;
			if (policyFast && fastCount <= m_fastPolicyThreshold)
			{
				// reset policy
				policyFast = false;
				UCHAR enable = FALSE;
				m_winUsb.SetPipePolicy(m_usbHandle[1], m_bulkInPipe, IGNORE_SHORT_PACKETS, sizeof(UCHAR), &enable);	//lint !e534
			}
			break;

		default:
			fastCount = 0;
			if (policyFast)
			{
				// reset policy
				policyFast = false;
				UCHAR enable = FALSE;
				m_winUsb.SetPipePolicy(m_usbHandle[1], m_bulkInPipe, IGNORE_SHORT_PACKETS, sizeof(UCHAR), &enable);	//lint !e534
			}
			break;
		}
#endif

		// handle data
		switch (waitResult)
		{
		case WAIT_TIMEOUT:
		case WAIT_FAILED:
		case WAIT_OBJECT_0:
			run = false;
			break;

		default:
			if (waitResult >= WAIT_ABANDONED_0)
			{
				JLDEBUG(gJournal, "WFMO abandoned: " << (waitResult - WAIT_OBJECT_0));
				break;
			}

#ifndef XSENS_RELEASE
			JLDEBUG(gJournal, "WFMO trigger: " << (waitResult - WAIT_OBJECT_0));
#endif
			{
				// put data into buffer
				int idx = m_readIdx;
				DWORD dataRead = 0;
				if (!m_winUsb.GetOverlappedResult(m_usbHandle[0], &m_overlapped[idx], &dataRead, FALSE))
				{
					// error
					DWORD err = ::GetLastError();
					switch (err)
					{
					case ERROR_SEM_TIMEOUT:
					case ERROR_IO_INCOMPLETE:
						//JLDEBUG(gJournal, "m_winUsb.GetOverlappedResult resulted in acceptable windows error " << err);
						break;

					default:
						JLALERT(gJournal, "m_winUsb.GetOverlappedResult resulted in windows error " << err);
						run = false;
						break;
					}
					//assert (err == ERROR_IO_INCOMPLETE);
				}
				else
				{
					// append unread data to var buffer
					JLTRACE(gJournal, "m_winUsb.GetOverlappedResult resulted in " << dataRead << " bytes being read");
					XsByteArray ref(&m_fixedBuffer[idx][0], dataRead, XSDF_None);
					::EnterCriticalSection(&m_mutex);
					m_varBuffer.append(ref);
					::LeaveCriticalSection(&m_mutex);
				}
			} break;
		}
	}
}
Esempio n. 6
0
/*! \brief Read data from the serial port and put it into the data buffer.
	\details This function reads up to \a maxLength bytes from the port (non-blocking) and
	puts it into the \a data buffer.
	\param maxLength The maximum amount of data read.
	\param data The buffer that will store the received data.
	\returns XRV_OK if no error occurred. It can be that no data is available and XRV_OK will be
			returned. Check data.size() for the number of bytes that were read.
*/
XsResultValue SerialInterface::readData(XsSize maxLength, XsByteArray& data)
{
	if (!isOpen())
		return (m_lastResult = XRV_NOPORTOPEN);

#ifdef _WIN32
	DWORD length;
	data.setSize(maxLength);
	BOOL rres = ::ReadFile(m_handle, data.data(), (DWORD) maxLength, &length, NULL);
	data.pop_back(maxLength-length);
	JLTRACE(gJournal, "ReadFile result " << rres << ", length " << length);

	if (!rres)
	{
		JLALERT(gJournal, "ReadFile returned windows error " << ::GetLastError());
		return (m_lastResult = XRV_ERROR);
	}

	if (length == 0)
		return (m_lastResult = XRV_TIMEOUT);
#else
	fd_set fd;
	fd_set err;
	timeval timeout;
	FD_ZERO(&fd);
	FD_ZERO(&err);
	FD_SET(m_handle, &fd);
	FD_SET(m_handle, &err);

	timeout.tv_sec = m_timeout/1000;
	timeout.tv_usec = (m_timeout - (timeout.tv_sec * 1000)) * 1000;

	int res = select(FD_SETSIZE, &fd, NULL, &err, &timeout);
	if (res < 0 || FD_ISSET(m_handle, &err))
	{
		data.clear();
		return (m_lastResult = XRV_ERROR);
	} else if (res == 0) {
		data.clear();
		return (m_lastResult = XRV_TIMEOUT);
	}

	data.setSize(maxLength);
	int length = read(m_handle, (void*)data.data(), maxLength);
	data.pop_back(maxLength - length);
//	if (m_callbackHandler != NULL && *length > 0) {
//		XsBinary bytes;
//		bytes.setPortNumber(m_port);
//		bytes.setData(data, *length);
//#ifdef LOG_CALLBACKS
//		JLDEBUG(gJournal, "XsensDeviceAPI", "C1: onBytesReceived(%d,(%d,%d),%p)\n",(int32_t) m_onBytesReceivedInstance, (int32_t) bytes->m_size, (int32_t) bytes->m_portNr, m_onBytesReceivedParam);
//#endif
////		m_callbackHandler->onBytesReceived(bytes);
//	}
#endif

#ifdef LOG_RX_TX
	if (length > 0)
	{
		if (rx_log == NULL)
		{
			char fname[XS_MAX_FILENAME_LENGTH];
#ifdef _WIN32
			sprintf(fname, "rx_%03d_%d.log", (int32_t) m_port, m_baudrate);
#else
			char *devname = strrchr(m_portname, '/');
			sprintf(fname, "rx_%s_%d.log", devname + 1, XsBaud::rateToNumeric(m_baudrate));
#endif
			rx_log = fopen(fname, "wb");
		}
		fwrite(data.data(), 1, length, rx_log);
		fflush(rx_log);
	}
#endif

	JLTRACE(gJournal, "returned success, read " << length << " of " << maxLength << " bytes, first: " << JLHEXLOG(data[0]));
	return (m_lastResult = XRV_OK);
}
Esempio n. 7
0
/*! \brief Read data from the serial port and put it into the data buffer.
	\details This function reads up to \a maxLength bytes from the port (non-blocking) and
	puts it into the \a data buffer.
	\param maxLength The maximum amount of data read.
	\param data The buffer that will store the received data.
	\returns XRV_OK if no error occurred. It can be that no data is available and XRV_OK will be
			returned. Check data.size() for the number of bytes that were read.
*/
XsResultValue SerialInterface::readData(XsSize maxLength, XsByteArray& data)
{
	if (!isOpen())
		return (m_lastResult = XRV_NOPORTOPEN);

#ifdef _WIN32
	DWORD length;
	data.setSize(maxLength);
	BOOL rres = ::ReadFile(m_handle, data.data(), (DWORD) maxLength, &length, NULL);
	data.pop_back(maxLength-length);
	JLTRACE(gJournal, "ReadFile result " << rres << ", length " << length);

	if (!rres)
	{
		DWORD wErr = ::GetLastError();
		JLALERT(gJournal, "ReadFile returned windows error " << wErr);
		if (wErr >= ERROR_INVALID_FUNCTION && wErr <= ERROR_INVALID_HANDLE)
			return (m_lastResult = XRV_NOFILEORPORTOPEN);
		return (m_lastResult = XRV_ERROR);
	}

	if (length == 0)
		return (m_lastResult = XRV_TIMEOUT);
#else
	fd_set fd;
	fd_set err;
	timeval timeout;
	FD_ZERO(&fd);
	FD_ZERO(&err);
	FD_SET(m_handle, &fd);
	FD_SET(m_handle, &err);

	timeout.tv_sec = m_timeout/1000;
	timeout.tv_usec = (m_timeout - (timeout.tv_sec * 1000)) * 1000;

	int res = select(FD_SETSIZE, &fd, NULL, &err, &timeout);
	if (res < 0 || FD_ISSET(m_handle, &err))
	{
		data.clear();
		return (m_lastResult = XRV_ERROR);
	} else if (res == 0) {
		data.clear();
		return (m_lastResult = XRV_TIMEOUT);
	}

	data.setSize(maxLength);
	int length = read(m_handle, (void*)data.data(), maxLength);
	data.pop_back(maxLength - length);
#endif

#ifdef LOG_RX_TX
	if (length > 0)
	{
		if (rx_log == NULL)
		{
			char fname[XS_MAX_FILENAME_LENGTH];
#ifdef _WIN32
			sprintf(fname, "rx_%03d_%d.log", (int32_t) m_port, m_baudrate);
#else
			char *devname = strrchr(m_portname, '/');
			sprintf(fname, "rx_%s_%d.log", devname + 1, XsBaud::rateToNumeric(m_baudrate));
#endif
			makeFilenameUnique(fname);
			
			rx_log = fopen(fname, "wb");
		}
		fwrite(data.data(), 1, length, rx_log);
#ifdef LOG_RX_TX_FLUSH
		fflush(rx_log);
#endif
	}
#endif

	JLTRACE(gJournal, "returned success, read " << length << " of " << maxLength << " bytes, first: " << JLHEXLOG(data[0]));
	return (m_lastResult = XRV_OK);
}
/*! \brief Read data from the serial port and put it into the data buffer.
	\details This function reads up to \a maxLength bytes from the port (non-blocking) and
	puts it into the \a data buffer.
	\param maxLength The maximum amount of data read.
	\param data The buffer that will store the received data.
	\returns XRV_OK if no error occurred. It can be that no data is available and XRV_OK will be
			returned. Check data.size() for the number of bytes that were read.
*/
XsResultValue SerialInterface::readData(XsSize maxLength, XsByteArray& data)
{
	if (!isOpen())
		return (m_lastResult = XRV_NOPORTOPEN);

#ifdef _WIN32
	DWORD length;
	data.setSize(maxLength);
	BOOL rres = ::ReadFile(m_handle, data.data(), (DWORD) maxLength, &length, NULL);
	data.pop_back(maxLength-length);
	JLTRACE(gJournal, "ReadFile result " << rres << ", length " << length);

	if (!rres)
	{
		DWORD wErr = ::GetLastError();
		JLALERT(gJournal, "ReadFile returned windows error " << wErr);
		if (wErr == ERROR_ACCESS_DENIED)
			return (m_lastResult = XRV_UNEXPECTED_DISCONNECT);
		if (wErr >= ERROR_INVALID_FUNCTION && wErr <= ERROR_INVALID_HANDLE)
			return (m_lastResult = XRV_NOFILEORPORTOPEN);
		return (m_lastResult = XRV_ERROR);
	}

	if (length == 0)
		return (m_lastResult = XRV_TIMEOUT);
#else
	fd_set fd;
	fd_set err;
	timeval timeout;
	FD_ZERO(&fd);
	FD_ZERO(&err);
	FD_SET(m_handle, &fd);
	FD_SET(m_handle, &err);

	timeout.tv_sec = m_timeout/1000;
	timeout.tv_usec = (m_timeout - (timeout.tv_sec * 1000)) * 1000;

	int res = select(FD_SETSIZE, &fd, NULL, &err, &timeout);
	if (res < 0 || FD_ISSET(m_handle, &err))
	{
		data.clear();
		return (m_lastResult = XRV_ERROR);
	} else if (res == 0) {
		data.clear();
		return (m_lastResult = XRV_TIMEOUT);
	}

	data.setSize(maxLength);
	int length = read(m_handle, (void*)data.data(), maxLength);
	if (length > 0)
	{
		data.pop_back(maxLength - length);
	}
	else
	{
		int err = errno;

		data.clear();
		switch (err)
		{
		case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
		case EWOULDBLOCK:
#endif
			return XRV_TIMEOUT;

		case EIO:
			return XRV_UNEXPECTED_DISCONNECT;

		default:
			break;
		}
	}
#if defined(JLLOGLEVEL) && JLLOGLEVEL <= JLL_TRACE && !defined(ANDROID)
	serial_icounter_struct ic;
	res = ioctl(m_handle, TIOCGICOUNT, &ic);
	if (res == 0)
	{
		JLTRACE(gJournal, "rx: " << ic.rx);
		JLTRACE(gJournal, "tx: " << ic.tx);
		JLTRACE(gJournal, "frame " << ic.frame);
		JLTRACE(gJournal, "overrun " << ic.overrun);
		JLTRACE(gJournal, "buf_overrun " << ic.buf_overrun);
	}
#endif
#endif

#ifdef LOG_RX_TX
	if (length > 0)
	{
		if (!rx_log.isOpen())
		{
			char fname[XS_MAX_FILENAME_LENGTH];
#ifdef _WIN32
			sprintf(fname, "rx_%03d_%d.log", (int32_t) m_port, m_baudrate);
#else
			char *devname = strrchr(m_portname, '/');
			sprintf(fname, "rx_%s_%d.log", devname + 1, XsBaud::rateToNumeric(m_baudrate));
#endif
			makeFilenameUnique(fname);

			rx_log.create(XsString(fname), true);
		}
		rx_log.write(data.data(), 1, length);
#ifdef LOG_RX_TX_FLUSH
		rx_log.flush();
#endif
	}
#endif

	JLTRACE(gJournal, "returned success, read " << length << " of " << maxLength << " bytes, first: " << JLHEXLOG(data[0]));
	return (m_lastResult = XRV_OK);
}
/*! \brief Open a communication channel to the given port info.
	\details If the baudrate in \a portInfo is set to XBR_Invalid, the baud rate is automatically
				detected if possible.
	\param portInfo The details of the port that should be opened. Depending on the type of interface,
				parts of this parameter may be ignored.
	\param readBufSize The size of the read buffer in bytes (if appliccable to the device)
	\param writeBufSize The size of the write buffer in bytes (if appliccable to the device)
	\param options The options to enable (flow control, stop bits)
	\returns XRV_OK if the device was opened successfully
*/
XsResultValue SerialInterface::open(const XsPortInfo& portInfo,
						uint32_t readBufSize,
						uint32_t writeBufSize,
						PortOptions options)
{
	m_endTime = 0;

	JLDEBUG(gJournal, portInfo);

	if (isOpen())
	{
		JLALERT(gJournal, "Port " << portInfo.portName() << " is already open");
		return (m_lastResult = XRV_ALREADYOPEN);
	}
	m_baudrate = portInfo.baudrate();

	if (options&PO_RtsCtsFlowControl)
		JLTRACE(gJournal, "Requested RTS/CTS flow control");
	if (options&PO_DtrDsrFlowControl)
		JLTRACE(gJournal, "Requested DTR/DSR flow control");
	if (options&PO_XonXoffFlowControl)
		JLTRACE(gJournal, "Requested Xon/Xoff flow control");

#ifdef _WIN32
	XsResultValue fail = XRV_OK;
	char winPortName[256];

	// Open port
	sprintf(winPortName, "\\\\.\\%s", portInfo.portName().c_str());
	m_handle = CreateFileA(winPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
									OPEN_EXISTING, 0, NULL);
	if (m_handle == INVALID_HANDLE_VALUE)
	{
		JLDEBUG(gJournal, "Port " << portInfo.portName() << " cannot be opened");
		return (m_lastResult = XRV_INPUTCANNOTBEOPENED);
	}

	DCB commState;		//!< Stored settings about the serial port

	commState.DCBlength = sizeof(DCB);

	//Get the current state & then change it
	if (!GetCommState(m_handle, &commState))	// Get current state
		fail = XRV_ERROR;

	commState.BaudRate = (int) portInfo.baudrate();		// Setup the baud rate
	commState.Parity = NOPARITY;				// Setup the Parity
	commState.ByteSize = 8;					// Setup the data bits
	commState.StopBits = (options&PO_TwoStopBits)?TWOSTOPBITS:ONESTOPBIT;

	// Setup flow control
	commState.fDsrSensitivity = (options&PO_DtrDsrFlowControl)?TRUE:FALSE;
	commState.fOutxDsrFlow = (options&PO_DtrDsrFlowControl)?DTR_CONTROL_HANDSHAKE:DTR_CONTROL_DISABLE;

	commState.fOutxCtsFlow = (options&PO_RtsCtsFlowControl)?TRUE:FALSE;
	commState.fRtsControl = (options&PO_RtsCtsFlowControl)?RTS_CONTROL_HANDSHAKE:RTS_CONTROL_ENABLE;

	commState.fOutX = (options&PO_XonXoffFlowControl)?TRUE:FALSE;
	commState.fInX = commState.fOutX;

	if (!SetCommState(m_handle, (LPDCB)&commState)) // Set new state
	{
		// Bluetooth ports cannot always be opened with 2 stopbits
		// Now try to open port with 1 stopbit.
		commState.StopBits = ONESTOPBIT;
		if (!SetCommState(m_handle, (LPDCB)&commState))
			fail = XRV_INPUTCANNOTBEOPENED;
	}
	std::string tmp = portInfo.portName().toStdString();
	m_port = atoi(&tmp.c_str()[3]);
	sprintf(m_portname, "%s", tmp.c_str());

	if (setTimeout(20))
		fail = m_lastResult;

	// Other initialization functions
	if ((options&PO_DtrDsrFlowControl) == 0)
	{
		if (!EscapeCommFunction(m_handle, SETDTR))			// Set DTR (Calibration sensors need DTR to startup, won't hurt otherwise
			fail = XRV_ERROR;
	}
	if (!SetupComm(m_handle,readBufSize,writeBufSize))	// Set queue size
		fail = XRV_ERROR;

	// Remove any 'old' data in buffer
	//PurgeComm(m_handle, PURGE_TXCLEAR | PURGE_RXCLEAR);
	if (!PurgeComm(m_handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR))
		fail = XRV_ERROR;

	if (fail != XRV_OK)
	{
		CloseHandle(m_handle);		//lint !e534
		m_handle = INVALID_HANDLE_VALUE;
		return (m_lastResult = fail);
	}

#else // !_WIN32
	(void)readBufSize;
	(void)writeBufSize;
	// Open port
	std::string pn = portInfo.portName().toStdString();
	m_handle = ::open(pn.c_str(), O_RDWR | O_NOCTTY);

	// O_RDWR: Read+Write
	// O_NOCTTY: Raw input, no "controlling terminal"
	// O_NDELAY: Don't care about DCD signal

	if (m_handle < 0) {
		// Port not open
		return m_lastResult = XRV_INPUTCANNOTBEOPENED;
	}

	// Check if the file is already opened by someome else (other thread/process)
	if (flock(m_handle, LOCK_EX | LOCK_NB))
	{
		closeLive();
		return m_lastResult = XRV_INPUTCANNOTBEOPENED;
	}

	/* Start configuring of port for non-canonical transfer mode */
	// Get current options for the port
	if (tcgetattr(m_handle, &m_commState) != 0)
		return XRV_ERROR;

	// Set baudrate.
	if (cfsetispeed(&m_commState, portInfo.baudrate()) != 0)
		return XRV_ERROR;

	if (cfsetospeed(&m_commState, portInfo.baudrate()) != 0)
		return XRV_ERROR;

	// Enable the receiver and set local mode
	m_commState.c_cflag |= (CLOCAL | CREAD);
	// Set character size to data bits and set no parity Mask the characte size bits
	m_commState.c_cflag &= ~(CSIZE|PARENB|PARODD);
	m_commState.c_cflag |= CS8;		// Select 8 data bits

	m_commState.c_cflag = setBitsEnabled(m_commState.c_cflag, (tcflag_t)CSTOPB, (options&PO_TwoStopBits) == PO_TwoStopBits);

	// Hardware flow control
	m_commState.c_cflag = setBitsEnabled(m_commState.c_cflag, (tcflag_t)CRTSCTS, (options&PO_RtsCtsFlowControl) == PO_RtsCtsFlowControl);
#ifdef CDTRDSR
	m_commState.c_cflag = setBitsEnabled(m_commState.c_cflag, (tcflag_t)CDTRDSR, (options&PO_DtrDsrFlowControl) == PO_DtrDsrFlowControl);
#endif

	m_commState.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
	// Software flow control
	m_commState.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL);
	m_commState.c_iflag = setBitsEnabled(m_commState.c_iflag, (tcflag_t)(IXON|IXOFF), options&PO_XonXoffFlowControl);
	// Set Raw output
	m_commState.c_oflag &= ~OPOST;
	// Timeout 0.001 sec for first byte, read minimum of 0 bytes
	m_commState.c_cc[VMIN]     = 0;
	m_commState.c_cc[VTIME]    = (m_timeout+99)/100;	// 1

	// Set the new options for the port
	if (tcsetattr(m_handle,TCSANOW, &m_commState) != 0)
		return XRV_INPUTCANNOTBEOPENED;

#if defined(JLLOGLEVEL) && JLLOGLEVEL <= JLL_ALERT
	termios checkCommState;
	if (tcgetattr(m_handle, &checkCommState) != 0)
		return XRV_ERROR;

	if (cfgetispeed(&checkCommState) != portInfo.baudrate())
		JLALERT(gJournal, "Set baudrate doesn't match requested baudrate");

	if (cfgetospeed(&checkCommState) != portInfo.baudrate())
		JLALERT(gJournal, "Set baudrate doesn't match requested baudrate");

	if (options&PO_RtsCtsFlowControl && !(checkCommState.c_cflag&CRTSCTS))
		JLALERT(gJournal, "Requested RTS/CTS flow control, but could not be set.");

	if (options&PO_DtrDsrFlowControl &&
#ifdef CDTRDSR
		!(checkCommState.c_cflag&CDTRDSR)
#else
		false
#endif
		)
		JLALERT(gJournal, "Requested DTR/DSR flow control, but could not be set.");

	if (options&PO_XonXoffFlowControl && !((checkCommState.c_iflag&(IXON|IXOFF)) == (IXON|IXOFF)))
		JLALERT(gJournal, "Requested Xon/Xoff flow control, but could not be set.");
#endif // JLLOGLEVEL < JLL_ALERT

#if defined(JLLOGLEVEL) && JLLOGLEVEL <= JLL_DEBUG
#define CHECK_COMMSTATE(req, res, field)\
	if (req.field != res.field) \
	{\
		JLDEBUG(gJournal, "field " << #field << " does not match");\
		JLDEBUG(gJournal, "actual  : " << std::oct << (uint64_t)res.field);\
		JLDEBUG(gJournal, "expected: " << std::oct << (uint64_t)req.field);\
	}
#else
#define CHECK_COMMSTATE(req, res, field)
#endif
	CHECK_COMMSTATE(m_commState, checkCommState, c_cflag);
	CHECK_COMMSTATE(m_commState, checkCommState, c_iflag);
	CHECK_COMMSTATE(m_commState, checkCommState, c_oflag);
	CHECK_COMMSTATE(m_commState, checkCommState, c_cc[VMIN]);
	CHECK_COMMSTATE(m_commState, checkCommState, c_cc[VTIME]);

	m_port = 1;
	sprintf(m_portname, "%s", pn.c_str());

	tcflush(m_handle, TCIOFLUSH);

	// setting RTS and DTR; RTS for Xbus Master, DTR for calibration sensors
	int cmbits;
	if (ioctl(m_handle, TIOCMGET, &cmbits) < 0)
	{
		JLDEBUG(gJournal, "TIOCMGET failed, which is OK for USB connected MkIV devices");
	}

	if ((options&PO_RtsCtsFlowControl) == 0)
		cmbits = setBitsEnabled(cmbits, TIOCM_RTS, true);
	// else don't touch them

	cmbits = setBitsEnabled(cmbits, TIOCM_DTR, !(options&PO_DtrDsrFlowControl));

	if (ioctl(m_handle, TIOCMSET, &cmbits) < 0)
	{
		JLDEBUG(gJournal, "TIOCMSET failed, which is OK for USB connected MkIV devices");
	}
#endif // !_WIN32

	JLDEBUG(gJournal, "Port " << portInfo.portName().toStdString() << " opened");
	return (m_lastResult = XRV_OK);
}