Coverage Report

Created: 2026-03-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/comm/comm_ioctl.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Serial Communication API
4
 *
5
 * Copyright 2011 O.S. Systems Software Ltda.
6
 * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
8
 * Copyright 2014 Hewlett-Packard Development Company, L.P.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
23
#include <winpr/config.h>
24
25
#include <winpr/assert.h>
26
#include <errno.h>
27
28
#include <winpr/wlog.h>
29
30
#include "comm.h"
31
#include "comm_ioctl.h"
32
#include "comm_serial_sys.h"
33
#include "comm_sercx_sys.h"
34
#include "comm_sercx2_sys.h"
35
36
static const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size);
37
38
/* NB: MS-RDPESP's recommendation:
39
 *
40
 * <2> Section 3.2.5.1.6: Windows Implementations use IOCTL constants
41
 * for IoControlCode values.  The content and values of the IOCTLs are
42
 * opaque to the protocol. On the server side, the data contained in
43
 * an IOCTL is simply packaged and sent to the client side. For
44
 * maximum compatibility between the different versions of the Windows
45
 * operating system, the client implementation only singles out
46
 * critical IOCTLs and invokes the applicable Win32 port API. The
47
 * other IOCTLS are passed directly to the client-side driver, and the
48
 * processing of this value depends on the drivers installed on the
49
 * client side. The values and parameters for these IOCTLS can be
50
 * found in [MSFT-W2KDDK] Volume 2, Part 2—Serial and Parallel
51
 * Drivers, and in [MSDN-PORTS].
52
 */
53
static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
54
                                  DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
55
                                  LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
56
0
{
57
0
  char buffer[128] = WINPR_C_ARRAY_INIT;
58
0
  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
59
0
  const SERIAL_DRIVER* pServerSerialDriver = nullptr;
60
61
0
  if (!CommIsHandleValid(hDevice))
62
0
    return FALSE;
63
64
0
  if (lpOverlapped)
65
0
  {
66
0
    SetLastError(ERROR_NOT_SUPPORTED);
67
0
    return FALSE;
68
0
  }
69
70
0
  if (lpBytesReturned == nullptr)
71
0
  {
72
0
    SetLastError(
73
0
        ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != nullptr */
74
0
    return FALSE;
75
0
  }
76
77
  /* clear any previous last error */
78
0
  SetLastError(ERROR_SUCCESS);
79
80
0
  *lpBytesReturned = 0; /* will be adjusted if required ... */
81
82
0
  CommLog_Print(WLOG_DEBUG, "CommDeviceIoControl: IoControlCode: %s [0x%08" PRIx32 "]",
83
0
                _comm_serial_ioctl_name(dwIoControlCode), dwIoControlCode);
84
85
  /* remoteSerialDriver to be use ...
86
   *
87
   * FIXME: might prefer to use an automatic rather than static structure
88
   */
89
0
  switch (pComm->serverSerialDriverId)
90
0
  {
91
0
    case SerialDriverSerialSys:
92
0
      pServerSerialDriver = SerialSys_s();
93
0
      break;
94
95
0
    case SerialDriverSerCxSys:
96
0
      pServerSerialDriver = SerCxSys_s();
97
0
      break;
98
99
0
    case SerialDriverSerCx2Sys:
100
0
      pServerSerialDriver = SerCx2Sys_s();
101
0
      break;
102
103
0
    case SerialDriverUnknown:
104
0
    default:
105
0
      CommLog_Print(WLOG_DEBUG, "Unknown remote serial driver (%u), using SerCx2.sys",
106
0
                    pComm->serverSerialDriverId);
107
0
      pServerSerialDriver = SerCx2Sys_s();
108
0
      break;
109
0
  }
110
111
0
  WINPR_ASSERT(pServerSerialDriver != nullptr);
112
113
0
  switch (dwIoControlCode)
114
0
  {
115
0
    case IOCTL_USBPRINT_GET_1284_ID:
116
0
    {
117
      /* FIXME:
118
       * http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */
119
0
      *lpBytesReturned = nOutBufferSize; /* an empty OutputBuffer will be returned */
120
0
      SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
121
0
      return FALSE;
122
0
    }
123
0
    case IOCTL_SERIAL_SET_BAUD_RATE:
124
0
    {
125
0
      if (pServerSerialDriver->set_baud_rate)
126
0
      {
127
0
        SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpInBuffer;
128
129
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_BAUD_RATE));
130
0
        if (nInBufferSize < sizeof(SERIAL_BAUD_RATE))
131
0
        {
132
0
          SetLastError(ERROR_INVALID_PARAMETER);
133
0
          return FALSE;
134
0
        }
135
136
0
        return pServerSerialDriver->set_baud_rate(pComm, pBaudRate);
137
0
      }
138
0
      break;
139
0
    }
140
0
    case IOCTL_SERIAL_GET_BAUD_RATE:
141
0
    {
142
0
      if (pServerSerialDriver->get_baud_rate)
143
0
      {
144
0
        SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpOutBuffer;
145
146
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_BAUD_RATE));
147
0
        if (nOutBufferSize < sizeof(SERIAL_BAUD_RATE))
148
0
        {
149
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
150
0
          return FALSE;
151
0
        }
152
153
0
        if (!pServerSerialDriver->get_baud_rate(pComm, pBaudRate))
154
0
          return FALSE;
155
156
0
        *lpBytesReturned = sizeof(SERIAL_BAUD_RATE);
157
0
        return TRUE;
158
0
      }
