0
|
1 |
/*
|
|
2 |
* Copyright (c) 2002,2016 Mario de Sousa (msousa@fe.up.pt)
|
|
3 |
*
|
|
4 |
* This file is part of the Modbus library for Beremiz and matiec.
|
|
5 |
*
|
|
6 |
* This Modbus library is free software: you can redistribute it and/or modify
|
|
7 |
* it under the terms of the GNU Lesser General Public License as published by
|
|
8 |
* the Free Software Foundation, either version 3 of the License, or
|
|
9 |
* (at your option) any later version.
|
|
10 |
*
|
|
11 |
* This program is distributed in the hope that it will be useful, but
|
|
12 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
|
14 |
* General Public License for more details.
|
|
15 |
*
|
|
16 |
* You should have received a copy of the GNU Lesser General Public License
|
|
17 |
* along with this Modbus library. If not, see <http://www.gnu.org/licenses/>.
|
|
18 |
*
|
|
19 |
* This code is made available on the understanding that it will not be
|
|
20 |
* used in safety-critical situations without a full and competent review.
|
|
21 |
*/
|
|
22 |
|
|
23 |
|
|
24 |
|
|
25 |
#include <fcntl.h> /* File control definitions */
|
|
26 |
#include <stdio.h> /* Standard input/output */
|
|
27 |
#include <string.h>
|
|
28 |
#include <stdlib.h>
|
|
29 |
#include <termio.h> /* POSIX terminal control definitions */
|
|
30 |
#include <sys/time.h> /* Time structures for select() */
|
|
31 |
#include <unistd.h> /* POSIX Symbolic Constants */
|
|
32 |
#include <assert.h>
|
|
33 |
#include <errno.h> /* Error definitions */
|
|
34 |
#include <ctype.h>
|
|
35 |
#include <time.h> /* clock_gettime() */
|
|
36 |
#include <limits.h> /* required for INT_MAX */
|
|
37 |
|
|
38 |
#include "mb_layer1.h"
|
|
39 |
#include "mb_ascii_private.h"
|
|
40 |
|
|
41 |
|
|
42 |
/* #define DEBUG */ /* uncomment to see the data sent and received */
|
|
43 |
|
|
44 |
|
|
45 |
|
|
46 |
|
|
47 |
/************************************/
|
|
48 |
/** **/
|
|
49 |
/** Include common code... **/
|
|
50 |
/** **/
|
|
51 |
/************************************/
|
|
52 |
|
|
53 |
#include "mb_ds_util.h" /* data structures... */
|
|
54 |
#include "mb_time_util.h" /* time conversion routines... */
|
|
55 |
|
|
56 |
|
|
57 |
/**************************************************************/
|
|
58 |
/**************************************************************/
|
|
59 |
/**** ****/
|
|
60 |
/**** ****/
|
|
61 |
/**** Purpose and Formats ****/
|
|
62 |
/**** ****/
|
|
63 |
/**** ****/
|
|
64 |
/**************************************************************/
|
|
65 |
/**************************************************************/
|
|
66 |
/*
|
|
67 |
|
|
68 |
This file implements the ascii formating of the modbus protocol.
|
|
69 |
Many values, protocol related, are hardcoded into the code, as it
|
|
70 |
seems very unlikely this code will ever get re-used for anything
|
|
71 |
else but this specific protocol.
|
|
72 |
|
|
73 |
Modbus ASCII frames have no timing restrictions whatsoever, and
|
|
74 |
abide by the following format:
|
|
75 |
|
|
76 |
Header
|
|
77 |
------
|
|
78 |
size : 1 byte
|
|
79 |
value: ':' (i.e. '\0x3A')
|
|
80 |
|
|
81 |
Body
|
|
82 |
----
|
|
83 |
size : variable, multiple of 2
|
|
84 |
value: binary data converted to ascii format,
|
|
85 |
i.e. each binary data byte is converted into two ascii characters
|
|
86 |
representing the byte in hexadecimal. Allowable characters are
|
|
87 |
'0' to '9' and 'A' to 'D'
|
|
88 |
|
|
89 |
LRC
|
|
90 |
---
|
|
91 |
size : 2 bytes
|
|
92 |
value: Longitudinal Redundancy Check of data, excluding any headers, tails,
|
|
93 |
etc...
|
|
94 |
|
|
95 |
Tail
|
|
96 |
----
|
|
97 |
size : 2 bytes
|
|
98 |
value: 'CR' + 'LF' (i.e. '\0x0D' + '\0x0A')
|
|
99 |
|
|
100 |
|
|
101 |
*/
|
|
102 |
|
|
103 |
|
|
104 |
|
|
105 |
/**************************************************************/
|
|
106 |
/**************************************************************/
|
|
107 |
/**** ****/
|
|
108 |
/**** ****/
|
|
109 |
/**** Forward Declarations ****/
|
|
110 |
/**** and Defaults ****/
|
|
111 |
/**** ****/
|
|
112 |
/**************************************************************/
|
|
113 |
/**************************************************************/
|
|
114 |
|
|
115 |
|
|
116 |
typedef enum {fp_header, fp_body, fp_lrc, fp_tail, fp_done} frame_part_t;
|
|
117 |
|
|
118 |
|
|
119 |
|
|
120 |
|
|
121 |
/**************************************************************/
|
|
122 |
/**************************************************************/
|
|
123 |
/**** ****/
|
|
124 |
/**** ****/
|
|
125 |
/**** Local Utility functions... ****/
|
|
126 |
/**** ****/
|
|
127 |
/**** ****/
|
|
128 |
/**************************************************************/
|
|
129 |
/**************************************************************/
|
|
130 |
|
|
131 |
|
|
132 |
/*****************************************/
|
|
133 |
/** **/
|
|
134 |
/** lrc functions **/
|
|
135 |
/** **/
|
|
136 |
/*****************************************/
|
|
137 |
|
|
138 |
|
|
139 |
static inline void lrc_init(u8 *lrc) {
|
|
140 |
*lrc = 0;
|
|
141 |
}
|
|
142 |
|
|
143 |
static inline void lrc_add_single(u8 *lrc, u8 data) {
|
|
144 |
*lrc += data;
|
|
145 |
}
|
|
146 |
|
|
147 |
static inline void lrc_add_many(u8 *lrc, u8 *data, int count) {
|
|
148 |
for (; count > 0; count--, *lrc += *data++);
|
|
149 |
}
|
|
150 |
|
|
151 |
static inline void lrc_end(u8 *lrc) {
|
|
152 |
*lrc = 1 + ~(*lrc);
|
|
153 |
}
|
|
154 |
|
|
155 |
|
|
156 |
|
|
157 |
/**************************************/
|
|
158 |
/** **/
|
|
159 |
/** Initialise a struct termios **/
|
|
160 |
/** **/
|
|
161 |
/**************************************/
|
|
162 |
static int termios_init(struct termios *tios,
|
|
163 |
int baud,
|
|
164 |
int parity,
|
|
165 |
int data_bits,
|
|
166 |
int stop_bits) {
|
|
167 |
speed_t baud_rate;
|
|
168 |
|
|
169 |
if (tios == NULL)
|
|
170 |
return -1;
|
|
171 |
|
|
172 |
/* reset all the values... */
|
|
173 |
/* NOTE: the following are initialised later on...
|
|
174 |
tios->c_iflag = 0;
|
|
175 |
tios->c_oflag = 0;
|
|
176 |
tios->c_cflag = 0;
|
|
177 |
tios->c_lflag = 0;
|
|
178 |
*/
|
|
179 |
tios->c_line = 0;
|
|
180 |
|
|
181 |
/* The minimum number of characters that should be received
|
|
182 |
* to satisfy a call to read().
|
|
183 |
*/
|
|
184 |
tios->c_cc[VMIN ] = 0;
|
|
185 |
|
|
186 |
/* The maximum inter-arrival interval between two characters,
|
|
187 |
* in deciseconds.
|
|
188 |
*
|
|
189 |
* NOTE: we could use this to detect the end of RTU frames,
|
|
190 |
* but we prefer to use select() that has higher resolution,
|
|
191 |
* even though this higher resolution is most probably not
|
|
192 |
* supported, and the effective resolution is 10ms,
|
|
193 |
* one tenth of a decisecond.
|
|
194 |
*/
|
|
195 |
tios->c_cc[VTIME] = 0;
|
|
196 |
|
|
197 |
/* configure the input modes... */
|
|
198 |
tios->c_iflag = IGNBRK | /* ignore BREAK condition on input */
|
|
199 |
IGNPAR | /* ignore framing errors and parity errors */
|
|
200 |
IXANY; /* enable any character to restart output */
|
|
201 |
/* BRKINT Only active if IGNBRK is not set.
|
|
202 |
* generate SIGINT on BREAK condition,
|
|
203 |
* otherwise read BREAK as character \0.
|
|
204 |
* PARMRK Only active if IGNPAR is not set.
|
|
205 |
* replace bytes with parity errors with
|
|
206 |
* \377 \0, instead of \0.
|
|
207 |
* INPCK enable input parity checking
|
|
208 |
* ISTRIP strip off eighth bit
|
|
209 |
* IGNCR ignore carriage return on input
|
|
210 |
* INLCR only active if IGNCR is not set.
|
|
211 |
* translate newline to carriage return on input
|
|
212 |
* ICRNL only active if IGNCR is not set.
|
|
213 |
* translate carriage return to newline on input
|
|
214 |
* IUCLC map uppercase characters to lowercase on input
|
|
215 |
* IXON enable XON/XOFF flow control on output
|
|
216 |
* IXOFF enable XON/XOFF flow control on input
|
|
217 |
* IMAXBEL ring bell when input queue is full
|
|
218 |
*/
|
|
219 |
|
|
220 |
/* configure the output modes... */
|
|
221 |
tios->c_oflag = OPOST; /* enable implementation-defined output processing */
|
|
222 |
/* ONOCR don't output CR at column 0
|
|
223 |
* OLCUC map lowercase characters to uppercase on output
|
|
224 |
* ONLCR map NL to CR-NL on output
|
|
225 |
* OCRNL map CR to NL on output
|
|
226 |
* OFILL send fill characters for a delay, rather than
|
|
227 |
* using a timed delay
|
|
228 |
* OFDEL fill character is ASCII DEL. If unset, fill
|
|
229 |
* character is ASCII NUL
|
|
230 |
* ONLRET don't output CR
|
|
231 |
* NLDLY NL delay mask. Values are NL0 and NL1.
|
|
232 |
* CRDLY CR delay mask. Values are CR0, CR1, CR2, or CR3.
|
|
233 |
* TABDLY horizontal tab delay mask. Values are TAB0, TAB1,
|
|
234 |
* TAB2, TAB3, or XTABS. A value of XTABS expands
|
|
235 |
* tabs to spaces (with tab stops every eight columns).
|
|
236 |
* BSDLY backspace delay mask. Values are BS0 or BS1.
|
|
237 |
* VTDLY vertical tab delay mask. Values are VT0 or VT1.
|
|
238 |
* FFDLY form feed delay mask. Values are FF0 or FF1.
|
|
239 |
*/
|
|
240 |
|
|
241 |
/* configure the control modes... */
|
|
242 |
tios->c_cflag = CREAD | /* enable receiver. */
|
|
243 |
CLOCAL; /* ignore modem control lines */
|
|
244 |
/* HUPCL lower modem control lines after last process
|
|
245 |
* closes the device (hang up).
|
|
246 |
* CRTSCTS flow control (Request/Clear To Send).
|
|
247 |
*/
|
|
248 |
if (data_bits == 5) tios->c_cflag |= CS5;
|
|
249 |
else if (data_bits == 6) tios->c_cflag |= CS6;
|
|
250 |
else if (data_bits == 7) tios->c_cflag |= CS7;
|
|
251 |
else if (data_bits == 8) tios->c_cflag |= CS8;
|
|
252 |
else return -1;
|
|
253 |
|
|
254 |
if (stop_bits == 1) tios->c_cflag &=~ CSTOPB;
|
|
255 |
else if (stop_bits == 2) tios->c_cflag |= CSTOPB;
|
|
256 |
else return -1;
|
|
257 |
|
|
258 |
if(parity == 0) { /* none */
|
|
259 |
tios->c_cflag &=~ PARENB;
|
|
260 |
tios->c_cflag &=~ PARODD;
|
|
261 |
} else if(parity == 2) { /* even */
|
|
262 |
tios->c_cflag |= PARENB;
|
|
263 |
tios->c_cflag &=~ PARODD;
|
|
264 |
} else if(parity == 1) { /* odd */
|
|
265 |
tios->c_cflag |= PARENB;
|
|
266 |
tios->c_cflag |= PARODD;
|
|
267 |
} else return -1;
|
|
268 |
|
|
269 |
|
|
270 |
/* configure the local modes... */
|
|
271 |
tios->c_lflag = IEXTEN; /* enable implementation-defined input processing */
|
|
272 |
/* ISIG when any of the characters INTR, QUIT, SUSP, or DSUSP
|
|
273 |
* are received, generate the corresponding signal.
|
|
274 |
* ICANON enable canonical mode. This enables the special
|
|
275 |
* characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
|
|
276 |
* STATUS, and WERASE, and buffers by lines.
|
|
277 |
* ECHO echo input characters.
|
|
278 |
*/
|
|
279 |
|
|
280 |
/* Set the baud rate */
|
|
281 |
/* Must be done before reseting all the values to 0! */
|
|
282 |
switch(baud) {
|
|
283 |
case 110: baud_rate = B110; break;
|
|
284 |
case 300: baud_rate = B300; break;
|
|
285 |
case 600: baud_rate = B600; break;
|
|
286 |
case 1200: baud_rate = B1200; break;
|
|
287 |
case 2400: baud_rate = B2400; break;
|
|
288 |
case 4800: baud_rate = B4800; break;
|
|
289 |
case 9600: baud_rate = B9600; break;
|
|
290 |
case 19200: baud_rate = B19200; break;
|
|
291 |
case 38400: baud_rate = B38400; break;
|
|
292 |
case 57600: baud_rate = B57600; break;
|
|
293 |
case 115200: baud_rate = B115200; break;
|
|
294 |
default: return -1;
|
|
295 |
} /* switch() */
|
|
296 |
|
|
297 |
if ((cfsetispeed(tios, baud_rate) < 0) ||
|
|
298 |
(cfsetospeed(tios, baud_rate) < 0))
|
|
299 |
return -1;;
|
|
300 |
|
|
301 |
return 0;
|
|
302 |
}
|
|
303 |
|
|
304 |
|
|
305 |
|
|
306 |
|
|
307 |
|
|
308 |
/*****************************************/
|
|
309 |
/** **/
|
|
310 |
/** u8/ascii conversion functions **/
|
|
311 |
/** **/
|
|
312 |
/*****************************************/
|
|
313 |
|
|
314 |
/* Convert binary data to ascii format.
|
|
315 |
* Both xxx_data_lengths specify the available bytes in
|
|
316 |
* in the respective arrays.
|
|
317 |
*/
|
|
318 |
/* NOTE: this function is only called from a single location
|
|
319 |
* so we might just as well make it inline...
|
|
320 |
*/
|
|
321 |
static inline int bin_2_asc(u8 *bin_data, int bin_data_length,
|
|
322 |
u8 *asc_data, int asc_data_length) {
|
|
323 |
u8 nibble;
|
|
324 |
int count = 0;
|
|
325 |
u8 *last_bin_data = bin_data + bin_data_length;
|
|
326 |
/* we need L2_TO_ASC_CODING ascii bytes for each bin byte, therefore the
|
|
327 |
* '- (L2_TO_ASC_CODING - 1)'
|
|
328 |
*/
|
|
329 |
u8 *last_asc_data = asc_data + asc_data_length - (L2_TO_ASC_CODING - 1);
|
|
330 |
|
|
331 |
while ((bin_data < last_bin_data) &&
|
|
332 |
(asc_data < last_asc_data)) {
|
|
333 |
|
|
334 |
nibble = (*bin_data & 0xF0) >> 4;
|
|
335 |
*(asc_data++) = (nibble <= 9)?nibble + '0':nibble - 10 + 'A';
|
|
336 |
|
|
337 |
nibble = (*bin_data & 0x0F);
|
|
338 |
*(asc_data++) = (nibble <= 9)?nibble + '0':nibble - 10 + 'A';
|
|
339 |
|
|
340 |
count++;
|
|
341 |
bin_data++;
|
|
342 |
}
|
|
343 |
|
|
344 |
/* return number of bytes converted... */
|
|
345 |
return count;
|
|
346 |
}
|
|
347 |
|
|
348 |
|
|
349 |
/* Convert from lowercase to upper case. */
|
|
350 |
/* It probably does not make sense calling the generic toupper()
|
|
351 |
* whose functionality depends on the current locale.
|
|
352 |
* Our own specific function is most probably much faster...
|
|
353 |
*/
|
|
354 |
static inline u8 local_toupper(u8 val) {
|
|
355 |
if ((val >= 'a') && (val <= 'z'))
|
|
356 |
return val - 'a' + 'A';
|
|
357 |
return val;
|
|
358 |
}
|
|
359 |
|
|
360 |
/* Convert ascii data to bin format.
|
|
361 |
* *asc_data must be a two byte array.
|
|
362 |
*
|
|
363 |
* If a non-ascii character is found, returns -1
|
|
364 |
*/
|
|
365 |
/* NOTE: this function is only called from a single location
|
|
366 |
* so we might just as well make it inline...
|
|
367 |
*/
|
|
368 |
static inline int asc_2_bin(u8 *asc_data, u8 *bin_data) {
|
|
369 |
if ((isxdigit(asc_data[0]) == 0) ||
|
|
370 |
(isxdigit(asc_data[1]) == 0))
|
|
371 |
return -1;
|
|
372 |
|
|
373 |
asc_data[0] = local_toupper(asc_data[0]);
|
|
374 |
asc_data[1] = local_toupper(asc_data[1]);
|
|
375 |
|
|
376 |
/* hi */ *(bin_data) = ((asc_data[0] <= '9')?
|
|
377 |
(asc_data[0] - '0'):(asc_data[0] - 'A' + 10)) * 0x10;
|
|
378 |
/* lo */ *(bin_data) += (asc_data[1] <= '9')?
|
|
379 |
(asc_data[1] - '0'):(asc_data[1] - 'A' + 10);
|
|
380 |
|
|
381 |
return 0;
|
|
382 |
}
|
|
383 |
|
|
384 |
|
|
385 |
|
|
386 |
|
|
387 |
/************************************/
|
|
388 |
/** **/
|
|
389 |
/** A data structure - send buffer **/
|
|
390 |
/** **/
|
|
391 |
/************************************/
|
|
392 |
|
|
393 |
/* data structure used to store the data to be sent in ascii format. */
|
|
394 |
/* The binary data is converted into ascii format before transmission. The
|
|
395 |
* frame is not converted as a single whole, but is rather done in chunks.
|
|
396 |
* The size of the chunks depends on the data size of the send_buffer.
|
|
397 |
*
|
|
398 |
* A lrc variable keeps a tab on the current value of the lrc as the data
|
|
399 |
* is being converted.
|
|
400 |
*
|
|
401 |
* Three special functions add the header, lrc and tail to the ascii frame.
|
|
402 |
*/
|
|
403 |
|
|
404 |
/* NOTE: The algorithms in the insert functions require a minimum buffer
|
|
405 |
* size to work correctly...
|
|
406 |
*/
|
|
407 |
#define SEND_BUF_MIN_LENGTH ASC_FRAME_MIN_ELE_LENGTH
|
|
408 |
|
|
409 |
typedef struct {
|
|
410 |
lb_buf_t data_buf;
|
|
411 |
|
|
412 |
u8 lrc; /* the current value of the lrc, in binary format */
|
|
413 |
} send_buf_t;
|
|
414 |
|
|
415 |
/* A small auxiliary function... */
|
|
416 |
static inline u8 *send_buf_init(send_buf_t *buf, int size, int max_data_start) {
|
|
417 |
/* The algorithms in other functions require a minimum size
|
|
418 |
* to work correctly...
|
|
419 |
*/
|
|
420 |
if (size < SEND_BUF_MIN_LENGTH)
|
|
421 |
return NULL;
|
|
422 |
|
|
423 |
lrc_init(&buf->lrc);
|
|
424 |
return lb_init(&buf->data_buf, size, max_data_start);
|
|
425 |
}
|
|
426 |
|
|
427 |
/* A small auxiliary function... */
|
|
428 |
static inline void send_buf_done(send_buf_t *buf) {
|
|
429 |
lb_done(&buf->data_buf);
|
|
430 |
}
|
|
431 |
|
|
432 |
/* A small auxiliary function... */
|
|
433 |
static inline void send_buf_reset(send_buf_t *buf) {
|
|
434 |
lrc_init(&buf->lrc);
|
|
435 |
lb_data_purge_all(&buf->data_buf);
|
|
436 |
}
|
|
437 |
|
|
438 |
/* A small auxiliary function... */
|
|
439 |
static inline int send_buf_data_count(send_buf_t *buf) {
|
|
440 |
return lb_data_count(&buf->data_buf);
|
|
441 |
}
|
|
442 |
|
|
443 |
/* A small auxiliary function... */
|
|
444 |
static inline int send_buf_free_count(send_buf_t *buf) {
|
|
445 |
return lb_free_count(&buf->data_buf);
|
|
446 |
}
|
|
447 |
/* A small auxiliary function... */
|
|
448 |
static inline u8 *send_buf_data(send_buf_t *buf) {
|
|
449 |
return lb_data(&buf->data_buf);
|
|
450 |
}
|
|
451 |
|
|
452 |
/* A small auxiliary function... */
|
|
453 |
static inline u8 *send_buf_free(send_buf_t *buf) {
|
|
454 |
return lb_free(&buf->data_buf);
|
|
455 |
}
|
|
456 |
|
|
457 |
/* A small auxiliary function... */
|
|
458 |
static inline int send_buf_data_add(send_buf_t *buf, u8 *data, int data_count) {
|
|
459 |
int res = bin_2_asc(data, data_count, send_buf_free(buf), send_buf_free_count(buf));
|
|
460 |
if (res <=0) return res;
|
|
461 |
lb_data_add(&buf->data_buf, L2_TO_ASC_CODING * res);
|
|
462 |
lrc_add_many(&buf->lrc, data, res);
|
|
463 |
return res;
|
|
464 |
}
|
|
465 |
|
|
466 |
/* A small auxiliary function... */
|
|
467 |
static inline void send_buf_data_purge(send_buf_t *buf, int count) {
|
|
468 |
lb_data_purge(&buf->data_buf, count);
|
|
469 |
}
|
|
470 |
|
|
471 |
/* A small auxiliary function... */
|
|
472 |
static inline void send_buf_data_purge_all(send_buf_t *buf) {
|
|
473 |
lb_data_purge_all(&buf->data_buf);
|
|
474 |
}
|
|
475 |
|
|
476 |
/* A small auxiliary function... */
|
|
477 |
static inline int send_buf_lrc_append(send_buf_t *buf) {
|
|
478 |
#if ASC_FRAME_LRC_LENGTH != 2
|
|
479 |
#error Code assumes LRC length of 2 bytes, but ASC_FRAME_LRC_LENGTH != 2
|
|
480 |
#endif
|
|
481 |
if (lb_free_count(&buf->data_buf) < ASC_FRAME_LRC_LENGTH)
|
|
482 |
return -1;
|
|
483 |
lrc_end(&buf->lrc);
|
|
484 |
bin_2_asc(&buf->lrc, sizeof(buf->lrc),
|
|
485 |
lb_free(&buf->data_buf), ASC_FRAME_LRC_LENGTH);
|
|
486 |
lb_data_add(&buf->data_buf, ASC_FRAME_LRC_LENGTH);
|
|
487 |
return 0;
|
|
488 |
}
|
|
489 |
|
|
490 |
static inline int send_buf_header_append(send_buf_t *buf) {
|
|
491 |
#if ASC_FRAME_HEADER_LENGTH != 1
|
|
492 |
#error Code assumes HEADER length of 1 bytes, but ASC_FRAME_HEADER_LENGTH != 1
|
|
493 |
#endif
|
|
494 |
if (lb_free_count(&buf->data_buf) < ASC_FRAME_HEADER_LENGTH)
|
|
495 |
return -1;
|
|
496 |
|
|
497 |
/* add the ':' frame header */
|
|
498 |
*lb_free(&buf->data_buf) = ASC_FRAME_HEADER;
|
|
499 |
lb_data_add(&buf->data_buf, ASC_FRAME_HEADER_LENGTH);
|
|
500 |
|
|
501 |
return 0;
|
|
502 |
}
|
|
503 |
|
|
504 |
static inline int send_buf_tail_append(send_buf_t *buf) {
|
|
505 |
#if ASC_FRAME_TAIL_LENGTH != 2
|
|
506 |
#error Code assumes TAIL length of 2 bytes, but ASC_FRAME_TAIL_LENGTH != 2
|
|
507 |
#endif
|
|
508 |
if (lb_free_count(&buf->data_buf) < ASC_FRAME_TAIL_LENGTH)
|
|
509 |
return -1;
|
|
510 |
|
|
511 |
/* add the CR+LF frame delimiter */
|
|
512 |
lb_free(&buf->data_buf)[0] = ASC_FRAME_TAIL_0;
|
|
513 |
lb_free(&buf->data_buf)[1] = ASC_FRAME_TAIL_1;
|
|
514 |
lb_data_add(&buf->data_buf, ASC_FRAME_TAIL_LENGTH);
|
|
515 |
|
|
516 |
return 0;
|
|
517 |
}
|
|
518 |
|
|
519 |
|
|
520 |
|
|
521 |
|
|
522 |
/************************************/
|
|
523 |
/** **/
|
|
524 |
/** A data structure - recv buffer **/
|
|
525 |
/** **/
|
|
526 |
/************************************/
|
|
527 |
|
|
528 |
/* data structure used to store the data received from the bus. */
|
|
529 |
|
|
530 |
/* The ascii data received from the bus is added to the buffer, and is
|
|
531 |
* dynamically converted to binary format. Once a valid frame has been
|
|
532 |
* converted, conversion stops until this valid frame is deleted/purged.
|
|
533 |
*/
|
|
534 |
|
|
535 |
/* NOTE: The algorithms in the insert functions require a minimum buffer
|
|
536 |
* size to work correctly...
|
|
537 |
*/
|
|
538 |
#define RECV_BUF_MIN_LENGTH ASC_FRAME_MIN_ELE_LENGTH
|
|
539 |
|
|
540 |
#define RECV_BUF_BIN_BUF_SIZE (MAX_L2_FRAME_LENGTH + \
|
|
541 |
ASC_FRAME_LRC_LENGTH / L2_TO_ASC_CODING)
|
|
542 |
|
|
543 |
typedef struct {
|
|
544 |
lb_buf_t asc_data_buf;
|
|
545 |
u8 bin_data_buf[RECV_BUF_BIN_BUF_SIZE];
|
|
546 |
int bin_data_free_ofs;
|
|
547 |
|
|
548 |
frame_part_t frame_part;
|
|
549 |
u8 lrc; /* the current value of the lrc, in binary format */
|
|
550 |
u8 lrc_1; /* the previous value of the lrc... */
|
|
551 |
/* NOTE: We do a running conversion between ascii and binary format,
|
|
552 |
* i.e. we start converting from ascii to binary before we
|
|
553 |
* have received the complete ascii frame. This means that
|
|
554 |
* we also do a running computation of our local version of
|
|
555 |
* the frame lrc.
|
|
556 |
* The lrc, transmitted at the end of the ascii frame,
|
|
557 |
* but before the frame tail, also gets converted to binary
|
|
558 |
* before we get a chance to realize that it is the lrc value,
|
|
559 |
* and should therefore not be taken into account when computing
|
|
560 |
* our local version of the lrc.
|
|
561 |
* So we keep the previous value of the running lrc, and use
|
|
562 |
* that to confirm whether we have a valid frame.
|
|
563 |
*/
|
|
564 |
} recv_buf_t;
|
|
565 |
|
|
566 |
/* A small auxiliary function... */
|
|
567 |
static inline u8 *recv_buf_init(recv_buf_t *buf, int size, int max_data_start) {
|
|
568 |
/* The algorithms in other functions require a minimum size
|
|
569 |
* to work correctly...
|
|
570 |
*/
|
|
571 |
if (size < RECV_BUF_MIN_LENGTH)
|
|
572 |
return NULL;
|
|
573 |
|
|
574 |
lrc_init(&buf->lrc);
|
|
575 |
buf->bin_data_free_ofs = 0;
|
|
576 |
buf->frame_part = fp_header;
|
|
577 |
return lb_init(&buf->asc_data_buf, size, max_data_start);
|
|
578 |
}
|
|
579 |
|
|
580 |
/* A small auxiliary function... */
|
|
581 |
static inline void recv_buf_done(recv_buf_t *buf) {
|
|
582 |
lb_done(&buf->asc_data_buf);
|
|
583 |
}
|
|
584 |
|
|
585 |
/* A small auxiliary function... */
|
|
586 |
static inline void recv_buf_reset(recv_buf_t *buf) {
|
|
587 |
lrc_init(&buf->lrc);
|
|
588 |
buf->bin_data_free_ofs = 0;
|
|
589 |
buf->frame_part = fp_header;
|
|
590 |
lb_data_purge_all(&buf->asc_data_buf);
|
|
591 |
}
|
|
592 |
|
|
593 |
/* A small auxiliary function... */
|
|
594 |
static inline u8 *recv_buf_data(recv_buf_t *buf) {
|
|
595 |
return lb_data(&buf->asc_data_buf);
|
|
596 |
}
|
|
597 |
|
|
598 |
/* A small auxiliary function... */
|
|
599 |
static inline u8 *recv_buf_free(recv_buf_t *buf) {
|
|
600 |
return lb_free(&buf->asc_data_buf);
|
|
601 |
}
|
|
602 |
|
|
603 |
/* The function that really does all the conversion work... */
|
|
604 |
/* It finds frame limits, converts the data into binary format,
|
|
605 |
* and checks for correct lrc in the received frame.
|
|
606 |
*/
|
|
607 |
/* NOTE: called indirectly from various locations! Do NOT inline! */
|
|
608 |
static void recv_buf_data_parse(recv_buf_t *buf) {
|
|
609 |
int count;
|
|
610 |
u8 *data;
|
|
611 |
|
|
612 |
data = lb_data(&buf->asc_data_buf);
|
|
613 |
count = lb_data_count(&buf->asc_data_buf);
|
|
614 |
|
|
615 |
/* NOTE: We need at least ASC_FRAME_MIN_ELE_LENGTH bytes to
|
|
616 |
* to be able to find that element of minimum length
|
|
617 |
*/
|
|
618 |
while ((count >= ASC_FRAME_MIN_ELE_LENGTH) && (buf->frame_part != fp_done)) {
|
|
619 |
/* Check for frame header... */
|
|
620 |
/* The following few lines of code assume that ASC_FRAME_HEADER_LENGTH is 1! */
|
|
621 |
#if ASC_FRAME_HEADER_LENGTH != 1
|
|
622 |
#error The code is written in such a way that can only handle ASC_FRAME_HEADER_LENGTH == 1
|
|
623 |
#endif
|
|
624 |
if (data[0] == ASC_FRAME_HEADER) {
|
|
625 |
/* found the beginning of a frame...
|
|
626 |
* Even if we were previously converting a frame without errors,
|
|
627 |
* if we receive a new frame header we discard the previous
|
|
628 |
* frame that went unfinished!
|
|
629 |
*/
|
|
630 |
data += ASC_FRAME_HEADER_LENGTH;
|
|
631 |
count -= ASC_FRAME_HEADER_LENGTH;
|
|
632 |
buf->frame_part = fp_body;
|
|
633 |
lrc_init(&buf->lrc);
|
|
634 |
buf->bin_data_free_ofs = 0;
|
|
635 |
continue;
|
|
636 |
}
|
|
637 |
|
|
638 |
/* Check for frame tail... */
|
|
639 |
/*
|
|
640 |
* Note that the while() condition guarantees that we have at least
|
|
641 |
* two ascii bytes to handle.
|
|
642 |
*/
|
|
643 |
/* The following few lines of code assume that ASC_FRAME_TAIL_LENGTH is 2! */
|
|
644 |
#if ASC_FRAME_TAIL_LENGTH != 2
|
|
645 |
#error The code is written in such a way that can only handle ASC_FRAME_TAIL_LENGTH == 2
|
|
646 |
#endif
|
|
647 |
if ((data[0] == ASC_FRAME_TAIL_0) &&
|
|
648 |
(data[1] == ASC_FRAME_TAIL_1)) {
|
|
649 |
/* end of binary data... */
|
|
650 |
data += ASC_FRAME_TAIL_LENGTH;
|
|
651 |
count -= ASC_FRAME_TAIL_LENGTH;
|
|
652 |
|
|
653 |
/* let's check the lrc... */
|
|
654 |
if (buf->bin_data_free_ofs <= 0)
|
|
655 |
/* we have reached the end of a frame that did not include
|
|
656 |
* any binary data, not even the lrc value itself!
|
|
657 |
*/
|
|
658 |
goto frame_error;
|
|
659 |
|
|
660 |
/* Remember that we do not use the most recent lrc value, as this
|
|
661 |
* incorrectly includes the ascii bytes in the frame that code the
|
|
662 |
* frame's lrc. (pls read the note in the recv_but_t typedef)
|
|
663 |
*/
|
|
664 |
/* The following few lines of code assume that
|
|
665 |
* (ASC_FRAME_LRC_LENGTH / L2_TO_ASC_CODING) is 1!
|
|
666 |
*/
|
|
667 |
#if L2_TO_ASC_CODING != ASC_FRAME_LRC_LENGTH
|
|
668 |
#error The code is written in such a way that can only handle L2_TO_ASC_CODING == ASC_FRAME_LRC_LENGTH
|
|
669 |
#endif
|
|
670 |
lrc_end(&(buf->lrc_1));
|
|
671 |
if (buf->lrc_1 == buf->bin_data_buf[buf->bin_data_free_ofs-1]) {
|
|
672 |
/* we have received a correct frame... */
|
|
673 |
buf->frame_part = fp_done;
|
|
674 |
continue;
|
|
675 |
} else {
|
|
676 |
/* we have found a frame with an lrc error */
|
|
677 |
goto frame_error;
|
|
678 |
}
|
|
679 |
}
|
|
680 |
|
|
681 |
if (buf->frame_part == fp_header) {
|
|
682 |
/* we are searching for beginning of a frame...
|
|
683 |
* but we did not find it in the previous condition!
|
|
684 |
* We continue looking in the next ascii byte
|
|
685 |
*/
|
|
686 |
data++;
|
|
687 |
count--;
|
|
688 |
continue;
|
|
689 |
}
|
|
690 |
|
|
691 |
if (buf->frame_part == fp_body) {
|
|
692 |
/* we have previously found the beginning of a frame,
|
|
693 |
* and are now converting the body into binary format...
|
|
694 |
*
|
|
695 |
* Note that the while() condition guarantees that we have at least
|
|
696 |
* two ascii bytes to convert into binary format.
|
|
697 |
*/
|
|
698 |
|
|
699 |
/* this is normal data... let's convert... */
|
|
700 |
if (asc_2_bin(data, buf->bin_data_buf + buf->bin_data_free_ofs) < 0)
|
|
701 |
/* error converting from ascii to binary.
|
|
702 |
* This must be due to an invalid ascii character in the ascii data.
|
|
703 |
* We discard the current frame...
|
|
704 |
*
|
|
705 |
* Note that we *do not* increment the data pointer,
|
|
706 |
* nor do we decrement the count variable. One of the ascii bytes could
|
|
707 |
* be the begining of a new valid frame, and we don't want to skip it!
|
|
708 |
*/
|
|
709 |
goto frame_error;
|
|
710 |
|
|
711 |
buf->lrc_1 = buf->lrc;
|
|
712 |
lrc_add_single(&(buf->lrc), *(buf->bin_data_buf + buf->bin_data_free_ofs));
|
|
713 |
|
|
714 |
data += L2_TO_ASC_CODING;
|
|
715 |
count -= L2_TO_ASC_CODING;
|
|
716 |
if (++(buf->bin_data_free_ofs) >= RECV_BUF_BIN_BUF_SIZE)
|
|
717 |
/* Whoops, this shouldn't be hapening!
|
|
718 |
* The frame we are receiving is larger than the alocated buffer!
|
|
719 |
* Our only alternative is to discard the frame
|
|
720 |
* we are currently receiving...
|
|
721 |
*/
|
|
722 |
goto frame_error;
|
|
723 |
|
|
724 |
continue;
|
|
725 |
}
|
|
726 |
|
|
727 |
/* if none of the above, then it must be some transmission error */
|
|
728 |
/* Actually, the code will never fall through since if we are in this loop,
|
|
729 |
* (frame_part == header) || (frame_part == body) is always true!
|
|
730 |
*/
|
|
731 |
data++;
|
|
732 |
count--;
|
|
733 |
frame_error:
|
|
734 |
lrc_init(&buf->lrc);
|
|
735 |
buf->bin_data_free_ofs = 0;
|
|
736 |
buf->frame_part = fp_header;
|
|
737 |
} /* while () */
|
|
738 |
|
|
739 |
lb_data_purge(&buf->asc_data_buf, lb_data_count(&buf->asc_data_buf) - count);
|
|
740 |
}
|
|
741 |
|
|
742 |
/* A small auxiliary function... */
|
|
743 |
static inline void recv_buf_search_frame(recv_buf_t *buf) {
|
|
744 |
if (buf->frame_part != fp_done)
|
|
745 |
recv_buf_data_parse(buf);
|
|
746 |
}
|
|
747 |
|
|
748 |
/* add ascii format data to buffer */
|
|
749 |
static inline void recv_buf_data_add(recv_buf_t *buf, int count) {
|
|
750 |
lb_data_add(&buf->asc_data_buf, count);
|
|
751 |
}
|
|
752 |
|
|
753 |
/* A small auxiliary function... */
|
|
754 |
static inline int recv_buf_data_count(recv_buf_t *buf) {
|
|
755 |
return lb_data_count(&buf->asc_data_buf);
|
|
756 |
}
|
|
757 |
|
|
758 |
/* A small auxiliary function... */
|
|
759 |
static inline int recv_buf_free_count(recv_buf_t *buf) {
|
|
760 |
return lb_free_count(&buf->asc_data_buf);
|
|
761 |
}
|
|
762 |
|
|
763 |
/* Return pointer to frame, if a valid frame is available */
|
|
764 |
static inline u8 *recv_buf_frame(recv_buf_t *buf) {
|
|
765 |
recv_buf_search_frame(buf);
|
|
766 |
if (buf->frame_part == fp_done)
|
|
767 |
/* we have found a frame...! */
|
|
768 |
return buf->bin_data_buf;
|
|
769 |
|
|
770 |
/* no frame... */
|
|
771 |
return NULL;
|
|
772 |
}
|
|
773 |
|
|
774 |
/* Return number of bytes in frame, if a valid frame is available */
|
|
775 |
static inline int recv_buf_frame_count(recv_buf_t *buf) {
|
|
776 |
recv_buf_search_frame(buf);
|
|
777 |
if (buf->frame_part == fp_done)
|
|
778 |
/* we have found a frame...! */
|
|
779 |
return buf->bin_data_free_ofs - ASC_FRAME_LRC_LENGTH/L2_TO_ASC_CODING;
|
|
780 |
|
|
781 |
/* no frame... */
|
|
782 |
return -1;
|
|
783 |
}
|
|
784 |
|
|
785 |
/* Delete valid binary format frame! */
|
|
786 |
static inline void recv_buf_frame_purge(recv_buf_t *buf) {
|
|
787 |
/* NOTE: we only delete valid frames!!
|
|
788 |
* Partially converted frames are not deleted, the
|
|
789 |
* remaining bytes may be received later!
|
|
790 |
*/
|
|
791 |
if (buf->frame_part == fp_done) {
|
|
792 |
buf->frame_part = fp_header;
|
|
793 |
buf->bin_data_free_ofs = 0;
|
|
794 |
}
|
|
795 |
}
|
|
796 |
|
|
797 |
|
|
798 |
|
|
799 |
/************************************/
|
|
800 |
/** **/
|
|
801 |
/** A data structure - nd entry **/
|
|
802 |
/** **/
|
|
803 |
/************************************/
|
|
804 |
|
|
805 |
/* NOTE: nd = node descriptor */
|
|
806 |
|
|
807 |
typedef struct {
|
|
808 |
/* The file descriptor associated with this node */
|
|
809 |
/* NOTE: if the node is not yet in use, i.e. if the node is free,
|
|
810 |
* then fd will be set to -1
|
|
811 |
*/
|
|
812 |
int fd;
|
|
813 |
struct timeval time_15_char_;
|
|
814 |
|
|
815 |
/* Modbus ascii frames are delimited by a ':' (colon) at the begining of
|
|
816 |
* a frame, and CR-LF sequence at the end the frame.
|
|
817 |
*
|
|
818 |
* Unless we want to take 'ages' reading the data off the serial line
|
|
819 |
* one byte at a time, we risk reading beyond the boundary of the
|
|
820 |
* frame currently being interpreted. The extra data belongs to the
|
|
821 |
* subsequent frame, and must therefore be buffered to be handled
|
|
822 |
* when the next frame is being interpreted.
|
|
823 |
*
|
|
824 |
* The receive buffer is therefore a static variable.
|
|
825 |
*/
|
|
826 |
recv_buf_t recv_buf_;
|
|
827 |
|
|
828 |
/* The send ascii buffer could be a local function variable
|
|
829 |
* instead of a static variable, but we might just as well
|
|
830 |
* allocate the memory at startup and not risk running out
|
|
831 |
* of memory while the module is running.
|
|
832 |
*/
|
|
833 |
send_buf_t send_buf_;
|
|
834 |
|
|
835 |
/* The old settings of the serial port, to be reset when the library is closed... */
|
|
836 |
struct termios old_tty_settings_;
|
|
837 |
|
|
838 |
/* ignore echo flag.
|
|
839 |
* If set to 1, then it means that we will be reading every byte we
|
|
840 |
* ourselves write out to the bus, so we must ignore those bytes read
|
|
841 |
* before we really read the data sent by remote nodes.
|
|
842 |
*
|
|
843 |
* This comes in useful when using a RS232-RS485 converter that does
|
|
844 |
* not correctly control the RTS-CTS lines...
|
|
845 |
*/
|
|
846 |
int ignore_echo;
|
|
847 |
} nd_entry_t;
|
|
848 |
|
|
849 |
|
|
850 |
static inline void nd_entry_init(nd_entry_t *nde) {
|
|
851 |
nde->fd = -1; /* The node is free... */
|
|
852 |
}
|
|
853 |
|
|
854 |
static int nd_entry_connect(nd_entry_t *nde,
|
|
855 |
node_addr_t *node_addr,
|
|
856 |
optimization_t opt) {
|
|
857 |
|
|
858 |
int parity_bits, start_bits, char_bits;
|
|
859 |
struct termios settings;
|
|
860 |
int buf_size;
|
|
861 |
|
|
862 |
/*
|
|
863 |
if (nde == NULL)
|
|
864 |
goto error_exit_0;
|
|
865 |
*/
|
|
866 |
if (nde->fd >= 0)
|
|
867 |
goto error_exit_0;
|
|
868 |
|
|
869 |
/* initialise the termios data structure */
|
|
870 |
if (termios_init(&settings,
|
|
871 |
node_addr->addr.ascii.baud,
|
|
872 |
node_addr->addr.ascii.parity,
|
|
873 |
node_addr->addr.ascii.data_bits,
|
|
874 |
node_addr->addr.ascii.stop_bits)
|
|
875 |
< 0) {
|
|
876 |
#ifdef DEBUG
|
|
877 |
fprintf(stderr, "Invalid serial line settings"
|
|
878 |
"(baud=%d, parity=%d, data_bits=%d, stop_bits=%d)",
|
|
879 |
node_addr->addr.ascii.baud,
|
|
880 |
node_addr->addr.ascii.parity,
|
|
881 |
node_addr->addr.ascii.data_bits,
|
|
882 |
node_addr->addr.ascii.stop_bits);
|
|
883 |
#endif
|
|
884 |
goto error_exit_1;
|
|
885 |
}
|
|
886 |
|
|
887 |
/* set the ignore_echo flag */
|
|
888 |
nde->ignore_echo = node_addr->addr.ascii.ignore_echo;
|
|
889 |
|
|
890 |
/* initialise send buffer */
|
|
891 |
buf_size = (opt == optimize_size)?SEND_BUFFER_SIZE_SMALL:
|
|
892 |
SEND_BUFFER_SIZE_LARGE;
|
|
893 |
if (send_buf_init(&nde->send_buf_, buf_size, buf_size - SEND_BUF_MIN_LENGTH)
|
|
894 |
== NULL) {
|
|
895 |
#ifdef DEBUG
|
|
896 |
fprintf(stderr, "Out of memory: error initializing send buffer");
|
|
897 |
#endif
|
|
898 |
goto error_exit_1;
|
|
899 |
}
|
|
900 |
|
|
901 |
/* initialise recv buffer */
|
|
902 |
buf_size = (opt == optimize_size)?RECV_BUFFER_SIZE_SMALL:
|
|
903 |
RECV_BUFFER_SIZE_LARGE;
|
|
904 |
if (recv_buf_init(&nde->recv_buf_, buf_size, buf_size - RECV_BUF_MIN_LENGTH)
|
|
905 |
== NULL) {
|
|
906 |
#ifdef DEBUG
|
|
907 |
fprintf(stderr, "Out of memory: error initializing receive buffer");
|
|
908 |
#endif
|
|
909 |
goto error_exit_2;
|
|
910 |
}
|
|
911 |
|
|
912 |
/* open the serial port */
|
|
913 |
if((nde->fd = open(node_addr->addr.ascii.device, O_RDWR | O_NOCTTY | O_NDELAY))
|
|
914 |
< 0) {
|
|
915 |
#ifdef DEBUG
|
|
916 |
fprintf(stderr, "Error opening device %s (errno=%d)",
|
|
917 |
node_addr->addr.ascii.device, errno);
|
|
918 |
#endif
|
|
919 |
goto error_exit_3;
|
|
920 |
}
|
|
921 |
|
|
922 |
if(tcgetattr(nde->fd, &nde->old_tty_settings_) < 0) {
|
|
923 |
#ifdef DEBUG
|
|
924 |
fprintf(stderr, "Error reading device's %s original settings.",
|
|
925 |
node_addr->addr.ascii.device);
|
|
926 |
#endif
|
|
927 |
goto error_exit_4;
|
|
928 |
}
|
|
929 |
|
|
930 |
if(tcsetattr(nde->fd, TCSANOW, &settings) < 0) {
|
|
931 |
#ifdef DEBUG
|
|
932 |
fprintf(stderr, "Error configuring device %s"
|
|
933 |
"(baud=%d, parity=%d, data_bits=%d, stop_bits=%d)",
|
|
934 |
node_addr->addr.ascii.device,
|
|
935 |
node_addr->addr.ascii.baud,
|
|
936 |
node_addr->addr.ascii.parity,
|
|
937 |
node_addr->addr.ascii.data_bits,
|
|
938 |
node_addr->addr.ascii.stop_bits);
|
|
939 |
#endif
|
|
940 |
goto error_exit_4;
|
|
941 |
}
|
|
942 |
|
|
943 |
parity_bits = (node_addr->addr.ascii.parity == 0)?0:1;
|
|
944 |
start_bits = 1;
|
|
945 |
char_bits = start_bits + node_addr->addr.ascii.data_bits +
|
|
946 |
parity_bits + node_addr->addr.ascii.stop_bits;
|
|
947 |
/* time_35_char_ = d_to_timeval(3.5*char_bits/baud); */
|
|
948 |
nde->time_15_char_ = d_to_timeval(1.5*char_bits/node_addr->addr.ascii.baud);
|
|
949 |
|
|
950 |
#ifdef DEBUG
|
|
951 |
printf("nd_entry_connect(): %s open\n", node_addr->addr.ascii.device );
|
|
952 |
printf("nd_entry_connect(): returning fd=%d\n", nde->fd);
|
|
953 |
#endif
|
|
954 |
return nde->fd;
|
|
955 |
|
|
956 |
error_exit_4:
|
|
957 |
close(nde->fd);
|
|
958 |
error_exit_3:
|
|
959 |
recv_buf_done(&nde->recv_buf_);
|
|
960 |
error_exit_2:
|
|
961 |
send_buf_done(&nde->send_buf_);
|
|
962 |
error_exit_1:
|
|
963 |
nde->fd = -1; /* set the node as free... */
|
|
964 |
error_exit_0:
|
|
965 |
return -1;
|
|
966 |
}
|
|
967 |
|
|
968 |
|
|
969 |
|
|
970 |
static int nd_entry_free(nd_entry_t *nde) {
|
|
971 |
if (nde->fd < 0)
|
|
972 |
/* already free */
|
|
973 |
return -1;
|
|
974 |
|
|
975 |
/* reset the tty device old settings... */
|
|
976 |
if(tcsetattr(nde->fd, TCSANOW, &nde->old_tty_settings_) < 0)
|
|
977 |
fprintf(stderr, "Error reconfiguring serial port to it's original settings.");
|
|
978 |
|
|
979 |
recv_buf_done(&nde->recv_buf_);
|
|
980 |
send_buf_done(&nde->send_buf_);
|
|
981 |
close(nde->fd);
|
|
982 |
nde->fd = -1;
|
|
983 |
|
|
984 |
return 0;
|
|
985 |
}
|
|
986 |
|
|
987 |
|
|
988 |
static inline int nd_entry_is_free(nd_entry_t *nde) {
|
|
989 |
return (nde->fd < 0);
|
|
990 |
}
|
|
991 |
|
|
992 |
|
|
993 |
|
|
994 |
|
|
995 |
/************************************/
|
|
996 |
/** **/
|
|
997 |
/** A data structure - nd table **/
|
|
998 |
/** **/
|
|
999 |
/************************************/
|
|
1000 |
|
|
1001 |
typedef struct {
|
|
1002 |
/* the array of node descriptors, and current size... */
|
|
1003 |
nd_entry_t *node;
|
|
1004 |
int node_count; /* total number of nodes in the node[] array */
|
|
1005 |
} nd_table_t;
|
|
1006 |
|
|
1007 |
|
|
1008 |
|
|
1009 |
#if 1
|
|
1010 |
/* nd_table_init()
|
|
1011 |
* Version 1 of the nd_table_init() function.
|
|
1012 |
* If called more than once, 2nd and any subsequent calls will
|
|
1013 |
* be interpreted as a request to confirm that it was already correctly
|
|
1014 |
* initialized with the requested number of nodes.
|
|
1015 |
*/
|
|
1016 |
static int nd_table_init(nd_table_t *ndt, int nd_count) {
|
|
1017 |
int count;
|
|
1018 |
|
|
1019 |
if (ndt->node != NULL) {
|
|
1020 |
/* this function has already been called, and the node table is already initialised */
|
|
1021 |
return (ndt->node_count == nd_count)?0:-1;
|
|
1022 |
}
|
|
1023 |
|
|
1024 |
/* initialise the node descriptor metadata array... */
|
|
1025 |
ndt->node = malloc(sizeof(nd_entry_t) * nd_count);
|
|
1026 |
if (ndt->node == NULL) {
|
|
1027 |
#ifdef DEBUG
|
|
1028 |
fprintf(stderr, "Out of memory: error initializing node address buffer");
|
|
1029 |
#endif
|
|
1030 |
return -1;
|
|
1031 |
}
|
|
1032 |
ndt->node_count = nd_count;
|
|
1033 |
|
|
1034 |
/* initialise the state of each node in the array... */
|
|
1035 |
for (count = 0; count < ndt->node_count; count++) {
|
|
1036 |
nd_entry_init(&ndt->node[count]);
|
|
1037 |
} /* for() */
|
|
1038 |
|
|
1039 |
return nd_count; /* number of succesfully created nodes! */
|
|
1040 |
}
|
|
1041 |
#else
|
|
1042 |
/* nd_table_init()
|
|
1043 |
* Version 2 of the nd_table_init() function.
|
|
1044 |
* If called more than once, 2nd and any subsequent calls will
|
|
1045 |
* be interpreted as a request to reserve an extra new_nd_count
|
|
1046 |
* number of nodes. This will be done using realloc().
|
|
1047 |
*/
|
|
1048 |
static int nd_table_init(nd_table_t *ndt, int new_nd_count) {
|
|
1049 |
int count;
|
|
1050 |
|
|
1051 |
/* initialise the node descriptor metadata array... */
|
|
1052 |
ndt->node = realloc(ndt->node, sizeof(nd_entry_t) * (ndt->node_count + new_nd_count));
|
|
1053 |
if (ndt->node == NULL) {
|
|
1054 |
#ifdef ERRMSG
|
|
1055 |
fprintf(stderr, ERRMSG_HEAD "Out of memory: error initializing node address buffer\n");
|
|
1056 |
#endif
|
|
1057 |
return -1;
|
|
1058 |
}
|
|
1059 |
|
|
1060 |
/* initialise the state of each newly added node in the array... */
|
|
1061 |
for (count = ndt->node_count; count < ndt->node_count + new_nd_count; count++) {
|
|
1062 |
nd_entry_init(&ndt->node[count]);
|
|
1063 |
} /* for() */
|
|
1064 |
ndt->node_count += new_nd_count;
|
|
1065 |
|
|
1066 |
return new_nd_count; /* number of succesfully created nodes! */
|
|
1067 |
}
|
|
1068 |
#endif
|
|
1069 |
|
|
1070 |
|
|
1071 |
static inline nd_entry_t *nd_table_get_nd(nd_table_t *ndt, int nd) {
|
|
1072 |
if ((nd < 0) || (nd >= ndt->node_count))
|
|
1073 |
return NULL;
|
|
1074 |
|
|
1075 |
return &ndt->node[nd];
|
|
1076 |
}
|
|
1077 |
|
|
1078 |
|
|
1079 |
static inline void nd_table_done(nd_table_t *ndt) {
|
|
1080 |
int i;
|
|
1081 |
|
|
1082 |
if (ndt->node == NULL)
|
|
1083 |
return;
|
|
1084 |
|
|
1085 |
/* close all the connections... */
|
|
1086 |
for (i = 0; i < ndt->node_count; i++)
|
|
1087 |
nd_entry_free(&ndt->node[i]);
|
|
1088 |
|
|
1089 |
/* Free memory... */
|
|
1090 |
free(ndt->node);
|
|
1091 |
*ndt = (nd_table_t){.node=NULL, .node_count=0};
|
|
1092 |
}
|
|
1093 |
|
|
1094 |
|
|
1095 |
|
|
1096 |
static inline int nd_table_get_free_nd(nd_table_t *ndt) {
|
|
1097 |
int count;
|
|
1098 |
|
|
1099 |
for (count = 0; count < ndt->node_count; count++) {
|
|
1100 |
if (nd_entry_is_free(&ndt->node[count]))
|
|
1101 |
return count;
|
|
1102 |
}
|
|
1103 |
|
|
1104 |
/* none found... */
|
|
1105 |
return -1;
|
|
1106 |
}
|
|
1107 |
|
|
1108 |
|
|
1109 |
static inline int nd_table_free_nd(nd_table_t *ndt, int nd) {
|
|
1110 |
if ((nd < 0) || (nd >= ndt->node_count))
|
|
1111 |
return -1;
|
|
1112 |
|
|
1113 |
return nd_entry_free(&ndt->node[nd]);
|
|
1114 |
}
|
|
1115 |
|
|
1116 |
|
|
1117 |
|
|
1118 |
/**************************************************************/
|
|
1119 |
/**************************************************************/
|
|
1120 |
/**** ****/
|
|
1121 |
/**** ****/
|
|
1122 |
/**** Global Library State ****/
|
|
1123 |
/**** ****/
|
|
1124 |
/**** ****/
|
|
1125 |
/**************************************************************/
|
|
1126 |
/**************************************************************/
|
|
1127 |
|
|
1128 |
|
|
1129 |
/* The node descriptor table... */
|
|
1130 |
/* NOTE: This variable must be correctly initialised here!! */
|
|
1131 |
static nd_table_t nd_table_ = {.node=NULL, .node_count=0};
|
|
1132 |
|
|
1133 |
/* The optimization choice... */
|
|
1134 |
static optimization_t optimization_;
|
|
1135 |
|
|
1136 |
|
|
1137 |
|
|
1138 |
/**************************************************************/
|
|
1139 |
/**************************************************************/
|
|
1140 |
/**** ****/
|
|
1141 |
/**** ****/
|
|
1142 |
/**** Sending of Modbus ASCII Frames ****/
|
|
1143 |
/**** ****/
|
|
1144 |
/**** ****/
|
|
1145 |
/**************************************************************/
|
|
1146 |
/**************************************************************/
|
|
1147 |
|
|
1148 |
/*
|
|
1149 |
* NOTE: for now the transmit_timeout is silently ignored in ASCII version!
|
|
1150 |
*/
|
|
1151 |
int modbus_ascii_write(int nd,
|
|
1152 |
u8 *data,
|
|
1153 |
size_t data_length,
|
|
1154 |
u16 transaction_id,
|
|
1155 |
const struct timespec *transmit_timeout
|
|
1156 |
)
|
|
1157 |
{
|
|
1158 |
fd_set rfds;
|
|
1159 |
struct timeval timeout;
|
|
1160 |
int res, bin_data_conv, send_retries;
|
|
1161 |
frame_part_t frame_part;
|
|
1162 |
nd_entry_t *nd_entry;
|
|
1163 |
|
|
1164 |
/* check if nd is correct... */
|
|
1165 |
if ((nd_entry = nd_table_get_nd(&nd_table_, nd)) == NULL)
|
|
1166 |
return -1;
|
|
1167 |
|
|
1168 |
/* check if nd is initialzed... */
|
|
1169 |
if (nd_entry->fd < 0)
|
|
1170 |
return -1;
|
|
1171 |
|
|
1172 |
/* THE MAIN LOOP!!! */
|
|
1173 |
send_retries = ASC_FRAME_SEND_RETRY + 1; /* must try at least once... */
|
|
1174 |
while (send_retries > 0) {
|
|
1175 |
|
|
1176 |
/*******************************
|
|
1177 |
* synchronise with the bus... *
|
|
1178 |
*******************************/
|
|
1179 |
/* Remember that a RS485 bus is half-duplex, so we have to wait until
|
|
1180 |
* nobody is transmitting over the bus for our turn to transmit.
|
|
1181 |
* This will never happen on a modbus network if the master and
|
|
1182 |
* slave state machines never get out of synch (granted, it probably
|
|
1183 |
* only has two states, but a state machine nonetheless), but we want
|
|
1184 |
* to make sure we can re-synchronise if they ever do get out of synch.
|
|
1185 |
*
|
|
1186 |
* The following lines are an attempt at re-synchronising with the bus.
|
|
1187 |
* Unfortunately, due to the completely asynchronous nature of the
|
|
1188 |
* modbus-ascii protocol (there are no time boundaries for sending a frame!),
|
|
1189 |
* it is impossible to guarantee that we will synchronise correctly.
|
|
1190 |
*
|
|
1191 |
* Use of RTS/CTS over a half-duplex coms chanel would eliminate this
|
|
1192 |
* 'feature', but not all RS232/RS485 converters delay activating the
|
|
1193 |
* CTS signal, even though there may be current activity on the bus.
|
|
1194 |
*
|
|
1195 |
* We first wait until the bus is silent for at least 1.5 character interval.
|
|
1196 |
* Note that we only get feedback from the device driver once a whole byte
|
|
1197 |
* has been received, so we must wait longer than 1 character interval to make
|
|
1198 |
* sure there is no current activity on the bus). We then flush the input and
|
|
1199 |
* output queues.
|
|
1200 |
*/
|
|
1201 |
/* NOTES:
|
|
1202 |
* - we do not need to reset the rfds with FD_SET(ttyfd, &rfds)
|
|
1203 |
* before every call to select! We only wait on one file descriptor,
|
|
1204 |
* so if select returns succesfully, it must have that same file
|
|
1205 |
* decriptor set in the rdfs!
|
|
1206 |
* If select returns with a timeout, then we do not get to call
|
|
1207 |
* select again!
|
|
1208 |
* - We do not reset the timeout value. Normally this value is left
|
|
1209 |
* unchanged when select() returns, so we will be witing for longer
|
|
1210 |
* than the desired period.
|
|
1211 |
* On Linux the timeout is changed to reflect the time remaining.
|
|
1212 |
* In this case, things will work more nicely.
|
|
1213 |
*/
|
|
1214 |
FD_ZERO(&rfds);
|
|
1215 |
FD_SET(nd_entry->fd, &rfds);
|
|
1216 |
timeout = nd_entry->time_15_char_;
|
|
1217 |
while ((res = select(nd_entry->fd+1, &rfds, NULL, NULL, &timeout)) != 0) {
|
|
1218 |
if (res > 0) {
|
|
1219 |
/* we are receiving data over the serial port! */
|
|
1220 |
/* Throw the data away! */
|
|
1221 |
tcflush(nd_entry->fd, TCIFLUSH); /* flush the input stream */
|
|
1222 |
/* reset the timeout value! */
|
|
1223 |
timeout = nd_entry->time_15_char_;
|
|
1224 |
} else {
|
|
1225 |
/* some kind of error ocurred */
|
|
1226 |
if (errno != EINTR)
|
|
1227 |
/* we were not interrupted by a signal */
|
|
1228 |
return -1;
|
|
1229 |
/* We will be callind select() again.
|
|
1230 |
* We need to reset the FD SET !
|
|
1231 |
*/
|
|
1232 |
FD_ZERO(&rfds);
|
|
1233 |
FD_SET(nd_entry->fd, &rfds);
|
|
1234 |
}
|
|
1235 |
} /* while (select()) */
|
|
1236 |
|
|
1237 |
/* Flush both input and output streams... */
|
|
1238 |
/* NOTE: Due to the nature of the modbus protocol,
|
|
1239 |
* when a frame is sent all previous
|
|
1240 |
* frames that may have arrived at the sending node become
|
|
1241 |
* irrelevant.
|
|
1242 |
*/
|
|
1243 |
tcflush(nd_entry->fd, TCIOFLUSH); /* flush the input & output streams */
|
|
1244 |
recv_buf_reset(&nd_entry->recv_buf_); /* reset the recv buffer */
|
|
1245 |
|
|
1246 |
/**********************
|
|
1247 |
* write to output... *
|
|
1248 |
**********************/
|
|
1249 |
send_buf_reset(&nd_entry->send_buf_);
|
|
1250 |
|
|
1251 |
frame_part = fp_header; /* start off by sending the header... */
|
|
1252 |
bin_data_conv = 0; /* binary format data already converted to ascii format... */
|
|
1253 |
while ((frame_part != fp_done) ||
|
|
1254 |
(send_buf_data_count(&nd_entry->send_buf_) > 0)) {
|
|
1255 |
|
|
1256 |
/* build the frame we will send over the wire... */
|
|
1257 |
/* We use a state machine with four states... */
|
|
1258 |
if (frame_part == fp_header) {
|
|
1259 |
if (send_buf_header_append(&nd_entry->send_buf_) >= 0)
|
|
1260 |
frame_part = fp_body;
|
|
1261 |
}
|
|
1262 |
if (frame_part == fp_body) {
|
|
1263 |
res = send_buf_data_add(&nd_entry->send_buf_, data + bin_data_conv, data_length - bin_data_conv);
|
|
1264 |
bin_data_conv += res;
|
|
1265 |
if (bin_data_conv == data_length)
|
|
1266 |
frame_part = fp_lrc;
|
|
1267 |
}
|
|
1268 |
if (frame_part == fp_lrc) {
|
|
1269 |
if (send_buf_lrc_append(&nd_entry->send_buf_) >= 0)
|
|
1270 |
frame_part = fp_tail;
|
|
1271 |
}
|
|
1272 |
if (frame_part == fp_tail) {
|
|
1273 |
if (send_buf_tail_append(&nd_entry->send_buf_) >= 0)
|
|
1274 |
frame_part = fp_done;
|
|
1275 |
}
|
|
1276 |
|
|
1277 |
/* send the frame... */
|
|
1278 |
if ((res = write(nd_entry->fd,
|
|
1279 |
send_buf_data(&nd_entry->send_buf_),
|
|
1280 |
send_buf_data_count(&nd_entry->send_buf_))) < 0) {
|
|
1281 |
if ((errno != EAGAIN ) && (errno != EINTR )) {
|
|
1282 |
break;
|
|
1283 |
} else {
|
|
1284 |
/* there is no harm if we get interrupted while writing the frame.
|
|
1285 |
* The ascii version of modbus does not place any timing
|
|
1286 |
* constraints whatsoever...
|
|
1287 |
*
|
|
1288 |
* We simply continue sending the frame...
|
|
1289 |
*/
|
|
1290 |
res = 0;
|
|
1291 |
}
|
|
1292 |
}
|
|
1293 |
#ifdef DEBUG
|
|
1294 |
/* Print each character that has just been sent on the bus */
|
|
1295 |
{ int i;
|
|
1296 |
printf("bytes sent -> [");
|
|
1297 |
for(i = 0; i < res; i++)
|
|
1298 |
printf("%c", send_buf_data(&nd_entry->send_buf_)[i]);
|
|
1299 |
printf("]\n");
|
|
1300 |
}
|
|
1301 |
#endif
|
|
1302 |
send_buf_data_purge(&nd_entry->send_buf_, res);
|
|
1303 |
|
|
1304 |
if ((frame_part == fp_done) &&
|
|
1305 |
(send_buf_data_count(&nd_entry->send_buf_) == 0))
|
|
1306 |
/* sent frame successfully! */
|
|
1307 |
return data_length;
|
|
1308 |
|
|
1309 |
} /* while(frame_not_sent) */
|
|
1310 |
/* NOTE: Some error occured while trying to transmit. It will probably
|
|
1311 |
* not go away by itself, but there is no harm in trying again
|
|
1312 |
* if the upper layer so wishes...
|
|
1313 |
*/
|
|
1314 |
send_retries--;
|
|
1315 |
} /* while() MAIN LOOP */
|
|
1316 |
|
|
1317 |
/* maximum retries exceeded */
|
|
1318 |
return -1;
|
|
1319 |
}
|
|
1320 |
|
|
1321 |
|
|
1322 |
|
|
1323 |
/**************************************************************/
|
|
1324 |
/**************************************************************/
|
|
1325 |
/**** ****/
|
|
1326 |
/**** ****/
|
|
1327 |
/**** Receiving Modbus ASCII Frames ****/
|
|
1328 |
/**** ****/
|
|
1329 |
/**** ****/
|
|
1330 |
/**************************************************************/
|
|
1331 |
/**************************************************************/
|
|
1332 |
|
|
1333 |
|
|
1334 |
/************************************/
|
|
1335 |
/** **/
|
|
1336 |
/** Read a frame **/
|
|
1337 |
/** **/
|
|
1338 |
/************************************/
|
|
1339 |
|
|
1340 |
/* A function to read a valid frame off the modbus ascii bus.
|
|
1341 |
*
|
|
1342 |
* NOTES:
|
|
1343 |
* - The returned frame is guaranteed to be a valid frame.
|
|
1344 |
* - The returned length does *not* include the LRC.
|
|
1345 |
*/
|
|
1346 |
/* NOTE: This function is only called from one place in the rest of the code,
|
|
1347 |
* so we might just as well make it inline...
|
|
1348 |
*/
|
|
1349 |
/* RETURNS: number of bytes in received frame
|
|
1350 |
* -1 on read file error
|
|
1351 |
* -2 on timeout
|
|
1352 |
*/
|
|
1353 |
static inline int read_frame(nd_entry_t *nd_entry,
|
|
1354 |
u8 **recv_data_ptr,
|
|
1355 |
struct timespec *end_time)
|
|
1356 |
{
|
|
1357 |
/* temporary variables... */
|
|
1358 |
fd_set rfds;
|
|
1359 |
int res;
|
|
1360 |
|
|
1361 |
/* start by deleting any previously converted frames... */
|
|
1362 |
recv_buf_frame_purge(&nd_entry->recv_buf_);
|
|
1363 |
|
|
1364 |
/*==============*
|
|
1365 |
* read a frame *
|
|
1366 |
*==============*/
|
|
1367 |
#ifdef DEBUG
|
|
1368 |
printf("bytes read -> <");
|
|
1369 |
#endif
|
|
1370 |
/* The main loop that reads one frame */
|
|
1371 |
/* (multiple calls to read() ) */
|
|
1372 |
/* and jumps out as soon as it finds a valid frame. */
|
|
1373 |
/* NOTE: Do not change this into a do {...} until() loop!
|
|
1374 |
* The while loop may find valid frames in the data read off the
|
|
1375 |
* bus in previous invocations of this same fucntion, and may
|
|
1376 |
* therefore not execute any loop iteration whatsoever,
|
|
1377 |
* and not call read()
|
|
1378 |
*/
|
|
1379 |
while ((*recv_data_ptr = recv_buf_frame(&nd_entry->recv_buf_)) == NULL) {
|
|
1380 |
/* We need more bytes... */
|
|
1381 |
|
|
1382 |
/*-----------------------------*
|
|
1383 |
* check for data availability *
|
|
1384 |
*-----------------------------*/
|
|
1385 |
/* if we can't find a valid frame in the existing data, or no data
|
|
1386 |
* was left over, then we need to read more bytes!
|
|
1387 |
*/
|
|
1388 |
FD_ZERO(&rfds);
|
|
1389 |
FD_SET(nd_entry->fd, &rfds);
|
|
1390 |
{int sel_res = my_select(nd_entry->fd + 1, &rfds, NULL, end_time);
|
|
1391 |
if (sel_res < 0)
|
|
1392 |
return -1;
|
|
1393 |
if (sel_res == 0)
|
|
1394 |
return -2;
|
|
1395 |
}
|
|
1396 |
|
|
1397 |
/*------------------*
|
|
1398 |
* read frame bytes *
|
|
1399 |
*------------------*/
|
|
1400 |
/* Read in as many bytes as possible... */
|
|
1401 |
res = read(nd_entry->fd,
|
|
1402 |
recv_buf_free(&nd_entry->recv_buf_),
|
|
1403 |
recv_buf_free_count(&nd_entry->recv_buf_));
|
|
1404 |
if (res < 0) {
|
|
1405 |
if (errno != EINTR)
|
|
1406 |
return -1;
|
|
1407 |
else
|
|
1408 |
res = 0;
|
|
1409 |
}
|
|
1410 |
#ifdef DEBUG
|
|
1411 |
{/* display the hex code of each character received */
|
|
1412 |
int i;
|
|
1413 |
for (i=0; i < res; i++)
|
|
1414 |
printf("%c", recv_buf_free(&nd_entry->recv_buf_)[i]);
|
|
1415 |
}
|
|
1416 |
#endif
|
|
1417 |
recv_buf_data_add(&nd_entry->recv_buf_, res);
|
|
1418 |
} /* while ()*/
|
|
1419 |
#ifdef DEBUG
|
|
1420 |
printf(">\n");
|
|
1421 |
#endif
|
|
1422 |
|
|
1423 |
/* We have a frame! */
|
|
1424 |
return recv_buf_frame_count(&nd_entry->recv_buf_);
|
|
1425 |
}
|
|
1426 |
|
|
1427 |
|
|
1428 |
|
|
1429 |
|
|
1430 |
|
|
1431 |
/**************************************/
|
|
1432 |
/** **/
|
|
1433 |
/** Read a Modbus ASCII frame **/
|
|
1434 |
/** **/
|
|
1435 |
/**************************************/
|
|
1436 |
|
|
1437 |
/* The public function that reads a valid modbus frame.
|
|
1438 |
*
|
|
1439 |
* The returned frame is guaranteed to be different to the
|
|
1440 |
* the frame stored in send_data, and to start with the
|
|
1441 |
* same slave address stored in send_data[0].
|
|
1442 |
*
|
|
1443 |
* If send_data is NULL, send_data_length = 0, or
|
|
1444 |
* ignore_echo == 0, then the first valid frame read off
|
|
1445 |
* the bus is returned.
|
|
1446 |
*
|
|
1447 |
* return value: The length (in bytes) of the valid frame,
|
|
1448 |
* -1 on error
|
|
1449 |
* -2 on timeout
|
|
1450 |
*/
|
|
1451 |
|
|
1452 |
int modbus_ascii_read(int *nd,
|
|
1453 |
u8 **recv_data_ptr,
|
|
1454 |
u16 *transaction_id,
|
|
1455 |
const u8 *send_data,
|
|
1456 |
int send_length,
|
|
1457 |
const struct timespec *recv_timeout) {
|
|
1458 |
|
|
1459 |
struct timespec end_time, *ts_ptr;
|
|
1460 |
int res, recv_length;
|
|
1461 |
int iter; /* Warning: if you change the var type of iter from int,
|
|
1462 |
* then you must also change the INT_MAX constant
|
|
1463 |
* further on in this function...
|
|
1464 |
*/
|
|
1465 |
u8 *local_recv_data_ptr;
|
|
1466 |
nd_entry_t *nd_entry;
|
|
1467 |
|
|
1468 |
/* Check input parameters... */
|
|
1469 |
if (nd == NULL)
|
|
1470 |
return -1;
|
|
1471 |
|
|
1472 |
if (recv_data_ptr == NULL)
|
|
1473 |
recv_data_ptr = &local_recv_data_ptr;
|
|
1474 |
|
|
1475 |
if ((send_data == NULL) && (send_length != 0))
|
|
1476 |
return -1;
|
|
1477 |
|
|
1478 |
/* check if nd is correct... */
|
|
1479 |
if ((nd_entry = nd_table_get_nd(&nd_table_, *nd)) == NULL)
|
|
1480 |
return -1;
|
|
1481 |
|
|
1482 |
/* check if nd is initialzed... */
|
|
1483 |
if (nd_entry->fd < 0)
|
|
1484 |
return -1;
|
|
1485 |
|
|
1486 |
/* We will potentially read many frames, and we cannot reset the timeout
|
|
1487 |
* for every frame we read. We therefore determine the absolute time_out,
|
|
1488 |
* and use this as a parameter for each call to read_frame() instead of
|
|
1489 |
* using a relative timeout.
|
|
1490 |
*
|
|
1491 |
* NOTE: see also the timeout related comment in the read_frame()= function!
|
|
1492 |
*/
|
|
1493 |
ts_ptr = NULL;
|
|
1494 |
if (recv_timeout != NULL) {
|
|
1495 |
ts_ptr = &end_time;
|
|
1496 |
*ts_ptr = timespec_add_curtime(*recv_timeout);
|
|
1497 |
}
|
|
1498 |
|
|
1499 |
/* NOTE: When using a half-duplex RS-485 bus, some (most ?) RS232-485
|
|
1500 |
* converters will send back to the RS232 port whatever we write,
|
|
1501 |
* so we will read in whatever we write out onto the bus.
|
|
1502 |
* We will therefore have to compare
|
|
1503 |
* the first frame we read with the one we sent. If they are
|
|
1504 |
* identical it is because we are in fact working on a RS-485
|
|
1505 |
* bus and must therefore read in a second frame which will be
|
|
1506 |
* the true response to our query.
|
|
1507 |
* If the first frame we receive is different to the last frame we
|
|
1508 |
* just sent, then we are *not* working on a RS-485 bus, and
|
|
1509 |
* that is already the real response to our query.
|
|
1510 |
*
|
|
1511 |
* Flushing the input cache immediately *after* sending a frame
|
|
1512 |
* could solve this issue, but we have no guarantee that this
|
|
1513 |
* process would not get swapped out between the write() and
|
|
1514 |
* flush() calls, and we could therefore be flushing the response
|
|
1515 |
* frame along with the last frame we sent!
|
|
1516 |
*/
|
|
1517 |
|
|
1518 |
iter = 0;
|
|
1519 |
while ((res = recv_length = read_frame(nd_entry, recv_data_ptr, ts_ptr)) >= 0) {
|
|
1520 |
if (iter < INT_MAX) iter++;
|
|
1521 |
|
|
1522 |
if ((send_length <= 0) || (nd_entry->ignore_echo == 0))
|
|
1523 |
/* any valid frame will do... */
|
|
1524 |
return recv_length;
|
|
1525 |
|
|
1526 |
if ((send_length > L2_FRAME_SLAVEID_OFS + 1) && (iter == 1))
|
|
1527 |
/* We have a frame in send_data,
|
|
1528 |
* so we must make sure we are not reading in the frame just sent...
|
|
1529 |
*
|
|
1530 |
* We must only do this for the first frame we read. Subsequent
|
|
1531 |
* frames are guaranteed not to be the previously sent frame
|
|
1532 |
* since the modbus_ascii_write() resets the recv buffer.
|
|
1533 |
* Remember too that valid modbus responses may be exactly the same
|
|
1534 |
* as the request frame!!
|
|
1535 |
*/
|
|
1536 |
if (recv_length == send_length)
|
|
1537 |
if (memcmp(*recv_data_ptr, send_data, recv_length) == 0)
|
|
1538 |
/* recv == send !!! */
|
|
1539 |
/* read in another frame. */
|
|
1540 |
continue;
|
|
1541 |
|
|
1542 |
/* The frame read is either:
|
|
1543 |
* - different to the frame in send_data
|
|
1544 |
* - or there is only the slave id in send_data[L2_FRAME_SLAVEID_OFS]
|
|
1545 |
* - or both of the above...
|
|
1546 |
*/
|
|
1547 |
if (send_length > L2_FRAME_SLAVEID_OFS)
|
|
1548 |
if (recv_length > L2_FRAME_SLAVEID_OFS)
|
|
1549 |
/* check that frame is from/to the correct slave... */
|
|
1550 |
if ((*recv_data_ptr)[L2_FRAME_SLAVEID_OFS] == send_data[L2_FRAME_SLAVEID_OFS])
|
|
1551 |
/* yep, it is... */
|
|
1552 |
return recv_length;
|
|
1553 |
|
|
1554 |
/* The frame we have received is not acceptable...
|
|
1555 |
* Let's read a new frame.
|
|
1556 |
*/
|
|
1557 |
} /* while(...) */
|
|
1558 |
|
|
1559 |
/* error reading response! */
|
|
1560 |
/* Return the error returned by read_frame! */
|
|
1561 |
return res;
|
|
1562 |
}
|
|
1563 |
|
|
1564 |
|
|
1565 |
|
|
1566 |
|
|
1567 |
|
|
1568 |
/**************************************************************/
|
|
1569 |
/**************************************************************/
|
|
1570 |
/**** ****/
|
|
1571 |
/**** ****/
|
|
1572 |
/**** Initialising and Shutting Down Library ****/
|
|
1573 |
/**** ****/
|
|
1574 |
/**** ****/
|
|
1575 |
/**************************************************************/
|
|
1576 |
/**************************************************************/
|
|
1577 |
|
|
1578 |
/******************************/
|
|
1579 |
/** **/
|
|
1580 |
/** Load Default Values **/
|
|
1581 |
/** **/
|
|
1582 |
/******************************/
|
|
1583 |
|
|
1584 |
static void set_defaults(int *baud,
|
|
1585 |
int *parity,
|
|
1586 |
int *data_bits,
|
|
1587 |
int *stop_bits) {
|
|
1588 |
/* Set the default values, if required... */
|
|
1589 |
if (*baud == 0)
|
|
1590 |
*baud = DEF_BAUD_RATE;
|
|
1591 |
if (*data_bits == 0)
|
|
1592 |
*data_bits = DEF_DATA_BITS;
|
|
1593 |
if (*stop_bits == 0) {
|
|
1594 |
if (*parity == 0)
|
|
1595 |
*stop_bits = DEF_STOP_BITS_NOP; /* no parity */
|
|
1596 |
else
|
|
1597 |
*stop_bits = DEF_STOP_BITS_PAR; /* parity used */
|
|
1598 |
}
|
|
1599 |
}
|
|
1600 |
|
|
1601 |
|
|
1602 |
|
|
1603 |
|
|
1604 |
|
|
1605 |
/******************************/
|
|
1606 |
/** **/
|
|
1607 |
/** Initialise Library **/
|
|
1608 |
/** **/
|
|
1609 |
/******************************/
|
|
1610 |
|
|
1611 |
int modbus_ascii_init(int nd_count,
|
|
1612 |
optimization_t opt,
|
|
1613 |
int *extra_bytes)
|
|
1614 |
{
|
|
1615 |
#ifdef DEBUG
|
|
1616 |
printf("modbus_asc_init(): called...\n");
|
|
1617 |
printf("creating %d node descriptors\n", nd_count);
|
|
1618 |
if (opt == optimize_speed)
|
|
1619 |
printf("optimizing for speed\n");
|
|
1620 |
if (opt == optimize_size)
|
|
1621 |
printf("optimizing for size\n");
|
|
1622 |
#endif
|
|
1623 |
|
|
1624 |
/* check input parameters...*/
|
|
1625 |
if (0 == nd_count) {
|
|
1626 |
if (extra_bytes != NULL)
|
|
1627 |
// Not the corect value for this layer.
|
|
1628 |
// What we set it to in case this layer is not used!
|
|
1629 |
*extra_bytes = 0;
|
|
1630 |
return 0;
|
|
1631 |
}
|
|
1632 |
if (nd_count <= 0)
|
|
1633 |
goto error_exit_0;
|
|
1634 |
|
|
1635 |
if (extra_bytes == NULL)
|
|
1636 |
goto error_exit_0;
|
|
1637 |
|
|
1638 |
/* initialise nd table... */
|
|
1639 |
if (nd_table_init(&nd_table_, nd_count) < 0)
|
|
1640 |
goto error_exit_0;
|
|
1641 |
|
|
1642 |
/* remember the optimization choice for later reference... */
|
|
1643 |
optimization_ = opt;
|
|
1644 |
|
|
1645 |
#ifdef DEBUG
|
|
1646 |
printf("modbus_asc_init(): returning succesfuly...\n");
|
|
1647 |
#endif
|
|
1648 |
return 0;
|
|
1649 |
|
|
1650 |
error_exit_0:
|
|
1651 |
if (extra_bytes != NULL)
|
|
1652 |
*extra_bytes = 0; // The value we set this to in case of error.
|
|
1653 |
return -1;
|
|
1654 |
}
|
|
1655 |
|
|
1656 |
|
|
1657 |
|
|
1658 |
/******************************/
|
|
1659 |
/** **/
|
|
1660 |
/** Open node descriptor **/
|
|
1661 |
/** **/
|
|
1662 |
/******************************/
|
|
1663 |
|
|
1664 |
/* Open a node for master or slave operation.
|
|
1665 |
* Returns the node descriptor, or -1 on error.
|
|
1666 |
*
|
|
1667 |
* This function is mapped onto both
|
|
1668 |
* modbus_connect() and modbus_listen()
|
|
1669 |
*/
|
|
1670 |
int modbus_ascii_connect(node_addr_t node_addr) {
|
|
1671 |
int node_descriptor;
|
|
1672 |
nd_entry_t *nd_entry;
|
|
1673 |
|
|
1674 |
#ifdef DEBUG
|
|
1675 |
printf("modbus_ascii_connect(): called...\n");
|
|
1676 |
printf("opening %s\n", node_addr.addr.ascii.device);
|
|
1677 |
printf("baud_rate = %d\n", node_addr.addr.ascii.baud);
|
|
1678 |
printf("parity = %d\n", node_addr.addr.ascii.parity);
|
|
1679 |
printf("data_bits = %d\n", node_addr.addr.ascii.data_bits);
|
|
1680 |
printf("stop_bits = %d\n", node_addr.addr.ascii.stop_bits);
|
|
1681 |
printf("ignore_echo = %d\n", node_addr.addr.ascii.ignore_echo);
|
|
1682 |
#endif
|
|
1683 |
|
|
1684 |
/* Check for valid address family */
|
|
1685 |
if (node_addr.naf != naf_ascii)
|
|
1686 |
/* wrong address type... */
|
|
1687 |
goto error_exit_0;
|
|
1688 |
|
|
1689 |
/* find a free node descriptor */
|
|
1690 |
if ((node_descriptor = nd_table_get_free_nd(&nd_table_)) < 0)
|
|
1691 |
/* if no free nodes to initialize, then we are finished... */
|
|
1692 |
goto error_exit_0;
|
|
1693 |
if ((nd_entry = nd_table_get_nd(&nd_table_, node_descriptor)) == NULL)
|
|
1694 |
/* strange, this should not occur... */
|
|
1695 |
goto error_exit_0;
|
|
1696 |
|
|
1697 |
/* set the default values... */
|
|
1698 |
set_defaults(&(node_addr.addr.ascii.baud),
|
|
1699 |
&(node_addr.addr.ascii.parity),
|
|
1700 |
&(node_addr.addr.ascii.data_bits),
|
|
1701 |
&(node_addr.addr.ascii.stop_bits));
|
|
1702 |
|
|
1703 |
if (nd_entry_connect(nd_entry, &node_addr, optimization_) < 0)
|
|
1704 |
goto error_exit_0;
|
|
1705 |
|
|
1706 |
#ifdef DEBUG
|
|
1707 |
printf("modbus_ascii_connect(): %s open\n", node_addr.addr.ascii.device );
|
|
1708 |
printf("modbus_ascii_connect(): returning nd=%d\n", node_descriptor);
|
|
1709 |
#endif
|
|
1710 |
return node_descriptor;
|
|
1711 |
|
|
1712 |
error_exit_0:
|
|
1713 |
return -1;
|
|
1714 |
}
|
|
1715 |
|
|
1716 |
|
|
1717 |
|
|
1718 |
int modbus_ascii_listen(node_addr_t node_addr) {
|
|
1719 |
return modbus_ascii_connect(node_addr);
|
|
1720 |
}
|
|
1721 |
|
|
1722 |
|
|
1723 |
|
|
1724 |
/******************************/
|
|
1725 |
/** **/
|
|
1726 |
/** Close node descriptor **/
|
|
1727 |
/** **/
|
|
1728 |
/******************************/
|
|
1729 |
|
|
1730 |
int modbus_ascii_close(int nd) {
|
|
1731 |
return nd_table_free_nd(&nd_table_, nd);
|
|
1732 |
}
|
|
1733 |
|
|
1734 |
|
|
1735 |
|
|
1736 |
/******************************/
|
|
1737 |
/** **/
|
|
1738 |
/** Shutdown Library **/
|
|
1739 |
/** **/
|
|
1740 |
/******************************/
|
|
1741 |
|
|
1742 |
int modbus_ascii_done(void) {
|
|
1743 |
nd_table_done(&nd_table_);
|
|
1744 |
return 0;
|
|
1745 |
}
|
|
1746 |
|
|
1747 |
|
|
1748 |
|
|
1749 |
|
|
1750 |
|
|
1751 |
/******************************/
|
|
1752 |
/** **/
|
|
1753 |
/** **/
|
|
1754 |
/** **/
|
|
1755 |
/******************************/
|
|
1756 |
int modbus_ascii_silence_init(void) {
|
|
1757 |
return 0;
|
|
1758 |
}
|
|
1759 |
|
|
1760 |
|
|
1761 |
|
|
1762 |
|
|
1763 |
/******************************/
|
|
1764 |
/** **/
|
|
1765 |
/** **/
|
|
1766 |
/** **/
|
|
1767 |
/******************************/
|
|
1768 |
|
|
1769 |
|
|
1770 |
double modbus_ascii_get_min_timeout(int baud,
|
|
1771 |
int parity,
|
|
1772 |
int data_bits,
|
|
1773 |
int stop_bits) {
|
|
1774 |
int parity_bits, start_bits, char_bits;
|
|
1775 |
|
|
1776 |
set_defaults(&baud, &parity, &data_bits, &stop_bits);
|
|
1777 |
parity_bits = (parity == 0)?0:1;
|
|
1778 |
start_bits = 1;
|
|
1779 |
char_bits = start_bits + data_bits + parity_bits + stop_bits;
|
|
1780 |
return (double)((MAX_ASC_FRAME_LENGTH * char_bits) / baud);
|
|
1781 |
}
|
|
1782 |
|
|
1783 |
|
|
1784 |
|