Coverage Report

Created: 2023-11-27 07:17

/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
#if defined __linux__ && !defined ANDROID
26
27
#include <winpr/assert.h>
28
#include <errno.h>
29
#include <fcntl.h>
30
#include <pthread.h>
31
#include <stdarg.h>
32
#include <sys/ioctl.h>
33
#include <sys/stat.h>
34
#include <sys/types.h>
35
#include <termios.h>
36
#include <unistd.h>
37
38
#include <winpr/crt.h>
39
#include <winpr/comm.h>
40
#include <winpr/tchar.h>
41
#include <winpr/wlog.h>
42
#include <winpr/handle.h>
43
44
#include "comm_ioctl.h"
45
46
/**
47
 * Communication Resources:
48
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363196/
49
 */
50
51
#include "comm.h"
52
53
static wLog* _Log = NULL;
54
55
struct comm_device
56
{
57
  LPTSTR name;
58
  LPTSTR path;
59
};
60
61
typedef struct comm_device COMM_DEVICE;
62
63
/* FIXME: get a clever data structure, see also io.h functions */
64
/* _CommDevices is a NULL-terminated array with a maximun of COMM_DEVICE_MAX COMM_DEVICE */
65
0
#define COMM_DEVICE_MAX 128
66
static COMM_DEVICE** _CommDevices = NULL;
67
static CRITICAL_SECTION _CommDevicesLock;
68
69
static HANDLE_CREATOR _CommHandleCreator;
70
71
static pthread_once_t _CommInitialized = PTHREAD_ONCE_INIT;
72
73
static int CommGetFd(HANDLE handle)
74
0
{
75
0
  WINPR_COMM* comm = (WINPR_COMM*)handle;
76
77
0
  if (!CommIsHandled(handle))
78
0
    return -1;
79
80
0
  return comm->fd;
81
0
}
82
83
HANDLE_CREATOR* GetCommHandleCreator(void)
84
0
{
85
0
  _CommHandleCreator.IsHandled = IsCommDevice;
86
0
  _CommHandleCreator.CreateFileA = CommCreateFileA;
87
0
  return &_CommHandleCreator;
88
0
}
89
90
static void _CommInit(void)
91
0
{
92
  /* NB: error management to be done outside of this function */
93
0
  WINPR_ASSERT(_Log == NULL);
94
0
  WINPR_ASSERT(_CommDevices == NULL);
95
0
  _CommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
96
97
0
  if (!_CommDevices)
98
0
    return;
99
100
0
  if (!InitializeCriticalSectionEx(&_CommDevicesLock, 0, 0))
101
0
  {
102
0
    free(_CommDevices);
103
0
    _CommDevices = NULL;
104
0
    return;
105
0
  }
106
107
0
  _Log = WLog_Get("com.winpr.comm");
108
0
  WINPR_ASSERT(_Log != NULL);
109
0
}
110
111
/**
112
 * Returns TRUE when the comm module is correctly intialized, FALSE otherwise
113
 * with ERROR_DLL_INIT_FAILED set as the last error.
114
 */
115
static BOOL CommInitialized(void)
116
0
{
117
0
  if (pthread_once(&_CommInitialized, _CommInit) != 0)
118
0
  {
119
0
    SetLastError(ERROR_DLL_INIT_FAILED);
120
0
    return FALSE;
121
0
  }
122
123
0
  return TRUE;
124
0
}
125
126
void CommLog_Print(DWORD level, ...)
127
0
{
128
0
  if (!CommInitialized())
129
0
    return;
130
131
0
  va_list ap;
132
0
  va_start(ap, level);
133
0
  WLog_PrintVA(_Log, level, ap);
134
0
  va_end(ap);
135
0
}
136
137
BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB)
138
0
{
139
0
  if (!CommInitialized())
140
0
    return FALSE;
141
142
  /* TODO: not implemented */
143
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
144
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
145
0
  return FALSE;
146
0
}
147
148
BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB)
149
0
{
150
0
  if (!CommInitialized())
151
0
    return FALSE;
152
153
  /* TODO: not implemented */
154
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
155
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
156
0
  return FALSE;
157
0
}
158
159
BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
160
0
{
161
0
  if (!CommInitialized())
162
0
    return FALSE;
163
164
  /* TODO: not implemented */
165
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
166
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
167
0
  return FALSE;
168
0
}
169
170
BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
171
0
{
172
0
  if (!CommInitialized())
173
0
    return FALSE;
174
175
  /* TODO: not implemented */
176
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
177
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
178
0
  return FALSE;
179
0
}
180
181
BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
182
0
{
183
0
  if (!CommInitialized())
184
0
    return FALSE;
185
186
  /* TODO: not implemented */
187
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
188
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
189
0
  return FALSE;
190
0
}
191
192
BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
193
0
{
194
0
  if (!CommInitialized())
195
0
    return FALSE;
196
197
  /* TODO: not implemented */
198
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
199
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
200
0
  return FALSE;
201
0
}
202
203
BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
204
0
{
205
0
  WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
206
207
0
  if (!CommInitialized())
208
0
    return FALSE;
209
210
  /* TODO: not implemented */
211
212
0
  if (!pComm)
213
0
    return FALSE;
214
215
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
216
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
217
0
  return FALSE;
218
0
}
219
220
BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize)
221
0
{
222
0
  WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
223
224
0
  if (!CommInitialized())
225
0
    return FALSE;
226
227
  /* TODO: not implemented */
228
229
0
  if (!pComm)
230
0
    return FALSE;
231
232
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
233
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
234
0
  return FALSE;
235
0
}
236
237
BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask)
238
0
{
239
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
240
241
0
  if (!CommInitialized())
242
0
    return FALSE;
243
244
  /* TODO: not implemented */
245
246
0
  if (!pComm)
247
0
    return FALSE;
248
249
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
250
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
251
0
  return FALSE;
252
0
}
253
254
BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask)
255
0
{
256
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
257
258
0
  if (!CommInitialized())
259
0
    return FALSE;
260
261
  /* TODO: not implemented */
262
263
0
  if (!pComm)
264
0
    return FALSE;
265
266
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
267
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
268
0
  return FALSE;
269
0
}
270
271
BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat)
272
0
{
273
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
274
275
0
  if (!CommInitialized())
276
0
    return FALSE;
277
278
  /* TODO: not implemented */
279
280
0
  if (!pComm)
281
0
    return FALSE;
282
283
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
284
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
285
0
  return FALSE;
286
0
}
287
288
/**
289
 * ERRORS:
290
 *   ERROR_DLL_INIT_FAILED
291
 *   ERROR_INVALID_HANDLE
292
 */