159
0
      break;
160
0
    }
161
0
    case IOCTL_SERIAL_GET_PROPERTIES:
162
0
    {
163
0
      if (pServerSerialDriver->get_properties)
164
0
      {
165
0
        COMMPROP* pProperties = (COMMPROP*)lpOutBuffer;
166
167
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(COMMPROP));
168
0
        if (nOutBufferSize < sizeof(COMMPROP))
169
0
        {
170
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
171
0
          return FALSE;
172
0
        }
173
174
0
        if (!pServerSerialDriver->get_properties(pComm, pProperties))
175
0
          return FALSE;
176
177
0
        *lpBytesReturned = sizeof(COMMPROP);
178
0
        return TRUE;
179
0
      }
180
0
      break;
181
0
    }
182
0
    case IOCTL_SERIAL_SET_CHARS:
183
0
    {
184
0
      if (pServerSerialDriver->set_serial_chars)
185
0
      {
186
0
        SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpInBuffer;
187
188
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_CHARS));
189
0
        if (nInBufferSize < sizeof(SERIAL_CHARS))
190
0
        {
191
0
          SetLastError(ERROR_INVALID_PARAMETER);
192
0
          return FALSE;
193
0
        }
194
195
0
        return pServerSerialDriver->set_serial_chars(pComm, pSerialChars);
196
0
      }
197
0
      break;
198
0
    }
199
0
    case IOCTL_SERIAL_GET_CHARS:
200
0
    {
201
0
      if (pServerSerialDriver->get_serial_chars)
202
0
      {
203
0
        SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpOutBuffer;
204
205
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_CHARS));
206
0
        if (nOutBufferSize < sizeof(SERIAL_CHARS))
207
0
        {
208
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
209
0
          return FALSE;
210
0
        }
211
212
0
        if (!pServerSerialDriver->get_serial_chars(pComm, pSerialChars))
213
0
          return FALSE;
214
215
0
        *lpBytesReturned = sizeof(SERIAL_CHARS);
216
0
        return TRUE;
217
0
      }
218
0
      break;
219
0
    }
220
0
    case IOCTL_SERIAL_SET_LINE_CONTROL:
221
0
    {
222
0
      if (pServerSerialDriver->set_line_control)
223
0
      {
224
0
        SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpInBuffer;
225
226
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
227
0
        if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
228
0
        {
229
0
          SetLastError(ERROR_INVALID_PARAMETER);
230
0
          return FALSE;
231
0
        }
232
233
0
        return pServerSerialDriver->set_line_control(pComm, pLineControl);
234
0
      }
235
0
      break;
236
0
    }
