wimpr-comm: got IOCTL_SERIAL_SET_LINE_CONTROL and IOCTL_SERIAL_GET_LINE_CONTROL

This commit is contained in:
Emmanuel Ledoux 2014-04-29 22:25:07 +02:00
parent ee2339addc
commit feb4405941
8 changed files with 393 additions and 28 deletions

View File

@ -223,7 +223,19 @@ BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
/* TMP: TODO: */
/* (...) */
SERIAL_LINE_CONTROL lineControl;
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl, sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
{
DEBUG_WARN("GetCommState failure: could not get the control settings.");
goto error_handle;
}
lpLocalDcb->ByteSize = lineControl.WordLength;
lpLocalDcb->Parity = lineControl.Parity;
lpLocalDcb->StopBits = lineControl.StopBits;
SERIAL_CHARS serialChars;
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars, sizeof(SERIAL_CHARS), &bytesReturned, NULL))
@ -316,6 +328,16 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
return FALSE;
}
SERIAL_LINE_CONTROL lineControl;
lineControl.StopBits = lpDCB->StopBits;
lineControl.Parity = lpDCB->Parity;
lineControl.WordLength = lpDCB->ByteSize;
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl, sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
{
DEBUG_WARN("SetCommState failure: could not set the control settings.");
return FALSE;
}
/** upcomingTermios stage **/

View File

@ -212,6 +212,43 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
return TRUE;
}
}
case IOCTL_SERIAL_SET_LINE_CONTROL:
{
if (pRemoteSerialDriver->set_line_control)
{
SERIAL_LINE_CONTROL *pLineControl = (SERIAL_LINE_CONTROL*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
return pRemoteSerialDriver->set_line_control(pComm, pLineControl);
}
}
case IOCTL_SERIAL_GET_LINE_CONTROL:
{
if (pRemoteSerialDriver->get_line_control)
{
SERIAL_LINE_CONTROL *pLineControl = (SERIAL_LINE_CONTROL*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nOutBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pRemoteSerialDriver->get_line_control(pComm, pLineControl))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_LINE_CONTROL);
return TRUE;
}
}
}
DEBUG_WARN(_T("unsupported IoControlCode: Ox%x (remote serial driver: %s)"), dwIoControlCode, pRemoteSerialDriver->name);

View File