293
BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp)
294
0
{
295
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
296
0
  DWORD bytesReturned;
297
298
0
  if (!CommIsHandleValid(hFile))
299
0
    return FALSE;
300
301
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp,
302
0
                           sizeof(COMMPROP), &bytesReturned, NULL))
303
0
  {
304
0
    CommLog_Print(WLOG_WARN, "GetCommProperties failure.");
305
0
    return FALSE;
306
0
  }
307
308
0
  return TRUE;
309
0
}
310
311
/**
312
 *
313
 *
314
 * ERRORS:
315
 *   ERROR_INVALID_HANDLE
316
 *   ERROR_INVALID_DATA
317
 *   ERROR_IO_DEVICE
318
 *   ERROR_OUTOFMEMORY
319
 */
320
BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
321
0
{
322
0
  DCB* lpLocalDcb;
323
0
  struct termios currentState;
324
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
325
0
  DWORD bytesReturned;
326
327
0
  if (!CommIsHandleValid(hFile))
328
0
    return FALSE;
329
330
0
  if (!lpDCB)
331
0
  {
332
0
    SetLastError(ERROR_INVALID_DATA);
333
0
    return FALSE;
334
0
  }
335
336
0
  if (lpDCB->DCBlength < sizeof(DCB))
337
0
  {
338
0
    SetLastError(ERROR_INVALID_DATA);
339
0
    return FALSE;
340
0
  }
341
342
0
  if (tcgetattr(pComm->fd, &currentState) < 0)
343
0
  {
344
0
    SetLastError(ERROR_IO_DEVICE);
345
0
    return FALSE;
346
0
  }
347
348
0
  lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
349
350
0
  if (lpLocalDcb == NULL)
351
0
  {
352
0
    SetLastError(ERROR_OUTOFMEMORY);
353
0
    return FALSE;
354
0
  }
355
356
  /* error_handle */
357
0
  lpLocalDcb->DCBlength = lpDCB->DCBlength;
358
0
  SERIAL_BAUD_RATE baudRate;
359
360
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate,
361
0
                           sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL))
362
0
  {
363
0
    CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate.");
364
0
    goto error_handle;
365
0
  }
366
367
0
  lpLocalDcb->BaudRate = baudRate.BaudRate;
368
0
  lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
369
370
0
  if (!lpLocalDcb->fBinary)
371
0
  {
372
0
    CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
373
0
  }
374
375
0
  lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
376
0
  SERIAL_HANDFLOW handflow;
377
378
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow,
379
0
                           sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
380
0
  {
381
0
    CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings.");
382
0
    goto error_handle;
383
0
  }
384
385
0
  lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
386
0
  lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
387
388
0
  if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
389
0
  {
390
0
    lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
391
0
  }
392
0
  else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
393
0
  {
394
0
    lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
395
0
  }
396
0
  else
397
0
  {
398
0
    lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
399
0
  }
400
401
0
  lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;
402
0
  lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;
403
0
  lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;
404
0
  lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;
405
0
  lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;
406
0
  lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;
407
408
0
  if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
409
0
  {
410
0
    lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
411
0
  }
412
0
  else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
413
0
  {
414
0
    lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
415
0
  }
416
0
  else
417
0
  {
418
0
    lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
419
0
  }
420
421
  // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow
422
  // Control Enabled bit in its Modem Control Register (MCR)
423
0
  lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
424
  /* lpLocalDcb->fDummy2 not used */
425
0
  lpLocalDcb->wReserved = 0; /* must be zero */
426
0
  lpLocalDcb->XonLim = handflow.XonLimit;
427
0
  lpLocalDcb->XoffLim = handflow.XoffLimit;