237
0
    case IOCTL_SERIAL_GET_LINE_CONTROL:
238
0
    {
239
0
      if (pServerSerialDriver->get_line_control)
240
0
      {
241
0
        SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpOutBuffer;
242
243
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_LINE_CONTROL));
244
0
        if (nOutBufferSize < sizeof(SERIAL_LINE_CONTROL))
245
0
        {
246
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
247
0
          return FALSE;
248
0
        }
249
250
0
        if (!pServerSerialDriver->get_line_control(pComm, pLineControl))
251
0
          return FALSE;
252
253
0
        *lpBytesReturned = sizeof(SERIAL_LINE_CONTROL);
254
0
        return TRUE;
255
0
      }
256
0
      break;
257
0
    }
258
0
    case IOCTL_SERIAL_SET_HANDFLOW:
259
0
    {
260
0
      if (pServerSerialDriver->set_handflow)
261
0
      {
262
0
        SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpInBuffer;
263
264
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
265
0
        if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
266
0
        {
267
0
          SetLastError(ERROR_INVALID_PARAMETER);
268
0
          return FALSE;
269
0
        }
270
271
0
        return pServerSerialDriver->set_handflow(pComm, pHandflow);
272
0
      }
273
0
      break;
274
0
    }
275
0
    case IOCTL_SERIAL_GET_HANDFLOW:
276
0
    {
277
0
      if (pServerSerialDriver->get_handflow)
278
0
      {
279
0
        SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpOutBuffer;
280
281
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_HANDFLOW));
282
0
        if (nOutBufferSize < sizeof(SERIAL_HANDFLOW))
283
0
        {
284
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
285
0
          return FALSE;
286
0
        }
287
288
0
        if (!pServerSerialDriver->get_handflow(pComm, pHandflow))
289
0
          return FALSE;
290
291
0
        *lpBytesReturned = sizeof(SERIAL_HANDFLOW);
292
0
        return TRUE;
293
0
      }
294
0
      break;
295
0
    }
296
0
    case IOCTL_SERIAL_SET_TIMEOUTS:
297
0
    {
298
0
      if (pServerSerialDriver->set_timeouts)
299
0
      {
300
0
        SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpInBuffer;
301
302
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_TIMEOUTS));
303
0
        if (nInBufferSize < sizeof(SERIAL_TIMEOUTS))
304
0
        {
305
0
          SetLastError(ERROR_INVALID_PARAMETER);
306
0
          return FALSE;
307
0
        }
308
309
0
        return pServerSerialDriver->set_timeouts(pComm, pHandflow);
310
0
      }
311
0
      break;
312
0
    }
313
0
    case IOCTL_SERIAL_GET_TIMEOUTS:
314
0
    {
315
0
      if (pServerSerialDriver->get_timeouts)
316
0
      {
317
0
        SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpOutBuffer;
318
319
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_TIMEOUTS));
320
0
        if (nOutBufferSize < sizeof(SERIAL_TIMEOUTS))
321
0
        {
322
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
323
0
          return FALSE;
324
0
        }
325
326
0
        if (!pServerSerialDriver->get_timeouts(pComm, pHandflow))
327
0
          return FALSE;
328
329
0
        *lpBytesReturned = sizeof(SERIAL_TIMEOUTS);
330
0
        return TRUE;
331
0
      }
332
0
      break;
333
0
    }
334
0
    case IOCTL_SERIAL_SET_DTR:
335
0
    {
336
0
      if (pServerSerialDriver->set_dtr)
337
0
      {
338
0
        return pServerSerialDriver->set_dtr(pComm);
339
0
      }
340
0
      break;
341
0
    }
342
0
    case IOCTL_SERIAL_CLR_DTR:
343
0
    {
344
0
      if (pServerSerialDriver->clear_dtr)
345
0
      {
346
0
        return pServerSerialDriver->clear_dtr(pComm);
347
0
      }
348
0
      break;
349
0
    }
350
0
    case IOCTL_SERIAL_SET_RTS:
