Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/winpr/libwinpr/comm/comm_serial_sys.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
#if defined __linux__ && !defined ANDROID
24
25
#include <winpr/assert.h>
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <sys/ioctl.h>
29
#include <termios.h>
30
#include <unistd.h>
31
32
#include "comm_serial_sys.h"
33
#ifdef __UCLIBC__
34
#include "comm.h"
35
#endif
36
37
#include <winpr/crt.h>
38
#include <winpr/wlog.h>
39
40
/* Undocumented flag, not supported everywhere.
41
 * Provide a sensible fallback to avoid compilation problems. */
42
#ifndef CMSPAR
43
#define CMSPAR 010000000000
44
#endif
45
46
/* hard-coded in N_TTY */
47
0
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
48
0
#define TTY_THRESHOLD_UNTHROTTLE 128
49
0
#define N_TTY_BUF_SIZE 4096
50
51
0
#define BAUD_TABLE_END 0010020 /* __MAX_BAUD + 1 */
52
53
/* 0: B* (Linux termios)
54
 * 1: CBR_* or actual baud rate
55
 * 2: BAUD_* (identical to SERIAL_BAUD_*)
56
 */
57
static const speed_t _BAUD_TABLE[][3] = {
58
#ifdef B0
59
  { B0, 0, 0 }, /* hang up */
60
#endif
61
#ifdef B50
62
  { B50, 50, 0 },
63
#endif
64
#ifdef B75
65
  { B75, 75, BAUD_075 },
66
#endif
67
#ifdef B110
68
  { B110, CBR_110, BAUD_110 },
69
#endif
70
#ifdef B134
71
  { B134, 134, 0 /*BAUD_134_5*/ },
72
#endif
73
#ifdef B150
74
  { B150, 150, BAUD_150 },
75
#endif
76
#ifdef B200
77
  { B200, 200, 0 },
78
#endif
79
#ifdef B300
80
  { B300, CBR_300, BAUD_300 },
81
#endif
82
#ifdef B600
83
  { B600, CBR_600, BAUD_600 },
84
#endif
85
#ifdef B1200
86
  { B1200, CBR_1200, BAUD_1200 },
87
#endif
88
#ifdef B1800
89
  { B1800, 1800, BAUD_1800 },
90
#endif
91
#ifdef B2400
92
  { B2400, CBR_2400, BAUD_2400 },
93
#endif
94
#ifdef B4800
95
  { B4800, CBR_4800, BAUD_4800 },
96
#endif
97
/* {, ,BAUD_7200} */
98
#ifdef B9600
99
  { B9600, CBR_9600, BAUD_9600 },
100
#endif
101
/* {, CBR_14400, BAUD_14400}, /\* unsupported on Linux *\/ */
102
#ifdef B19200
103
  { B19200, CBR_19200, BAUD_19200 },
104
#endif
105
#ifdef B38400
106
  { B38400, CBR_38400, BAUD_38400 },
107
#endif
108
/* {, CBR_56000, BAUD_56K}, /\* unsupported on Linux *\/ */
109
#ifdef B57600
110
  { B57600, CBR_57600, BAUD_57600 },
111
#endif
112
#ifdef B115200
113
  { B115200, CBR_115200, BAUD_115200 },
114
#endif
115
/* {, CBR_128000, BAUD_128K}, /\* unsupported on Linux *\/ */
116
/* {, CBR_256000, BAUD_USER}, /\* unsupported on Linux *\/ */
117
#ifdef B230400
118
  { B230400, 230400, BAUD_USER },
119
#endif
120
#ifdef B460800
121
  { B460800, 460800, BAUD_USER },
122
#endif
123
#ifdef B500000
124
  { B500000, 500000, BAUD_USER },
125
#endif
126
#ifdef B576000
127
  { B576000, 576000, BAUD_USER },
128
#endif
129
#ifdef B921600
130
  { B921600, 921600, BAUD_USER },
131
#endif
132
#ifdef B1000000
133
  { B1000000, 1000000, BAUD_USER },
134
#endif
135
#ifdef B1152000
136
  { B1152000, 1152000, BAUD_USER },
137
#endif
138
#ifdef B1500000
139
  { B1500000, 1500000, BAUD_USER },
140
#endif
141
#ifdef B2000000
142
  { B2000000, 2000000, BAUD_USER },
143
#endif
144
#ifdef B2500000
145
  { B2500000, 2500000, BAUD_USER },
146
#endif
147
#ifdef B3000000
148
  { B3000000, 3000000, BAUD_USER },
149
#endif
150
#ifdef B3500000
151
  { B3500000, 3500000, BAUD_USER },
152
#endif
153
#ifdef B4000000
154
  { B4000000, 4000000, BAUD_USER }, /* __MAX_BAUD */
155
#endif
156
  { BAUD_TABLE_END, 0, 0 }
157
};
158
159
static BOOL _get_properties(WINPR_COMM* pComm, COMMPROP* pProperties)
160
0
{
161
  /* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx
162
   * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx
163
   */
164
165
  /* FIXME: properties should be better probed. The current
166
   * implementation just relies on the Linux' implementation.
167
   */
168
0
  WINPR_ASSERT(pProperties);
169
0
  if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
170
0
  {
171
0
    ZeroMemory(pProperties, sizeof(COMMPROP));
172
0
    pProperties->wPacketLength = sizeof(COMMPROP);
173
0
  }
174
175
0
  pProperties->wPacketVersion = 2;
176
177
0
  pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
178
179
  /* pProperties->Reserved1; not used */
180
181
  /* FIXME: could be implemented on top of N_TTY */
182
0
  pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE;
183
0
  pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE;
184
185
  /* FIXME: to be probe on the device? */
186
0
  pProperties->dwMaxBaud = BAUD_USER;
187
188
  /* FIXME: what about PST_RS232? see also: serial_struct */
189
0
  pProperties->dwProvSubType = PST_UNSPECIFIED;
190
191
  /* TODO: to be finalized */
192
0
  pProperties->dwProvCapabilities =
193
      /*PCF_16BITMODE |*/ PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK | /*PCF_RLSD |*/
194
0
      PCF_RTSCTS | PCF_SETXCHAR | /*PCF_SPECIALCHARS |*/ PCF_TOTALTIMEOUTS | PCF_XONXOFF;
195
196
  /* TODO: double check SP_RLSD */
197
0
  pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
198
0
                                  SP_PARITY_CHECK | /*SP_RLSD |*/ SP_STOPBITS;
199
200
0
  pProperties->dwSettableBaud = 0;
201
0
  for (int i = 0; _BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
202
0
  {
203
0
    pProperties->dwSettableBaud |= _BAUD_TABLE[i][2];
204
0
  }
205
206
0
  pProperties->wSettableData =
207
0
      DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 /*| DATABITS_16 | DATABITS_16X*/;
208
209
0
  pProperties->wSettableStopParity = STOPBITS_10 | /*STOPBITS_15 |*/ STOPBITS_20 | PARITY_NONE |
210
0
                                     PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
211
212
  /* FIXME: additional input and output buffers could be implemented on top of N_TTY */
213
0
  pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE;
214
0
  pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE;
215
216
  /* pProperties->ProvSpec1; see above */
217
  /* pProperties->ProvSpec2; ignored */
218
  /* pProperties->ProvChar[1]; ignored */
219
220
0
  return TRUE;
221
0
}
222
223
static BOOL _set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
224
0
{
225
0
  speed_t newSpeed = 0;
226
0
  struct termios futureState;
227
228
0
  ZeroMemory(&futureState, sizeof(struct termios));
229
0
  if (tcgetattr(pComm->fd, &futureState) <
230
0
      0) /* NB: preserves current settings not directly handled by the Communication Functions */
231
0
  {
232
0
    SetLastError(ERROR_IO_DEVICE);
233
0
    return FALSE;
234
0
  }
235
236
0
  for (int i = 0; _BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
237
0
  {
238
0
    if (_BAUD_TABLE[i][1] == pBaudRate->BaudRate)
239
0
    {
240
0
      newSpeed = _BAUD_TABLE[i][0];
241
0
      if (cfsetspeed(&futureState, newSpeed) < 0)
242
0
      {
243
0
        CommLog_Print(WLOG_WARN, "failed to set speed 0x%x (%" PRIu32 ")", newSpeed,
244
0
                      pBaudRate->BaudRate);
245
0
        return FALSE;
246
0
      }
247
248
0
      WINPR_ASSERT(cfgetispeed(&futureState) == newSpeed);
249
250
0
      if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
251
0
      {
252
0
        CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
253
0
                      GetLastError());
254
0
        return FALSE;
255
0
      }
256
257
0
      return TRUE;
258
0
    }
259
0
  }
260
261
0
  CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %" PRIu32 "",
262
0
                pBaudRate->BaudRate);
263
0
  SetLastError(ERROR_INVALID_DATA);
264
0
  return FALSE;
265
0
}
266
267
static BOOL _get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
268
0
{
269
0
  speed_t currentSpeed = 0;
270
0
  struct termios currentState;
271
272
0
  ZeroMemory(&currentState, sizeof(struct termios));
273
0
  if (tcgetattr(pComm->fd, &currentState) < 0)
274
0
  {
275
0
    SetLastError(ERROR_IO_DEVICE);
276
0
    return FALSE;
277
0
  }
278
279
0
  currentSpeed = cfgetispeed(&currentState);
280
281
0
  for (int i = 0; _BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
282
0
  {
283
0
    if (_BAUD_TABLE[i][0] == currentSpeed)
284
0
    {
285
0
      pBaudRate->BaudRate = _BAUD_TABLE[i][1];
286
0
      return TRUE;
287
0
    }
288
0
  }
289
290
0
  CommLog_Print(WLOG_WARN, "could not find a matching baud rate for the speed 0x%x",
291
0
                currentSpeed);
292
0
  SetLastError(ERROR_INVALID_DATA);
293
0
  return FALSE;
294
0
}
295
296
/**
297
 * NOTE: Only XonChar and XoffChar are plenty supported with the Linux
298
 *       N_TTY line discipline.
299
 *
300
 * ERRORS:
301
 *   ERROR_IO_DEVICE
302
 *   ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same;
303
 *   ERROR_NOT_SUPPORTED
304
 */
305
static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
306
0
{
307
0
  BOOL result = TRUE;
308
0
  struct termios upcomingTermios;
309
310
0
  ZeroMemory(&upcomingTermios, sizeof(struct termios));
311
0
  if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
312
0
  {
313
0
    SetLastError(ERROR_IO_DEVICE);
314
0
    return FALSE;
315
0
  }
316
317
0
  if (pSerialChars->XonChar == pSerialChars->XoffChar)
318
0
  {
319
    /* https://msdn.microsoft.com/en-us/library/windows/hardware/ff546688?v=vs.85.aspx */
320
0
    SetLastError(ERROR_INVALID_PARAMETER);
321
0
    return FALSE;
322
0
  }
323
324
  /* termios(3): (..) above symbolic subscript values are all
325
   * different, except that VTIME, VMIN may have the same value
326
   * as VEOL, VEOF, respectively. In noncanonical mode the
327
   * special character meaning is replaced by the timeout
328
   * meaning.
329
   *
330
   * EofChar and c_cc[VEOF] are not quite the same, prefer to
331
   * don't use c_cc[VEOF] at all.
332
   *
333
   * FIXME: might be implemented during read/write I/O
334
   */
335
0
  if (pSerialChars->EofChar != '\0')
336
0
  {
337
0
    CommLog_Print(WLOG_WARN, "EofChar %02" PRIX8 " cannot be set\n", pSerialChars->EofChar);
338
0
    SetLastError(ERROR_NOT_SUPPORTED);
339
0
    result = FALSE; /* but keep on */
340
0
  }
341
342
  /* According the Linux's n_tty discipline, charaters with a
343
   * parity error can only be let unchanged, replaced by \0 or
344
   * get the prefix the prefix \377 \0
345
   */
346
347
  /* FIXME: see also: _set_handflow() */
348
0
  if (pSerialChars->ErrorChar != '\0')
349
0
  {
350
0
    CommLog_Print(WLOG_WARN, "ErrorChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
351
0
                  pSerialChars->ErrorChar, (char)pSerialChars->ErrorChar);
352
0
    SetLastError(ERROR_NOT_SUPPORTED);
353
0
    result = FALSE; /* but keep on */
354
0
  }
355
356
  /* FIXME: see also: _set_handflow() */
357
0
  if (pSerialChars->BreakChar != '\0')
358
0
  {
359
0
    CommLog_Print(WLOG_WARN, "BreakChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
360
0
                  pSerialChars->BreakChar, (char)pSerialChars->BreakChar);
361
0
    SetLastError(ERROR_NOT_SUPPORTED);
362
0
    result = FALSE; /* but keep on */
363
0
  }
364
365
0
  if (pSerialChars->EventChar != '\0')
366
0
  {
367
0
    pComm->eventChar = pSerialChars->EventChar;
368
0
  }
369
370
0
  upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
371
372
0
  upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
373
374
0
  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
375
0
  {
376
0
    CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
377
0
                  GetLastError());
378
0
    return FALSE;
379
0
  }
380
381
0
  return result;
382
0
}
383
384
static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
385
0
{
386
0
  struct termios currentTermios;
387
388
0
  ZeroMemory(&currentTermios, sizeof(struct termios));
389
0
  if (tcgetattr(pComm->fd, &currentTermios) < 0)
390
0
  {
391
0
    SetLastError(ERROR_IO_DEVICE);
392
0
    return FALSE;
393
0
  }
394
395
0
  ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
396
397
  /* EofChar unsupported */
398
399
  /* ErrorChar unsupported */
400
401
  /* BreakChar unsupported */
402
403
  /* FIXME: see also: _set_serial_chars() */
404
  /* EventChar */
405
406
0
  pSerialChars->XonChar = currentTermios.c_cc[VSTART];
407
408
0
  pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
409
410
0
  return TRUE;
411
0
}
412
413
static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl)
414
0
{
415
0
  BOOL result = TRUE;
416
0
  struct termios upcomingTermios;
417
418
  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
419
   *
420
   * The use of 5 data bits with 2 stop bits is an invalid
421
   * combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
422
   *
423
   * FIXME: prefered to let the underlying driver to deal with
424
   * this issue. At least produce a warning message?
425
   */
426
427
0
  ZeroMemory(&upcomingTermios, sizeof(struct termios));
428
0
  if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
429
0
  {
430
0
    SetLastError(ERROR_IO_DEVICE);
431
0
    return FALSE;
432
0
  }
433
434
  /* FIXME: use of a COMMPROP to validate new settings? */
435
436
0
  switch (pLineControl->StopBits)
437
0
  {
438
0
    case STOP_BIT_1:
439
0
      upcomingTermios.c_cflag &= ~CSTOPB;
440
0
      break;
441
442
0
    case STOP_BITS_1_5:
443
0
      CommLog_Print(WLOG_WARN, "Unsupported one and a half stop bits.");
444
0
      break;
445
446
0
    case STOP_BITS_2:
447
0
      upcomingTermios.c_cflag |= CSTOPB;
448
0
      break;
449
450
0
    default:
451
0
      CommLog_Print(WLOG_WARN, "unexpected number of stop bits: %" PRIu8 "\n",
452
0
                    pLineControl->StopBits);
453
0
      result = FALSE; /* but keep on */
454
0
      break;
455
0
  }
456
457
0
  switch (pLineControl->Parity)
458
0
  {
459
0
    case NO_PARITY:
460
0
      upcomingTermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);
461
0
      break;
462
463
0
    case ODD_PARITY:
464
0
      upcomingTermios.c_cflag &= ~CMSPAR;
465
0
      upcomingTermios.c_cflag |= PARENB | PARODD;
466
0
      break;
467
468
0
    case EVEN_PARITY:
469
0
      upcomingTermios.c_cflag &= ~(PARODD | CMSPAR);
470
0
      upcomingTermios.c_cflag |= PARENB;
471
0
      break;
472
473
0
    case MARK_PARITY:
474
0
      upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
475
0
      break;
476
477
0
    case SPACE_PARITY:
478
0
      upcomingTermios.c_cflag &= ~PARODD;
479
0
      upcomingTermios.c_cflag |= PARENB | CMSPAR;
480
0
      break;
481
482
0
    default:
483
0
      CommLog_Print(WLOG_WARN, "unexpected type of parity: %" PRIu8 "\n",
484
0
                    pLineControl->Parity);
485
0
      result = FALSE; /* but keep on */
486
0
      break;
487
0
  }
