Coverage Report

Created: 2024-09-08 06:20

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