Coverage Report

Created: 2026-08-31 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/comm/comm.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Serial Communication API
4
 *
5
 * Copyright 2011 O.S. Systems Software Ltda.
6
 * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
8
 * Copyright 2014 Hewlett-Packard Development Company, L.P.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
23
#include <winpr/config.h>
24
25
#include <winpr/assert.h>
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <pthread.h>
29
#include <stdarg.h>
30
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
31
#include <sys/eventfd.h>
32
#endif
33
#include <sys/ioctl.h>
34
#include <sys/stat.h>
35
#include <sys/types.h>
36
#include <termios.h>
37
#include <unistd.h>
38
39
#include <winpr/crt.h>
40
#include <winpr/comm.h>
41
#include <winpr/tchar.h>
42
#include <winpr/wlog.h>
43
#include <winpr/handle.h>
44
45
#include "comm_ioctl.h"
46
47
#include "../handle/handle.h"
48
#include "../log.h"
49
0
#define TAG WINPR_TAG("comm")
50
51
/**
52
 * Communication Resources:
53
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363196/
54
 */
55
56
#include "comm.h"
57
58
static wLog* sLog = nullptr;
59
60
struct comm_device
61
{
62
  LPTSTR name;
63
  LPTSTR path;
64
};
65
66
typedef struct comm_device COMM_DEVICE;
67
68
/* FIXME: get a clever data structure, see also io.h functions */
69
/* _CommDevices is a nullptr-terminated array with a maximum of COMM_DEVICE_MAX COMM_DEVICE */
70
0
#define COMM_DEVICE_MAX 128
71
static COMM_DEVICE** sCommDevices = nullptr;
72
static CRITICAL_SECTION sCommDevicesLock = WINPR_C_ARRAY_INIT;
73
74
static pthread_once_t sCommInitialized = PTHREAD_ONCE_INIT;
75
76
static const _SERIAL_IOCTL_NAME S_SERIAL_IOCTL_NAMES[] = {
77
  { IOCTL_SERIAL_SET_BAUD_RATE, "IOCTL_SERIAL_SET_BAUD_RATE" },
78
  { IOCTL_SERIAL_GET_BAUD_RATE, "IOCTL_SERIAL_GET_BAUD_RATE" },
79
  { IOCTL_SERIAL_SET_LINE_CONTROL, "IOCTL_SERIAL_SET_LINE_CONTROL" },
80
  { IOCTL_SERIAL_GET_LINE_CONTROL, "IOCTL_SERIAL_GET_LINE_CONTROL" },
81
  { IOCTL_SERIAL_SET_TIMEOUTS, "IOCTL_SERIAL_SET_TIMEOUTS" },
82
  { IOCTL_SERIAL_GET_TIMEOUTS, "IOCTL_SERIAL_GET_TIMEOUTS" },
83
  { IOCTL_SERIAL_GET_CHARS, "IOCTL_SERIAL_GET_CHARS" },
84
  { IOCTL_SERIAL_SET_CHARS, "IOCTL_SERIAL_SET_CHARS" },
85
  { IOCTL_SERIAL_SET_DTR, "IOCTL_SERIAL_SET_DTR" },
86
  { IOCTL_SERIAL_CLR_DTR, "IOCTL_SERIAL_CLR_DTR" },
87
  { IOCTL_SERIAL_RESET_DEVICE, "IOCTL_SERIAL_RESET_DEVICE" },
88
  { IOCTL_SERIAL_SET_RTS, "IOCTL_SERIAL_SET_RTS" },
89
  { IOCTL_SERIAL_CLR_RTS, "IOCTL_SERIAL_CLR_RTS" },
90
  { IOCTL_SERIAL_SET_XOFF, "IOCTL_SERIAL_SET_XOFF" },
91
  { IOCTL_SERIAL_SET_XON, "IOCTL_SERIAL_SET_XON" },
92
  { IOCTL_SERIAL_SET_BREAK_ON, "IOCTL_SERIAL_SET_BREAK_ON" },
93
  { IOCTL_SERIAL_SET_BREAK_OFF, "IOCTL_SERIAL_SET_BREAK_OFF" },
94
  { IOCTL_SERIAL_SET_QUEUE_SIZE, "IOCTL_SERIAL_SET_QUEUE_SIZE" },
95
  { IOCTL_SERIAL_GET_WAIT_MASK, "IOCTL_SERIAL_GET_WAIT_MASK" },
96
  { IOCTL_SERIAL_SET_WAIT_MASK, "IOCTL_SERIAL_SET_WAIT_MASK" },
97
  { IOCTL_SERIAL_WAIT_ON_MASK, "IOCTL_SERIAL_WAIT_ON_MASK" },
98
  { IOCTL_SERIAL_IMMEDIATE_CHAR, "IOCTL_SERIAL_IMMEDIATE_CHAR" },
99
  { IOCTL_SERIAL_PURGE, "IOCTL_SERIAL_PURGE" },
100
  { IOCTL_SERIAL_GET_HANDFLOW, "IOCTL_SERIAL_GET_HANDFLOW" },
101
  { IOCTL_SERIAL_SET_HANDFLOW, "IOCTL_SERIAL_SET_HANDFLOW" },
102
  { IOCTL_SERIAL_GET_MODEMSTATUS, "IOCTL_SERIAL_GET_MODEMSTATUS" },
103
  { IOCTL_SERIAL_GET_DTRRTS, "IOCTL_SERIAL_GET_DTRRTS" },
104
  { IOCTL_SERIAL_GET_COMMSTATUS, "IOCTL_SERIAL_GET_COMMSTATUS" },
105
  { IOCTL_SERIAL_GET_PROPERTIES, "IOCTL_SERIAL_GET_PROPERTIES" },
106
  // {IOCTL_SERIAL_XOFF_COUNTER,  "IOCTL_SERIAL_XOFF_COUNTER"},
107
  // {IOCTL_SERIAL_LSRMST_INSERT, "IOCTL_SERIAL_LSRMST_INSERT"},
108
  { IOCTL_SERIAL_CONFIG_SIZE, "IOCTL_SERIAL_CONFIG_SIZE" },
109
  // {IOCTL_SERIAL_GET_STATS, "IOCTL_SERIAL_GET_STATS"},
110
  // {IOCTL_SERIAL_CLEAR_STATS, "IOCTL_SERIAL_CLEAR_STATS"},
111
  // {IOCTL_SERIAL_GET_MODEM_CONTROL,"IOCTL_SERIAL_GET_MODEM_CONTROL"},
112
  // {IOCTL_SERIAL_SET_MODEM_CONTROL,"IOCTL_SERIAL_SET_MODEM_CONTROL"},
113
  // {IOCTL_SERIAL_SET_FIFO_CONTROL,  "IOCTL_SERIAL_SET_FIFO_CONTROL"},
114
115
  // {IOCTL_PAR_QUERY_INFORMATION,  "IOCTL_PAR_QUERY_INFORMATION"},
116
  // {IOCTL_PAR_SET_INFORMATION,  "IOCTL_PAR_SET_INFORMATION"},
117
  // {IOCTL_PAR_QUERY_DEVICE_ID,  "IOCTL_PAR_QUERY_DEVICE_ID"},
118
  // {IOCTL_PAR_QUERY_DEVICE_ID_SIZE,"IOCTL_PAR_QUERY_DEVICE_ID_SIZE"},
119
  // {IOCTL_IEEE1284_GET_MODE,  "IOCTL_IEEE1284_GET_MODE"},
120
  // {IOCTL_IEEE1284_NEGOTIATE, "IOCTL_IEEE1284_NEGOTIATE"},
121
  // {IOCTL_PAR_SET_WRITE_ADDRESS,  "IOCTL_PAR_SET_WRITE_ADDRESS"},
122
  // {IOCTL_PAR_SET_READ_ADDRESS, "IOCTL_PAR_SET_READ_ADDRESS"},
123
  // {IOCTL_PAR_GET_DEVICE_CAPS,  "IOCTL_PAR_GET_DEVICE_CAPS"},
124
  // {IOCTL_PAR_GET_DEFAULT_MODES,  "IOCTL_PAR_GET_DEFAULT_MODES"},
125
  // {IOCTL_PAR_QUERY_RAW_DEVICE_ID, "IOCTL_PAR_QUERY_RAW_DEVICE_ID"},
126
  // {IOCTL_PAR_IS_PORT_FREE, "IOCTL_PAR_IS_PORT_FREE"},
127
128
  { IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID" }
129
};
130
131
const char* _comm_serial_ioctl_name(ULONG number)
132
0
{
133
0
  for (size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
134
0
  {
135
0
    const _SERIAL_IOCTL_NAME* cur = &S_SERIAL_IOCTL_NAMES[x];
136
0
    if (cur->number == number)
137
0
      return cur->name;
138
0
  }
139
140
0
  return "(unknown ioctl name)";
141
0
}
142
143
static int CommGetFd(HANDLE handle)
144
0
{
145
0
  WINPR_COMM* comm = (WINPR_COMM*)handle;
146
147
0
  if (!CommIsHandled(handle))
148
0
    return -1;
149
150
0
  return comm->fd;
151
0
}
152
153
const HANDLE_CREATOR* GetCommHandleCreator(void)
154
0
{
155
0
#if defined(WINPR_HAVE_SERIAL_SUPPORT)
156
0
  static const HANDLE_CREATOR sCommHandleCreator = { .IsHandled = IsCommDevice,
157
0
                                                   .CreateFileA = CommCreateFileA };
158
0
  return &sCommHandleCreator;
159
#else
160
  return nullptr;
161
#endif
162
0
}
163
164
static void CommInit(void)
165
0
{
166
  /* NB: error management to be done outside of this function */
167
0
  WINPR_ASSERT(sLog == nullptr);
168
0
  WINPR_ASSERT(sCommDevices == nullptr);
169
0
  sCommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
170
171
0
  if (!sCommDevices)
172
0
    return;
173
174
0
  if (!InitializeCriticalSectionEx(&sCommDevicesLock, 0, 0))
175
0
  {
176
0
    free((void*)sCommDevices);
177
0
    sCommDevices = nullptr;
178
0
    return;
179
0
  }
180
181
0
  sLog = WLog_Get(TAG);
182
0
  WINPR_ASSERT(sLog != nullptr);
183
0
}
184
185
/**
186
 * Returns TRUE when the comm module is correctly initialized, FALSE otherwise
187
 * with ERROR_DLL_INIT_FAILED set as the last error.
188
 */
189
static BOOL CommInitialized(void)
190
0
{
191
0
  if (pthread_once(&sCommInitialized, CommInit) != 0)
192
0
  {
193
0
    SetLastError(ERROR_DLL_INIT_FAILED);
194
0
    return FALSE;
195
0
  }
196
197
0
  return TRUE;
198
0
}
199
200
WINPR_ATTR_FORMAT_ARG(5, 6)
201
void CommLog_PrintEx(DWORD level, const char* file, size_t line, const char* fkt,
202
                     WINPR_FORMAT_ARG const char* fmt, ...)
203
0
{
204
0
  if (!CommInitialized())
205
0
    return;
206
207
0
  if (!WLog_IsLevelActive(sLog, level))
208
0
    return;
209
0
  va_list ap = WINPR_C_ARRAY_INIT;
210
0
  va_start(ap, fmt);
211
0
  WLog_PrintTextMessageVA(sLog, level, line, file, fkt, fmt, ap);
212
0
  va_end(ap);
213
0
}
214
215
BOOL BuildCommDCBA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB)
216
0
{
217
0
  if (!CommInitialized())
218
0
    return FALSE;
219
220
  /* TODO: not implemented */
221
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
222
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
223
0
  return FALSE;
224
0
}
225
226
BOOL BuildCommDCBW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB)
227
0
{
228
0
  if (!CommInitialized())
229
0
    return FALSE;
230
231
  /* TODO: not implemented */
232
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
233
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
234
0
  return FALSE;
235
0
}
236
237
BOOL BuildCommDCBAndTimeoutsA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB,
238
                              WINPR_ATTR_UNUSED LPCOMMTIMEOUTS lpCommTimeouts)
