Project

General

Profile

Support Request #14449 » serialdevice.cpp

hidden, 2021-06-07 08:19

 
1
/**
2
 * This 
3
 * 
4
 *
5
 * @file
6
 * Copyright © Audi Electronics Venture GmbH. All rights reserved.
7
 *
8
 * $Author: ELAMIHA $
9
 * $Date: 2014-11-21 11:30:22 +0100 (Fri, 21 Nov 2014) $
10
 * $Revision: 1180 $
11
 *
12
 * @remarks
13
 *
14
 */
15

    
16
#include "stdafx.h"
17
#ifdef NO_GLOBAL_WINDOWS_SDK_FROM_STDAFX 
18
    #include "../library_includes_windows.h"
19
#endif
20
#ifndef WIN32
21
    #include <poll.h>
22
    #include <termios.h>
23
#endif
24
#include "a_utils_comm.h"
25

    
26

    
27
#ifdef WIN32
28

    
29
/*
30
                //open COM1 for read and write
31
                hPort=CreateFile( "COM1",
32
                  GENERIC_READ | GENERIC_WRITE, //bidirectional
33
                  0, 
34
                  NULL, //no security
35
                  OPEN_EXISTING,  //this must be set; the ports are already created
36
                  FILE_ATTRIBUTE_NORMAL, // maybe with | FILE_FLAG_OVERLAPPED  
37
                  NULL );
38

    
39
Standard ReadFile() and WriteFile() functions can then be used to read or write to the port.
40
To set the port up reliably, COM port specific functions need to be used, so look up the online
41
help for details on the following functions:
42
    GetCommModemStatus(), SetCommState(), SetCommMask(), SetupComm(),
43
    PurgeComm(), ClearCommError(), SetCommTimeouts() and EscapeCommFunction() 
44
Tips:- 
45

    
46
Even on Win9x, you can use overlapped IO with a com port 
47
The Signalled state of a port seems to indicates it is ready for reading or writing, even if you only opened the port unidirectionally. 
48
WaitCommEvent() lets you block waiting for something interesting to happen, such as data arriving or various lines (e.g. RI -Ring Indicator) toggling. 
49
On win98/NT5, RequestDeviceWakeup() may even let the external port wake up your app -especially if RI is used as the signal. 
50
*/
51

    
52
namespace A_UTILS_NS
53
{
54

    
55
/**************************************/
56
/* This still needs to be implemented.*/
57
/**************************************/
58
A_UTILS_P_DECLARE_PRE(cSerialDevice);
59

    
60
cSerialDevice::cSerialDevice()
61
{
62
    m_hDevice = NULL;
63
}
64

    
65
cSerialDevice::~cSerialDevice()
66
{
67
    Close();
68
}
69

    
70
tResult cSerialDevice::Open(const tChar* strDeviceName, tInt nBaudRate, tInt nParity, tInt nDataBits, 
71
                            tInt nStopBits, tInt nRxQueue, tInt nTxQueue, tTimeStamp nReadTimeout)
72
{
73
    RETURN_IF_POINTER_NULL(strDeviceName);
74
    if (nBaudRate < 0 ||
75
        nParity < 0 ||
76
        nDataBits < 0 ||
77
        nStopBits < 0 ||
78
        nRxQueue < 0 ||
79
        nTxQueue < 0 ||
80
        nReadTimeout < 0)
81
    {
82
        RETURN_ERROR(ERR_INVALID_ARG);
83
    }
84

    
85
    Close();
86

    
87
    BOOL bStatus = TRUE;
88

    
89
    m_hDevice = ::CreateFile(strDeviceName,
90
                           GENERIC_READ | GENERIC_WRITE,
91
                           0,
92
                           NULL,
93
                           OPEN_EXISTING,
94
                           FILE_ATTRIBUTE_NORMAL,
95
                           NULL);
96

    
97
    if (m_hDevice == NULL || m_hDevice == INVALID_HANDLE_VALUE)
98
    {
99
        DWORD dwError = cSystem::GetLastSystemError();
100
        RETURN_ERROR(ERR_OPEN_FAILED);
101
    }
102

    
103
    // get current config
104
    DCB sDevCtrl;
105
    sDevCtrl.DCBlength = sizeof(sDevCtrl);
106
    bStatus = ::GetCommState(m_hDevice, &sDevCtrl);
107
    if (bStatus == FALSE)
108
    {
109
        RETURN_ERROR(ERR_DEVICE_NOT_READY);
110
    }
111

    
112
    sDevCtrl.BaudRate    = nBaudRate;
113

    
114
    switch (nParity)
115
    {
116
        case SER_EVENPARITY:
117
            sDevCtrl.Parity = EVENPARITY;
118
            break;
119
        case SER_MARKPARITY:
120
            sDevCtrl.Parity = MARKPARITY;
121
            break;
122
        case SER_NOPARITY:
123
            sDevCtrl.Parity = NOPARITY;
124
            break;
125
        case SER_ODDPARITY:
126
            sDevCtrl.Parity = ODDPARITY;
127
            break;
128
        case SER_SPACEPARITY:
129
            sDevCtrl.Parity = SPACEPARITY;
130
            break;
131
        default:
132
            RETURN_ERROR(ERR_INVALID_ARG);
133
    }
134

    
135
    switch (nStopBits)
136
    {
137
        case SER_ONESTOPBIT:
138
            sDevCtrl.StopBits = ONESTOPBIT;
139
            break;
140
        case SER_ONE5STOPBITS:
141
            sDevCtrl.StopBits = ONE5STOPBITS;
142
            break;
143
        case SER_TWOSTOPBITS:
144
            sDevCtrl.StopBits = TWOSTOPBITS;
145
            break;
146
        default:
147
            RETURN_ERROR(ERR_INVALID_ARG);
148
    }
149

    
150
    sDevCtrl.ByteSize    = nDataBits;
151

    
152
    bStatus = ::SetCommState(m_hDevice, &sDevCtrl);
153
    if (bStatus == FALSE)
154
    {
155
        DWORD dwError = cSystem::GetLastSystemError();
156
        RETURN_ERROR(ERR_DEVICE_NOT_READY);
157
    }
158

    
159
    bStatus = ::SetupComm(m_hDevice, nRxQueue, nTxQueue);
160
    if (bStatus == FALSE)
161
    {
162
        DWORD dwError = cSystem::GetLastSystemError();
163
        RETURN_ERROR(ERR_DEVICE_NOT_READY);
164
    }
165

    
166
    COMMTIMEOUTS sCommTimeOuts;
167
    sCommTimeOuts.ReadIntervalTimeout         = MAXDWORD;
168
    sCommTimeOuts.ReadTotalTimeoutMultiplier  = MAXDWORD;
169
    sCommTimeOuts.ReadTotalTimeoutConstant    = (DWORD)(nReadTimeout / 1000);
170
    sCommTimeOuts.WriteTotalTimeoutMultiplier = 0;
171
    sCommTimeOuts.WriteTotalTimeoutConstant   = 5000;
172

    
173
    ::SetCommTimeouts(m_hDevice, &sCommTimeOuts);
174

    
175
    RETURN_NOERROR;
176
}
177

    
178
tResult cSerialDevice::Close()
179
{
180
    if (m_hDevice != NULL)
181
    {
182
        ::CloseHandle(m_hDevice);
183
        m_hDevice = NULL;
184
    }
185

    
186
    RETURN_NOERROR;
187
}
188

    
189
tResult cSerialDevice::CheckForDevice(const tChar* strDeviceName)
190
{
191
    HANDLE hDevice = ::CreateFile(strDeviceName,
192
                                  GENERIC_READ | GENERIC_WRITE,
193
                                  0,
194
                                  NULL,
195
                                  OPEN_EXISTING,
196
                                  FILE_ATTRIBUTE_NORMAL,
197
                                  NULL);
198

    
199
    if (hDevice != NULL)
200
    {
201
        ::CloseHandle(hDevice);
202
    }
203

    
204
    if (hDevice == NULL || hDevice == INVALID_HANDLE_VALUE)
205
    {
206
        RETURN_ERROR(ERR_NOT_FOUND);
207
    }
208

    
209
    RETURN_NOERROR;
210
}
211

    
212
tUInt32 cSerialDevice::GetDeviceMask()
213
{
214
    tUInt32 nMask = 0x0;
215

    
216
    for (tInt nDevNr=0; nDevNr<32; nDevNr++)
217
    {
218
        if (IS_OK(CheckForDevice("\\\\.\\COM" + cString::Format("%d", nDevNr+1))))
219
        {
220
            nMask |= (1 << nDevNr);
221
        }
222
    }
223

    
224
    return nMask;
225
}
226

    
227
tUInt32 cSerialDevice::GetUnusedDeviceMask()
228
{
229
    tUInt32 nMask = GetDeviceMask();
230

    
231
    tHandle hDevice;
232
    for (tInt nDevNr=0; nDevNr<32; nDevNr++)
233
    {
234
        if ((nMask & (1 << nDevNr)) != 0)
235
        {
236
            hDevice = ::CreateFile(cString::Format("COM%d", nDevNr+1),
237
                                GENERIC_READ | GENERIC_WRITE,
238
                                0,
239
                                NULL,
240
                                OPEN_EXISTING,
241
                                FILE_ATTRIBUTE_NORMAL,
242
                                NULL );
243

    
244
            if (hDevice == NULL  || hDevice == INVALID_HANDLE_VALUE)
245
            {
246
                nMask &= (~(1 << nDevNr));
247
            }
248
            else
249
            {
250
                ::CloseHandle(hDevice);
251
            }
252
        }
253
    }
254

    
255
    return nMask;
256
}
257

    
258

    
259
tResult cSerialDevice::FindUnusedDevice(cString& strDeviceName)
260
{
261
    tUInt32 nMask = GetUnusedDeviceMask();
262
    if (nMask == 0x0)
263
    {
264
        strDeviceName.Clear();
265
        RETURN_ERROR(ERR_BAD_DEVICE);
266
    }
267
    tInt nDevNr = 0;
268
    for (nDevNr = 0; nDevNr<32; nDevNr++)
269
    {
270
        if ((nMask & (1 << nDevNr)) != 0)
271
        {
272
            break;
273
        }
274
    }
275

    
276
    strDeviceName = cString::Format("COM%d", nDevNr+1);
277

    
278
    RETURN_NOERROR;
279
}
280

    
281
tInt32 cSerialDevice::Read(tVoid* pBuffer, tInt32 nBufferSize)
282
{
283
    if (pBuffer == NULL)
284
    {
285
        return -1;
286
    }
287

    
288
    tInt32 nReadCount = 0;
289

    
290
    #ifdef WIN32
291

    
292
        DWORD dwReadCount = 0;
293
        BOOL bStatus = ::ReadFile(m_hDevice, (BYTE*) pBuffer, nBufferSize, &dwReadCount, NULL);
294
        if (bStatus)
295
        {
296
            nReadCount = (tInt32) dwReadCount;
297
        }
298
        else
299
        {
300
            nReadCount = -1;
301
        }
302

    
303
    #else // WIN32
304
        
305
        nReadCount = -1;
306

    
307
    #endif // WIN32
308

    
309
    return nReadCount;
310
}
311

    
312
tInt32 cSerialDevice::Write(const tVoid* pBuffer, tInt32 nBufferSize)
313
{
314
    if (pBuffer == NULL)
315
    {
316
        return -1;
317
    }
318

    
319
    #ifdef WIN32
320

    
321
        DWORD dwWriteCount = 0;
322
        BOOL bStatus = ::WriteFile(m_hDevice,
323
            pBuffer,
324
            nBufferSize,
325
            &dwWriteCount,
326
            NULL);
327

    
328
        if (!bStatus || dwWriteCount != nBufferSize)
329
        {
330
            return -1;
331
        }
332

    
333
        return (tInt32) dwWriteCount;
334

    
335
    #else // WIN32
336

    
337
        return -1;
338

    
339
    #endif // WIN32
340
}
341

    
342
tBool cSerialDevice::IsConnected()
343
{
344
    return (m_hDevice != NULL);
345
}
346

    
347
} // namespace A_UTILS_NS
348

    
349

    
350
#else // WIN32
351

    
352
namespace A_UTILS_NS
353
{
354

    
355
A_UTILS_P_DECLARE(cSerialDevice)
356
{
357
    friend class cSerialDevice;
358
    public:
359
        pollfd m_sPoll;
360
        tInt   m_nTimeoutMilli;
361
};
362

    
363
cSerialDevice::cSerialDevice():
364
        m_nDev(-1)
365
{
366
    A_UTILS_D_CREATE(cSerialDevice);
367
}
368

    
369
cSerialDevice::~ cSerialDevice()
370
{
371
    Close();
372
}
373

    
374
#define BAUDCASE(rate) case rate: nBaudSetting = B##rate; break;
375

    
376
tResult cSerialDevice::Open(const tChar * strDeviceName, 
377
                            tInt nBaudRate, 
378
                            tInt nParity, 
379
                            tInt nDataBits, 
380
                            tInt nStopBits, 
381
                            tInt nRxQueue, 
382
                            tInt nTxQueue, 
383
                            tTimeStamp nReadTimeout)
384
{
385
    RETURN_IF_POINTER_NULL(strDeviceName);
386
    if (nBaudRate < 0 ||
387
        nParity < 0 ||
388
        nDataBits < 0 ||
389
        nStopBits < 0 ||
390
        nRxQueue < 0 ||
391
        nTxQueue < 0 ||
392
        nReadTimeout < 0)
393
    {
394
        RETURN_ERROR(ERR_INVALID_ARG);
395
    }
396

    
397
    m_nDev = open(strDeviceName, O_RDWR|O_NOCTTY);
398
    if (m_nDev < 0)
399
    {
400
        RETURN_ERROR(ERR_OPEN_FAILED);
401
    }
402

    
403

    
404
    // please see http://www.easysw.com/~mike/serial/serial.html
405
    // for an in-depth description of the following options.
406
    struct termios sOptions;
407

    
408
    if (tcgetattr(m_nDev, &sOptions) < 0)
409
    {
410
        Close();
411
        LOG_ERROR(cString::Format("Unable to get serial device options: %s", strerror(errno)));
412
        RETURN_ERROR(ERR_DEVICE_IO);
413
    }
414

    
415
    // set baud-rate 
416
    tInt nBaudSetting = 0;
417
    switch (nBaudRate)
418
    {
419
        BAUDCASE(0)
420
        BAUDCASE(50)
421
        BAUDCASE(75)
422
        BAUDCASE(110)
423
        BAUDCASE(134)
424
        BAUDCASE(150)
425
        BAUDCASE(300)
426
        BAUDCASE(600)
427
        BAUDCASE(1200)
428
        BAUDCASE(1800)
429
        BAUDCASE(2400)
430
        BAUDCASE(4800)
431
        BAUDCASE(9600)
432
        BAUDCASE(19200)
433
        BAUDCASE(38400)
434
        BAUDCASE(57600)
435
        //BAUDCASE(76800)
436
        BAUDCASE(115200)
437
        BAUDCASE(230400 )
438
        #ifndef __APPLE__
439
            BAUDCASE(460800)
440
            BAUDCASE(500000)
441
            BAUDCASE(576000)
442
            BAUDCASE(921600)
443
            BAUDCASE(1000000)
444
            BAUDCASE(1152000)
445
            BAUDCASE(1500000)
446
            BAUDCASE(2000000)
447
            BAUDCASE(2500000)
448
            BAUDCASE(3000000)
449
            BAUDCASE(3500000)
450
            BAUDCASE(4000000)
451
        #endif //__APPLE__
452
        default:
453
            RETURN_ERROR(ERR_NOT_SUPPORTED);
454
    }
455

    
456
    cfsetispeed(&sOptions, nBaudSetting);
457
    cfsetospeed(&sOptions, nBaudSetting);
458

    
459
    sOptions.c_cflag |= (CLOCAL | CREAD);
460

    
461
    // set parity
462
    switch (nParity)
463
    {
464
        case SER_EVENPARITY:
465
            sOptions.c_cflag |= PARENB;
466
            sOptions.c_cflag &= ~PARODD;
467
            sOptions.c_cflag &= ~CSTOPB;
468
            sOptions.c_cflag &= ~CSIZE;
469
            sOptions.c_cflag |= CS7;
470
            break;
471
        case SER_MARKPARITY:
472
            RETURN_ERROR(ERR_NOT_SUPPORTED);
473
            break;
474
        case SER_NOPARITY:
475
            sOptions.c_cflag &= ~PARENB;
476
            sOptions.c_cflag &= ~CSTOPB;
477
            sOptions.c_cflag &= ~CSIZE;
478
            sOptions.c_cflag |= CS8;
479
            break;
480
        case SER_ODDPARITY:
481
            sOptions.c_cflag |= PARENB;
482
            sOptions.c_cflag |= PARODD;
483
            sOptions.c_cflag &= ~CSTOPB;
484
            sOptions.c_cflag &= ~CSIZE;
485
            sOptions.c_cflag |= CS7;
486
            break;
487
        case SER_SPACEPARITY:
488
            RETURN_ERROR(ERR_NOT_SUPPORTED);
489
        default:
490
            RETURN_ERROR(ERR_INVALID_ARG);
491
    }
492

    
493
    // set data size
494
    sOptions.c_cflag &= ~CSIZE;
495
    switch (nDataBits)
496
    {
497
        case 5:
498
            sOptions.c_cflag |= CS5;
499
            break;
500
        case 6:
501
            sOptions.c_cflag |= CS6;
502
            break;
503
        case 7:
504
            sOptions.c_cflag |= CS7;
505
            break;
506
        case 8:
507
            sOptions.c_cflag |= CS8;
508
            break;
509
        default:
510
            RETURN_ERROR(ERR_INVALID_ARG);
511
    }
512

    
513
    // set stop bits
514
    switch (nStopBits)
515
    {
516
        case SER_ONESTOPBIT:
517
            sOptions.c_cflag &= ~CSTOPB;
518
            break;
519
        case SER_ONE5STOPBITS:
520
            RETURN_ERROR(ERR_NOT_SUPPORTED);
521
            break;
522
        case SER_TWOSTOPBITS:
523
            sOptions.c_cflag |= CSTOPB;
524
            break;
525
        default:
526
            RETURN_ERROR(ERR_INVALID_ARG);
527
    }
528

    
529
    // use raw mode
530
    sOptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
531
    sOptions.c_oflag &= ~OPOST;
532
#ifdef IUCLC
533
    sOptions.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
534
                          INLCR | IGNCR | ICRNL | IXON | IXOFF | IUCLC | IXANY |
535
                          IMAXBEL | ISIG);
536
#else
537
    sOptions.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
538
                          INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
539
                          IMAXBEL | ISIG);