488
489
0
  switch (pLineControl->WordLength)
490
0
  {
491
0
    case 5:
492
0
      upcomingTermios.c_cflag &= ~CSIZE;
493
0
      upcomingTermios.c_cflag |= CS5;
494
0
      break;
495
496
0
    case 6:
497
0
      upcomingTermios.c_cflag &= ~CSIZE;
498
0
      upcomingTermios.c_cflag |= CS6;
499
0
      break;
500
501
0
    case 7:
502
0
      upcomingTermios.c_cflag &= ~CSIZE;
503
0
      upcomingTermios.c_cflag |= CS7;
504
0
      break;
505
506
0
    case 8:
507
0
      upcomingTermios.c_cflag &= ~CSIZE;
508
0
      upcomingTermios.c_cflag |= CS8;
509
0
      break;
510
511
0
    default:
512
0
      CommLog_Print(WLOG_WARN, "unexpected number od data bits per character: %" PRIu8 "\n",
513
0
                    pLineControl->WordLength);
514
0
      result = FALSE; /* but keep on */
515
0
      break;
516
0
  }
517
518
0
  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
519
0
  {
520
0
    CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
521
0
                  GetLastError());
522
0
    return FALSE;
523
0
  }
524
525
0
  return result;
526
0
}
527
528
static BOOL _get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl)
529
0
{
530
0
  struct termios currentTermios;
531
532
0
  ZeroMemory(&currentTermios, sizeof(struct termios));
533
0
  if (tcgetattr(pComm->fd, &currentTermios) < 0)
534
0
  {
535
0
    SetLastError(ERROR_IO_DEVICE);
536
0
    return FALSE;
537
0
  }
538
539
0
  pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
540
541
0
  if (!(currentTermios.c_cflag & PARENB))
542
0
  {
543
0
    pLineControl->Parity = NO_PARITY;
544
0
  }
545
0
  else if (currentTermios.c_cflag & CMSPAR)
546
0
  {
547
0
    pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
548
0
  }
549
0
  else
550
0
  {
551
    /* PARENB is set */
552
0
    pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
553
0
  }
554
555
0
  switch (currentTermios.c_cflag & CSIZE)
556
0
  {
557
0
    case CS5:
558
0
      pLineControl->WordLength = 5;
559
0
      break;
560
0
    case CS6:
561
0
      pLineControl->WordLength = 6;
562
0
      break;
563
0
    case CS7:
564
0
      pLineControl->WordLength = 7;
565
0
      break;
566
0
    default:
567
0
      pLineControl->WordLength = 8;
568
0
      break;
569
0
  }
570
571
0
  return TRUE;
572
0
}
573
574
static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
575
0
{
576
0
  BOOL result = TRUE;
577
0
  struct termios upcomingTermios;
578
579
0
  ZeroMemory(&upcomingTermios, sizeof(struct termios));
580
0
  if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
581
0
  {
582
0
    SetLastError(ERROR_IO_DEVICE);
583
0
    return FALSE;
584
0
  }
585
586
  /* HUPCL */
587
588
  /* logical XOR */
589
0
  if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
590
0
       (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
591
0
      ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
592
0
       !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
593
0
  {
594
0
    CommLog_Print(WLOG_WARN,
595
0
                  "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL "
596
0
                  "will be set since it is claimed for one of the both lines.",
597
0
                  (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ? "ON" : "OFF",
598
0
                  (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ? "ON" : "OFF");
599
0
  }
600
601
0
  if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ||
602
0
      (pHandflow->FlowReplace & SERIAL_RTS_CONTROL))
603
0
  {
604
0
    upcomingTermios.c_cflag |= HUPCL;
605
0
  }
606
0
  else
607
0
  {
608
0
    upcomingTermios.c_cflag &= ~HUPCL;
609
610
    /* FIXME: is the DTR line also needs to be forced to a disable state according
611
     * SERIAL_DTR_CONTROL? */
612
    /* FIXME: is the RTS line also needs to be forced to a disable state according
613
     * SERIAL_RTS_CONTROL? */
614
0
  }
615
616
  /* CRTSCTS */
617
618
  /* logical XOR */
619
0
  if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
620
0
       (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) ||
621
0
      ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
622
0
       !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)))