239
0
{
240
0
  if (!CommInitialized())
241
0
    return FALSE;
242
243
  /* TODO: not implemented */
244
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
245
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
246
0
  return FALSE;
247
0
}
248
249
BOOL BuildCommDCBAndTimeoutsW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB,
250
                              WINPR_ATTR_UNUSED LPCOMMTIMEOUTS lpCommTimeouts)
251
0
{
252
0
  if (!CommInitialized())
253
0
    return FALSE;
254
255
  /* TODO: not implemented */
256
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
257
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
258
0
  return FALSE;
259
0
}
260
261
BOOL CommConfigDialogA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
262
                       WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC)
263
0
{
264
0
  if (!CommInitialized())
265
0
    return FALSE;
266
267
  /* TODO: not implemented */
268
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
269
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
270
0
  return FALSE;
271
0
}
272
273
BOOL CommConfigDialogW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
274
                       WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC)
275
0
{
276
0
  if (!CommInitialized())
277
0
    return FALSE;
278
279
  /* TODO: not implemented */
280
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
281
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
282
0
  return FALSE;
283
0
}
284
285
BOOL GetCommConfig(HANDLE hCommDev, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
286
                   WINPR_ATTR_UNUSED LPDWORD lpdwSize)
287
0
{
288
0
  WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
289
290
0
  if (!CommInitialized())
291
0
    return FALSE;
292
293
  /* TODO: not implemented */
294
295
0
  if (!pComm)
296
0
    return FALSE;
297
298
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
299
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
300
0
  return FALSE;
301
0
}
302
303
BOOL SetCommConfig(HANDLE hCommDev, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
304
                   WINPR_ATTR_UNUSED DWORD dwSize)
305
0
{
306
0
  WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
307
308
0
  if (!CommInitialized())
309
0
    return FALSE;
310
311
  /* TODO: not implemented */
312
313
0
  if (!pComm)
314
0
    return FALSE;
315
316
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
317
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
318
0
  return FALSE;
319
0
}
320
321
BOOL GetCommMask(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask)
322
0
{
323
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
324
325
0
  if (!CommInitialized())
326
0
    return FALSE;
327
328
  /* TODO: not implemented */
329
330
0
  if (!pComm)
331
0
    return FALSE;
332
333
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
334
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
335
0
  return FALSE;
336
0
}
337
338
BOOL SetCommMask(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwEvtMask)
339
0
{
340
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
341
342
0
  if (!CommInitialized())
343
0
    return FALSE;
344
345
  /* TODO: not implemented */
346
347
0
  if (!pComm)
348
0
    return FALSE;
349
350
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
351
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
352
0
  return FALSE;
353
0
}
354
355
BOOL GetCommModemStatus(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpModemStat)
356
0
{
357
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
358
359
0
  if (!CommInitialized())
360
0
    return FALSE;
361
362
  /* TODO: not implemented */
363
364
0
  if (!pComm)
365
0
    return FALSE;
366
367
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
368
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
369
0
  return FALSE;
370
0
}
371
372
/**
373
 * ERRORS:
374
 *   ERROR_DLL_INIT_FAILED
375
 *   ERROR_INVALID_HANDLE
376
 */
377
BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp)
378
0
{
379
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
380
0
  DWORD bytesReturned = 0;
381
382
0
  if (!CommIsHandleValid(hFile))
383
0
    return FALSE;
384
385
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, nullptr, 0, lpCommProp,
386
0
                           sizeof(COMMPROP), &bytesReturned, nullptr))
387
0
  {
388
0
    CommLog_Print(WLOG_WARN, "GetCommProperties failure.");
389
0
    return FALSE;
390
0
  }
391
392
0
  return TRUE;
393
0
}
394
395
/**
396
 *
397
 *
398
 * ERRORS:
399
 *   ERROR_INVALID_HANDLE
400
 *   ERROR_INVALID_DATA
401
 *   ERROR_IO_DEVICE
402
 *   ERROR_OUTOFMEMORY
403
 */
404
BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
405
0
{
406
0
  DCB* lpLocalDcb = nullptr;
407
0
  struct termios currentState;
408
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
409
0
  DWORD bytesReturned = 0;
410
411
0
  if (!CommIsHandleValid(hFile))
412
0
    return FALSE;
413
414
0
  if (!lpDCB)
415
0
  {
416
0
    SetLastError(ERROR_INVALID_DATA);
417
0
    return FALSE;
418
0
  }
419
420
0
  if (lpDCB->DCBlength < sizeof(DCB))
421
0
  {
422
0
    SetLastError(ERROR_INVALID_DATA);
423
0
    return FALSE;
424
0
  }
425
426
0
  if (tcgetattr(pComm->fd, &currentState) < 0)
427
0
  {
428
0
    SetLastError(ERROR_IO_DEVICE);
429
0
    return FALSE;
430
0
  }
431
432
0
  lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
433
434
0
  if (lpLocalDcb == nullptr)
435
0
  {
436
0
    SetLastError(ERROR_OUTOFMEMORY);
437
0
    return FALSE;
438
0
  }
439
440
  /* error_handle */
441
0
  lpLocalDcb->DCBlength = lpDCB->DCBlength;
442
0
  SERIAL_BAUD_RATE baudRate = WINPR_C_ARRAY_INIT;
443
444
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, nullptr, 0, &baudRate,
445
0
                           sizeof(SERIAL_BAUD_RATE), &bytesReturned, nullptr))