351
0
    {
352
0
      if (pServerSerialDriver->set_rts)
353
0
      {
354
0
        return pServerSerialDriver->set_rts(pComm);
355
0
      }
356
0
      break;
357
0
    }
358
0
    case IOCTL_SERIAL_CLR_RTS:
359
0
    {
360
0
      if (pServerSerialDriver->clear_rts)
361
0
      {
362
0
        return pServerSerialDriver->clear_rts(pComm);
363
0
      }
364
0
      break;
365
0
    }
366
0
    case IOCTL_SERIAL_GET_MODEMSTATUS:
367
0
    {
368
0
      if (pServerSerialDriver->get_modemstatus)
369
0
      {
370
0
        ULONG* pRegister = (ULONG*)lpOutBuffer;
371
372
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
373
0
        if (nOutBufferSize < sizeof(ULONG))
374
0
        {
375
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
376
0
          return FALSE;
377
0
        }
378
379
0
        if (!pServerSerialDriver->get_modemstatus(pComm, pRegister))
380
0
          return FALSE;
381
382
0
        CommLog_Print(WLOG_DEBUG, "modem status %s" PRIx32,
383
0
                      comm_ioctl_modem_status_string(*pRegister, buffer, sizeof(buffer)));
384
0
        *lpBytesReturned = sizeof(ULONG);
385
0
        return TRUE;
386
0
      }
387
0
      break;
388
0
    }
389
0
    case IOCTL_SERIAL_SET_WAIT_MASK:
390
0
    {
391
0
      if (pServerSerialDriver->set_wait_mask)
392
0
      {
393
0
        ULONG* pWaitMask = (ULONG*)lpInBuffer;
394
395
0
        WINPR_ASSERT(nInBufferSize >= sizeof(ULONG));
396
0
        if (nInBufferSize < sizeof(ULONG))
397
0
        {
398
0
          SetLastError(ERROR_INVALID_PARAMETER);
399
0
          return FALSE;
400
0
        }
401
402
0
        const BOOL rc = pServerSerialDriver->set_wait_mask(pComm, pWaitMask);
403
0
        CommLog_Print(WLOG_DEBUG, "set_wait_mask %s -> %d",
404
0
                      CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)), rc);
405
0
        return rc;
406
0
      }
407
0
      break;
408
0
    }
409
0
    case IOCTL_SERIAL_GET_WAIT_MASK:
410
0
    {
411
0
      if (pServerSerialDriver->get_wait_mask)
412
0
      {
413
0
        ULONG* pWaitMask = (ULONG*)lpOutBuffer;
414
415
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
416
0
        if (nOutBufferSize < sizeof(ULONG))
417
0
        {
418
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
419
0
          return FALSE;
420
0
        }
421
422
0
        if (!pServerSerialDriver->get_wait_mask(pComm, pWaitMask))
423
0
          return FALSE;
424
425
0
        CommLog_Print(WLOG_DEBUG, "get_wait_mask %s",
426
0
                      CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)));
427
0
        *lpBytesReturned = sizeof(ULONG);
428
0
        return TRUE;
429
0
      }
430
0
      break;
431
0
    }
432
0
    case IOCTL_SERIAL_WAIT_ON_MASK:
433
0
    {
434
0
      if (pServerSerialDriver->wait_on_mask)
435
0
      {
436
0
        ULONG* pOutputMask = (ULONG*)lpOutBuffer;
437
438
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
439
0
        if (nOutBufferSize < sizeof(ULONG))
440
0
        {
441
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
442
0
          return FALSE;
443
0
        }
444
445
0
        const BOOL rc = pServerSerialDriver->wait_on_mask(pComm, pOutputMask);
446
447
0
        *lpBytesReturned = sizeof(ULONG);
448
0
        CommLog_Print(WLOG_DEBUG, "wait_on_mask %s -> %d",
449
0
                      CommSerialEvString(*pOutputMask, buffer, sizeof(buffer)), rc);
450
0
        return rc;
451
0
      }
452
0
      break;
453
0
    }
