Subversion Repositories Projects

Rev

Rev 213 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
213 dhylands 1
/****************************************************************************
2
*
3
*   Copyright (c) 2009 Dave Hylands     <dhylands@gmail.com>
4
*
5
*   This program is free software; you can redistribute it and/or modify
6
*   it under the terms of the GNU General Public License version 2 as
7
*   published by the Free Software Foundation.
8
*
9
*   Alternatively, this software may be distributed under the terms of BSD
10
*   license.
11
*
12
*   See README and COPYING for more details.
13
*
14
****************************************************************************/
15
/**
16
*
17
*   @file   SerialPort.cpp
18
*
19
*   @brief  This file implements the SerialPort class using the posix
20
*           API, which is supported by cygwin and linux.
21
*
22
****************************************************************************/
23
 
24
// ---- Include Files -------------------------------------------------------
25
 
26
#include "Log.h"
27
#include "SerialPort.h"
28
#include "Str.h"
29
 
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <termios.h>
33
#include <errno.h>
34
#include <fcntl.h>
35
#include <sys/unistd.h>
36
#include <string.h>
37
#include <sys/ioctl.h>
38
 
39
// ---- Public Variables ----------------------------------------------------
40
 
41
// ---- Private Constants and Types -----------------------------------------
42
// ---- Private Variables ---------------------------------------------------
43
 
44
static struct
45
{
46
    speed_t     speed;
47
    unsigned    baudRate;
48
} gBaudTable[] =
49
{
50
    { B50,          50 },
51
    { B75,          75 },
52
    { B110,        110 },
53
    { B134,        134 },
54
    { B150,        150 },
55
    { B200,        200 },
56
    { B300,        300 },
57
    { B600,        600 },
58
    { B1200,      1200 },
59
    { B1800,      1800 },
60
    { B2400,      2400 },
61
    { B4800,      4800 },
62
    { B9600,      9600 },
63
    { B19200,    19200 },
64
    { B38400,    38400 },
65
    { B57600,    57600 },
66
    { B115200,  115200 },
67
    { B230400,  230400 },
68
    { B1000000,1000000 }
69
};
70
 
71
#define ARRAY_LEN(x)    ( sizeof( x ) / sizeof( x[ 0 ]))
72
 
73
// ---- Private Function Prototypes -----------------------------------------
74
// ---- Functions -----------------------------------------------------------
75
 
76
/**
77
 * @addtogroup SerialPort
78
 * @{
79
 */
80
 
81
//***************************************************************************
82
/**
83
*   Constructor
84
*/
85
 
86
SerialPort::SerialPort()
87
    : m_fd( -1 )
88
{
89
}
90
 
91
//***************************************************************************
92
/**
93
*   Destructor
94
*/
95
 
96
SerialPort::~SerialPort()
97
{
98
    Close();
99
}
100
 
101
//***************************************************************************
102
/**
103
*   Closes a previously opened serial port
104
*/
105
 
106
void SerialPort::Close()
107
{
108
    if ( m_fd >= 0 )
109
    {
110
        close( m_fd );
111
 
112
        m_fd = -1;
113
    }
114
 
115
} // Close
116
 
117
//***************************************************************************
118
/**
119
*   Opens a serial port.
120
*/
121
 
122
bool SerialPort::Open( const char *inDevName, const char *param )
123
{
124
    char        devName[ 40 ];
125
    unsigned    baudRate;
126
 
127
    devName[ 0 ] = '\0';
128
 
129
#if 1
130
    if ( inDevName[ 0 ] != '/' )
131
    {
132
        StrMaxCpy( devName, "/dev/", sizeof( devName ));
133
    }
134
#endif
135
    StrMaxCat( devName, inDevName, sizeof( devName ));
136
 
137
    // Translate the params, if any
138
 
139
    speed_t speed = B0;
140
    if ( param == NULL )
141
    {
142
        speed = B38400;
143
        baudRate = 38400;
144
    }
145
    else
146
    {
147
        baudRate = atoi( param );
148
 
149
        for ( unsigned i = 0; i < ARRAY_LEN( gBaudTable ); i++ )
150
        {
151
            if ( gBaudTable[ i ].baudRate == baudRate )
152
            {
153
                speed = gBaudTable[ i ].speed;
154
                break;
155
            }
156
        }
157
 
158
        if ( speed == B0 )
159
        {
160
            LogError( "Unrecognized baud rate: '%s'\n", param );
161
            return false;
162
        }
163
    }
164
 
165
    LogVerbose( "Port: '%s' Baud: %d\n", devName, baudRate );
166
 
167
    if (( m_fd = open( devName, O_RDWR | O_EXCL )) < 0 )
168
    {
169
        LogError( "Unable to open serial port '%s': %s\n", devName, strerror( errno ));
170
        return false;
171
    }
172
 
173
    // Setup the serial port
174
 
175
    struct termios  attr;
176
 
177
    if ( tcgetattr( m_fd, &attr ) < 0 )
178
    {
179
        LogError( "A: Call to tcgetattr failed: %s\n", strerror( errno ));
180
        return false;
181
    }
182
 
183
    attr.c_iflag = 0;
184
    attr.c_oflag = 0;
185
    attr.c_cflag = CLOCAL | CREAD | CS8;
186
    attr.c_lflag = 0;
187
    attr.c_cc[ VTIME ] = 0;   // timeout in tenths of a second
188
    attr.c_cc[ VMIN ] = 1;
189
 
190
    cfsetispeed( &attr, speed );
191
    cfsetospeed( &attr, speed );
192
 
193
    if ( tcsetattr( m_fd, TCSAFLUSH, &attr ) < 0 )
194
    {
195
        LogError( "Call to tcsetattr failed: %s\n", strerror( errno ));
196
        return false;
197
    }
198
 
199
    return true;
200
 
201
} // Open
202
 
