2014-04-25 02:20:48 +04:00
|
|
|
/**
|
|
|
|
* WinPR: Windows Portable Runtime
|
|
|
|
* Serial Communication API
|
|
|
|
*
|
|
|
|
* Copyright 2011 O.S. Systems Software Ltda.
|
|
|
|
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
|
|
|
|
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
|
|
|
* Copyright 2014 Hewlett-Packard Development Company, L.P.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2014-04-28 21:57:17 +04:00
|
|
|
#ifndef _WIN32
|
|
|
|
|
|
|
|
#include <termios.h>
|
|
|
|
|
|
|
|
#include <freerdp/utils/debug.h>
|
|
|
|
|
2014-04-25 02:20:48 +04:00
|
|
|
#include "comm_serial_sys.h"
|
|
|
|
|
2014-04-28 21:57:17 +04:00
|
|
|
#include <winpr/crt.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Linux, Windows speeds
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static const speed_t _SERIAL_SYS_BAUD_TABLE[][2] = {
|
|
|
|
#ifdef B0
|
|
|
|
{B0, 0}, /* hang up */
|
|
|
|
#endif
|
|
|
|
/* #ifdef B50 */
|
|
|
|
/* {B50, }, /\* undefined by serial.sys *\/ */
|
|
|
|
/* #endif */
|
|
|
|
#ifdef B75
|
|
|
|
{B75, SERIAL_BAUD_075},
|
|
|
|
#endif
|
|
|
|
#ifdef B110
|
|
|
|
{B110, SERIAL_BAUD_110},
|
|
|
|
#endif
|
|
|
|
/* #ifdef B134 */
|
|
|
|
/* {B134, SERIAL_BAUD_134_5}, /\* TODO: might be the same? *\/ */
|
|
|
|
/* #endif */
|
|
|
|
#ifdef B150
|
|
|
|
{B150, SERIAL_BAUD_150},
|
|
|
|
#endif
|
|
|
|
/* #ifdef B200 */
|
|
|
|
/* {B200, }, /\* undefined by serial.sys *\/ */
|
|
|
|
/* #endif */
|
|
|
|
#ifdef B300
|
|
|
|
{B300, SERIAL_BAUD_300},
|
|
|
|
#endif
|
|
|
|
#ifdef B600
|
|
|
|
{B600, SERIAL_BAUD_600},
|
|
|
|
#endif
|
|
|
|
#ifdef B1200
|
|
|
|
{B1200, SERIAL_BAUD_1200},
|
|
|
|
#endif
|
|
|
|
#ifdef B1800
|
|
|
|
{B1800, SERIAL_BAUD_1800},
|
|
|
|
#endif
|
|
|
|
#ifdef B2400
|
|
|
|
{B2400, SERIAL_BAUD_2400},
|
|
|
|
#endif
|
|
|
|
#ifdef B4800
|
|
|
|
{B4800, SERIAL_BAUD_4800},
|
|
|
|
#endif
|
|
|
|
/* {, SERIAL_BAUD_7200} /\* undefined on Linux *\/ */
|
|
|
|
#ifdef B9600
|
|
|
|
{B9600, SERIAL_BAUD_9600},
|
|
|
|
#endif
|
|
|
|
/* {, SERIAL_BAUD_14400} /\* undefined on Linux *\/ */
|
|
|
|
#ifdef B19200
|
|
|
|
{B19200, SERIAL_BAUD_19200},
|
|
|
|
#endif
|
|
|
|
#ifdef B38400
|
|
|
|
{B38400, SERIAL_BAUD_38400},
|
|
|
|
#endif
|
|
|
|
/* {, SERIAL_BAUD_56K}, /\* undefined on Linux *\/ */
|
|
|
|
#ifdef B57600
|
|
|
|
{B57600, SERIAL_BAUD_57600},
|
|
|
|
#endif
|
|
|
|
/* {, SERIAL_BAUD_128K} /\* undefined on Linux *\/ */
|
|
|
|
#ifdef B115200
|
|
|
|
{B115200, SERIAL_BAUD_115200}, /* _SERIAL_MAX_BAUD */
|
|
|
|
#endif
|
|
|
|
/* undefined by serial.sys:
|
|
|
|
#ifdef B230400
|
|
|
|
{B230400, },
|
|
|
|
#endif
|
|
|
|
#ifdef B460800
|
|
|
|
{B460800, },
|
|
|
|
#endif
|
|
|
|
#ifdef B500000
|
|
|
|
{B500000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B576000
|
|
|
|
{B576000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B921600
|
|
|
|
{B921600, },
|
|
|
|
#endif
|
|
|
|
#ifdef B1000000
|
|
|
|
{B1000000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B1152000
|
|
|
|
{B1152000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B1500000
|
|
|
|
{B1500000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B2000000
|
|
|
|
{B2000000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B2500000
|
|
|
|
{B2500000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B3000000
|
|
|
|
{B3000000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B3500000
|
|
|
|
{B3500000, },
|
|
|
|
#endif
|
|
|
|
#ifdef B4000000
|
|
|
|
{B4000000, }, __MAX_BAUD
|
|
|
|
#endif
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
|
|
|
#define _SERIAL_MAX_BAUD B115200
|
|
|
|
|
|
|
|
static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
|
2014-04-25 02:20:48 +04:00
|
|
|
{
|
2014-04-28 21:57:17 +04:00
|
|
|
int i;
|
2014-04-25 02:20:48 +04:00
|
|
|
|
2014-05-06 18:45:05 +04:00
|
|
|
/* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx
|
|
|
|
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx
|
|
|
|
*/
|
|
|
|
|
2014-04-30 00:25:07 +04:00
|
|
|
/* FIXME: properties should be better probe. The current
|
|
|
|
* implementation just relies on the Linux' implementation.
|
|
|
|
*/
|
|
|
|
|
2014-04-28 21:57:17 +04:00
|
|
|
// TMP: TODO:
|
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
|
2014-05-06 18:45:05 +04:00
|
|
|
if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
|
|
|
|
{
|
|
|
|
ZeroMemory(pProperties, sizeof(COMMPROP));
|
|
|
|
pProperties->wPacketLength = sizeof(COMMPROP);
|
|
|
|
}
|
|
|
|
|
|
|
|
pProperties->wPacketVersion = 2;
|
2014-04-28 21:57:17 +04:00
|
|
|
|
2014-05-06 18:45:05 +04:00
|
|
|
pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
|
|
|
|
|
|
|
|
/* pProperties->Reserved1; not used */
|
|
|
|
|
|
|
|
// TMP: FIXME: related to the UART's FIFO ?
|
2014-04-28 21:57:17 +04:00
|
|
|
/* pProperties->MaxTxQueue; */
|
|
|
|
/* pProperties->MaxRxQueue; */
|
2014-05-06 18:45:05 +04:00
|
|
|
|
2014-04-28 21:57:17 +04:00
|
|
|
pProperties->dwMaxBaud = SERIAL_BAUD_115200; /* _SERIAL_MAX_BAUD */
|
2014-05-06 18:08:58 +04:00
|
|
|
|
|
|
|
/* FIXME: what about PST_RS232? */
|
|
|
|
pProperties->dwProvSubType = PST_UNSPECIFIED;
|
|
|
|
|
|
|
|
/* TMP: TODO: to be finalized */
|
|
|
|
pProperties->dwProvCapabilities =
|
|
|
|
/*PCF_16BITMODE | PCF_DTRDSR | PCF_INTTIMEOUTS |*/ PCF_PARITY_CHECK | /*PCF_RLSD | */
|
|
|
|
PCF_RTSCTS | PCF_SETXCHAR | /*PCF_SPECIALCHARS | PCF_TOTALTIMEOUTS |*/ PCF_XONXOFF;
|
|
|
|
|
|
|
|
/* TMP: TODO: double check SP_RLSD */
|
|
|
|
pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY | SP_PARITY_CHECK | /*SP_RLSD |*/ SP_STOPBITS;
|
2014-04-28 21:57:17 +04:00
|
|
|
|
|
|
|
pProperties->dwSettableBaud = 0;
|
|
|
|
for (i=0; _SERIAL_SYS_BAUD_TABLE[i][0]<=_SERIAL_MAX_BAUD; i++)
|
|
|
|
{
|
|
|
|
pProperties->dwSettableBaud |= _SERIAL_SYS_BAUD_TABLE[i][1];
|
|
|
|
}
|
|
|
|
|
2014-04-30 00:25:07 +04:00
|
|
|
pProperties->wSettableData = DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 /*| DATABITS_16 | DATABITS_16X*/;
|
|
|
|
|
|
|
|
pProperties->wSettableStopParity = STOPBITS_10 | /*STOPBITS_15 |*/ STOPBITS_20 | PARITY_NONE | PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
|
|
|
|
|
2014-05-06 18:45:05 +04:00
|
|
|
// TMP: FIXME: related to the UART's FIFO ?
|
2014-04-28 21:57:17 +04:00
|
|
|
/* pProperties->CurrentTxQueue; */
|
|
|
|
/* pProperties->CurrentRxQueue; */
|
2014-05-06 18:45:05 +04:00
|
|
|
|
|
|
|
/* pProperties->ProvSpec1; see above */
|
|
|
|
/* pProperties->ProvSpec2; ignored */
|
|
|
|
/* pProperties->ProvChar[1]; ignored */
|
2014-04-28 21:57:17 +04:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-29 06:04:09 +04:00
|
|
|
static BOOL _set_baud_rate(WINPR_COMM *pComm, const SERIAL_BAUD_RATE *pBaudRate)
|
2014-04-28 21:57:17 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
speed_t newSpeed;
|
2014-04-29 06:04:09 +04:00
|
|
|
struct termios upcomingTermios;
|
2014-04-28 21:57:17 +04:00
|
|
|
|
2014-04-29 06:04:09 +04:00
|
|
|
ZeroMemory(&upcomingTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
|
2014-04-28 21:57:17 +04:00
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; _SERIAL_SYS_BAUD_TABLE[i][0]<=_SERIAL_MAX_BAUD; i++)
|
|
|
|
{
|
|
|
|
if (_SERIAL_SYS_BAUD_TABLE[i][1] == pBaudRate->BaudRate)
|
|
|
|
{
|
|
|
|
newSpeed = _SERIAL_SYS_BAUD_TABLE[i][0];
|
2014-04-29 06:04:09 +04:00
|
|
|
if (cfsetspeed(&upcomingTermios, newSpeed) < 0)
|
2014-04-28 21:57:17 +04:00
|
|
|
{
|
|
|
|
DEBUG_WARN("failed to set speed %d (%d)", newSpeed, pBaudRate->BaudRate);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-04-29 06:04:09 +04:00
|
|
|
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
|
2014-04-28 21:57:17 +04:00
|
|
|
{
|
|
|
|
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x0.8x", GetLastError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_WARN("could not find a matching speed for the baud rate %d", pBaudRate->BaudRate);
|
|
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL _get_baud_rate(WINPR_COMM *pComm, SERIAL_BAUD_RATE *pBaudRate)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
speed_t currentSpeed;
|
|
|
|
struct termios currentState;
|
|
|
|
|
|
|
|
ZeroMemory(¤tState, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, ¤tState) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentSpeed = cfgetispeed(¤tState);
|
|
|
|
|
|
|
|
for (i=0; _SERIAL_SYS_BAUD_TABLE[i][0]<=_SERIAL_MAX_BAUD; i++)
|
|
|
|
{
|
|
|
|
if (_SERIAL_SYS_BAUD_TABLE[i][0] == currentSpeed)
|
|
|
|
{
|
|
|
|
pBaudRate->BaudRate = _SERIAL_SYS_BAUD_TABLE[i][1];
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_WARN("could not find a matching baud rate for the speed 0x%x", currentSpeed);
|
|
|
|
SetLastError(ERROR_INVALID_DATA);
|
2014-04-25 02:20:48 +04:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-04-29 06:04:09 +04:00
|
|
|
/**
|
2014-05-06 18:08:58 +04:00
|
|
|
* NOTE: Only XonChar and XoffChar are plenty supported with the Linux
|
|
|
|
* N_TTY line discipline.
|
2014-04-29 06:04:09 +04:00
|
|
|
*
|
|
|
|
* ERRORS:
|
|
|
|
* ERROR_IO_DEVICE
|
|
|
|
* ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same;
|
2014-05-06 18:08:58 +04:00
|
|
|
* ERROR_NOT_SUPPORTED
|
2014-04-29 06:04:09 +04:00
|
|
|
*/
|
|
|
|
static BOOL _set_serial_chars(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChars)
|
|
|
|
{
|
2014-05-06 18:08:58 +04:00
|
|
|
BOOL result = TRUE;
|
2014-04-29 06:04:09 +04:00
|
|
|
struct termios upcomingTermios;
|
|
|
|
|
|
|
|
ZeroMemory(&upcomingTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pSerialChars->XonChar == pSerialChars->XoffChar)
|
|
|
|
{
|
|
|
|
/* http://msdn.microsoft.com/en-us/library/windows/hardware/ff546688%28v=vs.85%29.aspx */
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* termios(3): (..) above symbolic subscript values are all
|
|
|
|
* different, except that VTIME, VMIN may have the same value
|
|
|
|
* as VEOL, VEOF, respectively. In noncanonical mode the
|
|
|
|
* special character meaning is replaced by the timeout
|
|
|
|
* meaning.
|
|
|
|
*
|
2014-05-06 18:08:58 +04:00
|
|
|
* EofChar and c_cc[VEOF] are not quite the same, prefer to
|
|
|
|
* don't use c_cc[VEOF] at all.
|
|
|
|
*
|
|
|
|
* FIXME: might be implemented during read/write I/O
|
2014-04-29 06:04:09 +04:00
|
|
|
*/
|
2014-05-06 18:08:58 +04:00
|
|
|
if (pSerialChars->EofChar != '\0')
|
2014-04-29 06:04:09 +04:00
|
|
|
{
|
2014-05-06 18:08:58 +04:00
|
|
|
DEBUG_WARN("EofChar='%c' cannot be set\n", pSerialChars->EofChar);
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
2014-04-29 06:04:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* According the Linux's n_tty discipline, charaters with a
|
|
|
|
* parity error can only be let unchanged, replaced by \0 or
|
|
|
|
* get the prefix the prefix \377 \0
|
|
|
|
*/
|
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
/* FIXME: see also: _set_handflow() */
|
|
|
|
if (pSerialChars->ErrorChar != '\0')
|
2014-04-29 06:04:09 +04:00
|
|
|
{
|
2014-05-06 18:08:58 +04:00
|
|
|
DEBUG_WARN("ErrorChar='%c' (0x%x) cannot be set (unsupported).\n", pSerialChars->ErrorChar, pSerialChars->ErrorChar);
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
2014-04-29 06:04:09 +04:00
|
|
|
}
|
2014-05-06 18:08:58 +04:00
|
|
|
|
|
|
|
/* FIXME: see also: _set_handflow() */
|
|
|
|
if (pSerialChars->BreakChar != '\0')
|
2014-04-29 06:04:09 +04:00
|
|
|
{
|
2014-05-06 18:08:58 +04:00
|
|
|
DEBUG_WARN("BreakChar='%c' (0x%x) cannot be set (unsupported).\n", pSerialChars->BreakChar, pSerialChars->BreakChar);
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
2014-04-29 06:04:09 +04:00
|
|
|
}
|
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
/* TMP: FIXME: Didn't find anything similar yet on Linux */
|
|
|
|
if (pSerialChars->EventChar != '\0')
|
2014-04-29 06:04:09 +04:00
|
|
|
{
|
2014-05-06 18:08:58 +04:00
|
|
|
DEBUG_WARN("EventChar='%c' (0x%x) cannot be set\n", pSerialChars->EventChar, pSerialChars->EventChar);
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
2014-04-29 06:04:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
|
|
|
|
|
|
|
|
upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
|
|
|
|
|
|
|
|
|
|
|
|
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x0.8x", GetLastError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
return result;
|
2014-04-29 06:04:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL _get_serial_chars(WINPR_COMM *pComm, SERIAL_CHARS *pSerialChars)
|
|
|
|
{
|
|
|
|
struct termios currentTermios;
|
|
|
|
|
|
|
|
ZeroMemory(¤tTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, ¤tTermios) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
|
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
/* EofChar unsupported */
|
2014-04-29 06:04:09 +04:00
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
/* ErrorChar unsupported */
|
|
|
|
|
|
|
|
/* BreakChar unsupported */
|
2014-04-29 06:04:09 +04:00
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
/* TMP: FIXME: see also: _set_serial_chars() */
|
|
|
|
/* EventChar */
|
2014-04-29 06:04:09 +04:00
|
|
|
|
|
|
|
pSerialChars->XonChar = currentTermios.c_cc[VSTART];
|
|
|
|
|
|
|
|
pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-30 00:25:07 +04:00
|
|
|
static BOOL _set_line_control(WINPR_COMM *pComm, const SERIAL_LINE_CONTROL *pLineControl)
|
|
|
|
{
|
|
|
|
BOOL result = TRUE;
|
|
|
|
struct termios upcomingTermios;
|
|
|
|
|
|
|
|
|
|
|
|
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
|
|
|
|
*
|
|
|
|
* The use of 5 data bits with 2 stop bits is an invalid
|
|
|
|
* combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
|
|
|
|
*
|
|
|
|
* FIXME: prefered to let the underlying driver to deal with
|
|
|
|
* this issue. At least produce a warning message?
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
ZeroMemory(&upcomingTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: use of a COMMPROP to validate new settings? */
|
|
|
|
|
|
|
|
switch (pLineControl->StopBits)
|
|
|
|
{
|
|
|
|
case STOP_BIT_1:
|
|
|
|
upcomingTermios.c_cflag &= ~CSTOPB;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STOP_BITS_1_5:
|
|
|
|
DEBUG_WARN("Unsupported one and a half stop bits.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STOP_BITS_2:
|
|
|
|
upcomingTermios.c_cflag |= CSTOPB;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG_WARN("unexpected number of stop bits: %d\n", pLineControl->StopBits);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (pLineControl->Parity)
|
|
|
|
{
|
|
|
|
case NO_PARITY:
|
|
|
|
upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODD_PARITY:
|
|
|
|
upcomingTermios.c_cflag &= ~CMSPAR;
|
|
|
|
upcomingTermios.c_cflag |= PARENB | PARODD;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EVEN_PARITY:
|
|
|
|
upcomingTermios.c_cflag &= ~(PARODD | CMSPAR);
|
|
|
|
upcomingTermios.c_cflag |= PARENB;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MARK_PARITY:
|
|
|
|
upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPACE_PARITY:
|
|
|
|
upcomingTermios.c_cflag &= ~PARODD;
|
|
|
|
upcomingTermios.c_cflag |= PARENB | CMSPAR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG_WARN("unexpected type of parity: %d\n", pLineControl->Parity);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pLineControl->WordLength)
|
|
|
|
{
|
|
|
|
case 5:
|
|
|
|
upcomingTermios.c_cflag &= ~CSIZE;
|
|
|
|
upcomingTermios.c_cflag |= CS5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
upcomingTermios.c_cflag &= ~CSIZE;
|
|
|
|
upcomingTermios.c_cflag |= CS6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
upcomingTermios.c_cflag &= ~CSIZE;
|
|
|
|
upcomingTermios.c_cflag |= CS7;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
upcomingTermios.c_cflag &= ~CSIZE;
|
|
|
|
upcomingTermios.c_cflag |= CS8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DEBUG_WARN("unexpected number od data bits per character: %d\n", pLineControl->WordLength);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x0.8x", GetLastError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL _get_line_control(WINPR_COMM *pComm, SERIAL_LINE_CONTROL *pLineControl)
|
|
|
|
{
|
|
|
|
struct termios currentTermios;
|
|
|
|
|
|
|
|
ZeroMemory(¤tTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, ¤tTermios) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
|
|
|
|
|
|
|
|
if (!(currentTermios.c_cflag & PARENB))
|
|
|
|
{
|
|
|
|
pLineControl->Parity = NO_PARITY;
|
|
|
|
}
|
|
|
|
else if (currentTermios.c_cflag & CMSPAR)
|
|
|
|
{
|
|
|
|
pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* PARENB is set */
|
|
|
|
pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (currentTermios.c_cflag & CSIZE)
|
|
|
|
{
|
|
|
|
case CS5:
|
|
|
|
pLineControl->WordLength = 5;
|
|
|
|
break;
|
|
|
|
case CS6:
|
|
|
|
pLineControl->WordLength = 6;
|
|
|
|
break;
|
|
|
|
case CS7:
|
|
|
|
pLineControl->WordLength = 7;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pLineControl->WordLength = 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-06 18:08:58 +04:00
|
|
|
/* hard-coded in N_TTY */
|
|
|
|
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
|
|
|
|
#define TTY_THRESHOLD_UNTHROTTLE 128
|
|
|
|
|
|
|
|
static BOOL _set_handflow(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow)
|
|
|
|
{
|
|
|
|
BOOL result = TRUE;
|
|
|
|
struct termios upcomingTermios;
|
|
|
|
|
|
|
|
/* logical XOR */
|
|
|
|
if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) && (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
|
|
|
|
((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) && !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
|
|
|
|
{
|
|
|
|
DEBUG_WARN("SERIAL_DTR_CONTROL cannot be different SERIAL_RTS_CONTROL, HUPCL will be set according SERIAL_RTS_CONTROL.");
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZeroMemory(&upcomingTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ControlHandShake */
|
|
|
|
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag |= HUPCL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag &= ~HUPCL;
|
|
|
|
|
|
|
|
/* FIXME: is the DTR line also needs to be forced to a disable state? */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
|
|
|
|
{
|
|
|
|
/* DTR/DSR flow control not supported on Linux */
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag |= CRTSCTS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag &= ~CRTSCTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
|
|
|
|
{
|
|
|
|
/* DTR/DSR flow control not supported on Linux */
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
|
|
|
|
{
|
|
|
|
/* DCD flow control not supported on Linux */
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
// TMP: FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
|
|
|
|
{
|
|
|
|
/* DSR line control not supported on Linux */
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
// TMP: FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
|
|
|
|
{
|
|
|
|
/* Aborting operations on error not supported on Linux */
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FlowReplace */
|
|
|
|
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag |= IXON;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag &= ~IXON;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag |= IXOFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag &= ~IXOFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TMP: FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_ERROR_CHAR feature. A character with a parity error or framing error will be read as \0");
|
|
|
|
|
|
|
|
/* errors will be replaced by the character '\0' */
|
|
|
|
upcomingTermios.c_iflag &= ~IGNPAR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag |= IGNPAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag |= IGNBRK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_iflag &= ~IGNBRK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TMP: FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag |= HUPCL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag &= ~HUPCL;
|
|
|
|
|
|
|
|
/* FIXME: is the RTS line also needs to be forced to a disable state? */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag |= CRTSCTS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upcomingTermios.c_cflag &= ~CRTSCTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
|
|
|
|
{
|
|
|
|
/* not supported on Linux */
|
|
|
|
DEBUG_WARN("Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XonLimit */
|
|
|
|
|
|
|
|
// FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("Attempt to set XonLimit with an unsupported value: %d", pHandflow->XonLimit);
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XoffChar */
|
|
|
|
|
|
|
|
// FIXME: could be implemented during read/write I/O
|
|
|
|
if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("Attempt to set XoffLimit with an unsupported value: %d", pHandflow->XoffLimit);
|
|
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
|
result = FALSE; /* but keep on */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
|
|
|
|
{
|
|
|
|
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x0.8x", GetLastError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL _get_handflow(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow)
|
|
|
|
{
|
|
|
|
struct termios currentTermios;
|
|
|
|
|
|
|
|
ZeroMemory(¤tTermios, sizeof(struct termios));
|
|
|
|
if (tcgetattr(pComm->fd, ¤tTermios) < 0)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ControlHandShake */
|
|
|
|
|
|
|
|
pHandflow->ControlHandShake = 0;
|
|
|
|
|
|
|
|
if (currentTermios.c_cflag & HUPCL)
|
|
|
|
pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
|
|
|
|
|
|
|
|
/* SERIAL_DTR_HANDSHAKE unsupported */
|
|
|
|
|
|
|
|
if (currentTermios.c_cflag & CRTSCTS)
|
|
|
|
pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
|
|
|
|
|
|
|
|
/* SERIAL_DSR_HANDSHAKE unsupported */
|
|
|
|
|
|
|
|
/* SERIAL_DCD_HANDSHAKE unsupported */
|
|
|
|
|
|
|
|
/* SERIAL_DSR_SENSITIVITY unsupported */
|
|
|
|
|
|
|
|
/* SERIAL_ERROR_ABORT unsupported */
|
|
|
|
|
|
|
|
|
|
|
|
/* FlowReplace */
|
|
|
|
|
|
|
|
pHandflow->FlowReplace = 0;
|
|
|
|
|
|
|
|
if (currentTermios.c_iflag & IXON)
|
|
|
|
pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
|
|
|
|
|
|
|
|
if (currentTermios.c_iflag & IXOFF)
|
|
|
|
pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
|
|
|
|
|
|
|
|
if (!(currentTermios.c_iflag & IGNPAR))
|
|
|
|
pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
|
|
|
|
|
|
|
|
if (currentTermios.c_iflag & IGNBRK)
|
|
|
|
pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
|
|
|
|
|
|
|
|
/* SERIAL_BREAK_CHAR unsupported */
|
|
|
|
|
|
|
|
if (currentTermios.c_cflag & HUPCL)
|
|
|
|
pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
|
|
|
|
|
|
|
|
if (currentTermios.c_cflag & CRTSCTS)
|
|
|
|
pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
|
|
|
|
|
|
|
|
/* SERIAL_XOFF_CONTINUE unsupported */
|
|
|
|
|
|
|
|
|
|
|
|
/* XonLimit */
|
|
|
|
|
|
|
|
pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
|
|
|
|
|
|
|
|
|
|
|
|
/* XoffLimit */
|
|
|
|
|
|
|
|
pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2014-04-25 02:20:48 +04:00
|
|
|
|
|
|
|
static REMOTE_SERIAL_DRIVER _SerialSys =
|
|
|
|
{
|
2014-04-29 06:04:09 +04:00
|
|
|
.id = RemoteSerialDriverSerialSys,
|
|
|
|
.name = _T("Serial.sys"),
|
|
|
|
.set_baud_rate = _set_baud_rate,
|
|
|
|
.get_baud_rate = _get_baud_rate,
|
|
|
|
.get_properties = _get_properties,
|
|
|
|
.set_serial_chars = _set_serial_chars,
|
|
|
|
.get_serial_chars = _get_serial_chars,
|
2014-04-30 00:25:07 +04:00
|
|
|
.set_line_control = _set_line_control,
|
2014-05-06 18:08:58 +04:00
|
|
|
.get_line_control = _get_line_control,
|
|
|
|
.set_handflow = _set_handflow,
|
|
|
|
.get_handflow = _get_handflow,
|
2014-04-25 02:20:48 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-04-28 21:57:17 +04:00
|
|
|
REMOTE_SERIAL_DRIVER* SerialSys_s()
|
2014-04-25 02:20:48 +04:00
|
|
|
{
|
|
|
|
return &_SerialSys;
|
|
|
|
}
|
2014-04-28 21:57:17 +04:00
|
|
|
|
|
|
|
#endif /* _WIN32 */
|