428
0
  SERIAL_LINE_CONTROL lineControl;
429
430
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl,
431
0
                           sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
432
0
  {
433
0
    CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
434
0
    goto error_handle;
435
0
  }
436
437
0
  lpLocalDcb->ByteSize = lineControl.WordLength;
438
0
  lpLocalDcb->Parity = lineControl.Parity;
439
0
  lpLocalDcb->StopBits = lineControl.StopBits;
440
0
  SERIAL_CHARS serialChars;
441
442
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
443
0
                           sizeof(SERIAL_CHARS), &bytesReturned, NULL))
444
0
  {
445
0
    CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
446
0
    goto error_handle;
447
0
  }
448
449
0
  lpLocalDcb->XonChar = serialChars.XonChar;
450
0
  lpLocalDcb->XoffChar = serialChars.XoffChar;
451
0
  lpLocalDcb->ErrorChar = serialChars.ErrorChar;
452
0
  lpLocalDcb->EofChar = serialChars.EofChar;
453
0
  lpLocalDcb->EvtChar = serialChars.EventChar;
454
0
  memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
455
0
  free(lpLocalDcb);
456
0
  return TRUE;
457
0
error_handle:
458
0
  free(lpLocalDcb);
459
0
  return FALSE;
460
0
}
461
462
/**
463
 * @return TRUE on success, FALSE otherwise.
464
 *
465
 * As of today, SetCommState() can fail half-way with some settings
466
 * applied and some others not. SetCommState() returns on the first
467
 * failure met. FIXME: or is it correct?
468
 *
469
 * ERRORS:
470
 *   ERROR_INVALID_HANDLE
471
 *   ERROR_IO_DEVICE
472
 */
473
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
474
0
{
475
0
  struct termios upcomingTermios = { 0 };
476
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
477
0
  DWORD bytesReturned;
478
479
  /* FIXME: validate changes according GetCommProperties? */
480
481
0
  if (!CommIsHandleValid(hFile))
482
0
    return FALSE;
483
484
0
  if (!lpDCB)
485
0
  {
486
0
    SetLastError(ERROR_INVALID_DATA);
487
0
    return FALSE;
488
0
  }
489
490
  /* NB: did the choice to call ioctls first when available and
491
     then to setup upcomingTermios. Don't mix both stages. */
492
  /** ioctl calls stage **/
493
0
  SERIAL_BAUD_RATE baudRate;
494
0
  baudRate.BaudRate = lpDCB->BaudRate;
495
496
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE),
497
0
                           NULL, 0, &bytesReturned, NULL))
498
0
  {
499
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
500
0
    return FALSE;
501
0
  }
502
503
0
  SERIAL_CHARS serialChars;
504
505
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
506
0
                           sizeof(SERIAL_CHARS), &bytesReturned,
507
0
                           NULL)) /* as of today, required for BreakChar */
508
0
  {
509
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
510
0
    return FALSE;
511
0
  }
512
513
0
  serialChars.XonChar = lpDCB->XonChar;
514
0
  serialChars.XoffChar = lpDCB->XoffChar;
515
0
  serialChars.ErrorChar = lpDCB->ErrorChar;
516
0
  serialChars.EofChar = lpDCB->EofChar;
517
0
  serialChars.EventChar = lpDCB->EvtChar;
518
519
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS),
520
0
                           NULL, 0, &bytesReturned, NULL))
521
0
  {
522
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
523
0
    return FALSE;
524
0
  }
525
526
0
  SERIAL_LINE_CONTROL lineControl;
527
0
  lineControl.StopBits = lpDCB->StopBits;
528
0
  lineControl.Parity = lpDCB->Parity;
529
0
  lineControl.WordLength = lpDCB->ByteSize;
530
531
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
532
0
                           sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
533
0
  {
534
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
535
0
    return FALSE;
536
0
  }
537
538
0
  SERIAL_HANDFLOW handflow = { 0 };
539
540
0
  if (lpDCB->fOutxCtsFlow)
541
0
  {
542
0
    handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
543
0
  }
544
545
0
  if (lpDCB->fOutxDsrFlow)
546
0
  {
547
0
    handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
548
0
  }
549
550
0
  switch (lpDCB->fDtrControl)
551
0
  {
552
0
    case SERIAL_DTR_HANDSHAKE:
553
0
      handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
554
0
      break;
555
556
0
    case SERIAL_DTR_CONTROL:
557
0
      handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
558
0
      break;
559
560
0
    case DTR_CONTROL_DISABLE:
561
      /* do nothing since handflow is init-zeroed */
562
0
      break;
563
564
0
    default:
565
0
      CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRIu32 "\n",
566
0
                    lpDCB->fDtrControl);
567
0
      return FALSE;
568
0
  }
569
570
0
  if (lpDCB->fDsrSensitivity)
571
0
  {
572
0
    handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
573
0
  }
574
575
0
  if (lpDCB->fTXContinueOnXoff)
576
0
  {
577
0
    handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
578
0
  }
579
580
0
  if (lpDCB->fOutX)
581
0
  {
582
0
    handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
583
0
  }