454
0
    case IOCTL_SERIAL_SET_QUEUE_SIZE:
455
0
    {
456
0
      if (pServerSerialDriver->set_queue_size)
457
0
      {
458
0
        SERIAL_QUEUE_SIZE* pQueueSize = (SERIAL_QUEUE_SIZE*)lpInBuffer;
459
460
0
        WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_QUEUE_SIZE));
461
0
        if (nInBufferSize < sizeof(SERIAL_QUEUE_SIZE))
462
0
        {
463
0
          SetLastError(ERROR_INVALID_PARAMETER);
464
0
          return FALSE;
465
0
        }
466
467
0
        return pServerSerialDriver->set_queue_size(pComm, pQueueSize);
468
0
      }
469
0
      break;
470
0
    }
471
0
    case IOCTL_SERIAL_PURGE:
472
0
    {
473
0
      if (pServerSerialDriver->purge)
474
0
      {
475
0
        ULONG* pPurgeMask = (ULONG*)lpInBuffer;
476
477
0
        WINPR_ASSERT(nInBufferSize >= sizeof(ULONG));
478
0
        if (nInBufferSize < sizeof(ULONG))
479
0
        {
480
0
          SetLastError(ERROR_INVALID_PARAMETER);
481
0
          return FALSE;
482
0
        }
483
484
0
        return pServerSerialDriver->purge(pComm, pPurgeMask);
485
0
      }
486
0
      break;
487
0
    }
488
0
    case IOCTL_SERIAL_GET_COMMSTATUS:
489
0
    {
490
0
      if (pServerSerialDriver->get_commstatus)
491
0
      {
492
0
        SERIAL_STATUS* pCommstatus = (SERIAL_STATUS*)lpOutBuffer;
493
494
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_STATUS));
495
0
        if (nOutBufferSize < sizeof(SERIAL_STATUS))
496
0
        {
497
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
498
0
          return FALSE;
499
0
        }
500
501
0
        if (!pServerSerialDriver->get_commstatus(pComm, pCommstatus))
502
0
          return FALSE;
503
504
0
        *lpBytesReturned = sizeof(SERIAL_STATUS);
505
0
        return TRUE;
506
0
      }
507
0
      break;
508
0
    }
509
0
    case IOCTL_SERIAL_SET_BREAK_ON:
510
0
    {
511
0
      if (pServerSerialDriver->set_break_on)
512
0
      {
513
0
        return pServerSerialDriver->set_break_on(pComm);
514
0
      }
515
0
      break;
516
0
    }
517
0
    case IOCTL_SERIAL_SET_BREAK_OFF:
518
0
    {
519
0
      if (pServerSerialDriver->set_break_off)
520
0
      {
521
0
        return pServerSerialDriver->set_break_off(pComm);
522
0
      }
523
0
      break;
524
0
    }
525
0
    case IOCTL_SERIAL_SET_XOFF:
526
0
    {
527
0
      if (pServerSerialDriver->set_xoff)
528
0
      {
529
0
        return pServerSerialDriver->set_xoff(pComm);
530
0
      }
531
0
      break;
532
0
    }
533
0
    case IOCTL_SERIAL_SET_XON:
534
0
    {
535
0
      if (pServerSerialDriver->set_xon)
536
0
      {
537
0
        return pServerSerialDriver->set_xon(pComm);
538
0
      }
539
0
      break;
540
0
    }
541
0
    case IOCTL_SERIAL_GET_DTRRTS:
542
0
    {
543
0
      if (pServerSerialDriver->get_dtrrts)
544
0
      {
545
0
        ULONG* pMask = (ULONG*)lpOutBuffer;
546
547
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
548
0
        if (nOutBufferSize < sizeof(ULONG))
549
0
        {
550
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
551
0
          return FALSE;
552
0
        }
553
554
0
        if (!pServerSerialDriver->get_dtrrts(pComm, pMask))
555
0
          return FALSE;
556
557
0
        *lpBytesReturned = sizeof(ULONG);
558
0
        return TRUE;
559
0
      }
560
0
      break;
561
0
    }