446
0
  {
447
0
    CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate.");
448
0
    goto error_handle;
449
0
  }
450
451
0
  lpLocalDcb->BaudRate = baudRate.BaudRate;
452
0
  lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
453
454
0
  if (!lpLocalDcb->fBinary)
455
0
  {
456
0
    CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
457
0
  }
458
459
0
  lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
460
0
  SERIAL_HANDFLOW handflow;
461
462
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, nullptr, 0, &handflow,
463
0
                           sizeof(SERIAL_HANDFLOW), &bytesReturned, nullptr))
464
0
  {
465
0
    CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings.");
466
0
    goto error_handle;
467
0
  }
468
469
0
  lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
470
0
  lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
471
472
0
  if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
473
0
  {
474
0
    lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
475
0
  }
476
0
  else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
477
0
  {
478
0
    lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
479
0
  }
480
0
  else
481
0
  {
482
0
    lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
483
0
  }
484
485
0
  lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;
486
0
  lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;
487
0
  lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;
488
0
  lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;
489
0
  lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;
490
0
  lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;
491
492
0
  if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
493
0
  {
494
0
    lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
495
0
  }
496
0
  else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
497
0
  {
498
0
    lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
499
0
  }
500
0
  else
501
0
  {
502
0
    lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
503
0
  }
504
505
  // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow
506
  // Control Enabled bit in its Modem Control Register (MCR)
507
0
  lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
508
  /* lpLocalDcb->fDummy2 not used */
509
0
  lpLocalDcb->wReserved = 0; /* must be zero */
510
0
  lpLocalDcb->XonLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XonLimit);
511
0
  lpLocalDcb->XoffLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XoffLimit);
512
513
0
  {
514
0
    SERIAL_LINE_CONTROL lineControl = WINPR_C_ARRAY_INIT;
515
516
0
    if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, nullptr, 0, &lineControl,
517
0
                             sizeof(SERIAL_LINE_CONTROL), &bytesReturned, nullptr))
518
0
    {
519
0
      CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
520
0
      goto error_handle;
521
0
    }
522
523
0
    lpLocalDcb->ByteSize = lineControl.WordLength;
524
0
    lpLocalDcb->Parity = lineControl.Parity;
525
0
    lpLocalDcb->StopBits = lineControl.StopBits;
526
0
  }
527
528
0
  {
529
0
    SERIAL_CHARS serialChars = WINPR_C_ARRAY_INIT;
530
531
0
    if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, nullptr, 0, &serialChars,
532
0
                             sizeof(SERIAL_CHARS), &bytesReturned, nullptr))
533
0
    {
534
0
      CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
535
0
      goto error_handle;
536
0
    }
537
538
0
    lpLocalDcb->XonChar = serialChars.XonChar;
539
0
    lpLocalDcb->XoffChar = serialChars.XoffChar;
540
0
    lpLocalDcb->ErrorChar = serialChars.ErrorChar;
541
0
    lpLocalDcb->EofChar = serialChars.EofChar;
542
0
    lpLocalDcb->EvtChar = serialChars.EventChar;
543
0
  }
544
0
  memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
545
0
  free(lpLocalDcb);
546
0
  return TRUE;
547
0
error_handle:
548
0
  free(lpLocalDcb);
549
0
  return FALSE;
550
0
}
551
552
/**
553
 * @return TRUE on success, FALSE otherwise.
554
 *
555
 * As of today, SetCommState() can fail half-way with some settings
556
 * applied and some others not. SetCommState() returns on the first
557
 * failure met. FIXME: or is it correct?
558
 *
559
 * ERRORS:
560
 *   ERROR_INVALID_HANDLE
561
 *   ERROR_IO_DEVICE
562
 */
563
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
564
0
{
565
0
  struct termios upcomingTermios = WINPR_C_ARRAY_INIT;
566
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
567
0
  DWORD bytesReturned = 0;
568
569
  /* FIXME: validate changes according GetCommProperties? */
570
571
0
  if (!CommIsHandleValid(hFile))
572
0
    return FALSE;
573
574
0
  if (!lpDCB)
575
0
  {
576
0
    SetLastError(ERROR_INVALID_DATA);
577
0
    return FALSE;
578
0
  }
579
580
  /* NB: did the choice to call ioctls first when available and
581
     then to setup upcomingTermios. Don't mix both stages. */
582
  /** ioctl calls stage **/
583
0
  SERIAL_BAUD_RATE baudRate = WINPR_C_ARRAY_INIT;
584
0
  baudRate.BaudRate = lpDCB->BaudRate;
585
586
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE),
587
0
                           nullptr, 0, &bytesReturned, nullptr))
588
0
  {
589
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
590
0
    return FALSE;
591
0
  }
592
593
0
  SERIAL_CHARS serialChars;
594
595
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, nullptr, 0, &serialChars,
596
0
                           sizeof(SERIAL_CHARS), &bytesReturned,
597
0
                           nullptr)) /* as of today, required for BreakChar */
598
0
  {
599
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
600
0
    return FALSE;
601
0
  }
602
603
0
  serialChars.XonChar = lpDCB->XonChar;
604
0
  serialChars.XoffChar = lpDCB->XoffChar;
605
0
  serialChars.ErrorChar = lpDCB->ErrorChar;
606
0
  serialChars.EofChar = lpDCB->EofChar;
607
0
  serialChars.EventChar = lpDCB->EvtChar;
608
609
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS),
610
0
                           nullptr, 0, &bytesReturned, nullptr))
611
0
  {
612
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
613
0
    return FALSE;
614
0
  }
615
616
0
  SERIAL_LINE_CONTROL lineControl;
617
0
  lineControl.StopBits = lpDCB->StopBits;
618
0
  lineControl.Parity = lpDCB->Parity;
619
0
  lineControl.WordLength = lpDCB->ByteSize;
620
621
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
622
0
                           sizeof(SERIAL_LINE_CONTROL), nullptr, 0, &bytesReturned, nullptr))
623
0
  {
624
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
625
0
    return FALSE;
626
0
  }
627
628
0
  SERIAL_HANDFLOW handflow = WINPR_C_ARRAY_INIT;
629
630
0
  if (lpDCB->fOutxCtsFlow)
631
0
  {
632
0
    handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
633
0
  }
634
635
0
  if (lpDCB->fOutxDsrFlow)
636
0
  {
637
0
    handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
638
0
  }
639
640
0
  switch (lpDCB->fDtrControl)
641
0
  {
642
0
    case SERIAL_DTR_HANDSHAKE:
643
0
      handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
644
0
      break;
645
646
0
    case SERIAL_DTR_CONTROL:
647
0
      handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
648
0
      break;
649
650
0
    case DTR_CONTROL_DISABLE:
651
      /* do nothing since handflow is init-zeroed */
652
0
      break;
653
654
0
    default:
655
0
      CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRId32 "\n",
656
0
                    lpDCB->fDtrControl);
657
0
      return FALSE;
658
0
  }
659
660
0
  if (lpDCB->fDsrSensitivity)
661
0
  {
662
0
    handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
663
0
  }
664
665
0
  if (lpDCB->fTXContinueOnXoff)
666
0
  {
667
0
    handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
668
0
  }
669
670
0
  if (lpDCB->fOutX)
671
0
  {
672
0
    handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
673
0
  }
674
675
0
  if (lpDCB->fInX)
676
0
  {
677
0
    handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
678
0
  }
679
680
0
  if (lpDCB->fErrorChar)
681
0
  {
682
0
    handflow.FlowReplace |= SERIAL_ERROR_CHAR;
683
0
  }
684
685
0
  if (lpDCB->fNull)
686
0
  {
687
0
    handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
688
0
  }
689
690
0
  switch (lpDCB->fRtsControl)
691
0
  {
692
0
    case RTS_CONTROL_TOGGLE:
693
0
      CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
694
      // FIXME: see also GetCommState()
695
0
      return FALSE;
696
697
0
    case RTS_CONTROL_HANDSHAKE:
698
0
      handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
699
0
      break;
700
701
0
    case RTS_CONTROL_ENABLE:
702
0
      handflow.FlowReplace |= SERIAL_RTS_CONTROL;
703
0
      break;
704
705
0
    case RTS_CONTROL_DISABLE:
706
      /* do nothing since handflow is init-zeroed */
707
0
      break;
708
709
0
    default:
710
0
      CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRId32 "\n",
711
0
                    lpDCB->fRtsControl);
712
0
      return FALSE;
713
0
  }
714
715
0
  if (lpDCB->fAbortOnError)
716
0
  {
717
0
    handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
718
0
  }
719
720
  /* lpDCB->fDummy2 not used */
721
  /* lpLocalDcb->wReserved  ignored */
722
0
  handflow.XonLimit = lpDCB->XonLim;
