winpr-comm: got IOCTL_SERIAL_SET_HANDFLOW / IOCTL_SERIAL_GET_HANDFLOW
This commit is contained in:
parent
494b7e8f93
commit
c2b024512a
@ -298,7 +298,7 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
|
||||
break;
|
||||
|
||||
case ERROR_NOT_SUPPORTED:
|
||||
irp->IoStatus = STATUS_INVALID_PARAMETER;
|
||||
irp->IoStatus = STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
@ -309,6 +309,10 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
|
||||
irp->IoStatus = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
|
||||
case ERROR_CALL_NOT_IMPLEMENTED:
|
||||
irp->IoStatus = STATUS_NOT_IMPLEMENTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_SVC("unexpected last-error: 0x%x", GetLastError());
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
|
@ -224,8 +224,67 @@ BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
|
||||
|
||||
lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
|
||||
|
||||
/* TMP: TODO: */
|
||||
/* (...) */
|
||||
SERIAL_HANDFLOW handflow;
|
||||
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow, sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
|
||||
{
|
||||
DEBUG_WARN("GetCommState failure: could not get the handflow settings.");
|
||||
goto error_handle;
|
||||
}
|
||||
|
||||
lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
|
||||
|
||||
lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
|
||||
|
||||
if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
|
||||
{
|
||||
lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
|
||||
}
|
||||
else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
|
||||
{
|
||||
lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
|
||||
}
|
||||
|
||||
lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;
|
||||
|
||||
lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;
|
||||
|
||||
lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;
|
||||
|
||||
lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;
|
||||
|
||||
lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;
|
||||
|
||||
lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;
|
||||
|
||||
if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
|
||||
{
|
||||
lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
|
||||
}
|
||||
else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
|
||||
{
|
||||
lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
|
||||
}
|
||||
|
||||
// FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow Control Enabled bit in its Modem Control Register (MCR)
|
||||
|
||||
|
||||
lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
|
||||
|
||||
/* lpLocalDcb->fDummy2 not used */
|
||||
|
||||
lpLocalDcb->wReserved = 0; /* must be zero */
|
||||
|
||||
lpLocalDcb->XonLim = handflow.XonLimit;
|
||||
|
||||
lpLocalDcb->XoffLim = handflow.XoffLimit;
|
||||
|
||||
SERIAL_LINE_CONTROL lineControl;
|
||||
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl, sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
|
||||
@ -316,7 +375,7 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
|
||||
}
|
||||
|
||||
SERIAL_CHARS serialChars;
|
||||
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL))
|
||||
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL)) /* as of today, required for BreakChar */
|
||||
{
|
||||
DEBUG_WARN("SetCommState failure: could not get the initial serial chars.");
|
||||
return FALSE;
|
||||
@ -325,7 +384,7 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
|
||||
serialChars.XoffChar = lpDCB->XoffChar;
|
||||
serialChars.ErrorChar = lpDCB->ErrorChar;
|
||||
serialChars.EofChar = lpDCB->EofChar;
|
||||
serialChars.EventChar = lpDCB->EvtChar;
|
||||
serialChars.EventChar = lpDCB->EvtChar;
|
||||
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS), NULL, 0, &bytesReturned, NULL))
|
||||
{
|
||||
DEBUG_WARN("SetCommState failure: could not set the serial chars.");
|
||||
@ -343,6 +402,113 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
|
||||
}
|
||||
|
||||
|
||||
SERIAL_HANDFLOW handflow;
|
||||
ZeroMemory(&handflow, sizeof(SERIAL_HANDFLOW));
|
||||
|
||||
if (lpDCB->fOutxCtsFlow)
|
||||
{
|
||||
handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
|
||||
}
|
||||
|
||||
|
||||
if (lpDCB->fOutxDsrFlow)
|
||||
{
|
||||
handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
|
||||
}
|
||||
|
||||
switch (lpDCB->fDtrControl)
|
||||
{
|
||||
case SERIAL_DTR_HANDSHAKE:
|
||||
handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
|
||||
break;
|
||||
|
||||
case SERIAL_DTR_CONTROL:
|
||||
handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
|
||||
break;
|
||||
|
||||
case DTR_CONTROL_DISABLE:
|
||||
/* do nothing since handflow is init-zeroed */
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_WARN("Unexpected fDtrControl value: %d\n", lpDCB->fDtrControl);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpDCB->fDsrSensitivity)
|
||||
{
|
||||
handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
|
||||
}
|
||||
|
||||
if (lpDCB->fTXContinueOnXoff)
|
||||
{
|
||||
handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
|
||||
}
|
||||
|
||||
if (lpDCB->fOutX)
|
||||
{
|
||||
handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
|
||||
}
|
||||
|
||||
if (lpDCB->fInX)
|
||||
{
|
||||
handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
|
||||
}
|
||||
|
||||
if (lpDCB->fErrorChar)
|
||||
{
|
||||
handflow.FlowReplace |= SERIAL_ERROR_CHAR;
|
||||
}
|
||||
|
||||
if (lpDCB->fNull)
|
||||
{
|
||||
handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
|
||||
}
|
||||
|
||||
switch (lpDCB->fRtsControl)
|
||||
{
|
||||
case RTS_CONTROL_TOGGLE:
|
||||
DEBUG_WARN("Unsupported RTS_CONTROL_TOGGLE feature");
|
||||
// FIXME: see also GetCommState()
|
||||
return FALSE;
|
||||
|
||||
case RTS_CONTROL_HANDSHAKE:
|
||||
handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
|
||||
break;
|
||||
|
||||
case RTS_CONTROL_ENABLE:
|
||||
handflow.FlowReplace |= SERIAL_RTS_CONTROL;
|
||||
break;
|
||||
|
||||
case RTS_CONTROL_DISABLE:
|
||||
/* do nothing since handflow is init-zeroed */
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_WARN("Unexpected fRtsControl value: %d\n", lpDCB->fRtsControl);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpDCB->fAbortOnError)
|
||||
{
|
||||
handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
|
||||
}
|
||||
|
||||
/* lpDCB->fDummy2 not used */
|
||||
|
||||
/* lpLocalDcb->wReserved ignored */
|
||||
|
||||
handflow.XonLimit = lpDCB->XonLim;
|
||||
|
||||
handflow.XoffLimit = lpDCB->XoffLim;
|
||||
|
||||
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW), NULL, 0, &bytesReturned, NULL))
|
||||
{
|
||||
DEBUG_WARN("SetCommState failure: could not set the handflow settings.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/** upcomingTermios stage **/
|
||||
|
||||
|
||||
|
@ -135,6 +135,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
|
||||
return pRemoteSerialDriver->set_baud_rate(pComm, pBaudRate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_GET_BAUD_RATE:
|
||||
{
|
||||
@ -155,6 +156,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
*lpBytesReturned = sizeof(SERIAL_BAUD_RATE);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_GET_PROPERTIES:
|
||||
{
|
||||
@ -175,6 +177,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
*lpBytesReturned = sizeof(COMMPROP);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_SET_CHARS:
|
||||
{
|
||||
@ -191,6 +194,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
|
||||
return pRemoteSerialDriver->set_serial_chars(pComm, pSerialChars);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_GET_CHARS:
|
||||
{
|
||||
@ -211,6 +215,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
*lpBytesReturned = sizeof(SERIAL_CHARS);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_SET_LINE_CONTROL:
|
||||
{
|
||||
@ -227,6 +232,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
|
||||
return pRemoteSerialDriver->set_line_control(pComm, pLineControl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_GET_LINE_CONTROL:
|
||||
{
|
||||
@ -247,11 +253,50 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
|
||||
*lpBytesReturned = sizeof(SERIAL_LINE_CONTROL);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_SET_HANDFLOW:
|
||||
{
|
||||
if (pRemoteSerialDriver->set_handflow)
|
||||
{
|
||||
SERIAL_HANDFLOW *pHandflow = (SERIAL_HANDFLOW*)lpInBuffer;
|
||||
|
||||
assert(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
|
||||
if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_DATA);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return pRemoteSerialDriver->set_handflow(pComm, pHandflow);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IOCTL_SERIAL_GET_HANDFLOW:
|
||||
{
|
||||
if (pRemoteSerialDriver->get_handflow)
|
||||
{
|
||||
SERIAL_HANDFLOW *pHandflow = (SERIAL_HANDFLOW*)lpOutBuffer;
|
||||
|
||||
assert(nOutBufferSize >= sizeof(SERIAL_HANDFLOW));
|
||||
if (nOutBufferSize < sizeof(SERIAL_HANDFLOW))
|
||||
{
|
||||
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!pRemoteSerialDriver->get_handflow(pComm, pHandflow))
|
||||
return FALSE;
|
||||
|
||||
*lpBytesReturned = sizeof(SERIAL_HANDFLOW);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DEBUG_WARN(_T("unsupported IoControlCode: Ox%x (remote serial driver: %s)"), dwIoControlCode, pRemoteSerialDriver->name);
|
||||
DEBUG_WARN(_T("unsupported IoControlCode: Ox%0.8x (remote serial driver: %s)"), dwIoControlCode, pRemoteSerialDriver->name);
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
@ -67,8 +67,8 @@ extern "C" {
|
||||
/* IOCTL_SERIAL_WAIT_ON_MASK 0x001B0048 */
|
||||
/* IOCTL_SERIAL_IMMEDIATE_CHAR 0x001B0018 */
|
||||
/* IOCTL_SERIAL_PURGE 0x001B004C */
|
||||
/* IOCTL_SERIAL_GET_HANDFLOW 0x001B0060 */
|
||||
/* IOCTL_SERIAL_SET_HANDFLOW 0x001B0064 */
|
||||
#define IOCTL_SERIAL_GET_HANDFLOW 0x001B0060
|
||||
#define IOCTL_SERIAL_SET_HANDFLOW 0x001B0064
|
||||
/* IOCTL_SERIAL_GET_MODEMSTATUS 0x001B0068 */
|
||||
/* IOCTL_SERIAL_GET_DTRRTS 0x001B0078 */
|
||||
/* IOCTL_SERIAL_GET_COMMSTATUS 0x001B0084 */
|
||||
@ -132,6 +132,38 @@ typedef struct _SERIAL_LINE_CONTROL
|
||||
} SERIAL_LINE_CONTROL, *PSERIAL_LINE_CONTROL;
|
||||
|
||||
|
||||
typedef struct _SERIAL_HANDFLOW
|
||||
{
|
||||
ULONG ControlHandShake;
|
||||
ULONG FlowReplace;
|
||||
LONG XonLimit;
|
||||
LONG XoffLimit;
|
||||
} SERIAL_HANDFLOW, *PSERIAL_HANDFLOW;
|
||||
|
||||
|
||||
#define SERIAL_DTR_MASK ((ULONG)0x03)
|
||||
#define SERIAL_DTR_CONTROL ((ULONG)0x01)
|
||||
#define SERIAL_DTR_HANDSHAKE ((ULONG)0x02)
|
||||
#define SERIAL_CTS_HANDSHAKE ((ULONG)0x08)
|
||||
#define SERIAL_DSR_HANDSHAKE ((ULONG)0x10)
|
||||
#define SERIAL_DCD_HANDSHAKE ((ULONG)0x20)
|
||||
#define SERIAL_OUT_HANDSHAKEMASK ((ULONG)0x38)
|
||||
#define SERIAL_DSR_SENSITIVITY ((ULONG)0x40)
|
||||
#define SERIAL_ERROR_ABORT ((ULONG)0x80000000)
|
||||
#define SERIAL_CONTROL_INVALID ((ULONG)0x7fffff84)
|
||||
#define SERIAL_AUTO_TRANSMIT ((ULONG)0x01)
|
||||
#define SERIAL_AUTO_RECEIVE ((ULONG)0x02)
|
||||
#define SERIAL_ERROR_CHAR ((ULONG)0x04)
|
||||
#define SERIAL_NULL_STRIPPING ((ULONG)0x08)
|
||||
#define SERIAL_BREAK_CHAR ((ULONG)0x10)
|
||||
#define SERIAL_RTS_MASK ((ULONG)0xc0)
|
||||
#define SERIAL_RTS_CONTROL ((ULONG)0x40)
|
||||
#define SERIAL_RTS_HANDSHAKE ((ULONG)0x80)
|
||||
#define SERIAL_TRANSMIT_TOGGLE ((ULONG)0xc0)
|
||||
#define SERIAL_XOFF_CONTINUE ((ULONG)0x80000000)
|
||||
#define SERIAL_FLOW_INVALID ((ULONG)0x7fffff20)
|
||||
|
||||
|
||||
/**
|
||||
* A function might be NULL if not supported by the underlying remote driver.
|
||||
*
|
||||
@ -148,6 +180,8 @@ typedef struct _REMOTE_SERIAL_DRIVER
|
||||
BOOL (*get_serial_chars)(WINPR_COMM *pComm, SERIAL_CHARS *pSerialChars);
|
||||
BOOL (*set_line_control)(WINPR_COMM *pComm, const SERIAL_LINE_CONTROL *pLineControl);
|
||||
BOOL (*get_line_control)(WINPR_COMM *pComm, SERIAL_LINE_CONTROL *pLineControl);
|
||||
BOOL (*set_handflow)(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow);
|
||||
BOOL (*get_handflow)(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow);
|
||||
|
||||
} REMOTE_SERIAL_DRIVER;
|
||||
|
||||
|
@ -63,22 +63,31 @@ static REMOTE_SERIAL_DRIVER _SerCx2Sys =
|
||||
.get_serial_chars = _get_serial_chars,
|
||||
.set_line_control = NULL,
|
||||
.get_line_control = NULL,
|
||||
.set_handflow = NULL,
|
||||
.get_handflow = NULL,
|
||||
};
|
||||
|
||||
|
||||
REMOTE_SERIAL_DRIVER* SerCx2Sys_s()
|
||||
{
|
||||
/* _SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */
|
||||
//REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s();
|
||||
REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s();
|
||||
REMOTE_SERIAL_DRIVER* pSerCxSys = SerCxSys_s();
|
||||
|
||||
_SerCx2Sys.set_baud_rate = pSerCxSys->set_baud_rate;
|
||||
_SerCx2Sys.get_baud_rate = pSerCxSys->get_baud_rate;
|
||||
|
||||
_SerCx2Sys.get_properties = pSerCxSys->get_properties;
|
||||
|
||||
_SerCx2Sys.set_line_control = pSerCxSys->set_line_control;
|
||||
_SerCx2Sys.get_line_control = pSerCxSys->get_line_control;
|
||||
|
||||
/* Only SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL and SERIAL_RTS_HANDSHAKE flags are really required by SerCx2.sys
|
||||
* http://msdn.microsoft.com/en-us/library/jj680685%28v=vs.85%29.aspx
|
||||
*/
|
||||
_SerCx2Sys.set_handflow = pSerialSys->set_handflow;
|
||||
_SerCx2Sys.get_handflow = pSerialSys->get_handflow;
|
||||
|
||||
return &_SerCx2Sys;
|
||||
}
|
||||
|
||||
|
@ -231,6 +231,267 @@ static BOOL _get_baud_rate(WINPR_COMM *pComm, SERIAL_BAUD_RATE *pBaudRate)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* hard-coded in N_TTY */
|
||||
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
|
||||
#define TTY_THRESHOLD_UNTHROTTLE 128
|
||||
|
||||
/* FIXME: mostly copied/pasted from comm_serial_sys.c, better share this code */
|
||||
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 */
|
||||
}
|
||||
|
||||
/* SERIAL_DCD_HANDSHAKE unsupported by SerCx */
|
||||
if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_DCD_HANDSHAKE (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
/* SERIAL_DSR_SENSITIVITY unsupported by SerCx */
|
||||
if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_DSR_SENSITIVITY (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
/* SERIAL_ERROR_ABORT unsupported by SerCx */
|
||||
if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_ERROR_ABORT (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
|
||||
/* FlowReplace */
|
||||
|
||||
/* SERIAL_AUTO_TRANSMIT unsupported by SerCx */
|
||||
if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_AUTO_TRANSMIT (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
|
||||
/* SERIAL_AUTO_RECEIVE unsupported by SerCx */
|
||||
if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_AUTO_RECEIVE (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
/* SERIAL_ERROR_CHAR unsupported by SerCx */
|
||||
if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_ERROR_CHAR (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
/* SERIAL_NULL_STRIPPING unsupported by SerCx */
|
||||
if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_NULL_STRIPPING (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
/* SERIAL_BREAK_CHAR unsupported by SerCx */
|
||||
if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_BREAK_CHAR (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
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;
|
||||
}
|
||||
|
||||
/* SERIAL_XOFF_CONTINUE unsupported by SerCx */
|
||||
if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
|
||||
{
|
||||
DEBUG_WARN("Attempt to set SERIAL_XOFF_CONTINUE (not implemented)");
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: mostly copied/pasted from comm_serial_sys.c, better share this code */
|
||||
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 by SerCx */
|
||||
|
||||
/* SERIAL_DSR_SENSITIVITY unsupported by SerCx */
|
||||
|
||||
/* SERIAL_ERROR_ABORT unsupported by SerCx */
|
||||
|
||||
|
||||
/* FlowReplace */
|
||||
|
||||
pHandflow->FlowReplace = 0;
|
||||
|
||||
/* SERIAL_AUTO_TRANSMIT unsupported by SerCx */
|
||||
|
||||
/* SERIAL_AUTO_RECEIVE unsupported by SerCx */
|
||||
|
||||
/* SERIAL_ERROR_CHAR unsupported by SerCx */
|
||||
|
||||
/* SERIAL_NULL_STRIPPING unsupported by SerCx */
|
||||
|
||||
/* SERIAL_BREAK_CHAR unsupported by SerCx */
|
||||
|
||||
if (currentTermios.c_cflag & HUPCL)
|
||||
pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
|
||||
|
||||
if (currentTermios.c_cflag & CRTSCTS)
|
||||
pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
|
||||
|
||||
/* SERIAL_XOFF_CONTINUE unsupported by SerCx */
|
||||
|
||||
|
||||
/* XonLimit */
|
||||
|
||||
pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
|
||||
|
||||
|
||||
/* XoffLimit */
|
||||
|
||||
pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* specific functions only */
|
||||
static REMOTE_SERIAL_DRIVER _SerCxSys =
|
||||
@ -244,6 +505,8 @@ static REMOTE_SERIAL_DRIVER _SerCxSys =
|
||||
.get_serial_chars = NULL,
|
||||
.set_line_control = NULL,
|
||||
.get_line_control = NULL,
|
||||
.set_handflow = _set_handflow,
|
||||
.get_handflow = _get_handflow,
|
||||
};
|
||||
|
||||
|
||||
|
@ -148,6 +148,8 @@ static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
|
||||
|
||||
// TMP: TODO:
|
||||
|
||||
// TMP: COMMPROP_INITIALIZED ?
|
||||
|
||||
// TMP: required?
|
||||
// ZeroMemory(pProperties, sizeof(COMMPROP);
|
||||
|
||||
@ -158,9 +160,17 @@ static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
|
||||
/* pProperties->MaxTxQueue; */
|
||||
/* pProperties->MaxRxQueue; */
|
||||
pProperties->dwMaxBaud = SERIAL_BAUD_115200; /* _SERIAL_MAX_BAUD */
|
||||
/* pProperties->ProvSubType; */
|
||||
/* pProperties->ProvCapabilities; */
|
||||
/* pProperties->SettableParams; */
|
||||
|
||||
/* 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;
|
||||
|
||||
pProperties->dwSettableBaud = 0;
|
||||
for (i=0; _SERIAL_SYS_BAUD_TABLE[i][0]<=_SERIAL_MAX_BAUD; i++)
|
||||
@ -251,15 +261,17 @@ static BOOL _get_baud_rate(WINPR_COMM *pComm, SERIAL_BAUD_RATE *pBaudRate)
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: Only XonChar and XoffChar are plenty supported with Linux
|
||||
* N_TTY line discipline.
|
||||
* NOTE: Only XonChar and XoffChar are plenty supported with the Linux
|
||||
* N_TTY line discipline.
|
||||
*
|
||||
* ERRORS:
|
||||
* ERROR_IO_DEVICE
|
||||
* ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same;
|
||||
* ERROR_NOT_SUPPORTED
|
||||
*/
|
||||
static BOOL _set_serial_chars(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChars)
|
||||
{
|
||||
BOOL result = TRUE;
|
||||
struct termios upcomingTermios;
|
||||
|
||||
ZeroMemory(&upcomingTermios, sizeof(struct termios));
|
||||
@ -282,55 +294,46 @@ static BOOL _set_serial_chars(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChar
|
||||
* special character meaning is replaced by the timeout
|
||||
* meaning.
|
||||
*
|
||||
* It doesn't seem the case of the Linux's implementation but
|
||||
* in our context, the cannonical mode (fBinary=FALSE) should
|
||||
* never be enabled.
|
||||
* 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
|
||||
*/
|
||||
|
||||
if (upcomingTermios.c_lflag & ICANON)
|
||||
if (pSerialChars->EofChar != '\0')
|
||||
{
|
||||
upcomingTermios.c_cc[VEOF] = pSerialChars->EofChar;
|
||||
DEBUG_WARN("c_cc[VEOF] is not supposed to be modified!");
|
||||
DEBUG_WARN("EofChar='%c' cannot be set\n", pSerialChars->EofChar);
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
/* else let c_cc[VEOF] unchanged */
|
||||
|
||||
|
||||
/* 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
|
||||
*/
|
||||
|
||||
if (pSerialChars->ErrorChar == '\0')
|
||||
/* FIXME: see also: _set_handflow() */
|
||||
if (pSerialChars->ErrorChar != '\0')
|
||||
{
|
||||
/* Also suppose PARENB !IGNPAR !PARMRK to be effective */
|
||||
upcomingTermios.c_iflag |= INPCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* FIXME: develop a line discipline dedicated to the
|
||||
* RDP redirection. Erroneous characters might also be
|
||||
* caught during read/write operations?
|
||||
*/
|
||||
DEBUG_WARN("ErrorChar='%c' cannot be set, characters with a parity error will be let unchanged.\n", pSerialChars->ErrorChar);
|
||||
DEBUG_WARN("ErrorChar='%c' (0x%x) cannot be set (unsupported).\n", pSerialChars->ErrorChar, pSerialChars->ErrorChar);
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
if (pSerialChars->BreakChar == '\0')
|
||||
/* FIXME: see also: _set_handflow() */
|
||||
if (pSerialChars->BreakChar != '\0')
|
||||
{
|
||||
upcomingTermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* FIXME: develop a line discipline dedicated to the
|
||||
* RDP redirection. Break characters might also be
|
||||
* caught during read/write operations?
|
||||
*/
|
||||
DEBUG_WARN("BreakChar='%c' cannot be set.\n", pSerialChars->ErrorChar);
|
||||
DEBUG_WARN("BreakChar='%c' (0x%x) cannot be set (unsupported).\n", pSerialChars->BreakChar, pSerialChars->BreakChar);
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
/* FIXME: Didn't find anything similar inside N_TTY. Develop a
|
||||
* line discipline dedicated to the RDP redirection.
|
||||
*/
|
||||
DEBUG_WARN("EventChar='%c' cannot be set\n", pSerialChars->EventChar);
|
||||
/* TMP: FIXME: Didn't find anything similar yet on Linux */
|
||||
if (pSerialChars->EventChar != '\0')
|
||||
{
|
||||
DEBUG_WARN("EventChar='%c' (0x%x) cannot be set\n", pSerialChars->EventChar, pSerialChars->EventChar);
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
result = FALSE; /* but keep on */
|
||||
}
|
||||
|
||||
upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
|
||||
|
||||
@ -343,7 +346,7 @@ static BOOL _set_serial_chars(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChar
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -360,23 +363,14 @@ static BOOL _get_serial_chars(WINPR_COMM *pComm, SERIAL_CHARS *pSerialChars)
|
||||
|
||||
ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
|
||||
|
||||
if (currentTermios.c_lflag & ICANON)
|
||||
{
|
||||
pSerialChars->EofChar = currentTermios.c_cc[VEOF];
|
||||
}
|
||||
/* EofChar unsupported */
|
||||
|
||||
/* FIXME: see also: _set_serial_chars() */
|
||||
if (currentTermios.c_iflag & INPCK)
|
||||
pSerialChars->ErrorChar = '\0';
|
||||
/* else '\0' is currently used anyway */
|
||||
/* ErrorChar unsupported */
|
||||
|
||||
/* BreakChar unsupported */
|
||||
|
||||
/* FIXME: see also: _set_serial_chars() */
|
||||
if (currentTermios.c_iflag & ~IGNBRK & BRKINT)
|
||||
pSerialChars->BreakChar = '\0';
|
||||
/* else '\0' is currently used anyway */
|
||||
|
||||
/* FIXME: see also: _set_serial_chars() */
|
||||
pSerialChars->EventChar = '\0';
|
||||
/* TMP: FIXME: see also: _set_serial_chars() */
|
||||
/* EventChar */
|
||||
|
||||
pSerialChars->XonChar = currentTermios.c_cc[VSTART];
|
||||
|
||||
@ -548,6 +542,279 @@ static BOOL _get_line_control(WINPR_COMM *pComm, SERIAL_LINE_CONTROL *pLineContr
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
static REMOTE_SERIAL_DRIVER _SerialSys =
|
||||
{
|
||||
@ -559,7 +826,9 @@ static REMOTE_SERIAL_DRIVER _SerialSys =
|
||||
.set_serial_chars = _set_serial_chars,
|
||||
.get_serial_chars = _get_serial_chars,
|
||||
.set_line_control = _set_line_control,
|
||||
.get_line_control = _get_line_control
|
||||
.get_line_control = _get_line_control,
|
||||
.set_handflow = _set_handflow,
|
||||
.get_handflow = _get_handflow,
|
||||
};
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@ set(${MODULE_PREFIX}_TESTS
|
||||
TestSetCommState.c
|
||||
TestSerialChars.c
|
||||
TestControlSettings.c
|
||||
TestHandflow.c
|
||||
TestCommMonitor.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS
|
||||
|
@ -108,8 +108,6 @@ int TestCommConfig(int argc, char* argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* TODO: */
|
||||
|
||||
dcb.BaudRate = CBR_57600;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
@ -120,7 +118,7 @@ int TestCommConfig(int argc, char* argv[])
|
||||
if (!success)
|
||||
{
|
||||
fprintf(stderr, "SetCommState failure: GetLastError() = 0x%x\n", (int) GetLastError());
|
||||
return 0;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
success = GetCommState(hComm, &dcb);
|
||||
@ -131,8 +129,11 @@ int TestCommConfig(int argc, char* argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "BaudRate: %d ByteSize: %d Parity: %d StopBits: %d\n",
|
||||
(int) dcb.BaudRate, (int) dcb.ByteSize, (int) dcb.Parity, (int) dcb.StopBits);
|
||||
if ((dcb.BaudRate != CBR_57600) || (dcb.ByteSize != 8) || (dcb.Parity != NOPARITY) || (dcb.StopBits != ONESTOPBIT))
|
||||
{
|
||||
fprintf(stderr, "Got an unexpeted value among: BaudRate: %d ByteSize: %d Parity: %d StopBits: %d\n",
|
||||
(int) dcb.BaudRate, (int) dcb.ByteSize, (int) dcb.Parity, (int) dcb.StopBits);
|
||||
}
|
||||
|
||||
CloseHandle(hComm);
|
||||
|
||||
|
86
winpr/libwinpr/comm/test/TestHandflow.c
Normal file
86
winpr/libwinpr/comm/test/TestHandflow.c
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Serial Communication API
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <winpr/comm.h>
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include "../comm.h"
|
||||
|
||||
static BOOL test_SerialSys(HANDLE hComm)
|
||||
{
|
||||
// TMP: TODO:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int TestHandflow(int argc, char* argv[])
|
||||
{
|
||||
BOOL result;
|
||||
HANDLE hComm;
|
||||
|
||||
// TMP: FIXME: check if we can proceed with tests on the actual device, skip and warn otherwise but don't fail
|
||||
result = DefineCommDevice("COM1", "/dev/ttyS0");
|
||||
if (!result)
|
||||
{
|
||||
fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
hComm = CreateFile("COM1",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (hComm == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
_comm_setRemoteSerialDriver(hComm, RemoteSerialDriverSerialSys);
|
||||
if (!test_SerialSys(hComm))
|
||||
{
|
||||
fprintf(stderr, "test_SerCxSys failure\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* _comm_setRemoteSerialDriver(hComm, RemoteSerialDriverSerCxSys); */
|
||||
/* if (!test_SerCxSys(hComm)) */
|
||||
/* { */
|
||||
/* fprintf(stderr, "test_SerCxSys failure\n"); */
|
||||
/* return EXIT_FAILURE; */
|
||||
/* } */
|
||||
|
||||
/* _comm_setRemoteSerialDriver(hComm, RemoteSerialDriverSerCx2Sys); */
|
||||
/* if (!test_SerCx2Sys(hComm)) */
|
||||
/* { */
|
||||
/* fprintf(stderr, "test_SerCxSys failure\n"); */
|
||||
/* return EXIT_FAILURE; */
|
||||
/* } */
|
||||
|
||||
|
||||
if (!CloseHandle(hComm))
|
||||
{
|
||||
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <winpr/comm.h>
|
||||
#include <winpr/crt.h>
|
||||
@ -29,6 +30,16 @@ static BOOL test_SerCxSys(HANDLE hComm)
|
||||
DCB dcb;
|
||||
UCHAR XonChar, XoffChar;
|
||||
|
||||
struct termios currentTermios;
|
||||
|
||||
ZeroMemory(¤tTermios, sizeof(struct termios));
|
||||
if (tcgetattr(((WINPR_COMM*)hComm)->fd, ¤tTermios) < 0)
|
||||
{
|
||||
fprintf(stderr, "tcgetattr failure.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
ZeroMemory(&dcb, sizeof(DCB));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
if (!GetCommState(hComm, &dcb))
|
||||
@ -43,6 +54,14 @@ static BOOL test_SerCxSys(HANDLE hComm)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* retrieve Xon/Xoff chars */
|
||||
if ((dcb.XonChar != currentTermios.c_cc[VSTART]) || (dcb.XoffChar != currentTermios.c_cc[VSTOP]))
|
||||
{
|
||||
fprintf(stderr, "test_SerCxSys failure, could not retrieve XonChar and XoffChar\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* swap XonChar/XoffChar */
|
||||
|
||||
XonChar = dcb.XonChar;
|
||||
@ -98,9 +117,9 @@ static BOOL test_SerCx2Sys(HANDLE hComm)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((dcb.XonChar != '\0') || (dcb.XoffChar != '\0') || (dcb.ErrorChar != '\0') || (dcb.EofChar != '\0') || (dcb.EvtChar != '\0'))
|
||||
if ((dcb.ErrorChar != '\0') || (dcb.EofChar != '\0') || (dcb.EvtChar != '\0') || (dcb.XonChar != '\0') || (dcb.XoffChar != '\0'))
|
||||
{
|
||||
fprintf(stderr, "test_SerCx2Sys failure, expected all characters to '\0'\n");
|
||||
fprintf(stderr, "test_SerCx2Sys failure, expected all characters to be: '\\0'\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -314,6 +314,7 @@ static BOOL test_generic(HANDLE hComm)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int TestSetCommState(int argc, char* argv[])
|
||||
{
|
||||
BOOL result;
|
||||
|
Loading…
Reference in New Issue
Block a user