584
585
0
  if (lpDCB->fInX)
586
0
  {
587
0
    handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
588
0
  }
589
590
0
  if (lpDCB->fErrorChar)
591
0
  {
592
0
    handflow.FlowReplace |= SERIAL_ERROR_CHAR;
593
0
  }
594
595
0
  if (lpDCB->fNull)
596
0
  {
597
0
    handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
598
0
  }
599
600
0
  switch (lpDCB->fRtsControl)
601
0
  {
602
0
    case RTS_CONTROL_TOGGLE:
603
0
      CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
604
      // FIXME: see also GetCommState()
605
0
      return FALSE;
606
607
0
    case RTS_CONTROL_HANDSHAKE:
608
0
      handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
609
0
      break;
610
611
0
    case RTS_CONTROL_ENABLE:
612
0
      handflow.FlowReplace |= SERIAL_RTS_CONTROL;
613
0
      break;
614
615
0
    case RTS_CONTROL_DISABLE:
616
      /* do nothing since handflow is init-zeroed */
617
0
      break;
618
619
0
    default:
620
0
      CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRIu32 "\n",
621
0
                    lpDCB->fRtsControl);
622
0
      return FALSE;
623
0
  }
624
625
0
  if (lpDCB->fAbortOnError)
626
0
  {
627
0
    handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
628
0
  }
629
630
  /* lpDCB->fDummy2 not used */
631
  /* lpLocalDcb->wReserved  ignored */
632
0
  handflow.XonLimit = lpDCB->XonLim;
633
0
  handflow.XoffLimit = lpDCB->XoffLim;
634
635
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW),
636
0
                           NULL, 0, &bytesReturned, NULL))
637
0
  {
638
0
    CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
639
0
    return FALSE;
640
0
  }
641
642
  /** upcomingTermios stage **/
643
644
0
  if (tcgetattr(pComm->fd, &upcomingTermios) <
645
0
      0) /* NB: preserves current settings not directly handled by the Communication Functions */
646
0
  {
647
0
    SetLastError(ERROR_IO_DEVICE);
648
0
    return FALSE;
649
0
  }
650
651
0
  if (lpDCB->fBinary)
652
0
  {
653
0
    upcomingTermios.c_lflag &= ~ICANON;
654
0
  }
655
0
  else
656
0
  {
657
0
    upcomingTermios.c_lflag |= ICANON;
658
0
    CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
659
0
  }
660
661
0
  if (lpDCB->fParity)
662
0
  {
663
0
    upcomingTermios.c_iflag |= INPCK;
664
0
  }
665
0
  else
666
0
  {
667
0
    upcomingTermios.c_iflag &= ~INPCK;
668
0
  }
669
670
  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
671
   *
672
   * The SetCommState function reconfigures the communications
673
   * resource, but it does not affect the internal output and
674
   * input buffers of the specified driver. The buffers are not
675
   * flushed, and pending read and write operations are not
676
   * terminated prematurely.
677
   *
678
   * TCSANOW matches the best this definition
679
   */
680
681
0
  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
682
0
  {
683
0
    SetLastError(ERROR_IO_DEVICE);
684
0
    return FALSE;
685
0
  }
686
687
0
  return TRUE;
688
0
}
689
690
/**
691
 * ERRORS:
692
 *   ERROR_INVALID_HANDLE
693
 */
694
BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
695
0
{
696
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
697
0
  DWORD bytesReturned;
698
699
0
  if (!CommIsHandleValid(hFile))
700
0
    return FALSE;
701
702
  /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
703
704
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts,
705
0
                           sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
706
0
  {
707
0
    CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
708
0
    return FALSE;
709
0
  }
710
711
0
  return TRUE;
712
0
}
713
714
/**
715
 * ERRORS:
716
 *   ERROR_INVALID_HANDLE
717
 */
718
BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
719
0
{
720
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
721
0
  DWORD bytesReturned;
722
723
0
  if (!CommIsHandleValid(hFile))
724
0
    return FALSE;
725
726
  /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
727
728
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS),
729
0
                           NULL, 0, &bytesReturned, NULL))
730
0
  {
731
0
    CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
732
0
    return FALSE;
733
0
  }
734
735
0
  return TRUE;