540
#endif //IUCLC
541

    
542
    //set timeout
543
    sOptions.c_cc[VMIN] = 0;
544
    sOptions.c_cc[VTIME] = nReadTimeout / 100000;
545

    
546
    //Set the new options for the port...
547
    if (tcsetattr(m_nDev, TCSANOW, &sOptions) < 0)
548
    {
549
        Close();
550
        LOG_ERROR(cString::Format("Unable to set serial device options: %s", strerror(errno)));
551
        RETURN_ERROR(ERR_DEVICE_IO);
552
    }
553

    
554
    _d->m_sPoll.fd = m_nDev;
555
    _d->m_sPoll.events = POLLIN;
556
    _d->m_nTimeoutMilli = nReadTimeout / 1000;
557

    
558
    RETURN_NOERROR;
559
}
560

    
561
tResult cSerialDevice::Close()
562
{
563
    if (IsConnected())
564
    {
565
        close(m_nDev);
566
        m_nDev = -1;
567
    }
568

    
569
    RETURN_NOERROR;
570
}
571

    
572
tInt32 cSerialDevice::Read(tVoid * pBuffer, tInt32 nBufferSize)
573
{
574
    if (m_nDev == -1)
575
    {
576
        return -1;
577
    }
578

    
579
    if (_d->m_nTimeoutMilli)
580
    {
581
        if (0 == poll(&_d->m_sPoll, 1, _d->m_nTimeoutMilli))
582
        {
583
            // timeout
584
            return 0;
585
        }
586
    }
587

    
588
    return read(m_nDev, pBuffer, nBufferSize);;
589
}
590

    
591
tInt32 cSerialDevice::Write(const tVoid * pBuffer, tInt32 nBufferSize)
592
{
593
    return write(m_nDev, pBuffer, nBufferSize);
594
}
595

    
596
tBool cSerialDevice::IsConnected()
597
{
598
    return m_nDev != -1;
599
}
600

    
601
tResult cSerialDevice::CheckForDevice(const tChar* strDeviceName)
602
{
603
    if (cFileSystem::Exists(strDeviceName))
604
    {
605
        RETURN_NOERROR;
606
    }
607

    
608
    RETURN_ERROR(ERR_NOT_FOUND);
609
}
610

    
611
tUInt32 cSerialDevice::GetDeviceMask()
612
{
613
    return 0;
614
}
615

    
616
tUInt32 cSerialDevice::GetUnusedDeviceMask()
617
{
618
    return 0;
619
}
620

    
621

    
622
tResult cSerialDevice::FindUnusedDevice(cString& strDeviceName)
623
{
624
    RETURN_ERROR(ERR_NOT_SUPPORTED);
625
}
626

    
627
}
628
#endif
629

    
630

    
631
tResult A_UTILS_NS::cSerialDevice::WriteByteByByte(const cString &strCmd)
632
{
633
    char acSign[1];
634
    for (tInt32 nChar=0; nChar < strCmd.GetLength(); nChar++)
635
    {
636
        acSign[0] = strCmd.GetAt(nChar);
637
        if (Write(acSign, 1) != 1)
638
        {
639
            RETURN_ERROR(ERR_FAILED);
640
        }
641
        cSystem::Sleep(50);
642
    }
643
    RETURN_NOERROR;
644
}
(1-1/7)