203
//***************************************************************************
204
/**
205
*   Reads data from the serial port.
206
*/
207
 
208
size_t SerialPort::Read( void *buf, size_t bufSize )
209
{
210
    int  bytesRead;
211
 
212
    if ( m_fd < 0 )
213
    {
214
        return 0;
215
    }
216
 
217
    bytesRead = read( m_fd, buf, bufSize );
218
    if ( bytesRead < 0 )
219
    {
220
        if ( errno != EBADF )
221
        {
222
            // We get EBADF returned if the serial port is closed on
223
            // us.
224
 
225
            LogError( "read failed: %d\n", errno );
226
        }
227
        return 0;
228
    }
229
 
230
    return bytesRead;
231
 
232
} // Read
233
 
234
//***************************************************************************
235
/**
236
*   Sets the timeout to use when waiting for data. 0 = infinite
216 dhylands 237
*   The timeout is specified in milliseconds.
213 dhylands 238
*/
216 dhylands 239
bool SerialPort::SetTimeout( unsigned timeout )
213 dhylands 240
{
241
    struct termios  attr;
242
 
216 dhylands 243
    if ( m_fd < 0 )
244
    {
245
        return false;
246
    }
247
 
213 dhylands 248
    if ( tcgetattr( m_fd, &attr ) < 0 )
249
    {
250
        LogError( "B: Call to tcgetattr failed: %s\n", strerror( errno ));
251
        return false;
252
    }
253
 
254
    if ( timeout == 0 )
255
    {
256
        attr.c_cc[ VTIME ] = 0;
257
        attr.c_cc[ VMIN ]  = 1;
258
    }
259
    else
260
    {
216 dhylands 261
        // Note. termios only supports timeouts specified in tenths of 
262
        // a second, so we need convert from milliseconds.
263
 
264
        timeout = ( timeout + 99 ) / 100;
265
        if ( timeout > 255 )
266
        {
267
            timeout = 255;
268
        }
269
 
270
        attr.c_cc[ VTIME ] = (uint8_t)timeout;   // timeout in tenths of a second
213 dhylands 271
        attr.c_cc[ VMIN ]  = 0;
272
    }
273
 
274
    if ( tcsetattr( m_fd, TCSAFLUSH, &attr ) < 0 )
275
    {
276
        LogError( "Call to tcsetattr failed: %s\n", strerror( errno ));
277
        return false;
278
    }
279
 
280
    return true;
281
 
282
} // SetTimeout
283
 
284
//***************************************************************************
285
/**
286
*   Writes data to the serial port.
287
*/
288
 
289
size_t SerialPort::Write( const void *buf, size_t bytesToWrite )
290
{
291
    int  bytesWritten;
292
 
293
    bytesWritten = write( m_fd, buf, bytesToWrite );
294
    if ( bytesWritten < 0 )
295
    {
296
        LogError( "write failed: %d\n", errno );
297
        bytesWritten = 0;
298
    }
299
 
300
    return bytesWritten;
301
 
302
} // Write
303
 
304
//***************************************************************************
305
/**
306
*   Resets the target
307
*/
308
 
309
void SerialPort::StrobeRTS( int strobeWidthInMsec )
310
{
311
    int bits = TIOCM_RTS;
312
 
313
    // On my computer downstairs, settinging the RTS line makes it low.
314
 
315
    ioctl( m_fd, TIOCMBIS, &bits );
316
 
317
    // Sleep for 10 msec to allow the reset to take effect.
318
 
319
    usleep( strobeWidthInMsec * 1000 );
320
 
321
    // On my computer downstairs, clearing the RTS line makes it high.
322
 
323
    ioctl( m_fd, TIOCMBIC, &bits );
324
 
325
} // StrobeRTS
326
 
327
/** @} */
328