Blame winpr/libwinpr/comm/comm_serial_sys.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * WinPR: Windows Portable Runtime
Packit 1fb8d4
 * Serial Communication API
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2011 O.S. Systems Software Ltda.
Packit 1fb8d4
 * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
Packit 1fb8d4
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit 1fb8d4
 * Copyright 2014 Hewlett-Packard Development Company, L.P.
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#if defined __linux__ && !defined ANDROID
Packit 1fb8d4
Packit 1fb8d4
#include <assert.h>
Packit 1fb8d4
#include <errno.h>
Packit 1fb8d4
#include <fcntl.h>
Packit 1fb8d4
#include <sys/ioctl.h>
Packit 1fb8d4
#include <termios.h>
Packit 1fb8d4
#include <unistd.h>
Packit 1fb8d4
Packit 1fb8d4
#include "comm_serial_sys.h"
Packit 1fb8d4
#ifdef __UCLIBC__
Packit 1fb8d4
#include "comm.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/wlog.h>
Packit 1fb8d4
Packit 1fb8d4
/* Undocumented flag, not supported everywhere.
Packit 1fb8d4
 * Provide a sensible fallback to avoid compilation problems. */
Packit 1fb8d4
#ifndef CMSPAR
Packit 1fb8d4
#define CMSPAR 010000000000
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
/* hard-coded in N_TTY */
Packit Service 5a9772
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
Packit Service 5a9772
#define TTY_THRESHOLD_UNTHROTTLE 128
Packit Service 5a9772
#define N_TTY_BUF_SIZE 4096
Packit 1fb8d4
Packit Service 5a9772
#define _BAUD_TABLE_END 0010020 /* __MAX_BAUD + 1 */
Packit 1fb8d4
Packit 1fb8d4
/* 0: B* (Linux termios)
Packit 1fb8d4
 * 1: CBR_* or actual baud rate
Packit 1fb8d4
 * 2: BAUD_* (identical to SERIAL_BAUD_*)
Packit 1fb8d4
 */