562
0
    case IOCTL_SERIAL_CONFIG_SIZE:
563
0
    {
564
0
      if (pServerSerialDriver->config_size)
565
0
      {
566
0
        ULONG* pSize = (ULONG*)lpOutBuffer;
567
568
0
        WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
569
0
        if (nOutBufferSize < sizeof(ULONG))
570
0
        {
571
0
          SetLastError(ERROR_INSUFFICIENT_BUFFER);
572
0
          return FALSE;
573
0
        }
574
575
0
        if (!pServerSerialDriver->config_size(pComm, pSize))
576
0
          return FALSE;
577
578
0
        *lpBytesReturned = sizeof(ULONG);
579
0
        return TRUE;
580
0
      }
581
0
      break;
582
0
    }
583
0
    case IOCTL_SERIAL_IMMEDIATE_CHAR:
584
0
    {
585
0
      if (pServerSerialDriver->immediate_char)
586
0
      {
587
0
        UCHAR* pChar = (UCHAR*)lpInBuffer;
588
589
0
        WINPR_ASSERT(nInBufferSize >= sizeof(UCHAR));
590
0
        if (nInBufferSize < sizeof(UCHAR))
591
0
        {
592
0
          SetLastError(ERROR_INVALID_PARAMETER);
593
0
          return FALSE;
594
0
        }
595
596
0
        return pServerSerialDriver->immediate_char(pComm, pChar);
597
0
      }
598
0
      break;
599
0
    }
600
0
    case IOCTL_SERIAL_RESET_DEVICE:
601
0
    {
602
0
      if (pServerSerialDriver->reset_device)
603
0
      {
604
0
        return pServerSerialDriver->reset_device(pComm);
605
0
      }
606
0
      break;
607
0
    }
608
0
    default:
609
0
      break;
610
0
  }
611
612
0
  CommLog_Print(
613
0
      WLOG_WARN, _T("unsupported IoControlCode=[0x%08" PRIX32 "] %s (remote serial driver: %s)"),
614
0
      dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), pServerSerialDriver->name);
615
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED); /* => STATUS_NOT_IMPLEMENTED */
616
0
  return FALSE;
617
0
}
618
619
/**
620
 * FIXME: to be used through winpr-io's DeviceIoControl
621
 *
622
 * Any previous error as returned by GetLastError is cleared.
623
 *
624
 * ERRORS:
625
 *   ERROR_INVALID_HANDLE
626
 *   ERROR_INVALID_PARAMETER
627
 *   ERROR_NOT_SUPPORTED lpOverlapped is not supported
628
 *   ERROR_INSUFFICIENT_BUFFER
629
 *   ERROR_CALL_NOT_IMPLEMENTED unimplemented ioctl
630
 */
631
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
632
                         DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
633
                         LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
634
0
{
635
0
  WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
636
0
  BOOL result = 0;
637
638
0
  if (hDevice == INVALID_HANDLE_VALUE)
639
0
  {
640
0
    SetLastError(ERROR_INVALID_HANDLE);
641
0
    return FALSE;
642
0
  }
643
644
0
  if (!CommIsHandled(hDevice))
645
0
    return FALSE;
646
647
0
  if (!pComm->fd)
648
0
  {
649
0
    SetLastError(ERROR_INVALID_HANDLE);
650
0
    return FALSE;
651
0
  }
652
653
0
  result = s_CommDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer,
654
0
                                 nOutBufferSize, lpBytesReturned, lpOverlapped);
655
656
0
  if (lpBytesReturned && *lpBytesReturned != nOutBufferSize)
657
0
  {
658
    /* This might be a hint for a bug, especially when result==TRUE */
659
0
    CommLog_Print(WLOG_WARN,
660
0
                  "IoControlCode=[0x%08" PRIX32 "] %s: lpBytesReturned=%" PRIu32
661
0
                  " and nOutBufferSize=%" PRIu32 " are different!",
662
0
                  dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), *lpBytesReturned,
663
0
                  nOutBufferSize);
664
0
  }