@ -43,9 +43,8 @@ extern "C" {
#define IOCTL_SERIAL_SET_BAUD_RATE 0x001B0004
#define IOCTL_SERIAL_GET_BAUD_RATE 0x001B0050
/* #define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C */
/* IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054 */
#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C
#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054
/* IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C */
/* IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020 */
@ -97,6 +96,16 @@ extern "C" {
/* IOCTL_PAR_IS_PORT_FREE 0x00160054 */
#define STOP_BIT_1 0
#define STOP_BITS_1_5 1
#define STOP_BITS_2 2
#define NO_PARITY 0
#define ODD_PARITY 1
#define EVEN_PARITY 2
#define MARK_PARITY 3
#define SPACE_PARITY 4
typedef struct _SERIAL_BAUD_RATE
{
@ -115,6 +124,14 @@ typedef struct _SERIAL_CHARS
} SERIAL_CHARS, *PSERIAL_CHARS;
typedef struct _SERIAL_LINE_CONTROL
{
UCHAR StopBits;
UCHAR Parity;
UCHAR WordLength;
} SERIAL_LINE_CONTROL, *PSERIAL_LINE_CONTROL;
/**
* A function might be NULL if not supported by the underlying remote driver.
*
@ -129,6 +146,8 @@ typedef struct _REMOTE_SERIAL_DRIVER
BOOL (*get_properties)(WINPR_COMM *pComm, COMMPROP *pProperties);
BOOL (*set_serial_chars)(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChars);
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);
} REMOTE_SERIAL_DRIVER;

View File

@ -61,12 +61,14 @@ static REMOTE_SERIAL_DRIVER _SerCx2Sys =
.get_properties = NULL,
.set_serial_chars = _set_serial_chars,
.get_serial_chars = _get_serial_chars,
.set_line_control = NULL,
.get_line_control = NULL,
};
REMOTE_SERIAL_DRIVER* SerCx2Sys_s()
{
/* _SerCx2Sys completed with SerialSys or SerCxSys default functions */
/* _SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */
//REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s();
REMOTE_SERIAL_DRIVER* pSerCxSys = SerCxSys_s();
@ -74,6 +76,9 @@ REMOTE_SERIAL_DRIVER* SerCx2Sys_s()
_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;
return &_SerCx2Sys;
}

View File

@ -139,22 +139,16 @@ static const speed_t _SERCX_SYS_BAUD_TABLE[][3] = {
static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
{
int i;
REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s();
// TMP: TODO:
if (!pSerialSys->get_properties(pComm, pProperties))
{
return FALSE;
}
// TMP: required?
// ZeroMemory(pProperties, sizeof(COMMPROP);
/* override some of the inherited properties from SerialSys ... */
/* pProperties->PacketLength; */
/* pProperties->PacketVersion; */
/* pProperties->ServiceMask; */
/* pProperties->Reserved1; */
/* pProperties->MaxTxQueue; */
/* pProperties->MaxRxQueue; */
pProperties->dwMaxBaud = BAUD_USER;
/* pProperties->ProvSubType; */
/* pProperties->ProvCapabilities; */
/* pProperties->SettableParams; */
pProperties->dwSettableBaud = 0;
for (i=0; _SERCX_SYS_BAUD_TABLE[i][0]<=__MAX_BAUD; i++)
@ -162,14 +156,6 @@ static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
pProperties->dwSettableBaud |= _SERCX_SYS_BAUD_TABLE[i][2];
}
/* pProperties->SettableData; */
/* pProperties->SettableStopParity; */
/* pProperties->CurrentTxQueue; */
/* pProperties->CurrentRxQueue; */
/* pProperties->ProvSpec1; */
/* pProperties->ProvSpec2; */
/* pProperties->ProvChar[1]; */
return TRUE;
}
@ -256,17 +242,21 @@ static REMOTE_SERIAL_DRIVER _SerCxSys =
.get_properties = _get_properties,
.set_serial_chars = NULL,
.get_serial_chars = NULL,
.set_line_control = NULL,
.get_line_control = NULL,
};
REMOTE_SERIAL_DRIVER* SerCxSys_s()
{
/* _SerCxSys completed with default SerialSys_s functions */
/* _SerCxSys completed with inherited functions from SerialSys */
REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s();
_SerCxSys.set_serial_chars = pSerialSys->set_serial_chars;
_SerCxSys.get_serial_chars = pSerialSys->get_serial_chars;
_SerCxSys.set_line_control = pSerialSys->set_line_control;
_SerCxSys.get_line_control = pSerialSys->get_line_control;
return &_SerCxSys;
}

View File

@ -142,6 +142,10 @@ static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
{
int i;
/* FIXME: properties should be better probe. The current
* implementation just relies on the Linux' implementation.
*/
// TMP: TODO:
// TMP: required?
@ -164,8 +168,10 @@ static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
pProperties->dwSettableBaud |= _SERIAL_SYS_BAUD_TABLE[i][1];
}
/* pProperties->SettableData; */
/* pProperties->SettableStopParity; */
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;
/* pProperties->CurrentTxQueue; */
/* pProperties->CurrentRxQueue; */
/* pProperties->ProvSpec1; */
@ -380,6 +386,168 @@ static BOOL _get_serial_chars(WINPR_COMM *pComm, SERIAL_CHARS *pSerialChars)
}
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(&currentTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &currentTermios) < 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;
}
static REMOTE_SERIAL_DRIVER _SerialSys =
{
@ -390,6 +558,8 @@ static REMOTE_SERIAL_DRIVER _SerialSys =
.get_properties = _get_properties,
.set_serial_chars = _set_serial_chars,
.get_serial_chars = _get_serial_chars,
.set_line_control = _set_line_control,
.get_line_control = _get_line_control
};

View File

@ -10,6 +10,7 @@ set(${MODULE_PREFIX}_TESTS
TestGetCommState.c
TestSetCommState.c
TestSerialChars.c
TestControlSettings.c
TestCommMonitor.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS

View File

@ -0,0 +1,121 @@
/**
* 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 <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
int TestControlSettings(int argc, char* argv[])
{
BOOL result;
HANDLE hComm;
DCB dcb;
// 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;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
/* Test 1 */
dcb.ByteSize = 5;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = MARKPARITY;
if (!SetCommState(hComm, &dcb))
{
fprintf(stderr, "SetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.ByteSize != 5) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != MARKPARITY))
{
fprintf(stderr, "test1 failed.\n");
return FALSE;
}
/* Test 2 */
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if (!SetCommState(hComm, &dcb))
{
fprintf(stderr, "SetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.ByteSize != 8) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != NOPARITY))
{
fprintf(stderr, "test2 failed.\n");
return FALSE;
}
if (!CloseHandle(hComm))
{
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}