723
0
  handflow.XoffLimit = lpDCB->XoffLim;
724
725
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW),
726
0
                           nullptr, 0, &bytesReturned, nullptr))
727
0
  {
728
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
729
0
    return FALSE;
730
0
  }
731
732
  /** upcomingTermios stage **/
733
734
0
  if (tcgetattr(pComm->fd, &upcomingTermios) <
735
0
      0) /* NB: preserves current settings not directly handled by the Communication Functions */
736
0
  {
737
0
    SetLastError(ERROR_IO_DEVICE);
738
0
    return FALSE;
739
0
  }
740
741
0
  if (lpDCB->fBinary)
742
0
  {
743
0
    upcomingTermios.c_lflag &= (tcflag_t)~ICANON;
744
0
  }
745
0
  else
746
0
  {
747
0
    upcomingTermios.c_lflag |= ICANON;
748
0
    CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
749
0
  }
750
751
0
  if (lpDCB->fParity)
752
0
  {
753
0
    upcomingTermios.c_iflag |= INPCK;
754
0
  }
755
0
  else
756
0
  {
757
0
    upcomingTermios.c_iflag &= (tcflag_t)~INPCK;
758
0
  }
759
760
  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
761
   *
762
   * The SetCommState function reconfigures the communications
763
   * resource, but it does not affect the internal output and
764
   * input buffers of the specified driver. The buffers are not
765
   * flushed, and pending read and write operations are not
766
   * terminated prematurely.
767
   *
768
   * TCSANOW matches the best this definition
769
   */
770
771
0
  if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
772
0
  {
773
0
    SetLastError(ERROR_IO_DEVICE);
774
0
    return FALSE;
775
0
  }
776
777
0
  return TRUE;
778
0
}
779
780
/**
781
 * ERRORS:
782
 *   ERROR_INVALID_HANDLE
783
 */
784
BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
785
0
{
786
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
787
0
  DWORD bytesReturned = 0;
788
789
0
  if (!CommIsHandleValid(hFile))
790
0
    return FALSE;
791
792
  /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
793
794
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, nullptr, 0, lpCommTimeouts,
795
0
                           sizeof(COMMTIMEOUTS), &bytesReturned, nullptr))
796
0
  {
797
0
    CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
798
0
    return FALSE;
799
0
  }
800
801
0
  return TRUE;
802
0
}
803
804
/**
805
 * ERRORS:
806
 *   ERROR_INVALID_HANDLE
807
 */
808
BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
809
0
{
810
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
811
0
  DWORD bytesReturned = 0;
812
813
0
  if (!CommIsHandleValid(hFile))
814
0
    return FALSE;
815
816
  /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
817
818
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS),
819
0
                           nullptr, 0, &bytesReturned, nullptr))
820
0
  {
821
0
    CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
822
0
    return FALSE;
823
0
  }
824
825
0
  return TRUE;
826
0
}
827
828
BOOL GetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
829
                           WINPR_ATTR_UNUSED LPDWORD lpdwSize)
830
0
{
831
0
  if (!CommInitialized())
832
0
    return FALSE;
833
834
  /* TODO: not implemented */
835
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
836
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
837
0
  return FALSE;
838
0
}
839
840
BOOL GetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
841
                           WINPR_ATTR_UNUSED LPDWORD lpdwSize)
842
0
{
843
0
  if (!CommInitialized())
844
0
    return FALSE;
845
846
  /* TODO: not implemented */
847
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
848
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
849
0
  return FALSE;
850
0
}
851
852
BOOL SetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
853
                           WINPR_ATTR_UNUSED DWORD dwSize)
854
0
{
855
0
  if (!CommInitialized())
856
0
    return FALSE;
857
858
  /* TODO: not implemented */
859
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
860
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
861
0
  return FALSE;
862
0
}
863
864
BOOL SetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
865
                           WINPR_ATTR_UNUSED DWORD dwSize)
866
0
{
867
0
  if (!CommInitialized())
868
0
    return FALSE;
869
870
  /* TODO: not implemented */
871
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
872
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
873
0
  return FALSE;
874
0
}
875
876
BOOL SetCommBreak(HANDLE hFile)
877
0
{
878
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
879
880
0
  if (!CommInitialized())
881
0
    return FALSE;
882
883
  /* TODO: not implemented */
884
885
0
  if (!pComm)
886
0
    return FALSE;
887
888
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
889
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
890
0
  return FALSE;
891
0
}
892
893
BOOL ClearCommBreak(HANDLE hFile)
894
0
{
895
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
896
897
0
  if (!CommInitialized())
898
0
    return FALSE;
899
900
  /* TODO: not implemented */
901
902
0
  if (!pComm)
903
0
    return FALSE;
904
905
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
906
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
907
0
  return FALSE;
908
0
}
909
910
BOOL ClearCommError(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpErrors,
911
                    WINPR_ATTR_UNUSED LPCOMSTAT lpStat)
912
0
{
913
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
914
915
0
  if (!CommInitialized())
916
0
    return FALSE;
917
918
  /* TODO: not implemented */
919
920
0
  if (!pComm)
921
0
    return FALSE;
922
923
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
924
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
925
0
  return FALSE;
926
0
}
927
928
BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
929
0
{
930
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
931
0
  DWORD bytesReturned = 0;
932
933
0
  if (!CommIsHandleValid(hFile))
934
0
    return FALSE;
935
936
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), nullptr, 0,
937
0
                           &bytesReturned, nullptr))
938
0
  {
939
0
    CommLog_Print(WLOG_WARN, "PurgeComm failure.");
940
0
    return FALSE;
941
0
  }
942
943
0
  return TRUE;
944
0
}
945
946
BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
947
0
{
948
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
949
0
  SERIAL_QUEUE_SIZE queueSize;
950
0
  DWORD bytesReturned = 0;
951
952
0
  if (!CommIsHandleValid(hFile))
953
0
    return FALSE;
954
955
0
  queueSize.InSize = dwInQueue;
956
0
  queueSize.OutSize = dwOutQueue;
957
958
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
959
0
                           sizeof(SERIAL_QUEUE_SIZE), nullptr, 0, &bytesReturned, nullptr))
960
0
  {
961
0
    CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
962
0
    return FALSE;
963
0
  }
964
965
0
  return TRUE;
966
0
}
967
968
BOOL EscapeCommFunction(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwFunc)
969
0
{
970
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
971
972
0
  if (!CommInitialized())
973
0
    return FALSE;
974
975
  /* TODO: not implemented */
976
977
0
  if (!pComm)
978
0
    return FALSE;
979
980
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
981
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
982
0
  return FALSE;
983
0
}
984
985
BOOL TransmitCommChar(HANDLE hFile, WINPR_ATTR_UNUSED char cChar)
986
0
{
987
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
988
989
0
  if (!CommInitialized())
990
0
    return FALSE;
991
992
  /* TODO: not implemented */
993
994
0
  if (!pComm)
995
0
    return FALSE;
996
997
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
998
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
999
0
  return FALSE;
1000
0
}
1001
1002
BOOL WaitCommEvent(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask,
1003
                   WINPR_ATTR_UNUSED LPOVERLAPPED lpOverlapped)
1004
0
{
1005
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
1006
1007
0
  if (!CommInitialized())
1008
0
    return FALSE;
1009
1010
  /* TODO: not implemented */
1011
1012
0
  if (!pComm)
1013
0
    return FALSE;
1014
1015
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
1016
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1017
0
  return FALSE;
1018
0
}
1019
1020
/**
1021
 * Returns TRUE on success, FALSE otherwise. To get extended error
1022
 * information, call GetLastError.
1023
 *
1024
 * ERRORS:
1025
 *   ERROR_DLL_INIT_FAILED
1026
 *   ERROR_OUTOFMEMORY was not possible to get mappings.
1027
 *   ERROR_INVALID_DATA was not possible to add the device.
1028
 */