Packit 1fb8d4
static const speed_t _BAUD_TABLE[][3] = {
Packit 1fb8d4
#ifdef B0
Packit Service 5a9772
	{ B0, 0, 0 }, /* hang up */
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B50
Packit Service 5a9772
	{ B50, 50, 0 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B75
Packit Service 5a9772
	{ B75, 75, BAUD_075 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B110
Packit Service 5a9772
	{ B110, CBR_110, BAUD_110 },
Packit 1fb8d4
#endif
Packit Service 5a9772
#ifdef B134
Packit Service 5a9772
	{ B134, 134, 0 /*BAUD_134_5*/ },
Packit 1fb8d4
#endif
Packit Service 5a9772
#ifdef B150
Packit Service 5a9772
	{ B150, 150, BAUD_150 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B200
Packit Service 5a9772
	{ B200, 200, 0 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B300
Packit Service 5a9772
	{ B300, CBR_300, BAUD_300 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B600
Packit Service 5a9772
	{ B600, CBR_600, BAUD_600 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B1200
Packit Service 5a9772
	{ B1200, CBR_1200, BAUD_1200 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B1800
Packit Service 5a9772
	{ B1800, 1800, BAUD_1800 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B2400
Packit Service 5a9772
	{ B2400, CBR_2400, BAUD_2400 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B4800
Packit Service 5a9772
	{ B4800, CBR_4800, BAUD_4800 },
Packit 1fb8d4
#endif
Packit Service 5a9772
/* {, ,BAUD_7200} */
Packit 1fb8d4
#ifdef B9600
Packit Service 5a9772
	{ B9600, CBR_9600, BAUD_9600 },
Packit 1fb8d4
#endif
Packit Service 5a9772
/* {, CBR_14400, BAUD_14400},	/\* unsupported on Linux *\/ */
Packit 1fb8d4
#ifdef B19200
Packit Service 5a9772
	{ B19200, CBR_19200, BAUD_19200 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B38400
Packit Service 5a9772
	{ B38400, CBR_38400, BAUD_38400 },
Packit 1fb8d4
#endif
Packit Service 5a9772
/* {, CBR_56000, BAUD_56K},	/\* unsupported on Linux *\/ */
Packit Service 5a9772
#ifdef B57600
Packit Service 5a9772
	{ B57600, CBR_57600, BAUD_57600 },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B115200
Packit Service 5a9772
	{ B115200, CBR_115200, BAUD_115200 },
Packit 1fb8d4
#endif
Packit Service 5a9772
/* {, CBR_128000, BAUD_128K},	/\* unsupported on Linux *\/ */
Packit Service 5a9772
/* {, CBR_256000, BAUD_USER},	/\* unsupported on Linux *\/ */
Packit 1fb8d4
#ifdef B230400
Packit Service 5a9772
	{ B230400, 230400, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B460800
Packit Service 5a9772
	{ B460800, 460800, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B500000
Packit Service 5a9772
	{ B500000, 500000, BAUD_USER },
Packit 1fb8d4
#endif
Packit Service 5a9772
#ifdef B576000
Packit Service 5a9772
	{ B576000, 576000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B921600
Packit Service 5a9772
	{ B921600, 921600, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B1000000
Packit Service 5a9772
	{ B1000000, 1000000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B1152000
Packit Service 5a9772
	{ B1152000, 1152000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B1500000
Packit Service 5a9772
	{ B1500000, 1500000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B2000000
Packit Service 5a9772
	{ B2000000, 2000000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B2500000
Packit Service 5a9772
	{ B2500000, 2500000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B3000000
Packit Service 5a9772
	{ B3000000, 3000000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B3500000
Packit Service 5a9772
	{ B3500000, 3500000, BAUD_USER },
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef B4000000
Packit Service 5a9772
	{ B4000000, 4000000, BAUD_USER }, /* __MAX_BAUD */
Packit 1fb8d4
#endif
Packit Service 5a9772
	{ _BAUD_TABLE_END, 0, 0 }
Packit 1fb8d4
};
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_properties(WINPR_COMM* pComm, COMMPROP* pProperties)
Packit 1fb8d4
{
Packit 1fb8d4
	int i;
Packit 1fb8d4
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx
Packit 1fb8d4
	 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: properties should be better probed. The current
Packit 1fb8d4
	 * implementation just relies on the Linux' implementation.
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
Packit 1fb8d4
	{
Packit 1fb8d4
		ZeroMemory(pProperties, sizeof(COMMPROP));
Packit 1fb8d4
		pProperties->wPacketLength = sizeof(COMMPROP);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pProperties->wPacketVersion = 2;
Packit 1fb8d4
Packit 1fb8d4
	pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
Packit 1fb8d4
Packit 1fb8d4
	/* pProperties->Reserved1; not used */
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: could be implemented on top of N_TTY */
Packit 1fb8d4
	pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE;
Packit 1fb8d4
	pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE;
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: to be probe on the device? */
Packit Service 5a9772
	pProperties->dwMaxBaud = BAUD_USER;
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: what about PST_RS232? see also: serial_struct */
Packit 1fb8d4
	pProperties->dwProvSubType = PST_UNSPECIFIED;
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: to be finalized */
Packit 1fb8d4
	pProperties->dwProvCapabilities =
Packit Service 5a9772
	    /*PCF_16BITMODE |*/ PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK | /*PCF_RLSD |*/
Packit Service 5a9772
	    PCF_RTSCTS | PCF_SETXCHAR | /*PCF_SPECIALCHARS |*/ PCF_TOTALTIMEOUTS | PCF_XONXOFF;
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: double check SP_RLSD */
Packit Service 5a9772
	pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
Packit Service 5a9772
	                                SP_PARITY_CHECK | /*SP_RLSD |*/ SP_STOPBITS;
Packit 1fb8d4
Packit 1fb8d4
	pProperties->dwSettableBaud = 0;
Packit Service 5a9772
	for (i = 0; _BAUD_TABLE[i][0] < _BAUD_TABLE_END; i++)
Packit 1fb8d4
	{
Packit 1fb8d4
		pProperties->dwSettableBaud |= _BAUD_TABLE[i][2];
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	pProperties->wSettableData =
Packit Service 5a9772
	    DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 /*| DATABITS_16 | DATABITS_16X*/;
Packit 1fb8d4
Packit Service 5a9772
	pProperties->wSettableStopParity = STOPBITS_10 | /*STOPBITS_15 |*/ STOPBITS_20 | PARITY_NONE |
Packit Service 5a9772
	                                   PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: additional input and output buffers could be implemented on top of N_TTY */
Packit 1fb8d4
	pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE;
Packit 1fb8d4
	pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE;
Packit 1fb8d4
Packit 1fb8d4
	/* pProperties->ProvSpec1; see above */
Packit 1fb8d4
	/* pProperties->ProvSpec2; ignored */
Packit 1fb8d4
	/* pProperties->ProvChar[1]; ignored */
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
Packit 1fb8d4
{
Packit 1fb8d4
	int i;
Packit 1fb8d4
	speed_t newSpeed;
Packit 1fb8d4
	struct termios futureState;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&futureState, sizeof(struct termios));
Packit Service 5a9772
	if (tcgetattr(pComm->fd, &futureState) <
Packit Service 5a9772
	    0) /* NB: preserves current settings not directly handled by the Communication Functions */
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	for (i = 0; _BAUD_TABLE[i][0] < _BAUD_TABLE_END; i++)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (_BAUD_TABLE[i][1] == pBaudRate->BaudRate)
Packit 1fb8d4
		{
Packit 1fb8d4
			newSpeed = _BAUD_TABLE[i][0];
Packit 1fb8d4
			if (cfsetspeed(&futureState, newSpeed) < 0)
Packit 1fb8d4
			{
Packit Service 5a9772
				CommLog_Print(WLOG_WARN, "failed to set speed 0x%x (%" PRIu32 ")", newSpeed,
Packit Service 5a9772
				              pBaudRate->BaudRate);
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			assert(cfgetispeed(&futureState) == newSpeed);
Packit 1fb8d4
Packit 1fb8d4
			if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
Packit 1fb8d4
			{
Packit Service 5a9772
				CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
Packit Service 5a9772
				              GetLastError());
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %" PRIu32 "",
Packit Service 5a9772
	              pBaudRate->BaudRate);
Packit 1fb8d4
	SetLastError(ERROR_INVALID_DATA);
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
Packit 1fb8d4
{
Packit 1fb8d4
	int i;
Packit 1fb8d4
	speed_t currentSpeed;
Packit 1fb8d4
	struct termios currentState;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&currentState, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &currentState) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	currentSpeed = cfgetispeed(&currentState);
Packit 1fb8d4
Packit Service 5a9772
	for (i = 0; _BAUD_TABLE[i][0] < _BAUD_TABLE_END; i++)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (_BAUD_TABLE[i][0] == currentSpeed)
Packit 1fb8d4
		{
Packit 1fb8d4
			pBaudRate->BaudRate = _BAUD_TABLE[i][1];
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	CommLog_Print(WLOG_WARN, "could not find a matching baud rate for the speed 0x%x",
Packit Service 5a9772
	              currentSpeed);
Packit 1fb8d4
	SetLastError(ERROR_INVALID_DATA);
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * NOTE: Only XonChar and XoffChar are plenty supported with the Linux
Packit 1fb8d4
 *       N_TTY line discipline.
Packit 1fb8d4
 *
Packit 1fb8d4
 * ERRORS:
Packit 1fb8d4
 *   ERROR_IO_DEVICE
Packit 1fb8d4
 *   ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same;
Packit 1fb8d4
 *   ERROR_NOT_SUPPORTED
Packit 1fb8d4
 */
Packit Service 5a9772
static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
Packit 1fb8d4
{
Packit 1fb8d4
	BOOL result = TRUE;
Packit 1fb8d4
	struct termios upcomingTermios;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&upcomingTermios, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pSerialChars->XonChar == pSerialChars->XoffChar)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* https://msdn.microsoft.com/en-us/library/windows/hardware/ff546688?v=vs.85.aspx */
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* termios(3): (..) above symbolic subscript values are all
Packit Service 5a9772
	 * different, except that VTIME, VMIN may have the same value
Packit Service 5a9772
	 * as VEOL, VEOF, respectively. In noncanonical mode the
Packit Service 5a9772
	 * special character meaning is replaced by the timeout
Packit Service 5a9772
	 * meaning.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * EofChar and c_cc[VEOF] are not quite the same, prefer to
Packit 1fb8d4
	 * don't use c_cc[VEOF] at all.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * FIXME: might be implemented during read/write I/O
Packit 1fb8d4
	 */
Packit 1fb8d4
	if (pSerialChars->EofChar != '\0')
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "EofChar %02" PRIX8 " cannot be set\n", pSerialChars->EofChar);
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* According the Linux's n_tty discipline, charaters with a
Packit 1fb8d4
	 * parity error can only be let unchanged, replaced by \0 or
Packit 1fb8d4
	 * get the prefix the prefix \377 \0
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: see also: _set_handflow() */
Packit 1fb8d4
	if (pSerialChars->ErrorChar != '\0')
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "ErrorChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
Packit Service 5a9772
		              pSerialChars->ErrorChar, (char)pSerialChars->ErrorChar);
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: see also: _set_handflow() */
Packit 1fb8d4
	if (pSerialChars->BreakChar != '\0')
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "BreakChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
Packit Service 5a9772
		              pSerialChars->BreakChar, (char)pSerialChars->BreakChar);
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pSerialChars->EventChar != '\0')
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->eventChar = pSerialChars->EventChar;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
Packit 1fb8d4
Packit 1fb8d4
	upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
Packit 1fb8d4
Packit 1fb8d4
	if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
Packit Service 5a9772
		              GetLastError());
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
Packit 1fb8d4
{
Packit 1fb8d4
	struct termios currentTermios;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&currentTermios, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &currentTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
Packit 1fb8d4
Packit 1fb8d4
	/* EofChar unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* ErrorChar unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* BreakChar unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: see also: _set_serial_chars() */
Packit 1fb8d4
	/* EventChar */
Packit 1fb8d4
Packit 1fb8d4
	pSerialChars->XonChar = currentTermios.c_cc[VSTART];
Packit 1fb8d4
Packit 1fb8d4
	pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl)
Packit 1fb8d4
{
Packit 1fb8d4
	BOOL result = TRUE;
Packit 1fb8d4
	struct termios upcomingTermios;
Packit 1fb8d4
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * The use of 5 data bits with 2 stop bits is an invalid
Packit 1fb8d4
	 * combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * FIXME: prefered to let the underlying driver to deal with
Packit 1fb8d4
	 * this issue. At least produce a warning message?
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&upcomingTermios, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: use of a COMMPROP to validate new settings? */
Packit 1fb8d4
Packit 1fb8d4
	switch (pLineControl->StopBits)
Packit 1fb8d4
	{
Packit 1fb8d4
		case STOP_BIT_1:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~CSTOPB;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case STOP_BITS_1_5:
Packit 1fb8d4
			CommLog_Print(WLOG_WARN, "Unsupported one and a half stop bits.");
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case STOP_BITS_2:
Packit 1fb8d4
			upcomingTermios.c_cflag |= CSTOPB;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "unexpected number of stop bits: %" PRIu8 "\n",
Packit Service 5a9772
			              pLineControl->StopBits);
Packit 1fb8d4
			result = FALSE; /* but keep on */
Packit 1fb8d4
			break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	switch (pLineControl->Parity)
Packit 1fb8d4
	{
Packit 1fb8d4
		case NO_PARITY:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case ODD_PARITY:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~CMSPAR;
Packit 1fb8d4
			upcomingTermios.c_cflag |= PARENB | PARODD;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case EVEN_PARITY:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~(PARODD | CMSPAR);
Packit 1fb8d4
			upcomingTermios.c_cflag |= PARENB;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case MARK_PARITY:
Packit 1fb8d4
			upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case SPACE_PARITY:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~PARODD;
Packit 1fb8d4
			upcomingTermios.c_cflag |= PARENB | CMSPAR;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "unexpected type of parity: %" PRIu8 "\n",
Packit Service 5a9772
			              pLineControl->Parity);
Packit 1fb8d4
			result = FALSE; /* but keep on */
Packit 1fb8d4
			break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	switch (pLineControl->WordLength)
Packit 1fb8d4
	{
Packit 1fb8d4
		case 5:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~CSIZE;
Packit 1fb8d4
			upcomingTermios.c_cflag |= CS5;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case 6:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~CSIZE;
Packit 1fb8d4
			upcomingTermios.c_cflag |= CS6;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case 7:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~CSIZE;
Packit 1fb8d4
			upcomingTermios.c_cflag |= CS7;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case 8:
Packit 1fb8d4
			upcomingTermios.c_cflag &= ~CSIZE;
Packit 1fb8d4
			upcomingTermios.c_cflag |= CS8;
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "unexpected number od data bits per character: %" PRIu8 "\n",
Packit Service 5a9772
			              pLineControl->WordLength);
Packit 1fb8d4
			result = FALSE; /* but keep on */
Packit 1fb8d4
			break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
Packit Service 5a9772
		              GetLastError());
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl)
Packit 1fb8d4
{
Packit 1fb8d4
	struct termios currentTermios;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&currentTermios, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &currentTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
Packit 1fb8d4
Packit 1fb8d4
	if (!(currentTermios.c_cflag & PARENB))
Packit 1fb8d4
	{
Packit 1fb8d4
		pLineControl->Parity = NO_PARITY;
Packit 1fb8d4
	}
Packit 1fb8d4
	else if (currentTermios.c_cflag & CMSPAR)
Packit 1fb8d4
	{
Packit 1fb8d4
		pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		/* PARENB is set */
Packit Service 5a9772
		pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	switch (currentTermios.c_cflag & CSIZE)
Packit 1fb8d4
	{
Packit 1fb8d4
		case CS5:
Packit 1fb8d4
			pLineControl->WordLength = 5;
Packit 1fb8d4
			break;
Packit 1fb8d4
		case CS6:
Packit 1fb8d4
			pLineControl->WordLength = 6;
Packit 1fb8d4
			break;
Packit 1fb8d4
		case CS7:
Packit 1fb8d4
			pLineControl->WordLength = 7;
Packit 1fb8d4
			break;
Packit 1fb8d4
		default:
Packit 1fb8d4
			pLineControl->WordLength = 8;
Packit 1fb8d4
			break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
Packit 1fb8d4
{
Packit 1fb8d4
	BOOL result = TRUE;
Packit 1fb8d4
	struct termios upcomingTermios;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&upcomingTermios, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* HUPCL */
Packit 1fb8d4
Packit 1fb8d4
	/* logical XOR */
Packit Service 5a9772
	if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
Packit Service 5a9772
	     (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
Packit Service 5a9772
	    ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
Packit Service 5a9772
	     !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN,
Packit Service 5a9772
		              "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL "
Packit Service 5a9772
		              "will be set since it is claimed for one of the both lines.",
Packit Service 5a9772
		              (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ? "ON" : "OFF",
Packit Service 5a9772
		              (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ? "ON" : "OFF");
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ||
Packit Service 5a9772
	    (pHandflow->FlowReplace & SERIAL_RTS_CONTROL))
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_cflag |= HUPCL;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_cflag &= ~HUPCL;
Packit 1fb8d4
Packit Service 5a9772
		/* FIXME: is the DTR line also needs to be forced to a disable state according
Packit Service 5a9772
		 * SERIAL_DTR_CONTROL? */
Packit Service 5a9772
		/* FIXME: is the RTS line also needs to be forced to a disable state according
Packit Service 5a9772
		 * SERIAL_RTS_CONTROL? */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* CRTSCTS */
Packit 1fb8d4
Packit 1fb8d4
	/* logical XOR */
Packit Service 5a9772
	if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
Packit Service 5a9772
	     (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) ||
Packit Service 5a9772
	    ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
Packit Service 5a9772
	     !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)))
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN,
Packit Service 5a9772
		              "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, "
Packit Service 5a9772
		              "CRTSCTS will be set since it is claimed for one of the both lines.",
Packit Service 5a9772
		              (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ? "ON" : "OFF",
Packit Service 5a9772
		              (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ? "ON" : "OFF");
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
Packit Service 5a9772
	    (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_cflag |= CRTSCTS;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_cflag &= ~CRTSCTS;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* ControlHandShake */
Packit 1fb8d4
Packit 1fb8d4
	if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* DTR/DSR flow control not supported on Linux */
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* DTR/DSR flow control not supported on Linux */
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* DCD flow control not supported on Linux */
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O
Packit 1fb8d4
	if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* DSR line control not supported on Linux */
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O
Packit 1fb8d4
	if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Aborting operations on error not supported on Linux */
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* FlowReplace */
Packit 1fb8d4
Packit 1fb8d4
	if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag |= IXON;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag &= ~IXON;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag |= IXOFF;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag &= ~IXOFF;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O, as of today ErrorChar is necessary '\0'
Packit 1fb8d4
	if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* errors will be replaced by the character '\0'. */
Packit 1fb8d4
		upcomingTermios.c_iflag &= ~IGNPAR;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag |= IGNPAR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag |= IGNBRK;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		upcomingTermios.c_iflag &= ~IGNBRK;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O
Packit 1fb8d4
	if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O
Packit 1fb8d4
	if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* not supported on Linux */
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* XonLimit */
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O
Packit 1fb8d4
	if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "Attempt to set XonLimit with an unsupported value: %" PRId32 "",
Packit Service 5a9772
		              pHandflow->XonLimit);
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* XoffChar */
Packit 1fb8d4
Packit 1fb8d4
	// FIXME: could be implemented during read/write I/O
Packit 1fb8d4
	if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "Attempt to set XoffLimit with an unsupported value: %" PRId32 "",
Packit Service 5a9772
		              pHandflow->XoffLimit);
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		result = FALSE; /* but keep on */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
Packit Service 5a9772
		              GetLastError());
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
Packit 1fb8d4
{
Packit 1fb8d4
	struct termios currentTermios;
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&currentTermios, sizeof(struct termios));
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &currentTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* ControlHandShake */
Packit 1fb8d4
Packit 1fb8d4
	pHandflow->ControlHandShake = 0;
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_cflag & HUPCL)
Packit 1fb8d4
		pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_DTR_HANDSHAKE unsupported */
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_cflag & CRTSCTS)
Packit 1fb8d4
		pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_DSR_HANDSHAKE unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_DCD_HANDSHAKE unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_DSR_SENSITIVITY unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_ERROR_ABORT unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* FlowReplace */
Packit 1fb8d4
Packit 1fb8d4
	pHandflow->FlowReplace = 0;
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_iflag & IXON)
Packit 1fb8d4
		pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_iflag & IXOFF)
Packit 1fb8d4
		pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
Packit 1fb8d4
Packit 1fb8d4
	if (!(currentTermios.c_iflag & IGNPAR))
Packit 1fb8d4
		pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_iflag & IGNBRK)
Packit 1fb8d4
		pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_BREAK_CHAR unsupported */
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_cflag & HUPCL)
Packit 1fb8d4
		pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_cflag & CRTSCTS)
Packit 1fb8d4
		pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_XOFF_CONTINUE unsupported */
Packit 1fb8d4
Packit 1fb8d4
	/* XonLimit */
Packit 1fb8d4
Packit 1fb8d4
	pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
Packit 1fb8d4
Packit 1fb8d4
	/* XoffLimit */
Packit 1fb8d4
Packit 1fb8d4
	pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
Packit 1fb8d4
{
Packit 1fb8d4
	/* NB: timeouts are applied on system during read/write I/O */
Packit 1fb8d4
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */
Packit Service 5a9772
	if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
Packit Service 5a9772
	    (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(
Packit Service 5a9772
		    WLOG_WARN,
Packit Service 5a9772
		    "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
Packit Service 5a9772
	pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
Packit Service 5a9772
	pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
Packit 1fb8d4
	pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
Packit Service 5a9772
	pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
Packit Service 5a9772
Packit Service 5a9772
	CommLog_Print(WLOG_DEBUG, "ReadIntervalTimeout %" PRIu32 "",
Packit Service 5a9772
	              pComm->timeouts.ReadIntervalTimeout);
Packit Service 5a9772
	CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutMultiplier %" PRIu32 "",
Packit Service 5a9772
	              pComm->timeouts.ReadTotalTimeoutMultiplier);
Packit Service 5a9772
	CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutConstant %" PRIu32 "",
Packit Service 5a9772
	              pComm->timeouts.ReadTotalTimeoutConstant);
Packit Service 5a9772
	CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutMultiplier %" PRIu32 "",
Packit Service 5a9772
	              pComm->timeouts.WriteTotalTimeoutMultiplier);
Packit Service 5a9772
	CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutConstant %" PRIu32 "",
Packit Service 5a9772
	              pComm->timeouts.WriteTotalTimeoutConstant);
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
Packit 1fb8d4
{
Packit Service 5a9772
	pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
Packit Service 5a9772
	pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
Packit Service 5a9772
	pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
Packit 1fb8d4
	pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
Packit Service 5a9772
	pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_lines(WINPR_COMM* pComm, UINT32 lines)
Packit 1fb8d4
{
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "TIOCMBIS ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
Packit Service 5a9772
		              errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _clear_lines(WINPR_COMM* pComm, UINT32 lines)
Packit 1fb8d4
{
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "TIOCMBIC ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
Packit Service 5a9772
		              errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_dtr(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	SERIAL_HANDFLOW handflow;
Packit 1fb8d4
	if (!_get_handflow(pComm, &handflow))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_DTR_HANDSHAKE not supported as of today */
Packit 1fb8d4
	assert((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
Packit 1fb8d4
Packit 1fb8d4
	if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return _set_lines(pComm, TIOCM_DTR);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _clear_dtr(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	SERIAL_HANDFLOW handflow;
Packit 1fb8d4
	if (!_get_handflow(pComm, &handflow))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	/* SERIAL_DTR_HANDSHAKE not supported as of today */
Packit 1fb8d4
	assert((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
Packit 1fb8d4
Packit 1fb8d4
	if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return _clear_lines(pComm, TIOCM_DTR);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_rts(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	SERIAL_HANDFLOW handflow;
Packit 1fb8d4
	if (!_get_handflow(pComm, &handflow))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return _set_lines(pComm, TIOCM_RTS);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _clear_rts(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	SERIAL_HANDFLOW handflow;
Packit 1fb8d4
	if (!_get_handflow(pComm, &handflow))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return _clear_lines(pComm, TIOCM_RTS);
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
Packit 1fb8d4
{
Packit Service 5a9772
	UINT32 lines = 0;
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(pRegister, sizeof(ULONG));
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: Is the last read of the MSR register available or
Packit 1fb8d4
	 * cached somewhere? Not quite sure we need to return the 4
Packit 1fb8d4
	 * LSBits anyway. A direct access to the register -- which
Packit 1fb8d4
	 * would reset the register -- is likely not expected from
Packit 1fb8d4
	 * this function.
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	/* #define SERIAL_MSR_DCTS     0x01 */
Packit 1fb8d4
	/* #define SERIAL_MSR_DDSR     0x02 */
Packit 1fb8d4
	/* #define SERIAL_MSR_TERI     0x04 */
Packit 1fb8d4
	/* #define SERIAL_MSR_DDCD     0x08 */
Packit 1fb8d4
Packit 1fb8d4
	if (lines & TIOCM_CTS)
Packit 1fb8d4
		*pRegister |= SERIAL_MSR_CTS;
Packit 1fb8d4
	if (lines & TIOCM_DSR)
Packit 1fb8d4
		*pRegister |= SERIAL_MSR_DSR;
Packit 1fb8d4
	if (lines & TIOCM_RI)
Packit 1fb8d4
		*pRegister |= SERIAL_MSR_RI;
Packit 1fb8d4
	if (lines & TIOCM_CD)
Packit 1fb8d4
		*pRegister |= SERIAL_MSR_DCD;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
Packit 1fb8d4
static const ULONG _SERIAL_SYS_SUPPORTED_EV_MASK =
Packit Service 5a9772
    SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR |
Packit Service 5a9772
    SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
Packit Service 5a9772
    /* SERIAL_EV_PERR     | */
Packit Service 5a9772
    SERIAL_EV_RX80FULL /*|
Packit Service 5a9772
    SERIAL_EV_EVENT1   |
Packit Service 5a9772
    SERIAL_EV_EVENT2*/
Packit Service 5a9772
    ;
Packit Service 5a9772
Packit Service 5a9772
static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
Packit 1fb8d4
{
Packit 1fb8d4
	ULONG possibleMask;
Packit 1fb8d4
Packit 1fb8d4
	/* Stops pending IOCTL_SERIAL_WAIT_ON_MASK
Packit 1fb8d4
	 * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	if (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* FIXME: any doubt on reading PendingEvents out of a critical section? */
Packit 1fb8d4
Packit 1fb8d4
		EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_FREERDP_STOP;
Packit 1fb8d4
		LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
Packit 1fb8d4
		/* waiting the end of the pending _wait_on_mask() */
Packit 1fb8d4
		while (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING)
Packit 1fb8d4
			Sleep(10); /* 10ms */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* NB: ensure to leave the critical section before to return */
Packit 1fb8d4
	EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
Packit 1fb8d4
	if (*pWaitMask == 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* clearing pending events */
Packit 1fb8d4
Packit 1fb8d4
		if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
Packit 1fb8d4
		{
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
Packit Service 5a9772
			              strerror(errno));
Packit Service 5a9772
Packit 1fb8d4
			if (pComm->permissive)
Packit 1fb8d4
			{
Packit 1fb8d4
				/* counters could not be reset but keep on */
Packit 1fb8d4
				ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
Packit 1fb8d4
			}
Packit 1fb8d4
			else
Packit 1fb8d4
			{
Packit 1fb8d4
				SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
				LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		pComm->PendingEvents = 0;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	possibleMask = *pWaitMask & _SERIAL_SYS_SUPPORTED_EV_MASK;
Packit 1fb8d4
Packit 1fb8d4
	if (possibleMask != *pWaitMask)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN,
Packit Service 5a9772
		              "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32
Packit Service 5a9772
		              ", possible events= 0x%08" PRIX32 "",
Packit Service 5a9772
		              *pWaitMask, possibleMask);
Packit 1fb8d4
Packit 1fb8d4
		/* FIXME: shall we really set the possibleMask and return FALSE? */
Packit 1fb8d4
		pComm->WaitEventMask = possibleMask;
Packit 1fb8d4
Packit 1fb8d4
		LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pComm->WaitEventMask = possibleMask;
Packit 1fb8d4
Packit 1fb8d4
	LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
Packit 1fb8d4
{
Packit 1fb8d4
	*pWaitMask = pComm->WaitEventMask;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize)
Packit 1fb8d4
{
Packit 1fb8d4
	if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
Packit 1fb8d4
		return TRUE; /* nothing to do */
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: could be implemented on top of N_TTY */
Packit 1fb8d4
Packit 1fb8d4
	if (pQueueSize->InSize > N_TTY_BUF_SIZE)
Packit Service 5a9772
		CommLog_Print(WLOG_WARN,
Packit Service 5a9772
		              "Requested an incompatible input buffer size: %" PRIu32
Packit Service 5a9772
		              ", keeping on with a %" PRIu32 " bytes buffer.",
Packit Service 5a9772
		              pQueueSize->InSize, N_TTY_BUF_SIZE);
Packit 1fb8d4
Packit 1fb8d4
	if (pQueueSize->OutSize > N_TTY_BUF_SIZE)
Packit Service 5a9772
		CommLog_Print(WLOG_WARN,
Packit Service 5a9772
		              "Requested an incompatible output buffer size: %" PRIu32
Packit Service 5a9772
		              ", keeping on with a %" PRIu32 " bytes buffer.",
Packit Service 5a9772
		              pQueueSize->OutSize, N_TTY_BUF_SIZE);
Packit 1fb8d4
Packit 1fb8d4
	SetLastError(ERROR_CANCELLED);
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
Packit 1fb8d4
{
Packit Service 5a9772
	if ((*pPurgeMask & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR |
Packit Service 5a9772
	                     SERIAL_PURGE_RXCLEAR)) > 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "Invalid purge mask: 0x%" PRIX32 "\n", *pPurgeMask);
Packit 1fb8d4
		SetLastError(ERROR_INVALID_PARAMETER);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: currently relying too much on the fact the server
Packit 1fb8d4
	 * sends a single IRP_MJ_WRITE or IRP_MJ_READ at a time
Packit 1fb8d4
	 * (taking care though that one IRP_MJ_WRITE and one
Packit 1fb8d4
	 * IRP_MJ_READ can be sent simultaneously) */
Packit 1fb8d4
Packit 1fb8d4
	if (*pPurgeMask & SERIAL_PURGE_TXABORT)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Purges all write (IRP_MJ_WRITE) requests. */
Packit 1fb8d4
Packit 1fb8d4
		if (eventfd_write(pComm->fd_write_event, FREERDP_PURGE_TXABORT) < 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			if (errno != EAGAIN)
Packit 1fb8d4
			{
Packit Service 5a9772
				CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
Packit Service 5a9772
				              strerror(errno));
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			assert(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_WRITE */
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (*pPurgeMask & SERIAL_PURGE_RXABORT)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Purges all read (IRP_MJ_READ) requests. */
Packit 1fb8d4
Packit 1fb8d4
		if (eventfd_write(pComm->fd_read_event, FREERDP_PURGE_RXABORT) < 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			if (errno != EAGAIN)
Packit 1fb8d4
			{
Packit Service 5a9772
				CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
Packit Service 5a9772
				              strerror(errno));
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			assert(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_READ */
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (*pPurgeMask & SERIAL_PURGE_TXCLEAR)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Purges the transmit buffer, if one exists. */
Packit 1fb8d4
Packit 1fb8d4
		if (tcflush(pComm->fd, TCOFLUSH) < 0)
Packit 1fb8d4
		{
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "tcflush(TCOFLUSH) failure, errno=[%d] %s", errno,
Packit Service 5a9772
			              strerror(errno));
Packit 1fb8d4
			SetLastError(ERROR_CANCELLED);
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (*pPurgeMask & SERIAL_PURGE_RXCLEAR)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Purges the receive buffer, if one exists. */
Packit 1fb8d4
Packit 1fb8d4
		if (tcflush(pComm->fd, TCIFLUSH) < 0)
Packit 1fb8d4
		{
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "tcflush(TCIFLUSH) failure, errno=[%d] %s", errno,
Packit Service 5a9772
			              strerror(errno));
Packit 1fb8d4
			SetLastError(ERROR_CANCELLED);
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/* NB: _get_commstatus also produces most of the events consumed by _wait_on_mask(). Exceptions:
Packit 1fb8d4
 *  - SERIAL_EV_RXFLAG: FIXME: once EventChar supported
Packit 1fb8d4
 *
Packit 1fb8d4
 */
Packit Service 5a9772
static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
Packit 1fb8d4
{
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/jj673022%28v=vs.85%29.aspx */
Packit 1fb8d4
Packit 1fb8d4
	struct serial_icounter_struct currentCounters;
Packit 1fb8d4
Packit 1fb8d4
	/* NB: ensure to leave the critical section before to return */
Packit 1fb8d4
	EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS));
Packit 1fb8d4
Packit 1fb8d4
	ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCGICOUNT, &currentCounters) < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
Packit Service 5a9772
		              strerror(errno));
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "  could not read counters.");
Packit Service 5a9772
Packit 1fb8d4
		if (pComm->permissive)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* Errors and events based on counters could not be
Packit 1fb8d4
			 * detected but keep on.
Packit 1fb8d4
			 */
Packit 1fb8d4
			ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
Packit 1fb8d4
		}
Packit 1fb8d4
		else
Packit 1fb8d4
		{
Packit 1fb8d4
			SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
			LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	/* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* >
Packit Service 5a9772
	 * pComm->counters.*) thinking the counters can loop */
Packit 1fb8d4
Packit 1fb8d4
	/* Errors */
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.buf_overrun != pComm->counters.buf_overrun)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.overrun != pComm->counters.overrun)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCommstatus->Errors |= SERIAL_ERROR_OVERRUN;
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_ERR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.brk != pComm->counters.brk)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCommstatus->Errors |= SERIAL_ERROR_BREAK;
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_BREAK;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.parity != pComm->counters.parity)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCommstatus->Errors |= SERIAL_ERROR_PARITY;
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_ERR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.frame != pComm->counters.frame)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCommstatus->Errors |= SERIAL_ERROR_FRAMING;
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_ERR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* HoldReasons */
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: SERIAL_TX_WAITING_FOR_CTS */
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: SERIAL_TX_WAITING_FOR_DSR */
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: SERIAL_TX_WAITING_FOR_DCD */
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: SERIAL_TX_WAITING_FOR_XON */
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: SERIAL_TX_WAITING_ON_BREAK, see LCR's bit 6 */
Packit 1fb8d4
Packit 1fb8d4
	/* TODO: SERIAL_TX_WAITING_XOFF_SENT */
Packit 1fb8d4
Packit 1fb8d4
	/* AmountInInQueue */
Packit 1fb8d4
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TIOCINQ ioctl failed, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
Packit 1fb8d4
		LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/*  AmountInOutQueue */
Packit 1fb8d4
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TIOCOUTQ ioctl failed, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
Packit 1fb8d4
		LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/*  BOOLEAN EofReceived; FIXME: once EofChar supported */
Packit 1fb8d4
Packit 1fb8d4
	/*  BOOLEAN WaitForImmediate; TODO: once IOCTL_SERIAL_IMMEDIATE_CHAR fully supported */
Packit 1fb8d4
Packit 1fb8d4
	/* other events based on counters */
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.rx != pComm->counters.rx)
Packit 1fb8d4
	{
Packit Service 5a9772
		pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if ((currentCounters.tx != pComm->counters.tx) && /* at least a transmission occurred AND ...*/
Packit Service 5a9772
	    (pCommstatus->AmountInOutQueue == 0))         /* output bufer is now empty */
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit Service 5a9772
		/* FIXME: "now empty" from the specs is ambiguous, need to track previous completed
Packit Service 5a9772
		 * transmission? */
Packit 1fb8d4
		pComm->PendingEvents &= ~SERIAL_EV_TXEMPTY;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.cts != pComm->counters.cts)
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_CTS;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.dsr != pComm->counters.dsr)
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_DSR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.dcd != pComm->counters.dcd)
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_RLSD;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentCounters.rng != pComm->counters.rng)
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_RING;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE))
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents |= SERIAL_EV_RX80FULL;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit Service 5a9772
		/* FIXME: "is 80 percent full" from the specs is ambiguous, need to track when it previously
Packit Service 5a9772
		 * * occurred? */
Packit 1fb8d4
		pComm->PendingEvents &= ~SERIAL_EV_RX80FULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pComm->counters = currentCounters;
Packit 1fb8d4
Packit 1fb8d4
	LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _refresh_PendingEvents(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	SERIAL_STATUS serialStatus;
Packit 1fb8d4
Packit 1fb8d4
	/* NB: also ensures PendingEvents to be up to date */
Packit 1fb8d4
	ZeroMemory(&serialStatus, sizeof(SERIAL_STATUS));
Packit 1fb8d4
	if (!_get_commstatus(pComm, &serialStatus))
Packit 1fb8d4
	{
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static void _consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
Packit 1fb8d4
{
Packit 1fb8d4
	if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
Packit 1fb8d4
	{
Packit 1fb8d4
		pComm->PendingEvents &= ~event; /* consumed */
Packit 1fb8d4
		*pOutputMask |= event;
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/*
Packit 1fb8d4
 * NB: see also: _set_wait_mask()
Packit 1fb8d4
 */
Packit Service 5a9772
static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
Packit 1fb8d4
{
Packit 1fb8d4
	assert(*pOutputMask == 0);
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
	pComm->PendingEvents |= SERIAL_EV_FREERDP_WAITING;
Packit 1fb8d4
	LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
Packit 1fb8d4
	while (TRUE)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* NB: EventsLock also used by _refresh_PendingEvents() */
Packit 1fb8d4
		if (!_refresh_PendingEvents(pComm))
Packit 1fb8d4
		{
Packit 1fb8d4
			EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
			pComm->PendingEvents &= ~SERIAL_EV_FREERDP_WAITING;
Packit 1fb8d4
			LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* NB: ensure to leave the critical section before to return */
Packit 1fb8d4
		EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
Packit 1fb8d4
		if (pComm->PendingEvents & SERIAL_EV_FREERDP_STOP)
Packit 1fb8d4
		{
Packit 1fb8d4
			pComm->PendingEvents &= ~SERIAL_EV_FREERDP_STOP;
Packit 1fb8d4
Packit 1fb8d4
			/* pOutputMask must remain empty but should
Packit 1fb8d4
			 * not have been modified.
Packit 1fb8d4
			 *
Packit 1fb8d4
			 * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
Packit 1fb8d4
			 */
Packit 1fb8d4
			assert(*pOutputMask == 0);
Packit 1fb8d4
Packit 1fb8d4
			pComm->PendingEvents &= ~SERIAL_EV_FREERDP_WAITING;
Packit 1fb8d4
			LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
Packit Service 5a9772
		_consume_event(pComm, pOutputMask, SERIAL_EV_RING);
Packit 1fb8d4
		_consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
Packit 1fb8d4
Packit 1fb8d4
		LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
Packit 1fb8d4
		/* NOTE: PendingEvents can be modified from now on but
Packit 1fb8d4
		 * not pOutputMask */
Packit 1fb8d4
Packit 1fb8d4
		if (*pOutputMask != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* at least an event occurred */
Packit 1fb8d4
Packit 1fb8d4
			EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
			pComm->PendingEvents &= ~SERIAL_EV_FREERDP_WAITING;
Packit 1fb8d4
			LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
			return TRUE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* waiting for a modification of PendingEvents.
Packit 1fb8d4
		 *
Packit 1fb8d4
		 * NOTE: previously used a semaphore but used
Packit 1fb8d4
		 * sem_timedwait() anyway. Finally preferred a simpler
Packit 1fb8d4
		 * solution with Sleep() without the burden of the
Packit 1fb8d4
		 * semaphore initialization and destroying.
Packit 1fb8d4
		 */
Packit 1fb8d4
Packit 1fb8d4
		Sleep(100); /* 100 ms */
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_break_on(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_break_off(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_xoff(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	if (tcflow(pComm->fd, TCIOFF) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TCIOFF failure, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _set_xon(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	if (tcflow(pComm->fd, TCION) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TCION failure, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
Packit 1fb8d4
{
Packit Service 5a9772
	UINT32 lines = 0;
Packit 1fb8d4
	if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	*pMask = 0;
Packit 1fb8d4
Packit 1fb8d4
	if (!(lines & TIOCM_DTR))
Packit 1fb8d4
		*pMask |= SERIAL_DTR_STATE;
Packit 1fb8d4
	if (!(lines & TIOCM_RTS))
Packit 1fb8d4
		*pMask |= SERIAL_RTS_STATE;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _config_size(WINPR_COMM* pComm, ULONG* pSize)
Packit 1fb8d4
{
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/ff546548%28v=vs.85%29.aspx */
Packit Service 5a9772
	if (!pSize)
Packit Service 5a9772
		return FALSE;
Packit Service 5a9772
Packit Service 5a9772
	*pSize = 0;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
Packit 1fb8d4
{
Packit 1fb8d4
	BOOL result;
Packit 1fb8d4
	DWORD nbBytesWritten = -1;
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: CommWriteFile uses a critical section, shall it be
Packit 1fb8d4
	 * interrupted?
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * FIXME: see also _get_commstatus()'s WaitForImmediate boolean
Packit 1fb8d4
	 */
Packit 1fb8d4
Packit 1fb8d4
	result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
Packit 1fb8d4
Packit 1fb8d4
	assert(nbBytesWritten == 1);
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static BOOL _reset_device(WINPR_COMM* pComm)
Packit 1fb8d4
{
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static SERIAL_DRIVER _SerialSys = {
Packit Service 5a9772
	.id = SerialDriverSerialSys,
Packit Service 5a9772
	.name = _T("Serial.sys"),
Packit Service 5a9772
	.set_baud_rate = _set_baud_rate,
Packit Service 5a9772
	.get_baud_rate = _get_baud_rate,
Packit Service 5a9772
	.get_properties = _get_properties,
Packit 1fb8d4
	.set_serial_chars = _set_serial_chars,
Packit 1fb8d4
	.get_serial_chars = _get_serial_chars,
Packit 1fb8d4
	.set_line_control = _set_line_control,
Packit 1fb8d4
	.get_line_control = _get_line_control,
Packit Service 5a9772
	.set_handflow = _set_handflow,
Packit Service 5a9772
	.get_handflow = _get_handflow,
Packit Service 5a9772
	.set_timeouts = _set_timeouts,
Packit Service 5a9772
	.get_timeouts = _get_timeouts,
Packit Service 5a9772
	.set_dtr = _set_dtr,
Packit Service 5a9772
	.clear_dtr = _clear_dtr,
Packit Service 5a9772
	.set_rts = _set_rts,
Packit Service 5a9772
	.clear_rts = _clear_rts,
Packit Service 5a9772
	.get_modemstatus = _get_modemstatus,
Packit Service 5a9772
	.set_wait_mask = _set_wait_mask,
Packit Service 5a9772
	.get_wait_mask = _get_wait_mask,
Packit Service 5a9772
	.wait_on_mask = _wait_on_mask,
Packit Service 5a9772
	.set_queue_size = _set_queue_size,
Packit Service 5a9772
	.purge = _purge,
Packit Service 5a9772
	.get_commstatus = _get_commstatus,
Packit Service 5a9772
	.set_break_on = _set_break_on,
Packit Service 5a9772
	.set_break_off = _set_break_off,
Packit Service 5a9772
	.set_xoff = _set_xoff,
Packit Service 5a9772
	.set_xon = _set_xon,
Packit Service 5a9772
	.get_dtrrts = _get_dtrrts,
Packit Service 5a9772
	.config_size = _config_size,
Packit Service 5a9772
	.immediate_char = _immediate_char,
Packit Service 5a9772
	.reset_device = _reset_device,
Packit 1fb8d4
};
Packit 1fb8d4
Packit 1fb8d4
SERIAL_DRIVER* SerialSys_s()
Packit 1fb8d4
{
Packit 1fb8d4
	return &_SerialSys;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif /* __linux__ */