|
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, ¤tTermios) < 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, ¤tTermios) < 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__ */
|