1029
BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
1030
0
{
1031
0
  LPTSTR storedDeviceName = nullptr;
1032
0
  LPTSTR storedTargetPath = nullptr;
1033
1034
0
  if (!CommInitialized())
1035
0
    return FALSE;
1036
1037
0
  EnterCriticalSection(&sCommDevicesLock);
1038
1039
0
  if (sCommDevices == nullptr)
1040
0
  {
1041
0
    SetLastError(ERROR_DLL_INIT_FAILED);
1042
0
    goto error_handle;
1043
0
  }
1044
1045
0
  storedDeviceName = _tcsdup(lpDeviceName);
1046
1047
0
  if (storedDeviceName == nullptr)
1048
0
  {
1049
0
    SetLastError(ERROR_OUTOFMEMORY);
1050
0
    goto error_handle;
1051
0
  }
1052
1053
0
  storedTargetPath = _tcsdup(lpTargetPath);
1054
1055
0
  if (storedTargetPath == nullptr)
1056
0
  {
1057
0
    SetLastError(ERROR_OUTOFMEMORY);
1058
0
    goto error_handle;
1059
0
  }
1060
1061
0
  {
1062
0
    int i = 0;
1063
0
    for (; i < COMM_DEVICE_MAX; i++)
1064
0
    {
1065
0
      if (sCommDevices[i] != nullptr)
1066
0
      {
1067
0
        if (_tcscmp(sCommDevices[i]->name, storedDeviceName) == 0)
1068
0
        {
1069
          /* take over the emplacement */
1070
0
          free(sCommDevices[i]->name);
1071
0
          free(sCommDevices[i]->path);
1072
0
          sCommDevices[i]->name = storedDeviceName;
1073
0
          sCommDevices[i]->path = storedTargetPath;
1074
0
          break;
1075
0
        }
1076
0
      }
1077
0
      else
1078
0
      {
1079
        /* new emplacement */
1080
0
        sCommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
1081
1082
0
        if (sCommDevices[i] == nullptr)
1083
0
        {
1084
0
          SetLastError(ERROR_OUTOFMEMORY);
1085
0
          goto error_handle;
1086
0
        }
1087
1088
0
        sCommDevices[i]->name = storedDeviceName;
1089
0
        sCommDevices[i]->path = storedTargetPath;
1090
0
        break;
1091
0
      }
1092
0
    }
1093
1094
0
    if (i == COMM_DEVICE_MAX)
1095
0
    {
1096
0
      SetLastError(ERROR_OUTOFMEMORY);
1097
0
      goto error_handle;
1098
0
    }
1099
0
  }
1100
1101
0
  LeaveCriticalSection(&sCommDevicesLock);
1102
0
  return TRUE;
1103
0
error_handle:
1104
0
  free(storedDeviceName);
1105
0
  free(storedTargetPath);
1106
0
  LeaveCriticalSection(&sCommDevicesLock);
1107
0
  return FALSE;
1108
0
}
1109
1110
/**
1111
 * Returns the number of target paths in the buffer pointed to by
1112
 * lpTargetPath.
1113
 *
1114
 * The current implementation returns in any case 0 and 1 target
1115
 * path. A nullptr lpDeviceName is not supported yet to get all the
1116
 * paths.
1117
 *
1118
 * ERRORS:
1119
 *   ERROR_SUCCESS
1120
 *   ERROR_DLL_INIT_FAILED
1121
 *   ERROR_OUTOFMEMORY was not possible to get mappings.
1122
 *   ERROR_NOT_SUPPORTED equivalent QueryDosDevice feature not supported.
1123
 *   ERROR_INVALID_DATA was not possible to retrieve any device information.
1124
 *   ERROR_INSUFFICIENT_BUFFER too small lpTargetPath
1125
 */
1126
DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
1127
0
{
1128
0
  LPTSTR storedTargetPath = nullptr;
1129
0
  SetLastError(ERROR_SUCCESS);
1130
1131
0
  if (!CommInitialized())
1132
0
    return 0;
1133
1134
0
  if (sCommDevices == nullptr)
1135
0
  {
1136
0
    SetLastError(ERROR_DLL_INIT_FAILED);
1137
0
    return 0;
1138
0
  }
1139
1140
0
  if (lpDeviceName == nullptr || lpTargetPath == nullptr)
1141
0
  {
1142
0
    SetLastError(ERROR_NOT_SUPPORTED);
1143
0
    return 0;
1144
0
  }
1145
1146
0
  EnterCriticalSection(&sCommDevicesLock);
1147
0
  storedTargetPath = nullptr;
1148
1149
0
  for (int i = 0; i < COMM_DEVICE_MAX; i++)
1150
0
  {
1151
0
    if (sCommDevices[i] != nullptr)
1152
0
    {
1153
0
      if (_tcscmp(sCommDevices[i]->name, lpDeviceName) == 0)
1154
0
      {
1155
0
        storedTargetPath = sCommDevices[i]->path;
1156
0
        break;
1157
0
      }
1158
1159
0
      continue;
1160
0
    }
1161
1162
0
    break;
1163
0
  }
1164
1165
0
  LeaveCriticalSection(&sCommDevicesLock);
1166
1167
0
  if (storedTargetPath == nullptr)
1168
0
  {
1169
0
    SetLastError(ERROR_INVALID_DATA);
1170
0
    return 0;
1171
0
  }
1172
1173
0
  const size_t size = _tcsnlen(storedTargetPath, ucchMax);
1174
0
  if (size + 2 > ucchMax)
1175
0
  {
1176
0
    SetLastError(ERROR_INSUFFICIENT_BUFFER);
1177
0
    return 0;
1178
0
  }
1179
1180
0
  _tcsncpy(lpTargetPath, storedTargetPath, size + 1);
1181
0
  lpTargetPath[size + 2] = '\0'; /* 2nd final '\0' */
1182
0
  return (DWORD)size + 2UL;
1183
0
}
1184
1185
/**
1186
 * Checks whether lpDeviceName is a valid and registered Communication device.
1187
 */
1188
BOOL IsCommDevice(LPCTSTR lpDeviceName)
1189
0
{
1190
0
  TCHAR lpTargetPath[MAX_PATH];
1191
1192
0
  if (!CommInitialized())
1193
0
    return FALSE;
1194
1195
0
  if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
1196
0
  {
1197
0
    return TRUE;
1198
0
  }
1199
1200
0
  return FALSE;
1201
0
}
1202
1203
/**
1204
 * Sets
1205
 */
1206
void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
1207
0
{
1208
0
  ULONG Type = 0;
1209
0
  WINPR_HANDLE* Object = nullptr;
1210
0
  WINPR_COMM* pComm = nullptr;
1211
1212
0
  if (!CommInitialized())
1213
0
    return;
1214
1215
0
  if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
1216
0
  {
1217
0
    CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
1218
0
    return;
1219
0
  }
1220
1221
0
  pComm = (WINPR_COMM*)Object;
1222
0
  pComm->serverSerialDriverId = driverId;
1223
0
}
1224
1225
static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle, CommGetFd, nullptr, /* CleanupHandle */
1226
                        nullptr,       nullptr,         nullptr,   nullptr, nullptr, nullptr,
1227
                        nullptr,       nullptr,         nullptr,   nullptr, nullptr, nullptr,
1228
                        nullptr,       nullptr,         nullptr,   nullptr, nullptr };
1229
1230
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1231
/* eventfd() with EFD_NONBLOCK|EFD_CLOEXEC, falling back to the flag-less call plus separate
1232
 * fcntl()s on kernels older than 2.6.27 (which reject unknown flags with EINVAL) */
1233
WINPR_ATTR_NODISCARD
1234
static int comm_eventfd_cloexec(void)
1235
0
{
1236
0
  int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1237
0
  if ((fd < 0) && (errno == EINVAL))
1238
0
  {
1239
    /* old kernels reject any nonzero flags argument outright, so both flags have to be
1240
     * applied separately afterward - O_NONBLOCK (F_SETFL) and FD_CLOEXEC (F_SETFD) live in
1241
     * different fcntl() namespaces and can't be folded into one call. No need to read the
1242
     * previous flags first: this fd is brand new, so both start unset. */
1243
0
    fd = eventfd(0, 0);
1244
0
    if ((fd >= 0) &&
1245
0
        ((fcntl(fd, F_SETFL, O_NONBLOCK) < 0) || (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)))
1246
0
    {
1247
0
      close(fd);
1248
0
      fd = -1;
1249
0
    }
1250
0
  }
1251
0
  return fd;
1252
0
}
1253
#endif
1254
1255
/**
1256
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363198%28v=vs.85%29.aspx
1257
 *
1258
 * @param lpDeviceName e.g. COM1, ...
1259
 *
1260
 * @param dwDesiredAccess expects GENERIC_READ | GENERIC_WRITE, a
1261
 * warning message is printed otherwise. TODO: better support.
1262
 *
1263
 * @param dwShareMode must be zero, INVALID_HANDLE_VALUE is returned
1264
 * otherwise and GetLastError() should return ERROR_SHARING_VIOLATION.
1265
 *
1266
 * @param lpSecurityAttributes nullptr expected, a warning message is printed
1267
 * otherwise. TODO: better support.
1268
 *
1269
 * @param dwCreationDisposition must be OPEN_EXISTING. If the
1270
 * communication device doesn't exist INVALID_HANDLE_VALUE is returned
1271
 * and GetLastError() returns ERROR_FILE_NOT_FOUND.
1272
 *
1273
 * @param dwFlagsAndAttributes zero expected, a warning message is
1274
 * printed otherwise.
1275
 *
1276
 * @param hTemplateFile must be nullptr.
1277
 *
1278
 * @return INVALID_HANDLE_VALUE on error.
1279
 */