623
0
  {
624
0
    CommLog_Print(WLOG_WARN,
625
0
                  "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, "
626
0
                  "CRTSCTS will be set since it is claimed for one of the both lines.",
627
0
                  (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ? "ON" : "OFF",
628
0
                  (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ? "ON" : "OFF");
629
0
  }
630
631
0
  if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
632
0
      (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))
633
0
  {
634
0
    upcomingTermios.c_cflag |= CRTSCTS;
635
0
  }
636
0
  else
637
0
  {
638
0
    upcomingTermios.c_cflag &= ~CRTSCTS;
639
0
  }
640
641
  /* ControlHandShake */
642
643
0
  if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
644
0
  {
645
    /* DTR/DSR flow control not supported on Linux */
646
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
647
0
    SetLastError(ERROR_NOT_SUPPORTED);
648
0
    result = FALSE; /* but keep on */
649
0
  }
650
651
0
  if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
652
0
  {
653
    /* DTR/DSR flow control not supported on Linux */
654
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
655
0
    SetLastError(ERROR_NOT_SUPPORTED);
656
0
    result = FALSE; /* but keep on */
657
0
  }
658
659
0
  if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
660
0
  {
661
    /* DCD flow control not supported on Linux */
662
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
663
0
    SetLastError(ERROR_NOT_SUPPORTED);
664
0
    result = FALSE; /* but keep on */
665
0
  }
666
667
  // FIXME: could be implemented during read/write I/O
668
0
  if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
669
0
  {
670
    /* DSR line control not supported on Linux */
671
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
672
0
    SetLastError(ERROR_NOT_SUPPORTED);
673
0
    result = FALSE; /* but keep on */
674
0
  }
675
676
  // FIXME: could be implemented during read/write I/O
677
0
  if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
678
0
  {
679
    /* Aborting operations on error not supported on Linux */
680
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
681
0
    SetLastError(ERROR_NOT_SUPPORTED);
682
0
    result = FALSE; /* but keep on */
683
0
  }
684
685
  /* FlowReplace */
686
687
0
  if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
688
0
  {
689
0
    upcomingTermios.c_iflag |= IXON;
690
0
  }
691
0
  else
692
0
  {
693
0
    upcomingTermios.c_iflag &= ~IXON;
694
0
  }
695
696
0
  if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
697
0
  {
698
0
    upcomingTermios.c_iflag |= IXOFF;
699
0
  }
700
0
  else
701
0
  {
702
0
    upcomingTermios.c_iflag &= ~IXOFF;
703
0
  }
704
705
  // FIXME: could be implemented during read/write I/O, as of today ErrorChar is necessary '\0'
706
0
  if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
707
0
  {
708
    /* errors will be replaced by the character '\0'. */
709
0
    upcomingTermios.c_iflag &= ~IGNPAR;
710
0
  }
711
0
  else
712
0
  {
713
0
    upcomingTermios.c_iflag |= IGNPAR;
714
0
  }
715
716
0
  if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
717
0
  {
718
0
    upcomingTermios.c_iflag |= IGNBRK;
719
0
  }
720
0
  else
721
0
  {
722
0
    upcomingTermios.c_iflag &= ~IGNBRK;
723
0
  }
724
725
  // FIXME: could be implemented during read/write I/O
726
0
  if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
727
0
  {
728
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
729
0
    SetLastError(ERROR_NOT_SUPPORTED);
730
0
    result = FALSE; /* but keep on */
731
0
  }
732
733
  // FIXME: could be implemented during read/write I/O
734
0
  if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
735
0
  {
736
    /* not supported on Linux */
737
0
    CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
738
0
    SetLastError(ERROR_NOT_SUPPORTED);
739
0
    result = FALSE; /* but keep on */
740
0
  }
741
742
  /* XonLimit */
743
744
  // FIXME: could be implemented during read/write I/O
745
0
  if (pHandflow->XonLimit != TTY_THRESHOLD_UNTHROTTLE)
746
0
  {
747
0
    CommLog_Print(WLOG_WARN, "Attempt to set XonLimit with an unsupported value: %" PRId32 "",
748
0
                  pHandflow->XonLimit);
749
0
    SetLastError(ERROR_NOT_SUPPORTED);
750
0
    result = FALSE; /* but keep on */
751
0
  }
752
753
  /* XoffChar */
754
755
  // FIXME: could be implemented during read/write I/O
756
0
  if (pHandflow->XoffLimit != TTY_THRESHOLD_THROTTLE)
757
0
  {
758
0
    CommLog_Print(WLOG_WARN, "Attempt to set XoffLimit with an unsupported value: %" PRId32 "",
759
0
                  pHandflow->XoffLimit);
760
0
    SetLastError(ERROR_NOT_SUPPORTED);
761
0
    result = FALSE; /* but keep on */
762
0
  }
763
764
0
  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
765
0
  {
766
0
    CommLog_Print(WLOG_WARN, "_comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
767
0
                  GetLastError());
768
0
    return FALSE;
769
0
  }
770
771
0
  return result;
772
0
}
773
774
static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
775
0
{
776
0
  struct termios currentTermios;
777
778
0
  ZeroMemory(&currentTermios, sizeof(struct termios));
779
0
  if (tcgetattr(pComm->fd, &currentTermios) < 0)
780
0
  {
781
0
    SetLastError(ERROR_IO_DEVICE);
782
0
    return FALSE;
783
0
  }
784
785
  /* ControlHandShake */
786
787
0
  pHandflow->ControlHandShake = 0;
788
789
0
  if (currentTermios.c_cflag & HUPCL)
790
0
    pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
791
792
  /* SERIAL_DTR_HANDSHAKE unsupported */
793
794
0
  if (currentTermios.c_cflag & CRTSCTS)
795
0
    pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
796
797
  /* SERIAL_DSR_HANDSHAKE unsupported */
798
799
  /* SERIAL_DCD_HANDSHAKE unsupported */
800
801
  /* SERIAL_DSR_SENSITIVITY unsupported */
802
803
  /* SERIAL_ERROR_ABORT unsupported */
804
805
  /* FlowReplace */
806
807
0
  pHandflow->FlowReplace = 0;
808
809
0
  if (currentTermios.c_iflag & IXON)
810
0
    pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
811
812
0
  if (currentTermios.c_iflag & IXOFF)
813
0
    pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
814
815
0
  if (!(currentTermios.c_iflag & IGNPAR))
816
0
    pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
817
818
0
  if (currentTermios.c_iflag & IGNBRK)
819
0
    pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
820
821
  /* SERIAL_BREAK_CHAR unsupported */
822
823
0
  if (currentTermios.c_cflag & HUPCL)
824
0
    pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
825
826
0
  if (currentTermios.c_cflag & CRTSCTS)
827
0
    pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
828
829
  /* SERIAL_XOFF_CONTINUE unsupported */
830
831
  /* XonLimit */
832
833
0
  pHandflow->XonLimit = TTY_THRESHOLD_UNTHROTTLE;
834
835
  /* XoffLimit */
836
837
0
  pHandflow->XoffLimit = TTY_THRESHOLD_THROTTLE;
838
839
0
  return TRUE;
840
0
}
841
842
static BOOL _set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
843
0
{
844
  /* NB: timeouts are applied on system during read/write I/O */
845
846
  /* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */
847
0
  if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
848
0
      (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
849
0
  {
850
0
    CommLog_Print(
851
0
        WLOG_WARN,
852
0
        "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
853
0
    SetLastError(ERROR_INVALID_PARAMETER);
854
0
    return FALSE;
855
0
  }
856
857
0
  pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
858
0
  pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
859
0
  pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
860
0
  pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
861
0
  pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
862
863
0
  CommLog_Print(WLOG_DEBUG, "ReadIntervalTimeout %" PRIu32 "",
864
0
                pComm->timeouts.ReadIntervalTimeout);
865
0
  CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutMultiplier %" PRIu32 "",
866
0
                pComm->timeouts.ReadTotalTimeoutMultiplier);
867
0
  CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutConstant %" PRIu32 "",
868
0
                pComm->timeouts.ReadTotalTimeoutConstant);
869
0
  CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutMultiplier %" PRIu32 "",
870
0
                pComm->timeouts.WriteTotalTimeoutMultiplier);