736
0
}
737
738
BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
739
0
{
740
0
  if (!CommInitialized())
741
0
    return FALSE;
742
743
  /* TODO: not implemented */
744
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
745
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
746
0
  return FALSE;
747
0
}
748
749
BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
750
0
{
751
0
  if (!CommInitialized())
752
0
    return FALSE;
753
754
  /* TODO: not implemented */
755
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
756
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
757
0
  return FALSE;
758
0
}
759
760
BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
761
0
{
762
0
  if (!CommInitialized())
763
0
    return FALSE;
764
765
  /* TODO: not implemented */
766
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
767
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
768
0
  return FALSE;
769
0
}
770
771
BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
772
0
{
773
0
  if (!CommInitialized())
774
0
    return FALSE;
775
776
  /* TODO: not implemented */
777
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
778
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
779
0
  return FALSE;
780
0
}
781
782
BOOL SetCommBreak(HANDLE hFile)
783
0
{
784
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
785
786
0
  if (!CommInitialized())
787
0
    return FALSE;
788
789
  /* TODO: not implemented */
790
791
0
  if (!pComm)
792
0
    return FALSE;
793
794
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
795
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
796
0
  return FALSE;
797
0
}
798
799
BOOL ClearCommBreak(HANDLE hFile)
800
0
{
801
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
802
803
0
  if (!CommInitialized())
804
0
    return FALSE;
805
806
  /* TODO: not implemented */
807
808
0
  if (!pComm)
809
0
    return FALSE;
810
811
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
812
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
813
0
  return FALSE;
814
0
}
815
816
BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat)
817
0
{
818
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
819
820
0
  if (!CommInitialized())
821
0
    return FALSE;
822
823
  /* TODO: not implemented */
824
825
0
  if (!pComm)
826
0
    return FALSE;
827
828
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
829
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
830
0
  return FALSE;
831
0
}
832
833
BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
834
0
{
835
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
836
0
  DWORD bytesReturned = 0;
837
838
0
  if (!CommIsHandleValid(hFile))
839
0
    return FALSE;
840
841
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0,
842
0
                           &bytesReturned, NULL))
843
0
  {
844
0
    CommLog_Print(WLOG_WARN, "PurgeComm failure.");
845
0
    return FALSE;
846
0
  }
847
848
0
  return TRUE;
849
0
}
850
851
BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
852
0
{
853
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
854
0
  SERIAL_QUEUE_SIZE queueSize;
855
0
  DWORD bytesReturned = 0;
856
857
0
  if (!CommIsHandleValid(hFile))
858
0
    return FALSE;
859
860
0
  queueSize.InSize = dwInQueue;
861
0
  queueSize.OutSize = dwOutQueue;
862
863
0
  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
864
0
                           sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL))
865
0
  {
866
0
    CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
867
0
    return FALSE;
868
0
  }
869
870
0
  return TRUE;
871
0
}
872
873
BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc)
874
0
{
875
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
876
877
0
  if (!CommInitialized())
878
0
    return FALSE;
879
880
  /* TODO: not implemented */
881
882
0
  if (!pComm)
883
0
    return FALSE;
884
885
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
886
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
887
0
  return FALSE;
888
0
}
889
890
BOOL TransmitCommChar(HANDLE hFile, char cChar)
891
0
{
892
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
893
894
0
  if (!CommInitialized())
895
0
    return FALSE;
896
897
  /* TODO: not implemented */
898
899
0
  if (!pComm)
900
0
    return FALSE;
901
902
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
903
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
904
0
  return FALSE;
905
0
}
906
907
BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped)
908
0
{
909
0
  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
910
911
0
  if (!CommInitialized())
912
0
    return FALSE;
913
914
  /* TODO: not implemented */
915
916
0
  if (!pComm)
917
0
    return FALSE;
918
919
0
  CommLog_Print(WLOG_ERROR, "Not implemented");
920
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
921
0
  return FALSE;
922
0
}
923
924
/**
925
 * Returns TRUE on success, FALSE otherwise. To get extended error
926
 * information, call GetLastError.
927
 *
928
 * ERRORS:
929
 *   ERROR_DLL_INIT_FAILED
930
 *   ERROR_OUTOFMEMORY was not possible to get mappings.
931
 *   ERROR_INVALID_DATA was not possible to add the device.
932
 */
933
BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
934
0
{
935
0
  int i = 0;
936
0
  LPTSTR storedDeviceName = NULL;
937
0
  LPTSTR storedTargetPath = NULL;
938
939
0
  if (!CommInitialized())
940
0
    return FALSE;
941
942
0
  EnterCriticalSection(&_CommDevicesLock);
943
944
0
  if (_CommDevices == NULL)
945
0
  {
946
0
    SetLastError(ERROR_DLL_INIT_FAILED);
947
0
    goto error_handle;
948
0
  }
949
950
0
  storedDeviceName = _tcsdup(lpDeviceName);
951
952
0
  if (storedDeviceName == NULL)
953
0
  {
954
0
    SetLastError(ERROR_OUTOFMEMORY);
955
0
    goto error_handle;
956
0
  }
957
958
0
  storedTargetPath = _tcsdup(lpTargetPath);
959
960
0
  if (storedTargetPath == NULL)
961
0
  {
962
0
    SetLastError(ERROR_OUTOFMEMORY);
963
0
    goto error_handle;
964
0
  }
965
966
0
  for (i = 0; i < COMM_DEVICE_MAX; i++)
967
0
  {
968
0
    if (_CommDevices[i] != NULL)
969
0
    {
970
0
      if (_tcscmp(_CommDevices[i]->name, storedDeviceName) == 0)
971
0
      {
972
        /* take over the emplacement */
973
0
        free(_CommDevices[i]->name);
974
0
        free(_CommDevices[i]->path);
975
0
        _CommDevices[i]->name = storedDeviceName;
976
0
        _CommDevices[i]->path = storedTargetPath;
977
0
        break;
978
0
      }
979
0
    }
980
0
    else
981
0
    {
982
      /* new emplacement */
983
0
      _CommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
984
985
0
      if (_CommDevices[i] == NULL)
986
0
      {
987
0
        SetLastError(ERROR_OUTOFMEMORY);
988
0
        goto error_handle;
989
0
      }
990
991
0
      _CommDevices[i]->name = storedDeviceName;
992
0
      _CommDevices[i]->path = storedTargetPath;
993
0
      break;
994
0
    }
995
0
  }
996
997
0
  if (i == COMM_DEVICE_MAX)
998
0
  {
999
0
    SetLastError(ERROR_OUTOFMEMORY);
1000
0
    goto error_handle;
1001
0
  }
1002
1003
0
  LeaveCriticalSection(&_CommDevicesLock);
1004
0
  return TRUE;
1005
0
error_handle:
1006
0
  free(storedDeviceName);
1007
0
  free(storedTargetPath);
1008
0
  LeaveCriticalSection(&_CommDevicesLock);
1009
0
  return FALSE;
1010
0
}
1011
1012
/**
1013
 * Returns the number of target paths in the buffer pointed to by
1014
 * lpTargetPath.
1015
 *
1016
 * The current implementation returns in any case 0 and 1 target
1017
 * path. A NULL lpDeviceName is not supported yet to get all the
1018
 * paths.
1019
 *
1020
 * ERRORS:
1021
 *   ERROR_SUCCESS
1022
 *   ERROR_DLL_INIT_FAILED
1023
 *   ERROR_OUTOFMEMORY was not possible to get mappings.
1024
 *   ERROR_NOT_SUPPORTED equivalent QueryDosDevice feature not supported.
1025
 *   ERROR_INVALID_DATA was not possible to retrieve any device information.
1026
 *   ERROR_INSUFFICIENT_BUFFER too small lpTargetPath
1027
 */