665
666
0
  if (pComm->permissive)
667
0
  {
668
0
    if (!result)
669
0
    {
670
0
      CommLog_Print(WLOG_WARN,
671
0
                    "[permissive]: IoControlCode=[0x%08" PRIX32 "] %s failed, ignoring",
672
0
                    dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode));
673
0
    }
674
675
0
    return TRUE; /* always! */
676
0
  }
677
678
0
  return result;
679
0
}
680
681
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
682
0
{
683
0
  struct termios currentState = WINPR_C_ARRAY_INIT;
684
0
  size_t count = 0;
685
0
  do
686
0
  {
687
0
    const int src = tcsetattr(fd, optional_actions, termios_p);
688
0
    if (src < 0)
689
0
    {
690
0
      char buffer[64] = WINPR_C_ARRAY_INIT;
691
0
      CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcsetattr failure, errno: %s [%d]", count,
692
0
                    winpr_strerror(errno, buffer, sizeof(buffer)), errno);
693
0
      return src;
694
0
    }
695
696
    /* NB: tcsetattr() can succeed even if not all changes have been applied. */
697
0
    const int rrc = tcgetattr(fd, &currentState);
698
0
    if (rrc < 0)
699
0
    {
700
0
      char buffer[64] = WINPR_C_ARRAY_INIT;
701
0
      CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcgetattr failure, errno: %s [%d]", count,
702
0
                    winpr_strerror(errno, buffer, sizeof(buffer)), errno);
703
0
      return rrc;
704
0
    }
705
    // NOLINTNEXTLINE(bugprone-suspicious-memory-comparison,cert-exp42-c,cert-flp37-c)
706
0
  } while ((memcmp(&currentState, termios_p, sizeof(struct termios)) != 0) && (count++ < 2));
707
708
0
  return 0;
709
0
}
710
711
static const char* comm_ioctl_modem_flag_str(ULONG flag)
712
0
{
713
0
  switch (flag)
714
0
  {
715
0
    case SERIAL_MSR_DCTS:
716
0
      return "SERIAL_MSR_DCTS";
717
0
    case SERIAL_MSR_DDSR:
718
0
      return "SERIAL_MSR_DDSR";
719
0
    case SERIAL_MSR_TERI:
720
0
      return "SERIAL_MSR_TERI";
721
0
    case SERIAL_MSR_DDCD:
722
0
      return "SERIAL_MSR_DDCD";
723
0
    case SERIAL_MSR_CTS:
724
0
      return "SERIAL_MSR_CTS";
725
0
    case SERIAL_MSR_DSR:
726
0
      return "SERIAL_MSR_DSR";
727
0
    case SERIAL_MSR_RI:
728
0
      return "SERIAL_MSR_RI";
729
0
    case SERIAL_MSR_DCD:
730
0
      return "SERIAL_MSR_DCD";
731
0
    default:
732
0
      return "SERIAL_MSR_UNKNOWN";
733
0
  }
734
0
}
735
736
const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size)
737
0
{
738
0
  const ULONG flags[] = { SERIAL_MSR_DCTS, SERIAL_MSR_DDSR, SERIAL_MSR_TERI, SERIAL_MSR_DDCD,
739
0
                        SERIAL_MSR_CTS,  SERIAL_MSR_DSR,  SERIAL_MSR_RI,   SERIAL_MSR_DCD
740
741
0
  };
742
0
  winpr_str_append("{", buffer, size, "");
743
744
0
  const char* sep = "";
745
0
  for (size_t x = 0; x < ARRAYSIZE(flags); x++)
746
0
  {
747
0
    const ULONG flag = flags[x];
748
0
    if (status & flag)
749
0
    {
750
0
      winpr_str_append(comm_ioctl_modem_flag_str(flag), buffer, size, sep);
751
0
      sep = "|";
752
0
    }
753
0
  }
754
755
0
  char number[32] = WINPR_C_ARRAY_INIT;
756
0
  (void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
757
0
  winpr_str_append(number, buffer, size, "");
758
0
  return buffer;
759
0
}