1280
HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
1281
                       LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1282
                       DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1283
0
{
1284
0
  CHAR devicePath[MAX_PATH] = WINPR_C_ARRAY_INIT;
1285
0
  struct stat deviceStat = WINPR_C_ARRAY_INIT;
1286
0
  WINPR_COMM* pComm = nullptr;
1287
0
  struct termios upcomingTermios = WINPR_C_ARRAY_INIT;
1288
1289
0
  if (!CommInitialized())
1290
0
    return INVALID_HANDLE_VALUE;
1291
1292
0
  if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
1293
0
  {
1294
0
    CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "",
1295
0
                  dwDesiredAccess);
1296
0
  }
1297
1298
0
  if (dwShareMode != 0)
1299
0
  {
1300
0
    SetLastError(ERROR_SHARING_VIOLATION);
1301
0
    return INVALID_HANDLE_VALUE;
1302
0
  }
1303
1304
  /* TODO: Prevents other processes from opening a file or
1305
   * device if they request delete, read, or write access. */
1306
1307
0
  if (lpSecurityAttributes != nullptr)
1308
0
  {
1309
0
    CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "",
1310
0
                  lpSecurityAttributes->nLength);
1311
0
  }
1312
1313
0
  if (dwCreationDisposition != OPEN_EXISTING)
1314
0
  {
1315
0
    SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
1316
0
    return INVALID_HANDLE_VALUE;
1317
0
  }
1318
1319
0
  if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
1320
0
  {
1321
    /* SetLastError(GetLastError()); */
1322
0
    return INVALID_HANDLE_VALUE;
1323
0
  }
1324
1325
0
  if (stat(devicePath, &deviceStat) < 0)
1326
0
  {
1327
0
    CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
1328
0
    SetLastError(ERROR_FILE_NOT_FOUND);
1329
0
    return INVALID_HANDLE_VALUE;
1330
0
  }
1331
1332
0
  if (!S_ISCHR(deviceStat.st_mode))
1333
0
  {
1334
0
    CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
1335
0
    SetLastError(ERROR_BAD_DEVICE);
1336
0
    return INVALID_HANDLE_VALUE;
1337
0
  }
1338
1339
0
  if (dwFlagsAndAttributes != 0)
1340
0
  {
1341
0
    CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "",
1342
0
                  dwFlagsAndAttributes);
1343
0
  }
1344
1345
0
  if (hTemplateFile != nullptr)
1346
0
  {
1347
0
    SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
1348
0
    return INVALID_HANDLE_VALUE;
1349
0
  }
1350
1351
0
  pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
1352
1353
0
  if (pComm == nullptr)
1354
0
  {
1355
0
    SetLastError(ERROR_OUTOFMEMORY);
1356
0
    return INVALID_HANDLE_VALUE;
1357
0
  }
1358
1359
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
1360
0
  pComm->common.ops = &ops;
1361
  /* error_handle */
1362
0
  pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
1363
1364
0
  if (pComm->fd < 0)
1365
0
  {
1366
0
    CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
1367
0
    SetLastError(ERROR_BAD_DEVICE);
1368
0
    goto error_handle;
1369
0
  }
1370
1371
0
  pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
1372
1373
0
  if (pComm->fd_read < 0)
1374
0
  {
1375
0
    CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
1376
0
    SetLastError(ERROR_BAD_DEVICE);
1377
0
    goto error_handle;
1378
0
  }
1379
1380
0
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1381
0
  pComm->fd_read_event = comm_eventfd_cloexec();
1382
0
#endif
1383
1384
0
  if (pComm->fd_read_event < 0)
1385
0
  {
1386
0
    CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
1387
0
    SetLastError(ERROR_BAD_DEVICE);
1388
0
    goto error_handle;
1389
0
  }
1390
1391
0
  InitializeCriticalSection(&pComm->ReadLock);
1392
0
  pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
1393
1394
0
  if (pComm->fd_write < 0)
1395
0
  {
1396
0
    CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
1397
0
    SetLastError(ERROR_BAD_DEVICE);
1398
0
    goto error_handle;
1399
0
  }
1400
1401
0
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1402
0
  pComm->fd_write_event = comm_eventfd_cloexec();
1403
0
#endif
1404
1405
0
  if (pComm->fd_write_event < 0)
1406
0
  {
1407
0
    CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
1408
0
    SetLastError(ERROR_BAD_DEVICE);
1409
0
    goto error_handle;
1410
0
  }
1411
1412
0
  InitializeCriticalSection(&pComm->WriteLock);
1413
  /* can also be setup later on with _comm_setServerSerialDriver() */
1414
0
  pComm->serverSerialDriverId = SerialDriverUnknown;
1415
0
  InitializeCriticalSection(&pComm->EventsLock);
1416
1417
0
  (void)CommUpdateIOCount(pComm, TRUE);
1418
1419
  /* The binary/raw mode is required for the redirection but
1420
   * only flags that are not handle somewhere-else, except
1421
   * ICANON, are forced here. */
1422
0
  ZeroMemory(&upcomingTermios, sizeof(struct termios));
1423
1424
0
  if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
1425
0
  {
1426
0
    SetLastError(ERROR_IO_DEVICE);
1427
0
    goto error_handle;
1428
0
  }
1429
1430
0
  upcomingTermios.c_iflag &=
1431
0
      (tcflag_t) ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
1432
0
  upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
1433
0
  upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
1434
  /* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
1435
  /* upcomingTermios.c_cflag |= CS8; */
1436
  /* About missing flags recommended by termios(3):
1437
   *
1438
   *   IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
1439
   *   CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
1440
   */
1441
  /* a few more settings required for the redirection */
1442
0
  upcomingTermios.c_cflag |= CLOCAL | CREAD;
1443
1444
0
  if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
1445
0
  {
1446
0
    SetLastError(ERROR_IO_DEVICE);
1447
0
    goto error_handle;
1448
0
  }
1449
1450
0
  return (HANDLE)pComm;
1451
0
error_handle:
1452
0
  WINPR_PRAGMA_DIAG_PUSH
1453
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC(void) CloseHandle(pComm);
1454
0
  WINPR_PRAGMA_DIAG_POP
1455
0
  return INVALID_HANDLE_VALUE;