1028
DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
1029
0
{
1030
0
  int i;
1031
0
  LPTSTR storedTargetPath;
1032
0
  SetLastError(ERROR_SUCCESS);
1033
1034
0
  if (!CommInitialized())
1035
0
    return 0;
1036
1037
0
  if (_CommDevices == NULL)
1038
0
  {
1039
0
    SetLastError(ERROR_DLL_INIT_FAILED);
1040
0
    return 0;
1041
0
  }
1042
1043
0
  if (lpDeviceName == NULL || lpTargetPath == NULL)
1044
0
  {
1045
0
    SetLastError(ERROR_NOT_SUPPORTED);
1046
0
    return 0;
1047
0
  }
1048
1049
0
  EnterCriticalSection(&_CommDevicesLock);
1050
0
  storedTargetPath = NULL;
1051
1052
0
  for (i = 0; i < COMM_DEVICE_MAX; i++)
1053
0
  {
1054
0
    if (_CommDevices[i] != NULL)
1055
0
    {
1056
0
      if (_tcscmp(_CommDevices[i]->name, lpDeviceName) == 0)
1057
0
      {
1058
0
        storedTargetPath = _CommDevices[i]->path;
1059
0
        break;
1060
0
      }
1061
1062
0
      continue;
1063
0
    }
1064
1065
0
    break;
1066
0
  }
1067
1068
0
  LeaveCriticalSection(&_CommDevicesLock);
1069
1070
0
  if (storedTargetPath == NULL)
1071
0
  {
1072
0
    SetLastError(ERROR_INVALID_DATA);
1073
0
    return 0;
1074
0
  }
1075
1076
0
  if (_tcslen(storedTargetPath) + 2 > ucchMax)
1077
0
  {
1078
0
    SetLastError(ERROR_INSUFFICIENT_BUFFER);
1079
0
    return 0;
1080
0
  }
1081
1082
0
  _tcscpy(lpTargetPath, storedTargetPath);
1083
0
  lpTargetPath[_tcslen(storedTargetPath) + 1] = '\0'; /* 2nd final '\0' */
1084
0
  return _tcslen(lpTargetPath) + 2;
1085
0
}
1086
1087
/**
1088
 * Checks whether lpDeviceName is a valid and registered Communication device.
1089
 */
1090
BOOL IsCommDevice(LPCTSTR lpDeviceName)
1091
0
{
1092
0
  TCHAR lpTargetPath[MAX_PATH];
1093
1094
0
  if (!CommInitialized())
1095
0
    return FALSE;
1096
1097
0
  if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
1098
0
  {
1099
0
    return TRUE;
1100
0
  }
1101
1102
0
  return FALSE;
1103
0
}
1104
1105
/**
1106
 * Sets
1107
 */
1108
void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
1109
0
{
1110
0
  ULONG Type;
1111
0
  WINPR_HANDLE* Object;
1112
0
  WINPR_COMM* pComm;
1113
1114
0
  if (!CommInitialized())
1115
0
    return;
1116
1117
0
  if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
1118
0
  {
1119
0
    CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
1120
0
    return;
1121
0
  }
1122
1123
0
  pComm = (WINPR_COMM*)Object;
1124
0
  pComm->serverSerialDriverId = driverId;
1125
0
}
1126
1127
static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle,
1128
                        CommGetFd,     NULL, /* CleanupHandle */
1129
                        NULL,          NULL,
1130
                        NULL,          NULL,