871
0
  CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutConstant %" PRIu32 "",
872
0
                pComm->timeouts.WriteTotalTimeoutConstant);
873
874
0
  return TRUE;
875
0
}
876
877
static BOOL _get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
878
0
{
879
0
  pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
880
0
  pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
881
0
  pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
882
0
  pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
883
0
  pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
884
885
0
  return TRUE;
886
0
}
887
888
static BOOL _set_lines(WINPR_COMM* pComm, UINT32 lines)
889
0
{
890
0
  if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0)
891
0
  {
892
0
    char ebuffer[256] = { 0 };
893
0
    CommLog_Print(WLOG_WARN, "TIOCMBIS ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
894
0
                  errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
895
0
    SetLastError(ERROR_IO_DEVICE);
896
0
    return FALSE;
897
0
  }
898
899
0
  return TRUE;
900
0
}
901
902
static BOOL _clear_lines(WINPR_COMM* pComm, UINT32 lines)
903
0
{
904
0
  if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0)
905
0
  {
906
0
    char ebuffer[256] = { 0 };
907
0
    CommLog_Print(WLOG_WARN, "TIOCMBIC ioctl failed, lines=0x%" PRIX32 ", errno=[%d] %s", lines,
908
0
                  errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
909
0
    SetLastError(ERROR_IO_DEVICE);
910
0
    return FALSE;
911
0
  }
912
913
0
  return TRUE;
914
0
}
915
916
static BOOL _set_dtr(WINPR_COMM* pComm)
917
0
{
918
0
  SERIAL_HANDFLOW handflow;
919
0
  if (!_get_handflow(pComm, &handflow))
920
0
    return FALSE;
921
922
  /* SERIAL_DTR_HANDSHAKE not supported as of today */
923
0
  WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
924
925
0
  if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
926
0
  {
927
0
    SetLastError(ERROR_INVALID_PARAMETER);
928
0
    return FALSE;
929
0
  }
930
931
0
  return _set_lines(pComm, TIOCM_DTR);
932
0
}
933
934
static BOOL _clear_dtr(WINPR_COMM* pComm)
935
0
{
936
0
  SERIAL_HANDFLOW handflow;
937
0
  if (!_get_handflow(pComm, &handflow))
938
0
    return FALSE;
939
940
  /* SERIAL_DTR_HANDSHAKE not supported as of today */
941
0
  WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
942
943
0
  if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
944
0
  {
945
0
    SetLastError(ERROR_INVALID_PARAMETER);
946
0
    return FALSE;
947
0
  }
948
949
0
  return _clear_lines(pComm, TIOCM_DTR);
950
0
}
951
952
static BOOL _set_rts(WINPR_COMM* pComm)
953
0
{
954
0
  SERIAL_HANDFLOW handflow;
955
0
  if (!_get_handflow(pComm, &handflow))
956
0
    return FALSE;
957
958
0
  if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
959
0
  {
960
0
    SetLastError(ERROR_INVALID_PARAMETER);
961
0
    return FALSE;
962
0
  }
963
964
0
  return _set_lines(pComm, TIOCM_RTS);
965
0
}
966
967
static BOOL _clear_rts(WINPR_COMM* pComm)
968
0
{
969
0
  SERIAL_HANDFLOW handflow;
970
0
  if (!_get_handflow(pComm, &handflow))
971
0
    return FALSE;
972
973
0
  if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
974
0
  {
975
0
    SetLastError(ERROR_INVALID_PARAMETER);
976
0
    return FALSE;
977
0
  }
978
979
0
  return _clear_lines(pComm, TIOCM_RTS);
980
0
}
981
982
static BOOL _get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
983
0
{
984
0
  UINT32 lines = 0;
985
0
  if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
986
0
  {
987
0
    char ebuffer[256] = { 0 };
988
0
    CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno,
989
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
990
0
    SetLastError(ERROR_IO_DEVICE);
991
0
    return FALSE;
992
0
  }
993
994
0
  ZeroMemory(pRegister, sizeof(ULONG));
995
996
  /* FIXME: Is the last read of the MSR register available or
997
   * cached somewhere? Not quite sure we need to return the 4
998
   * LSBits anyway. A direct access to the register -- which
999
   * would reset the register -- is likely not expected from
1000
   * this function.
1001
   */
1002
1003
  /* #define SERIAL_MSR_DCTS     0x01 */
1004
  /* #define SERIAL_MSR_DDSR     0x02 */
1005
  /* #define SERIAL_MSR_TERI     0x04 */
1006
  /* #define SERIAL_MSR_DDCD     0x08 */
1007
1008
0
  if (lines & TIOCM_CTS)
1009
0
    *pRegister |= SERIAL_MSR_CTS;
1010
0
  if (lines & TIOCM_DSR)
1011
0
    *pRegister |= SERIAL_MSR_DSR;
1012
0
  if (lines & TIOCM_RI)
1013
0
    *pRegister |= SERIAL_MSR_RI;
1014
0
  if (lines & TIOCM_CD)
1015
0
    *pRegister |= SERIAL_MSR_DCD;
1016
1017
0
  return TRUE;
1018
0
}
1019
1020
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
1021
static const ULONG _SERIAL_SYS_SUPPORTED_EV_MASK =
1022
    SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR |
1023
    SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
1024
    /* SERIAL_EV_PERR     | */
1025
    SERIAL_EV_RX80FULL /*|
1026
    SERIAL_EV_EVENT1   |
1027
    SERIAL_EV_EVENT2*/
1028
    ;
1029
1030
static BOOL is_wait_set(WINPR_COMM* pComm)
1031
0
{
1032
0
  WINPR_ASSERT(pComm);
1033
1034
0
  EnterCriticalSection(&pComm->EventsLock);
1035
0
  const BOOL isWaiting = (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) != 0;
1036
0
  LeaveCriticalSection(&pComm->EventsLock);
1037
0
  return isWaiting;
1038
0
}
1039
1040
static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
1041
0
{
1042
0
  ULONG possibleMask = 0;
1043
1044
0
  WINPR_ASSERT(pComm);
1045
0
  WINPR_ASSERT(pWaitMask);
1046
1047
  /* Stops pending IOCTL_SERIAL_WAIT_ON_MASK
1048
   * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
1049
   */
1050
0
  if (is_wait_set(pComm))
1051
0
  {
1052
    /* FIXME: any doubt on reading PendingEvents out of a critical section? */
1053
1054
0
    EnterCriticalSection(&pComm->EventsLock);
1055
0
    pComm->PendingEvents |= SERIAL_EV_WINPR_STOP;
1056
0
    LeaveCriticalSection(&pComm->EventsLock);
1057
1058
    /* waiting the end of the pending _wait_on_mask() */
1059
0
    while (is_wait_set(pComm))
1060
0
      Sleep(10); /* 10ms */
1061
0
  }
1062
1063
  /* NB: ensure to leave the critical section before to return */
1064
0
  EnterCriticalSection(&pComm->EventsLock);
1065
1066
0
  if (*pWaitMask == 0)
1067
0
  {
1068
    /* clearing pending events */
1069
1070
0
    if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
1071
0
    {
1072
0
      char ebuffer[256] = { 0 };
1073
0
      CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
1074
0
                    winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1075
1076
0
      if (pComm->permissive)
1077
0
      {
1078
        /* counters could not be reset but keep on */
1079
0
        ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
1080
0
      }
1081
0
      else
1082
0
      {
1083
0
        SetLastError(ERROR_IO_DEVICE);
1084
0
        LeaveCriticalSection(&pComm->EventsLock);
1085
0
        return FALSE;
1086
0
      }
1087
0
    }
1088
1089
0
    pComm->PendingEvents = 0;
1090
0
  }
1091
1092
0
  possibleMask = *pWaitMask & _SERIAL_SYS_SUPPORTED_EV_MASK;
1093
1094
0
  if (possibleMask != *pWaitMask)
1095
0
  {
1096
0
    CommLog_Print(WLOG_WARN,
1097
0
                  "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32
1098
0
                  ", possible events= 0x%08" PRIX32 "",
1099
0
                  *pWaitMask, possibleMask);
1100
1101
    /* FIXME: shall we really set the possibleMask and return FALSE? */
1102
0
    pComm->WaitEventMask = possibleMask;
1103
1104
0
    LeaveCriticalSection(&pComm->EventsLock);
1105
0
    return FALSE;
1106
0
  }
1107
1108
0
  pComm->WaitEventMask = possibleMask;
1109
1110
0
  LeaveCriticalSection(&pComm->EventsLock);
1111
0
  return TRUE;
1112
0
}
1113
1114
static BOOL _get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
1115
0
{
1116
0
  *pWaitMask = pComm->WaitEventMask;
1117
0
  return TRUE;
1118
0
}
1119
1120
static BOOL _set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize)
1121
0
{
1122
0
  if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
1123
0
    return TRUE; /* nothing to do */
1124
1125
  /* FIXME: could be implemented on top of N_TTY */
1126
1127
0
  if (pQueueSize->InSize > N_TTY_BUF_SIZE)
1128
0
    CommLog_Print(WLOG_WARN,
1129
0
                  "Requested an incompatible input buffer size: %" PRIu32
1130
0
                  ", keeping on with a %" PRIu32 " bytes buffer.",
1131
0
                  pQueueSize->InSize, N_TTY_BUF_SIZE);
1132
1133
0
  if (pQueueSize->OutSize > N_TTY_BUF_SIZE)
1134
0
    CommLog_Print(WLOG_WARN,
1135
0
                  "Requested an incompatible output buffer size: %" PRIu32
1136
0
                  ", keeping on with a %" PRIu32 " bytes buffer.",
1137
0
                  pQueueSize->OutSize, N_TTY_BUF_SIZE);
1138
1139
0
  SetLastError(ERROR_CANCELLED);
1140
0
  return FALSE;
1141
0
}
1142
1143
static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
1144
0
{
1145
0
  if ((*pPurgeMask & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR |
1146
0
                       SERIAL_PURGE_RXCLEAR)) > 0)