1456
0
}
1457
1458
BOOL CommIsHandled(HANDLE handle)
1459
0
{
1460
0
  if (!CommInitialized())
1461
0
    return FALSE;
1462
1463
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
1464
0
}
1465
1466
BOOL CommIsHandleValid(HANDLE handle)
1467
0
{
1468
0
  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1469
0
  if (!CommIsHandled(handle))
1470
0
    return FALSE;
1471
0
  if (pComm->fd <= 0)
1472
0
  {
1473
0
    SetLastError(ERROR_INVALID_HANDLE);
1474
0
    return FALSE;
1475
0
  }
1476
0
  return TRUE;
1477
0
}
1478
1479
BOOL CommCloseHandle(HANDLE handle)
1480
0
{
1481
0
  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1482
1483
0
  if (!CommIsHandled(handle))
1484
0
    return FALSE;
1485
1486
0
  DeleteCriticalSection(&pComm->ReadLock);
1487
0
  DeleteCriticalSection(&pComm->WriteLock);
1488
0
  DeleteCriticalSection(&pComm->EventsLock);
1489
1490
0
  if (pComm->fd > 0)
1491
0
    close(pComm->fd);
1492
1493
0
  if (pComm->fd_write > 0)
1494
0
    close(pComm->fd_write);
1495
1496
0
  if (pComm->fd_write_event > 0)
1497
0
    close(pComm->fd_write_event);
1498
1499
0
  if (pComm->fd_read > 0)
1500
0
    close(pComm->fd_read);
1501
1502
0
  if (pComm->fd_read_event > 0)
1503
0
    close(pComm->fd_read_event);
1504
1505
0
  return TRUE;
1506
0
}
1507
1508
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1509
#ifndef WITH_EVENTFD_READ_WRITE
1510
int eventfd_read(int fd, eventfd_t* value)
1511
{
1512
  return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
1513
}
1514
1515
int eventfd_write(int fd, eventfd_t value)
1516
{
1517
  return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
1518
}
1519
#endif
1520
#endif
1521
1522
static const char* CommIoCtlToStr(unsigned long int io)
1523
0
{
1524
0
  switch (io)
1525
0
  {
1526
0
#if defined(WINPR_HAVE_SERIAL_SUPPORT)
1527
0
#if defined(TCGETS)
1528
0
    case TCGETS:
1529
0
      return "TCGETS";
1530
0
#endif
1531
0
#if defined(TCSETS)
1532
0
    case TCSETS:
1533
0
      return "TCSETS";
1534
0
#endif
1535
0
#if defined(TCSETSW)
1536
0
    case TCSETSW:
1537
0
      return "TCSETSW";
1538
0
#endif
1539
0
#if defined(TCSETSF)
1540
0
    case TCSETSF:
1541
0
      return "TCSETSF";
1542
0
#endif
1543
0
#if defined(TCGETA)
1544
0
    case TCGETA:
1545
0
      return "TCGETA";
1546
0
#endif
1547
0
#if defined(TCSETA)
1548
0
    case TCSETA:
1549
0
      return "TCSETA";
1550
0
#endif
1551
0
#if defined(TCSETAW)
1552
0
    case TCSETAW:
1553
0
      return "TCSETAW";
1554
0
#endif
1555
0
#if defined(TCSETAF)
1556
0
    case TCSETAF:
1557
0
      return "TCSETAF";
1558
0
#endif
1559
0
#if defined(TCSBRK)
1560
0
    case TCSBRK:
1561
0
      return "TCSBRK";
1562
0
#endif
1563
0
#if defined(TCXONC)
1564
0
    case TCXONC:
1565
0
      return "TCXONC";
1566
0
#endif
1567
0
#if defined(TCFLSH)
1568
0
    case TCFLSH:
1569
0
      return "TCFLSH";
1570
0
#endif
1571
0
#if defined(TIOCEXCL)
1572
0
    case TIOCEXCL:
1573
0
      return "TIOCEXCL";
1574
0
#endif
1575
0
#if defined(TIOCNXCL)
1576
0
    case TIOCNXCL:
1577
0
      return "TIOCNXCL";
1578
0
#endif
1579
0
#if defined(TIOCSCTTY)
1580
0
    case TIOCSCTTY:
1581
0
      return "TIOCSCTTY";
1582
0
#endif
1583
0
#if defined(TIOCGPGRP)
1584
0
    case TIOCGPGRP:
1585
0
      return "TIOCGPGRP";
1586
0
#endif
1587
0
#if defined(TIOCSPGRP)
1588
0
    case TIOCSPGRP:
1589
0
      return "TIOCSPGRP";
1590
0
#endif
1591
0
#if defined(TIOCOUTQ)
1592
0
    case TIOCOUTQ:
1593
0
      return "TIOCOUTQ";
1594
0
#endif
1595
0
#if defined(TIOCSTI)
1596
0
    case TIOCSTI:
1597
0
      return "TIOCSTI";
1598
0
#endif
1599
0
#if defined(TIOCGWINSZ)
1600
0
    case TIOCGWINSZ:
1601
0
      return "TIOCGWINSZ";
1602
0
#endif
1603
0
#if defined(TIOCSWINSZ)
1604
0
    case TIOCSWINSZ:
1605
0
      return "TIOCSWINSZ";
1606
0
#endif
1607
0
#if defined(TIOCMGET)
1608
0
    case TIOCMGET:
1609
0
      return "TIOCMGET";
1610
0
#endif
1611
0
#if defined(TIOCMBIS)
1612
0
    case TIOCMBIS:
1613
0
      return "TIOCMBIS";
1614
0
#endif
1615
0
#if defined(TIOCMBIC)
1616
0
    case TIOCMBIC:
1617
0
      return "TIOCMBIC";
1618
0
#endif
1619
0
#if defined(TIOCMSET)
1620
0
    case TIOCMSET:
1621
0
      return "TIOCMSET";
1622
0
#endif
1623
0
#if defined(TIOCGSOFTCAR)
1624
0
    case TIOCGSOFTCAR:
1625
0
      return "TIOCGSOFTCAR";
1626
0
#endif
1627
0
#if defined(TIOCSSOFTCAR)
1628
0
    case TIOCSSOFTCAR:
1629
0
      return "TIOCSSOFTCAR";
1630
0
#endif
1631
0
#if defined(FIONREAD)
1632
0
    case FIONREAD:
1633
0
      return "FIONREAD/TIOCINQ";
1634
0
#endif
1635
0
#if defined(TIOCLINUX)
1636
0
    case TIOCLINUX:
1637
0
      return "TIOCLINUX";
1638
0
#endif
1639
0
#if defined(TIOCCONS)
1640
0
    case TIOCCONS:
1641
0
      return "TIOCCONS";
1642
0
#endif
1643
0
#if defined(TIOCGSERIAL)
1644
0
    case TIOCGSERIAL:
1645
0
      return "TIOCGSERIAL";
1646
0
#endif
1647
0
#if defined(TIOCSSERIAL)
1648
0
    case TIOCSSERIAL:
1649
0
      return "TIOCSSERIAL";
1650
0
#endif
1651
0
#if defined(TIOCPKT)
1652
0
    case TIOCPKT:
1653
0
      return "TIOCPKT";
1654
0
#endif
1655
0
#if defined(FIONBIO)
1656
0
    case FIONBIO:
1657
0
      return "FIONBIO";
1658
0
#endif
1659
0
#if defined(TIOCNOTTY)
1660
0
    case TIOCNOTTY:
1661
0
      return "TIOCNOTTY";
1662
0
#endif
1663
0
#if defined(TIOCSETD)
1664
0
    case TIOCSETD:
1665
0
      return "TIOCSETD";
1666
0
#endif
1667
0
#if defined(TIOCGETD)
1668
0
    case TIOCGETD:
1669
0
      return "TIOCGETD";
1670
0
#endif
1671
0
#if defined(TCSBRKP)
1672
0
    case TCSBRKP:
1673
0
      return "TCSBRKP";
1674
0
#endif
1675
0
#if defined(TIOCSBRK)
1676
0
    case TIOCSBRK:
1677
0
      return "TIOCSBRK";
1678
0
#endif
1679
0
#if defined(TIOCCBRK)
1680
0
    case TIOCCBRK:
1681
0
      return "TIOCCBRK";
1682
0
#endif
1683
0
#if defined(TIOCGSID)
1684
0
    case TIOCGSID:
1685
0
      return "TIOCGSID";
1686
0
#endif
1687
0
#if defined(TIOCGRS485)
1688
0
    case TIOCGRS485:
1689
0
      return "TIOCGRS485";
1690
0
#endif
1691
0
#if defined(TIOCSRS485)
1692
0
    case TIOCSRS485:
1693
0
      return "TIOCSRS485";
1694
0
#endif
1695
0
#if defined(TIOCSPTLCK)
1696
0
    case TIOCSPTLCK:
1697
0
      return "TIOCSPTLCK";
1698
0
#endif
1699
0
#if defined(TCGETX)
1700
0
    case TCGETX:
1701
0
      return "TCGETX";
1702
0
#endif
1703
0
#if defined(TCSETX)
1704
0
    case TCSETX:
1705
0
      return "TCSETX";
1706
0
#endif
1707
0
#if defined(TCSETXF)
1708
0
    case TCSETXF:
1709
0
      return "TCSETXF";
1710
0
#endif
1711
0
#if defined(TCSETXW)
1712
0
    case TCSETXW:
1713
0
      return "TCSETXW";
1714
0
#endif
1715
0
#if defined(TIOCSIG)
1716
0
    case TIOCSIG:
1717
0
      return "TIOCSIG";
1718
0
#endif
1719
0
#if defined(TIOCVHANGUP)
1720
0
    case TIOCVHANGUP:
1721
0
      return "TIOCVHANGUP";
1722
0
#endif
1723
0
#if defined(TIOCGPTPEER)
1724
0
    case TIOCGPTPEER:
1725
0
      return "TIOCGPTPEER";
1726
0
#endif
1727
0
#if defined(FIONCLEX)
1728
0
    case FIONCLEX:
1729
0
      return "FIONCLEX";
1730
0
#endif
1731
0
#if defined(FIOCLEX)
1732
0
    case FIOCLEX:
1733
0
      return "FIOCLEX";
1734
0
#endif
1735
0
#if defined(FIOASYNC)
1736
0
    case FIOASYNC:
1737
0
      return "FIOASYNC";
1738
0
#endif
1739
0
#if defined(TIOCSERCONFIG)
1740
0
    case TIOCSERCONFIG:
1741
0
      return "TIOCSERCONFIG";
1742
0
#endif
1743
0
#if defined(TIOCSERGWILD)
1744
0
    case TIOCSERGWILD:
1745
0
      return "TIOCSERGWILD";
1746
0
#endif
1747
0
#if defined(TIOCSERSWILD)
1748
0
    case TIOCSERSWILD:
1749
0
      return "TIOCSERSWILD";
1750
0
#endif
1751
0
#if defined(TIOCGLCKTRMIOS)
1752
0
    case TIOCGLCKTRMIOS:
1753
0
      return "TIOCGLCKTRMIOS";
1754
0
#endif
1755
0
#if defined(TIOCSLCKTRMIOS)
1756
0
    case TIOCSLCKTRMIOS:
1757
0
      return "TIOCSLCKTRMIOS";
1758
0
#endif
1759
0
#if defined(TIOCSERGSTRUCT)
1760
0
    case TIOCSERGSTRUCT:
1761
0
      return "TIOCSERGSTRUCT";
1762
0
#endif
1763
0
#if defined(TIOCSERGETLSR)
1764
0
    case TIOCSERGETLSR:
1765
0
      return "TIOCSERGETLSR";
1766
0
#endif
1767
0
#if defined(TIOCSERGETMULTI)
1768
0
    case TIOCSERGETMULTI:
1769
0
      return "TIOCSERGETMULTI";
1770
0
#endif
1771
0
#if defined(TIOCSERSETMULTI)
1772
0
    case TIOCSERSETMULTI:
1773
0
      return "TIOCSERSETMULTI";
1774
0
#endif
1775
0
#if defined(TIOCMIWAIT)
1776
0
    case TIOCMIWAIT:
1777
0
      return "TIOCMIWAIT";
1778
0
#endif
1779
0
#if defined(TIOCGICOUNT)
1780
0
    case TIOCGICOUNT:
1781
0
      return "TIOCGICOUNT";
1782
0
#endif
1783
0
#if defined(FIOQSIZE)
1784
0
    case FIOQSIZE:
1785
0
      return "FIOQSIZE";
1786
0
#endif
1787
0
#if defined(TIOCPKT_DATA)
1788
0
    case TIOCPKT_DATA:
1789
0
      return "TIOCPKT_DATA";
1790
0
#endif
1791
0
#if defined(TIOCPKT_FLUSHWRITE)
1792
0
    case TIOCPKT_FLUSHWRITE:
1793
0
      return "TIOCPKT_FLUSHWRITE";
1794
0
#endif
1795
0
#if defined(TIOCPKT_STOP)
1796
0
    case TIOCPKT_STOP:
1797
0
      return "TIOCPKT_STOP";
1798
0
#endif
1799
0
#if defined(TIOCPKT_START)
1800
0
    case TIOCPKT_START:
1801
0
      return "TIOCPKT_START";
1802
0
#endif
1803
0
#if defined(TIOCPKT_NOSTOP)
1804
0
    case TIOCPKT_NOSTOP:
1805
0
      return "TIOCPKT_NOSTOP";
1806
0
#endif
1807
0
#if defined(TIOCPKT_DOSTOP)
1808
0
    case TIOCPKT_DOSTOP:
1809
0
      return "TIOCPKT_DOSTOP";
1810
0
#endif
1811
0
#if defined(TIOCPKT_IOCTL)
1812
0
    case TIOCPKT_IOCTL:
1813
0
      return "TIOCPKT_IOCTL";
1814
0
#endif
1815
0
#endif
1816
0
    default:
1817
0
      return "UNKNOWN";
1818
0
  }
1819
0
}
1820
1821
static BOOL CommStatusErrorEx(WINPR_COMM* pComm, unsigned long int ctl, const char* file,
1822
                              const char* fkt, size_t line)