1131
                        NULL,          NULL,
1132
                        NULL,          NULL,
1133
                        NULL,          NULL,
1134
                        NULL,          NULL,
1135
                        NULL,          NULL,
1136
                        NULL,          NULL,
1137
                        NULL };
1138
1139
/**
1140
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363198%28v=vs.85%29.aspx
1141
 *
1142
 * @param lpDeviceName e.g. COM1, ...
1143
 *
1144
 * @param dwDesiredAccess expects GENERIC_READ | GENERIC_WRITE, a
1145
 * warning message is printed otherwise. TODO: better support.
1146
 *
1147
 * @param dwShareMode must be zero, INVALID_HANDLE_VALUE is returned
1148
 * otherwise and GetLastError() should return ERROR_SHARING_VIOLATION.
1149
 *
1150
 * @param lpSecurityAttributes NULL expected, a warning message is printed
1151
 * otherwise. TODO: better support.
1152
 *
1153
 * @param dwCreationDisposition must be OPEN_EXISTING. If the
1154
 * communication device doesn't exist INVALID_HANDLE_VALUE is returned
1155
 * and GetLastError() returns ERROR_FILE_NOT_FOUND.
1156
 *
1157
 * @param dwFlagsAndAttributes zero expected, a warning message is
1158
 * printed otherwise.
1159
 *
1160
 * @param hTemplateFile must be NULL.
1161
 *
1162
 * @return INVALID_HANDLE_VALUE on error.
1163
 */
1164
HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
1165
                       LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1166
                       DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1167
0
{
1168
0
  CHAR devicePath[MAX_PATH];
1169
0
  struct stat deviceStat;
1170
0
  WINPR_COMM* pComm = NULL;
1171
0
  struct termios upcomingTermios;
1172
1173
0
  if (!CommInitialized())
1174
0
    return INVALID_HANDLE_VALUE;
1175
1176
0
  if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
1177
0
  {
1178
0
    CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "",
1179
0
                  dwDesiredAccess);
1180
0
  }
1181
1182
0
  if (dwShareMode != 0)
1183
0
  {
1184
0
    SetLastError(ERROR_SHARING_VIOLATION);
1185
0
    return INVALID_HANDLE_VALUE;
1186
0
  }
1187
1188
  /* TODO: Prevents other processes from opening a file or
1189
   * device if they request delete, read, or write access. */
1190
1191
0
  if (lpSecurityAttributes != NULL)
1192
0
  {
1193
0
    CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "",
1194
0
                  lpSecurityAttributes->nLength);
1195
0
  }
1196
1197
0
  if (dwCreationDisposition != OPEN_EXISTING)
1198
0
  {
1199
0
    SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
1200
0
    return INVALID_HANDLE_VALUE;
1201
0
  }
1202
1203
0
  if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
1204
0
  {
1205
    /* SetLastError(GetLastError()); */
1206
0
    return INVALID_HANDLE_VALUE;
1207
0
  }
1208
1209
0
  if (stat(devicePath, &deviceStat) < 0)
1210
0
  {
1211
0
    CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
1212
0
    SetLastError(ERROR_FILE_NOT_FOUND);
1213
0
    return INVALID_HANDLE_VALUE;
1214
0
  }
1215
1216
0
  if (!S_ISCHR(deviceStat.st_mode))
1217
0
  {
1218
0
    CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
1219
0
    SetLastError(ERROR_BAD_DEVICE);
1220
0
    return INVALID_HANDLE_VALUE;
1221
0
  }
1222
1223
0
  if (dwFlagsAndAttributes != 0)
1224
0
  {
1225
0
    CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "",
1226
0
                  dwFlagsAndAttributes);
1227
0
  }
1228
1229
0
  if (hTemplateFile != NULL)
1230
0
  {
1231
0
    SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
1232
0
    return INVALID_HANDLE_VALUE;
1233
0
  }
1234
1235
0
  pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
1236
1237
0
  if (pComm == NULL)
1238
0
  {
1239
0
    SetLastError(ERROR_OUTOFMEMORY);
1240
0
    return INVALID_HANDLE_VALUE;
1241
0
  }
1242
1243
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
1244
0
  pComm->common.ops = &ops;
1245
  /* error_handle */
1246
0
  pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
1247
1248
0
  if (pComm->fd < 0)
1249
0
  {
1250
0
    CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
1251
0
    SetLastError(ERROR_BAD_DEVICE);
1252
0
    goto error_handle;
1253
0
  }
1254
1255
0
  pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK);
1256
1257
0
  if (pComm->fd_read < 0)
1258
0
  {
1259
0
    CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
1260
0
    SetLastError(ERROR_BAD_DEVICE);
1261
0
    goto error_handle;
1262
0
  }
1263
1264
0
  pComm->fd_read_event = eventfd(
1265
0
      0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1266
1267
0
  if (pComm->fd_read_event < 0)
1268
0
  {
1269
0
    CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
1270
0
    SetLastError(ERROR_BAD_DEVICE);
1271
0
    goto error_handle;
1272
0
  }
1273
1274
0
  InitializeCriticalSection(&pComm->ReadLock);
1275
0
  pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK);
