Coverage Report

Created: 2025-07-01 06:46

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