1147
0
  {
1148
0
    CommLog_Print(WLOG_WARN, "Invalid purge mask: 0x%" PRIX32 "\n", *pPurgeMask);
1149
0
    SetLastError(ERROR_INVALID_PARAMETER);
1150
0
    return FALSE;
1151
0
  }
1152
1153
  /* FIXME: currently relying too much on the fact the server
1154
   * sends a single IRP_MJ_WRITE or IRP_MJ_READ at a time
1155
   * (taking care though that one IRP_MJ_WRITE and one
1156
   * IRP_MJ_READ can be sent simultaneously) */
1157
1158
0
  if (*pPurgeMask & SERIAL_PURGE_TXABORT)
1159
0
  {
1160
    /* Purges all write (IRP_MJ_WRITE) requests. */
1161
1162
0
    if (eventfd_write(pComm->fd_write_event, WINPR_PURGE_TXABORT) < 0)
1163
0
    {
1164
0
      if (errno != EAGAIN)
1165
0
      {
1166
0
        char ebuffer[256] = { 0 };
1167
0
        CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
1168
0
                      winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1169
0
      }
1170
1171
0
      WINPR_ASSERT(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_WRITE */
1172
0
    }
1173
0
  }
1174
1175
0
  if (*pPurgeMask & SERIAL_PURGE_RXABORT)
1176
0
  {
1177
    /* Purges all read (IRP_MJ_READ) requests. */
1178
1179
0
    if (eventfd_write(pComm->fd_read_event, WINPR_PURGE_RXABORT) < 0)
1180
0
    {
1181
0
      if (errno != EAGAIN)
1182
0
      {
1183
0
        char ebuffer[256] = { 0 };
1184
0
        CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
1185
0
                      winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1186
0
      }
1187
1188
0
      WINPR_ASSERT(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_READ */
1189
0
    }
1190
0
  }
1191
1192
0
  if (*pPurgeMask & SERIAL_PURGE_TXCLEAR)
1193
0
  {
1194
    /* Purges the transmit buffer, if one exists. */
1195
1196
0
    if (tcflush(pComm->fd, TCOFLUSH) < 0)
1197
0
    {
1198
0
      char ebuffer[256] = { 0 };
1199
0
      CommLog_Print(WLOG_WARN, "tcflush(TCOFLUSH) failure, errno=[%d] %s", errno,
1200
0
                    winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1201
0
      SetLastError(ERROR_CANCELLED);
1202
0
      return FALSE;
1203
0
    }
1204
0
  }
1205
1206
0
  if (*pPurgeMask & SERIAL_PURGE_RXCLEAR)
1207
0
  {
1208
    /* Purges the receive buffer, if one exists. */
1209
1210
0
    if (tcflush(pComm->fd, TCIFLUSH) < 0)
1211
0
    {
1212
0
      char ebuffer[256] = { 0 };
1213
0
      CommLog_Print(WLOG_WARN, "tcflush(TCIFLUSH) failure, errno=[%d] %s", errno,
1214
0
                    winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1215
0
      SetLastError(ERROR_CANCELLED);
1216
0
      return FALSE;
1217
0
    }
1218
0
  }
1219
1220
0
  return TRUE;
1221
0
}
1222
1223
/* NB: _get_commstatus also produces most of the events consumed by _wait_on_mask(). Exceptions:
1224
 *  - SERIAL_EV_RXFLAG: FIXME: once EventChar supported
1225
 *
1226
 */
1227
static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
1228
0
{
1229
  /* http://msdn.microsoft.com/en-us/library/jj673022%28v=vs.85%29.aspx */
1230
1231
0
  struct serial_icounter_struct currentCounters;
1232
1233
  /* NB: ensure to leave the critical section before to return */
1234
0
  EnterCriticalSection(&pComm->EventsLock);
1235
1236
0
  ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS));
1237
1238
0
  ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
1239
0
  if (ioctl(pComm->fd, TIOCGICOUNT, &currentCounters) < 0)
1240
0
  {
1241
0
    char ebuffer[256] = { 0 };
1242
0
    CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
1243
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1244
0
    CommLog_Print(WLOG_WARN, "  could not read counters.");
1245
1246
0
    if (pComm->permissive)
1247
0
    {
1248
      /* Errors and events based on counters could not be
1249
       * detected but keep on.
1250
       */
1251
0
      ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
1252
0
    }
1253
0
    else
1254
0
    {
1255
0
      SetLastError(ERROR_IO_DEVICE);
1256
0
      LeaveCriticalSection(&pComm->EventsLock);
1257
0
      return FALSE;
1258
0
    }
1259
0
  }
1260
1261
  /* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* >
1262
   * pComm->counters.*) thinking the counters can loop */
1263
1264
  /* Errors */
1265
1266
0
  if (currentCounters.buf_overrun != pComm->counters.buf_overrun)
1267
0
  {
1268
0
    pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
1269
0
  }
1270
1271
0
  if (currentCounters.overrun != pComm->counters.overrun)
1272
0
  {
1273
0
    pCommstatus->Errors |= SERIAL_ERROR_OVERRUN;
1274
0
    pComm->PendingEvents |= SERIAL_EV_ERR;
1275
0
  }
1276
1277
0
  if (currentCounters.brk != pComm->counters.brk)
1278
0
  {
1279
0
    pCommstatus->Errors |= SERIAL_ERROR_BREAK;
1280
0
    pComm->PendingEvents |= SERIAL_EV_BREAK;
1281
0
  }
1282
1283
0
  if (currentCounters.parity != pComm->counters.parity)
1284
0
  {
1285
0
    pCommstatus->Errors |= SERIAL_ERROR_PARITY;
1286
0
    pComm->PendingEvents |= SERIAL_EV_ERR;
1287
0
  }
1288
1289
0
  if (currentCounters.frame != pComm->counters.frame)
1290
0
  {
1291
0
    pCommstatus->Errors |= SERIAL_ERROR_FRAMING;
1292
0
    pComm->PendingEvents |= SERIAL_EV_ERR;
1293
0
  }
1294
1295
  /* HoldReasons */
1296
1297
  /* TODO: SERIAL_TX_WAITING_FOR_CTS */
1298
1299
  /* TODO: SERIAL_TX_WAITING_FOR_DSR */
1300
1301
  /* TODO: SERIAL_TX_WAITING_FOR_DCD */
1302
1303
  /* TODO: SERIAL_TX_WAITING_FOR_XON */
1304
1305
  /* TODO: SERIAL_TX_WAITING_ON_BREAK, see LCR's bit 6 */
1306
1307
  /* TODO: SERIAL_TX_WAITING_XOFF_SENT */
1308
1309
  /* AmountInInQueue */
1310
1311
0
  if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0)
1312
0
  {
1313
0
    char ebuffer[256] = { 0 };
1314
0
    CommLog_Print(WLOG_WARN, "TIOCINQ ioctl failed, errno=[%d] %s", errno,
1315
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1316
0
    SetLastError(ERROR_IO_DEVICE);
1317
1318
0
    LeaveCriticalSection(&pComm->EventsLock);
1319
0
    return FALSE;
1320
0
  }
1321
1322
  /*  AmountInOutQueue */
1323
1324
0
  if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0)
1325
0
  {
1326
0
    char ebuffer[256] = { 0 };
1327
0
    CommLog_Print(WLOG_WARN, "TIOCOUTQ ioctl failed, errno=[%d] %s", errno,
1328
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1329
0
    SetLastError(ERROR_IO_DEVICE);
1330
1331
0
    LeaveCriticalSection(&pComm->EventsLock);
1332
0
    return FALSE;
1333
0
  }
1334
1335
  /*  BOOLEAN EofReceived; FIXME: once EofChar supported */
1336
1337
  /*  BOOLEAN WaitForImmediate; TODO: once IOCTL_SERIAL_IMMEDIATE_CHAR fully supported */
1338
1339
  /* other events based on counters */
1340
1341
0
  if (currentCounters.rx != pComm->counters.rx)
1342
0
  {
1343
0
    pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1344
0
  }
1345
1346
0
  if ((currentCounters.tx != pComm->counters.tx) && /* at least a transmission occurred AND ...*/
1347
0
      (pCommstatus->AmountInOutQueue == 0))         /* output bufer is now empty */
1348
0
  {
1349
0
    pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
1350
0
  }
1351
0
  else
1352
0
  {
1353
    /* FIXME: "now empty" from the specs is ambiguous, need to track previous completed
1354
     * transmission? */
1355
0
    pComm->PendingEvents &= ~SERIAL_EV_TXEMPTY;
1356
0
  }
1357
1358
0
  if (currentCounters.cts != pComm->counters.cts)
1359
0
  {
1360
0
    pComm->PendingEvents |= SERIAL_EV_CTS;
1361
0
  }
1362
1363
0
  if (currentCounters.dsr != pComm->counters.dsr)
1364
0
  {
1365
0
    pComm->PendingEvents |= SERIAL_EV_DSR;
1366
0
  }
1367
1368
0
  if (currentCounters.dcd != pComm->counters.dcd)
1369
0
  {
1370
0
    pComm->PendingEvents |= SERIAL_EV_RLSD;
1371
0
  }
1372
1373
0
  if (currentCounters.rng != pComm->counters.rng)
1374
0
  {
1375
0
    pComm->PendingEvents |= SERIAL_EV_RING;
1376
0
  }
1377
1378
0
  if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE))