1823
0
{
1824
0
  WINPR_ASSERT(pComm);
1825
0
  BOOL rc = (pComm->permissive);
1826
0
  const DWORD level = rc ? WLOG_DEBUG : WLOG_WARN;
1827
0
  char ebuffer[256] = WINPR_C_ARRAY_INIT;
1828
0
  const char* str = CommIoCtlToStr(ctl);
1829
1830
0
  if (CommInitialized())
1831
0
  {
1832
0
    if (WLog_IsLevelActive(sLog, level))
1833
0
    {
1834
0
      WLog_PrintTextMessage(sLog, level, line, file, fkt,
1835
0
                            "%s [0x%08lx] ioctl failed, errno=[%d] %s.", str, ctl, errno,
1836
0
                            winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1837
0
    }
1838
0
  }
1839
1840
0
  if (!rc)
1841
0
    SetLastError(ERROR_IO_DEVICE);
1842
1843
0
  return rc;
1844
0
}
1845
1846
BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
1847
                   const char* fkt, size_t line)
1848
0
{
1849
0
  if (ioctl(pComm->fd, ctl, data) < 0)
1850
0
  {
1851
0
    if (!CommStatusErrorEx(pComm, ctl, file, fkt, line))
1852
0
      return FALSE;
1853
0
  }
1854
0
  return TRUE;
1855
0
}
1856
1857
BOOL CommUpdateIOCount(WINPR_ATTR_UNUSED HANDLE handle, WINPR_ATTR_UNUSED BOOL checkSupportStatus)
1858
0
{
1859
0
  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1860
0
  WINPR_ASSERT(pComm);
1861
1862
0
#if defined(WINPR_HAVE_COMM_COUNTERS)
1863
0
  ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
1864
0
  if (pComm->TIOCGICOUNTSupported || checkSupportStatus)
1865
0
  {
1866
0
    const int rc = ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters));
1867
0
    if (checkSupportStatus)
1868
0
      pComm->TIOCGICOUNTSupported = rc >= 0;
1869
0
    else if (rc < 0)
1870
0
    {
1871
0
      if (!CommStatusErrorEx(pComm, TIOCGICOUNT, __FILE__, __func__, __LINE__))
1872
0
        return FALSE;
1873
0
    }
1874
0
  }
1875
0
#endif
1876
0
  return TRUE;
1877
0
}
1878
1879
static const char* CommSerialEvFlagString(ULONG flag)
1880
0
{
1881
0
  switch (flag)
1882
0
  {
1883
0
    case SERIAL_EV_RXCHAR:
1884
0
      return "SERIAL_EV_RXCHAR";
1885
0
    case SERIAL_EV_RXFLAG:
1886
0
      return "SERIAL_EV_RXFLAG";
1887
0
    case SERIAL_EV_TXEMPTY:
1888
0
      return "SERIAL_EV_TXEMPTY";
1889
0
    case SERIAL_EV_CTS:
1890
0
      return "SERIAL_EV_CTS ";
1891
0
    case SERIAL_EV_DSR:
1892
0
      return "SERIAL_EV_DSR ";
1893
0
    case SERIAL_EV_RLSD:
1894
0
      return "SERIAL_EV_RLSD";
1895
0
    case SERIAL_EV_BREAK:
1896
0
      return "SERIAL_EV_BREAK";
1897
0
    case SERIAL_EV_ERR:
1898
0
      return "SERIAL_EV_ERR ";
1899
0
    case SERIAL_EV_RING:
1900
0
      return "SERIAL_EV_RING";
1901
0
    case SERIAL_EV_PERR:
1902
0
      return "SERIAL_EV_PERR";
1903
0
    case SERIAL_EV_RX80FULL:
1904
0
      return "SERIAL_EV_RX80FULL";
1905
0
    case SERIAL_EV_EVENT1:
1906
0
      return "SERIAL_EV_EVENT1";
1907
0
    case SERIAL_EV_EVENT2:
1908
0
      return "SERIAL_EV_EVENT2";
1909
0
    case SERIAL_EV_WINPR_WAITING:
1910
0
      return "SERIAL_EV_WINPR_WAITING";
1911
0
    case SERIAL_EV_WINPR_STOP:
1912
0
      return "SERIAL_EV_WINPR_STOP";
1913
0
    default:
1914
0
      return "SERIAL_EV_UNKNOWN";
1915
0
  }
1916
0
}
1917
1918
const char* CommSerialEvString(ULONG status, char* buffer, size_t size)
1919
0
{
1920
0
  const ULONG flags[] = { SERIAL_EV_RXCHAR, SERIAL_EV_RXFLAG,        SERIAL_EV_TXEMPTY,
1921
0
                        SERIAL_EV_CTS,    SERIAL_EV_DSR,           SERIAL_EV_RLSD,
1922
0
                        SERIAL_EV_BREAK,  SERIAL_EV_ERR,           SERIAL_EV_RING,
1923
0
                        SERIAL_EV_PERR,   SERIAL_EV_RX80FULL,      SERIAL_EV_EVENT1,
1924
0
                        SERIAL_EV_EVENT2, SERIAL_EV_WINPR_WAITING, SERIAL_EV_WINPR_STOP };
1925
1926
0
  winpr_str_append("{", buffer, size, "");
1927
1928
0
  const char* sep = "";
1929
0
  for (size_t x = 0; x < ARRAYSIZE(flags); x++)
1930
0
  {
1931
0
    const ULONG flag = flags[x];
1932
0
    if (status & flag)
1933
0
    {
1934
0
      winpr_str_append(CommSerialEvFlagString(flag), buffer, size, sep);
1935
0
      sep = "|";
1936
0
    }
1937
0
  }
1938
1939
0
  char number[32] = WINPR_C_ARRAY_INIT;
1940
0
  (void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
1941
0
  winpr_str_append(number, buffer, size, "");
1942
0
  return buffer;
1943
0
}