1276
1277
0
  if (pComm->fd_write < 0)
1278
0
  {
1279
0
    CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
1280
0
    SetLastError(ERROR_BAD_DEVICE);
1281
0
    goto error_handle;
1282
0
  }
1283
1284
0
  pComm->fd_write_event = eventfd(
1285
0
      0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1286
1287
0
  if (pComm->fd_write_event < 0)
1288
0
  {
1289
0
    CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
1290
0
    SetLastError(ERROR_BAD_DEVICE);
1291
0
    goto error_handle;
1292
0
  }
1293
1294
0
  InitializeCriticalSection(&pComm->WriteLock);
1295
  /* can also be setup later on with _comm_setServerSerialDriver() */
1296
0
  pComm->serverSerialDriverId = SerialDriverUnknown;
1297
0
  InitializeCriticalSection(&pComm->EventsLock);
1298
1299
0
  if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
1300
0
  {
1301
0
    CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
1302
0
                  strerror(errno));
1303
0
    CommLog_Print(WLOG_WARN, "could not read counters.");
1304
    /* could not initialize counters but keep on.
1305
     *
1306
     * Not all drivers, especially for USB to serial
1307
     * adapters (e.g. those based on pl2303), does support
1308
     * this call.
1309
     */
1310
0
    ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
1311
0
  }
1312
1313
  /* The binary/raw mode is required for the redirection but
1314
   * only flags that are not handle somewhere-else, except
1315
   * ICANON, are forced here. */
1316
0
  ZeroMemory(&upcomingTermios, sizeof(struct termios));
1317
1318
0
  if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
1319
0
  {
1320
0
    SetLastError(ERROR_IO_DEVICE);
1321
0
    goto error_handle;
1322
0
  }
1323
1324
0
  upcomingTermios.c_iflag &=
1325
0
      ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
1326
0
  upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
1327
0
  upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
1328
  /* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
1329
  /* upcomingTermios.c_cflag |= CS8; */
1330
  /* About missing flags recommended by termios(3):
1331
   *
1332
   *   IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
1333
   *   CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
1334
   */
1335
  /* a few more settings required for the redirection */
1336
0
  upcomingTermios.c_cflag |= CLOCAL | CREAD;
1337
1338
0
  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
1339
0
  {
1340
0
    SetLastError(ERROR_IO_DEVICE);
1341
0
    goto error_handle;
1342
0
  }
1343
1344
0
  return (HANDLE)pComm;
1345
0
error_handle:
1346
0
  CloseHandle(pComm);
1347
0
  return INVALID_HANDLE_VALUE;
1348
0
}
1349
1350
BOOL CommIsHandled(HANDLE handle)
1351
0
{
1352
0
  if (!CommInitialized())
1353
0
    return FALSE;
1354
1355
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
1356
0
}
1357
1358
BOOL CommIsHandleValid(HANDLE handle)
1359
0
{
1360
0
  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1361
0
  if (!CommIsHandled(handle))
1362
0
    return FALSE;
1363
0
  if (pComm->fd <= 0)
1364
0
  {
1365
0
    SetLastError(ERROR_INVALID_HANDLE);
1366
0
    return FALSE;
1367
0
  }
1368
0
  return TRUE;
1369
0
}
1370
1371
BOOL CommCloseHandle(HANDLE handle)
1372
0
{
1373
0
  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1374
1375
0
  if (!CommIsHandled(handle))
1376
0
    return FALSE;
1377
1378
0
  if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
1379
0
  {
1380
0
    ULONG WaitMask = 0;
1381
0
    DWORD BytesReturned = 0;
1382
1383
    /* ensures to gracefully stop the WAIT_ON_MASK's loop */
1384
0
    if (!CommDeviceIoControl(handle, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof(ULONG), NULL,
1385
0
                             0, &BytesReturned, NULL))
1386
0
    {
1387
0
      CommLog_Print(WLOG_WARN, "failure to WAIT_ON_MASK's loop!");
1388
0
    }
1389
0
  }
1390
1391
0
  DeleteCriticalSection(&pComm->ReadLock);
1392
0
  DeleteCriticalSection(&pComm->WriteLock);
1393
0
  DeleteCriticalSection(&pComm->EventsLock);
1394
1395
0
  if (pComm->fd > 0)
1396
0
    close(pComm->fd);
1397
1398
0
  if (pComm->fd_write > 0)
1399
0
    close(pComm->fd_write);
1400
1401
0
  if (pComm->fd_write_event > 0)
1402
0
    close(pComm->fd_write_event);
1403
1404
0
  if (pComm->fd_read > 0)
1405
0
    close(pComm->fd_read);
1406
1407
0
  if (pComm->fd_read_event > 0)
1408
0
    close(pComm->fd_read_event);
1409
1410
0
  free(pComm);
1411
0
  return TRUE;
1412
0
}
1413
1414
#ifndef WITH_EVENTFD_READ_WRITE
1415
int eventfd_read(int fd, eventfd_t* value)
1416
{
1417
  return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
1418
}
1419
1420
int eventfd_write(int fd, eventfd_t value)
1421
{
1422
  return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
1423
}
1424
#endif
1425
1426
#endif /* __linux__ */