1379
0
  {
1380
0
    pComm->PendingEvents |= SERIAL_EV_RX80FULL;
1381
0
  }
1382
0
  else
1383
0
  {
1384
    /* FIXME: "is 80 percent full" from the specs is ambiguous, need to track when it previously
1385
     * * occurred? */
1386
0
    pComm->PendingEvents &= ~SERIAL_EV_RX80FULL;
1387
0
  }
1388
1389
0
  pComm->counters = currentCounters;
1390
1391
0
  LeaveCriticalSection(&pComm->EventsLock);
1392
0
  return TRUE;
1393
0
}
1394
1395
static BOOL _refresh_PendingEvents(WINPR_COMM* pComm)
1396
0
{
1397
0
  SERIAL_STATUS serialStatus;
1398
1399
  /* NB: also ensures PendingEvents to be up to date */
1400
0
  ZeroMemory(&serialStatus, sizeof(SERIAL_STATUS));
1401
0
  if (!_get_commstatus(pComm, &serialStatus))
1402
0
  {
1403
0
    return FALSE;
1404
0
  }
1405
1406
0
  return TRUE;
1407
0
}
1408
1409
static void _consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
1410
0
{
1411
0
  if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
1412
0
  {
1413
0
    pComm->PendingEvents &= ~event; /* consumed */
1414
0
    *pOutputMask |= event;
1415
0
  }
1416
0
}
1417
1418
/*
1419
 * NB: see also: _set_wait_mask()
1420
 */
