Blame winpr/libwinpr/comm/comm_io.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * WinPR: Windows Portable Runtime
Packit 1fb8d4
 * Serial Communication API
Packit 1fb8d4
 *
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
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#if defined __linux__ && !defined ANDROID
Packit 1fb8d4
Packit 1fb8d4
#include <assert.h>
Packit 1fb8d4
#include <errno.h>
Packit 1fb8d4
#include <termios.h>
Packit 1fb8d4
#include <unistd.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/io.h>
Packit 1fb8d4
#include <winpr/wlog.h>
Packit 1fb8d4
#include <winpr/wtypes.h>
Packit 1fb8d4
Packit 1fb8d4
#include "comm.h"
Packit 1fb8d4
Packit 1fb8d4
BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
Packit 1fb8d4
{
Packit Service 5a9772
	WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
Packit 1fb8d4
Packit 1fb8d4
	if (hDevice == INVALID_HANDLE_VALUE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_HANDLE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_HANDLE);
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pComm->permissive = permissive;
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/* Computes VTIME in deciseconds from Ti in milliseconds */
Packit 1fb8d4
static UCHAR _vtime(ULONG Ti)
Packit 1fb8d4
{
Packit 1fb8d4
	/* FIXME: look for an equivalent math function otherwise let
Packit 1fb8d4
	 * do the compiler do the optimization */
Packit 1fb8d4
	if (Ti == 0)
Packit 1fb8d4
		return 0;
Packit 1fb8d4
	else if (Ti < 100)
Packit 1fb8d4
		return 1;
Packit 1fb8d4
	else if (Ti > 25500)
Packit 1fb8d4
		return 255; /* 0xFF */
Packit 1fb8d4
	else
Packit 1fb8d4
		return Ti / 100;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * ERRORS:
Packit 1fb8d4
 *   ERROR_INVALID_HANDLE
Packit 1fb8d4
 *   ERROR_NOT_SUPPORTED
Packit 1fb8d4
 *   ERROR_INVALID_PARAMETER
Packit 1fb8d4
 *   ERROR_TIMEOUT
Packit 1fb8d4
 *   ERROR_IO_DEVICE
Packit 1fb8d4
 *   ERROR_BAD_DEVICE
Packit 1fb8d4
 */
Packit 1fb8d4
BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
Packit 1fb8d4
                  LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
Packit 1fb8d4
{
Packit Service 5a9772
	WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
Packit 1fb8d4
	int biggestFd = -1;
Packit 1fb8d4
	fd_set read_set;
Packit 1fb8d4
	int nbFds;
Packit 1fb8d4
	COMMTIMEOUTS* pTimeouts;
Packit 1fb8d4
	UCHAR vmin = 0;
Packit 1fb8d4
	UCHAR vtime = 0;
Packit 1fb8d4
	ULONGLONG Tmax = 0;
Packit 1fb8d4
	struct timeval tmaxTimeout, *pTmaxTimeout;
Packit 1fb8d4
	struct termios currentTermios;
Packit 1fb8d4
	EnterCriticalSection(&pComm->ReadLock); /* KISSer by the function's beginning */
Packit 1fb8d4
Packit 1fb8d4
	if (hDevice == INVALID_HANDLE_VALUE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_HANDLE);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_HANDLE);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (lpOverlapped != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (lpNumberOfBytesRead == NULL)
Packit 1fb8d4
	{
Packit Service 5a9772
		SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	*lpNumberOfBytesRead = 0; /* will be ajusted if required ... */
Packit 1fb8d4
Packit 1fb8d4
	if (nNumberOfBytesToRead <= 0) /* N */
Packit 1fb8d4
	{
Packit 1fb8d4
		goto return_true; /* FIXME: or FALSE? */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (tcgetattr(pComm->fd, &currentTermios) < 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (currentTermios.c_lflag & ICANON)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "Canonical mode not supported"); /* the timeout could not be set */
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* http://msdn.microsoft.com/en-us/library/hh439614%28v=vs.85%29.aspx
Packit 1fb8d4
	 * http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx
Packit 1fb8d4
	 *
Packit Service 5a9772
	 * ReadIntervalTimeout  | ReadTotalTimeoutMultiplier | ReadTotalTimeoutConstant | VMIN | VTIME |
Packit Service 5a9772
	 * TMAX  | 0            |            0               |           0              |   N  |   0   |
Packit Service 5a9772
	 * INDEF | Blocks for N bytes available. 0< Ti 
Packit Service 5a9772
	 * N  |   Ti  | INDEF | Blocks on first byte, then use Ti between bytes. MAXULONG       | 0 | 0
Packit Service 5a9772
	 * |   0  |   0   |   0   | Returns immediately with bytes available (don't block) MAXULONG |
Packit Service 5a9772
	 * MAXULONG           |      0< Tc 
Packit Service 5a9772
	 * during Tc or returns immediately whith bytes available MAXULONG       |            m |
Packit Service 5a9772
	 * MAXULONG          |                      | Invalid 0            |            m |      0< Tc
Packit Service 5a9772
	 * 
Packit Service 5a9772
	 * immediately whith bytes available 0< Ti 
Packit Service 5a9772
	 * Tc 
Packit Service 5a9772
	 * Tmax is used for the whole system call.
Packit 1fb8d4
	 */
Packit 1fb8d4
	/* NB: timeouts are in milliseconds, VTIME are in deciseconds and is an unsigned char */
Packit Service 5a9772
	/* FIXME: double check whether open(pComm->fd_read_event, O_NONBLOCK) doesn't conflict with
Packit Service 5a9772
	 * above use cases */
Packit 1fb8d4
	pTimeouts = &(pComm->timeouts);
Packit 1fb8d4
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
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* VMIN */
Packit 1fb8d4
Packit Service 5a9772
	if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
Packit Service 5a9772
	    (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
Packit 1fb8d4
	{
Packit 1fb8d4
		vmin = 0;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		/* N */
Packit 1fb8d4
		/* vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255;*/ /* 0xFF */
Packit 1fb8d4
		/* NB: we might wait endlessly with vmin=N, prefer to
Packit 1fb8d4
		 * force vmin=1 and return with bytes
Packit 1fb8d4
		 * available. FIXME: is a feature disarded here? */
Packit 1fb8d4
		vmin = 1;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* VTIME */
Packit 1fb8d4
Packit Service 5a9772
	if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Ti */
Packit 1fb8d4
		vtime = _vtime(pTimeouts->ReadIntervalTimeout);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* TMAX */
Packit 1fb8d4
	pTmaxTimeout = &tmaxTimeout;
Packit 1fb8d4
Packit Service 5a9772
	if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
Packit Service 5a9772
	    (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Tc */
Packit 1fb8d4
		Tmax = pTimeouts->ReadTotalTimeoutConstant;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Tmax */
Packit 1fb8d4
		Tmax = nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier +
Packit 1fb8d4
		       pTimeouts->ReadTotalTimeoutConstant;
Packit 1fb8d4
Packit 1fb8d4
		/* INDEFinitely */
Packit Service 5a9772
		if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) &&
Packit Service 5a9772
		    (pTimeouts->ReadTotalTimeoutMultiplier == 0))
Packit 1fb8d4
			pTmaxTimeout = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
Packit 1fb8d4
	{
Packit Service 5a9772
		currentTermios.c_cc[VMIN] = vmin;
Packit 1fb8d4
		currentTermios.c_cc[VTIME] = vtime;
Packit 1fb8d4
Packit 1fb8d4
		if (tcsetattr(pComm->fd, TCSANOW, &currentTermios) < 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			CommLog_Print(WLOG_WARN,
Packit Service 5a9772
			              "CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8
Packit Service 5a9772
			              ", VTIME=%" PRIu8 "",
Packit 1fb8d4
			              vmin, vtime);
Packit 1fb8d4
			SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
			goto return_false;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* wait indefinitely if pTmaxTimeout is NULL */
Packit 1fb8d4
Packit 1fb8d4
	if (pTmaxTimeout != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
Packit 1fb8d4
Packit 1fb8d4
		if (Tmax > 0) /* return immdiately if Tmax == 0 */
Packit 1fb8d4
		{
Packit Service 5a9772
			pTmaxTimeout->tv_sec = Tmax / 1000;           /* s */
Packit 1fb8d4
			pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: had expected eventfd_write() to return EAGAIN when
Packit 1fb8d4
	 * there is no eventfd_read() but this not the case. */
Packit 1fb8d4
	/* discard a possible and no more relevant event */
Packit 1fb8d4
	eventfd_read(pComm->fd_read_event, NULL);
Packit 1fb8d4
	biggestFd = pComm->fd_read;
Packit 1fb8d4
Packit 1fb8d4
	if (pComm->fd_read_event > biggestFd)
Packit 1fb8d4
		biggestFd = pComm->fd_read_event;
Packit 1fb8d4
Packit 1fb8d4
	FD_ZERO(&read_set);
Packit 1fb8d4
	assert(pComm->fd_read_event < FD_SETSIZE);
Packit 1fb8d4
	assert(pComm->fd_read < FD_SETSIZE);
Packit 1fb8d4
	FD_SET(pComm->fd_read_event, &read_set);
Packit 1fb8d4
	FD_SET(pComm->fd_read, &read_set);
Packit 1fb8d4
	nbFds = select(biggestFd + 1, &read_set, NULL, NULL, pTmaxTimeout);
Packit 1fb8d4
Packit 1fb8d4
	if (nbFds < 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno, strerror(errno));
Packit 1fb8d4
		SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (nbFds == 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* timeout */
Packit 1fb8d4
		SetLastError(ERROR_TIMEOUT);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* read_set */
Packit 1fb8d4
Packit 1fb8d4
	if (FD_ISSET(pComm->fd_read_event, &read_set))
Packit 1fb8d4
	{
Packit 1fb8d4
		eventfd_t event = 0;
Packit 1fb8d4
Packit 1fb8d4
		if (eventfd_read(pComm->fd_read_event, &event) < 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			if (errno == EAGAIN)
Packit 1fb8d4
			{
Packit 1fb8d4
				assert(FALSE); /* not quite sure this should ever happen */
Packit Service 5a9772
				               /* keep on */
Packit 1fb8d4
			}
Packit 1fb8d4
			else
Packit 1fb8d4
			{
Packit 1fb8d4
				CommLog_Print(WLOG_WARN,
Packit 1fb8d4
				              "unexpected error on reading fd_read_event, errno=[%d] %s\n", errno,
Packit 1fb8d4
				              strerror(errno));
Packit 1fb8d4
				/* FIXME: goto return_false ? */
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			assert(errno == EAGAIN);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (event == FREERDP_PURGE_RXABORT)
Packit 1fb8d4
		{
Packit 1fb8d4
			SetLastError(ERROR_CANCELLED);
Packit 1fb8d4
			goto return_false;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		assert(event == FREERDP_PURGE_RXABORT); /* no other expected event so far */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (FD_ISSET(pComm->fd_read, &read_set))
Packit 1fb8d4
	{
Packit 1fb8d4
		ssize_t nbRead = 0;
Packit 1fb8d4
		nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
Packit 1fb8d4
Packit 1fb8d4
		if (nbRead < 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			CommLog_Print(WLOG_WARN,
Packit Service 5a9772
			              "CommReadFile failed, ReadIntervalTimeout=%" PRIu32
Packit Service 5a9772
			              ", ReadTotalTimeoutMultiplier=%" PRIu32
Packit Service 5a9772
			              ", ReadTotalTimeoutConstant=%" PRIu32 " VMIN=%u, VTIME=%u",
Packit 1fb8d4
			              pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier,
Packit Service 5a9772
			              pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN],
Packit Service 5a9772
			              currentTermios.c_cc[VTIME]);
Packit 1fb8d4
			CommLog_Print(WLOG_WARN,
Packit Service 5a9772
			              "CommReadFile failed, nNumberOfBytesToRead=%" PRIu32 ", errno=[%d] %s",
Packit 1fb8d4
			              nNumberOfBytesToRead, errno, strerror(errno));
Packit 1fb8d4
Packit 1fb8d4
			if (errno == EAGAIN)
Packit 1fb8d4
			{
Packit 1fb8d4
				/* keep on */
Packit 1fb8d4
				goto return_true; /* expect a read-loop to be implemented on the server side */
Packit 1fb8d4
			}
Packit 1fb8d4
			else if (errno == EBADF)
Packit 1fb8d4
			{
Packit 1fb8d4
				SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
Packit 1fb8d4
				goto return_false;
Packit 1fb8d4
			}
Packit 1fb8d4
			else
Packit 1fb8d4
			{
Packit 1fb8d4
				assert(FALSE);
Packit 1fb8d4
				SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
				goto return_false;
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (nbRead == 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* termios timeout */
Packit 1fb8d4
			SetLastError(ERROR_TIMEOUT);
Packit 1fb8d4
			goto return_false;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		*lpNumberOfBytesRead = nbRead;
Packit 1fb8d4
Packit 1fb8d4
		EnterCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
		if (pComm->PendingEvents & SERIAL_EV_FREERDP_WAITING)
Packit 1fb8d4
		{
Packit 1fb8d4
			if (pComm->eventChar != '\0' && memchr(lpBuffer, pComm->eventChar, nbRead))
Packit Service 5a9772
				pComm->PendingEvents |= SERIAL_EV_RXCHAR;
Packit 1fb8d4
		}
Packit 1fb8d4
		LeaveCriticalSection(&pComm->EventsLock);
Packit 1fb8d4
		goto return_true;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	assert(FALSE);
Packit 1fb8d4
	*lpNumberOfBytesRead = 0;
Packit 1fb8d4
return_false:
Packit 1fb8d4
	LeaveCriticalSection(&pComm->ReadLock);
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
return_true:
Packit 1fb8d4
	LeaveCriticalSection(&pComm->ReadLock);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * ERRORS:
Packit 1fb8d4
 *   ERROR_INVALID_HANDLE
Packit 1fb8d4
 *   ERROR_NOT_SUPPORTED
Packit 1fb8d4
 *   ERROR_INVALID_PARAMETER
Packit 1fb8d4
 *   ERROR_BAD_DEVICE
Packit 1fb8d4
 */
Packit Service 5a9772
BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
Packit 1fb8d4
                   LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
Packit 1fb8d4
{
Packit Service 5a9772
	WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
Packit 1fb8d4
	struct timeval tmaxTimeout, *pTmaxTimeout;
Packit Service 5a9772
	EnterCriticalSection(&pComm->WriteLock); /* KISSer by the function's beginning */
Packit 1fb8d4
Packit 1fb8d4
	if (hDevice == INVALID_HANDLE_VALUE)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_HANDLE);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_INVALID_HANDLE);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (lpOverlapped != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		SetLastError(ERROR_NOT_SUPPORTED);
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (lpNumberOfBytesWritten == NULL)
Packit 1fb8d4
	{
Packit Service 5a9772
		SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
Packit 1fb8d4
		goto return_false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	*lpNumberOfBytesWritten = 0; /* will be ajusted if required ... */
Packit 1fb8d4
Packit 1fb8d4
	if (nNumberOfBytesToWrite <= 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		goto return_true; /* FIXME: or FALSE? */
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: had expected eventfd_write() to return EAGAIN when
Packit 1fb8d4
	 * there is no eventfd_read() but this not the case. */
Packit 1fb8d4
	/* discard a possible and no more relevant event */
Packit 1fb8d4
	eventfd_read(pComm->fd_write_event, NULL);
Packit 1fb8d4
	/* ms */
Packit Service 5a9772
	ULONGLONG Tmax = nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
Packit 1fb8d4
	                 pComm->timeouts.WriteTotalTimeoutConstant;
Packit 1fb8d4
	/* NB: select() may update the timeout argument to indicate
Packit 1fb8d4
	 * how much time was left. Keep the timeout variable out of
Packit 1fb8d4
	 * the while() */
Packit 1fb8d4
	pTmaxTimeout = &tmaxTimeout;
Packit 1fb8d4
	ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
Packit 1fb8d4
Packit 1fb8d4
	if (Tmax > 0)
Packit 1fb8d4
	{
Packit Service 5a9772
		pTmaxTimeout->tv_sec = Tmax / 1000;           /* s */
Packit 1fb8d4
		pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
Packit 1fb8d4
	}
Packit Service 5a9772
	else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
Packit Service 5a9772
	         (pComm->timeouts.WriteTotalTimeoutConstant == 0))
Packit 1fb8d4
	{
Packit 1fb8d4
		pTmaxTimeout = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* else return immdiately */
Packit 1fb8d4
Packit 1fb8d4
	while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
Packit 1fb8d4
	{
Packit 1fb8d4
		int biggestFd = -1;
Packit 1fb8d4
		fd_set event_set, write_set;
Packit 1fb8d4
		int nbFds;
Packit 1fb8d4
		biggestFd = pComm->fd_write;
Packit 1fb8d4
Packit 1fb8d4
		if (pComm->fd_write_event > biggestFd)
Packit 1fb8d4
			biggestFd = pComm->fd_write_event;
Packit 1fb8d4
Packit 1fb8d4
		FD_ZERO(&event_set);
Packit 1fb8d4
		FD_ZERO(&write_set);
Packit 1fb8d4
		assert(pComm->fd_write_event < FD_SETSIZE);
Packit 1fb8d4
		assert(pComm->fd_write < FD_SETSIZE);
Packit 1fb8d4
		FD_SET(pComm->fd_write_event, &event_set);
Packit 1fb8d4
		FD_SET(pComm->fd_write, &write_set);
Packit 1fb8d4
		nbFds = select(biggestFd + 1, &event_set, &write_set, NULL, pTmaxTimeout);
Packit 1fb8d4
Packit 1fb8d4
		if (nbFds < 0)
Packit 1fb8d4
		{
Packit Service 5a9772
			CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno, strerror(errno));
Packit 1fb8d4
			SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
			goto return_false;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (nbFds == 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* timeout */
Packit 1fb8d4
			SetLastError(ERROR_TIMEOUT);
Packit 1fb8d4
			goto return_false;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* event_set */
Packit 1fb8d4
Packit 1fb8d4
		if (FD_ISSET(pComm->fd_write_event, &event_set))
Packit 1fb8d4
		{
Packit 1fb8d4
			eventfd_t event = 0;
Packit 1fb8d4
Packit 1fb8d4
			if (eventfd_read(pComm->fd_write_event, &event) < 0)
Packit 1fb8d4
			{
Packit 1fb8d4
				if (errno == EAGAIN)
Packit 1fb8d4
				{
Packit 1fb8d4
					assert(FALSE); /* not quite sure this should ever happen */
Packit Service 5a9772
					               /* keep on */
Packit 1fb8d4
				}
Packit 1fb8d4
				else
Packit 1fb8d4
				{
Packit 1fb8d4
					CommLog_Print(WLOG_WARN,
Packit Service 5a9772
					              "unexpected error on reading fd_write_event, errno=[%d] %s\n",
Packit Service 5a9772
					              errno, strerror(errno));
Packit 1fb8d4
					/* FIXME: goto return_false ? */
Packit 1fb8d4
				}
Packit 1fb8d4
Packit 1fb8d4
				assert(errno == EAGAIN);
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			if (event == FREERDP_PURGE_TXABORT)
Packit 1fb8d4
			{
Packit 1fb8d4
				SetLastError(ERROR_CANCELLED);
Packit 1fb8d4
				goto return_false;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			assert(event == FREERDP_PURGE_TXABORT); /* no other expected event so far */
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* write_set */
Packit 1fb8d4
Packit 1fb8d4
		if (FD_ISSET(pComm->fd_write, &write_set))
Packit 1fb8d4
		{
Packit 1fb8d4
			ssize_t nbWritten;
Packit Service 5a9772
			nbWritten = write(pComm->fd_write, ((BYTE*)lpBuffer) + (*lpNumberOfBytesWritten),
Packit 1fb8d4
			                  nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
Packit 1fb8d4
Packit 1fb8d4
			if (nbWritten < 0)
Packit 1fb8d4
			{
Packit 1fb8d4
				CommLog_Print(WLOG_WARN,
Packit Service 5a9772
				              "CommWriteFile failed after %" PRIu32
Packit Service 5a9772
				              " bytes written, errno=[%d] %s\n",
Packit 1fb8d4
				              *lpNumberOfBytesWritten, errno, strerror(errno));
Packit 1fb8d4
Packit 1fb8d4
				if (errno == EAGAIN)
Packit 1fb8d4
				{
Packit 1fb8d4
					/* keep on */
Packit 1fb8d4
					continue;
Packit 1fb8d4
				}
Packit 1fb8d4
				else if (errno == EBADF)
Packit 1fb8d4
				{
Packit 1fb8d4
					SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
Packit 1fb8d4
					goto return_false;
Packit 1fb8d4
				}
Packit 1fb8d4
				else
Packit 1fb8d4
				{
Packit 1fb8d4
					assert(FALSE);
Packit 1fb8d4
					SetLastError(ERROR_IO_DEVICE);
Packit 1fb8d4
					goto return_false;
Packit 1fb8d4
				}
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			*lpNumberOfBytesWritten += nbWritten;
Packit 1fb8d4
		}
Packit 1fb8d4
	} /* while */
Packit 1fb8d4
Packit 1fb8d4
	/* FIXME: this call to tcdrain() doesn't look correct and
Packit 1fb8d4
	 * might hide a bug but was required while testing a serial
Packit 1fb8d4
	 * printer. Its driver was expecting the modem line status
Packit 1fb8d4
	 * SERIAL_MSR_DSR true after the sending which was never
Packit 1fb8d4
	 * happenning otherwise. A purge was also done before each
Packit 1fb8d4
	 * Write operation. The serial port was opened with:
Packit 1fb8d4
	 * DesiredAccess=0x0012019F. The printer worked fine with
Packit 1fb8d4
	 * mstsc. */
Packit 1fb8d4
	tcdrain(pComm->fd_write);
Packit 1fb8d4
Packit 1fb8d4
return_true:
Packit 1fb8d4
	LeaveCriticalSection(&pComm->WriteLock);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
Packit 1fb8d4
return_false:
Packit 1fb8d4
	LeaveCriticalSection(&pComm->WriteLock);
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif /* __linux__ */