1421
static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
1422
0
{
1423
0
  WINPR_ASSERT(*pOutputMask == 0);
1424
1425
0
  EnterCriticalSection(&pComm->EventsLock);
1426
0
  pComm->PendingEvents |= SERIAL_EV_WINPR_WAITING;
1427
0
  LeaveCriticalSection(&pComm->EventsLock);
1428
1429
0
  while (TRUE)
1430
0
  {
1431
    /* NB: EventsLock also used by _refresh_PendingEvents() */
1432
0
    if (!_refresh_PendingEvents(pComm))
1433
0
    {
1434
0
      EnterCriticalSection(&pComm->EventsLock);
1435
0
      pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
1436
0
      LeaveCriticalSection(&pComm->EventsLock);
1437
0
      return FALSE;
1438
0
    }
1439
1440
    /* NB: ensure to leave the critical section before to return */
1441
0
    EnterCriticalSection(&pComm->EventsLock);
1442
1443
0
    if (pComm->PendingEvents & SERIAL_EV_WINPR_STOP)
1444
0
    {
1445
0
      pComm->PendingEvents &= ~SERIAL_EV_WINPR_STOP;
1446
1447
      /* pOutputMask must remain empty but should
1448
       * not have been modified.
1449
       *
1450
       * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
1451
       */
1452
0
      WINPR_ASSERT(*pOutputMask == 0);
1453
1454
0
      pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
1455
0
      LeaveCriticalSection(&pComm->EventsLock);
1456
0
      return TRUE;
1457
0
    }
1458
1459
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
1460
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
1461
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
1462
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
1463
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
1464
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
1465
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
1466
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
1467
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_RING);
1468
0
    _consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
1469
1470
0
    LeaveCriticalSection(&pComm->EventsLock);
1471
1472
    /* NOTE: PendingEvents can be modified from now on but
1473
     * not pOutputMask */
1474
1475
0
    if (*pOutputMask != 0)
1476
0
    {
1477
      /* at least an event occurred */
1478
1479
0
      EnterCriticalSection(&pComm->EventsLock);
1480
0
      pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
1481
0
      LeaveCriticalSection(&pComm->EventsLock);
1482
0
      return TRUE;
1483
0
    }
1484
1485
    /* waiting for a modification of PendingEvents.
1486
     *
1487
     * NOTE: previously used a semaphore but used
1488
     * sem_timedwait() anyway. Finally preferred a simpler
1489
     * solution with Sleep() without the burden of the
1490
     * semaphore initialization and destroying.
1491
     */
1492
1493
0
    Sleep(100); /* 100 ms */
1494
0
  }
1495
0
}
1496
1497
static BOOL _set_break_on(WINPR_COMM* pComm)
1498
0
{
1499
0
  if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0)
1500
0
  {
1501
0
    char ebuffer[256] = { 0 };
1502
0
    CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno,
1503
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1504
0
    SetLastError(ERROR_IO_DEVICE);
1505
0
    return FALSE;
1506
0
  }
1507
1508
0
  return TRUE;
1509
0
}
1510
1511
static BOOL _set_break_off(WINPR_COMM* pComm)
1512
0
{
1513
0
  if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0)
1514
0
  {
1515
0
    char ebuffer[256] = { 0 };
1516
0
    CommLog_Print(WLOG_WARN, "TIOCSBRK ioctl failed, errno=[%d] %s", errno,
1517
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1518
0
    SetLastError(ERROR_IO_DEVICE);
1519
0
    return FALSE;
1520
0
  }
1521
1522
0
  return TRUE;
1523
0
}
1524
1525
static BOOL _set_xoff(WINPR_COMM* pComm)
1526
0
{
1527
0
  if (tcflow(pComm->fd, TCIOFF) < 0)
1528
0
  {
1529
0
    char ebuffer[256] = { 0 };
1530
0
    CommLog_Print(WLOG_WARN, "TCIOFF failure, errno=[%d] %s", errno,
1531
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1532
0
    SetLastError(ERROR_IO_DEVICE);
1533
0
    return FALSE;
1534
0
  }
1535
1536
0
  return TRUE;
1537
0
}
1538
1539
static BOOL _set_xon(WINPR_COMM* pComm)
1540
0
{
1541
0
  if (tcflow(pComm->fd, TCION) < 0)
1542
0
  {
1543
0
    char ebuffer[256] = { 0 };
1544
0
    CommLog_Print(WLOG_WARN, "TCION failure, errno=[%d] %s", errno,
1545
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1546
0
    SetLastError(ERROR_IO_DEVICE);
1547
0
    return FALSE;
1548
0
  }
1549
1550
0
  return TRUE;
1551
0
}
1552
1553
static BOOL _get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
1554
0
{
1555
0
  UINT32 lines = 0;
1556
0
  if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
1557
0
  {
1558
0
    char ebuffer[256] = { 0 };
1559
0
    CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno,
1560
0
                  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1561
0
    SetLastError(ERROR_IO_DEVICE);
1562
0
    return FALSE;
1563
0
  }
1564
1565
0
  *pMask = 0;
1566
1567
0
  if (!(lines & TIOCM_DTR))
1568
0
    *pMask |= SERIAL_DTR_STATE;
1569
0
  if (!(lines & TIOCM_RTS))
1570
0
    *pMask |= SERIAL_RTS_STATE;
1571
1572
0
  return TRUE;
1573
0
}
1574
1575
static BOOL _config_size(WINPR_COMM* pComm, ULONG* pSize)
1576
0
{
1577
  /* http://msdn.microsoft.com/en-us/library/ff546548%28v=vs.85%29.aspx */
1578
0
  if (!pSize)
1579
0
    return FALSE;
1580
1581
0
  *pSize = 0;
1582
0
  return TRUE;
1583
0
}
1584
1585
static BOOL _immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
1586
0
{
1587
0
  BOOL result = 0;
1588
0
  DWORD nbBytesWritten = -1;
1589
1590
  /* FIXME: CommWriteFile uses a critical section, shall it be
1591
   * interrupted?
1592
   *
1593
   * FIXME: see also _get_commstatus()'s WaitForImmediate boolean
1594
   */
1595
1596
0
  result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
1597
1598
0
  WINPR_ASSERT(nbBytesWritten == 1);
1599
1600
0
  return result;
1601
0
}
1602
1603
static BOOL _reset_device(WINPR_COMM* pComm)
1604
0
{
1605
  /* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */
1606
0
  return TRUE;
1607
0
}
1608
1609
static SERIAL_DRIVER _SerialSys = {
1610
  .id = SerialDriverSerialSys,
1611
  .name = _T("Serial.sys"),
1612
  .set_baud_rate = _set_baud_rate,
1613
  .get_baud_rate = _get_baud_rate,
1614
  .get_properties = _get_properties,
1615
  .set_serial_chars = _set_serial_chars,
1616
  .get_serial_chars = _get_serial_chars,
1617
  .set_line_control = _set_line_control,
1618
  .get_line_control = _get_line_control,
1619
  .set_handflow = _set_handflow,
1620
  .get_handflow = _get_handflow,
1621
  .set_timeouts = _set_timeouts,
1622
  .get_timeouts = _get_timeouts,
1623
  .set_dtr = _set_dtr,
1624
  .clear_dtr = _clear_dtr,
1625
  .set_rts = _set_rts,
1626
  .clear_rts = _clear_rts,
1627
  .get_modemstatus = _get_modemstatus,
1628
  .set_wait_mask = _set_wait_mask,
1629
  .get_wait_mask = _get_wait_mask,
1630
  .wait_on_mask = _wait_on_mask,
1631
  .set_queue_size = _set_queue_size,
1632
  .purge = _purge,
1633
  .get_commstatus = _get_commstatus,
1634
  .set_break_on = _set_break_on,
1635
  .set_break_off = _set_break_off,
1636
  .set_xoff = _set_xoff,
1637
  .set_xon = _set_xon,
1638
  .get_dtrrts = _get_dtrrts,
1639
  .config_size = _config_size,
1640
  .immediate_char = _immediate_char,
1641
  .reset_device = _reset_device,
1642
};
1643
1644
SERIAL_DRIVER* SerialSys_s(void)
1645
0
{
1646
0
  return &_SerialSys;
1647
0
}
1648
1649
#endif /* __linux__ */