author | Mario de Sousa <msousa@fe.up.pt> |
Tue, 10 Dec 2019 16:07:49 +0000 | |
changeset 7 | 9334c8280602 |
parent 6 | fe4088d5573a |
child 8 | f14859c24751 |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* Copyright (c) 2001,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 |
/* mb_slave.c */ |
|
25 |
||
26 |
#include <fcntl.h> /* File control definitions */ |
|
27 |
#include <stdio.h> /* Standard input/output */ |
|
28 |
#include <string.h> |
|
29 |
#include <stdlib.h> |
|
30 |
#include <termio.h> /* POSIX terminal control definitions */ |
|
31 |
#include <sys/time.h> /* Time structures for select() */ |
|
32 |
#include <unistd.h> /* POSIX Symbolic Constants */ |
|
33 |
#include <errno.h> /* Error definitions */ |
|
34 |
||
35 |
#include <netinet/in.h> /* required for htons() and ntohs() */ |
|
36 |
#include "mb_layer1.h" |
|
37 |
#include "mb_slave.h" |
|
38 |
#include "mb_slave_private.h" |
|
39 |
||
40 |
/* #define DEBUG */ /* uncomment to see the data sent and received */ |
|
41 |
||
42 |
||
43 |
#define modbus_write fptr_[layer1_fin].modbus_write |
|
44 |
#define modbus_read fptr_[layer1_fin].modbus_read |
|
45 |
#define modbus_init fptr_[layer1_fin].modbus_init |
|
46 |
#define modbus_done fptr_[layer1_fin].modbus_done |
|
47 |
#define modbus_connect fptr_[layer1_fin].modbus_connect |
|
48 |
#define modbus_listen fptr_[layer1_fin].modbus_listen |
|
49 |
#define modbus_close fptr_[layer1_fin].modbus_close |
|
50 |
#define modbus_silence_init fptr_[layer1_fin].modbus_silence_init |
|
51 |
#define modbus_get_min_timeout fptr_[layer1_fin].modbus_get_min_timeout |
|
52 |
||
53 |
/* the lower two bits of ttyfd are used to store the index to layer1 function pointers */ |
|
54 |
/* layer1_fin index to fptr_[] is in lowest 2 bits of fd */ |
|
55 |
#define get_ttyfd() int layer1_fin = fd & 3; int ttyfd = fd / 4;\ |
|
56 |
if (fd < 0) {ttyfd = fd; layer1_fin = 0; /* use modbusTCP */} |
|
57 |
||
58 |
||
59 |
||
60 |
||
61 |
/******************************************/ |
|
62 |
/******************************************/ |
|
63 |
/** **/ |
|
64 |
/** Global Variables... **/ |
|
65 |
/** **/ |
|
66 |
/******************************************/ |
|
67 |
/******************************************/ |
|
68 |
/* The layer 1 (RTU, ASCII, TCP) implementations will be adding some |
|
69 |
* header and tail bytes (e.g. CRC) to the packet we build here. Since |
|
70 |
* layer1 will re-use the same buffer allocated in this slave layer |
|
71 |
* (so as not to continuosly copy the same info from buffer to buffer), |
|
72 |
* we need to allocate more bytes than those strictly required for this |
|
73 |
* slave layer. Therefore, the extra_bytes parameter. |
|
74 |
* |
|
75 |
* Note that we add one more extra byte to the response buffer. |
|
76 |
* This is because some response packets will not be starting off |
|
77 |
* at byte 0, but rather at byte 1 of the buffer. This is in order |
|
78 |
* to guarantee that the data that is sent on the buffer is aligned |
|
79 |
* on even bytes (the 16 bit words!). This will allow the application |
|
80 |
* (layer above the one implemented in this file - i.e. the callback |
|
81 |
* functions) to reference this memory as an u16 *, without producing |
|
82 |
* 'bus error' messages in some embedded devices that do not allow |
|
83 |
* acessing u16 on odd numbered addresses. |
|
84 |
*/ |
|
85 |
static int buff_extra_bytes_; |
|
86 |
#define RESP_BUFFER_SIZE (MAX_L2_FRAME_LENGTH + buff_extra_bytes_ + 1) |
|
87 |
||
88 |
/******************************************/ |
|
89 |
/******************************************/ |
|
90 |
/** **/ |
|
91 |
/** Local Utility functions... **/ |
|
92 |
/** **/ |
|
93 |
/******************************************/ |
|
94 |
/******************************************/ |
|
95 |
||
96 |
||
97 |
/* |
|
98 |
* Function to determine next transaction id. |
|
99 |
* |
|
100 |
* We use a library wide transaction id, which means that we |
|
101 |
* use a new transaction id no matter what slave to which we will |
|
102 |
* be sending the request... |
|
103 |
*/ |
|
104 |
static inline u16 next_transaction_id(void) { |
|
105 |
static u16 next_id = 0; |
|
106 |
return next_id++; |
|
107 |
} |
|
108 |
||
109 |
||
110 |
/* |
|
111 |
* Functions to convert u16 variables |
|
112 |
* between network and host byte order |
|
113 |
* |
|
114 |
* NOTE: Modbus uses MSByte first, just like |
|
115 |
* tcp/ip, so we could be tempted to use the htons() and |
|
116 |
* ntohs() functions to guarantee code portability. |
|
117 |
* |
|
118 |
* However, on some embedded systems running Linux |
|
119 |
* these functions only work if the 16 bit words are |
|
120 |
* stored on even addresses. This is not always the |
|
121 |
* case in our code, so we have to define our own |
|
122 |
* conversion functions... |
|
123 |
*/ |
|
124 |
||
125 |
/* if using gcc, use it to determine byte order... */ |
|
126 |
#ifndef __BYTE_ORDER |
|
127 |
#if defined(__GNUC__) |
|
128 |
/* We have GCC, which should define __LITTLE_ENDIAN__ */ |
|
129 |
# if defined(__LITTLE_ENDIAN__) |
|
130 |
# define __BYTE_ORDER __LITTLE_ENDIAN |
|
131 |
# else |
|
132 |
# define __BYTE_ORDER __BIG_ENDIAN |
|
133 |
# endif |
|
134 |
#endif /* __GNUC__ */ |
|
135 |
#endif /* __BYTE_ORDER */ |
|
136 |
||
137 |
||
138 |
/* If we still don't know byte order, try to get it from <sys/param.h> */ |
|
139 |
#ifndef __BYTE_ORDER |
|
140 |
#include <sys/param.h> |
|
141 |
#endif |
|
142 |
||
143 |
||
144 |
#ifndef __BYTE_ORDER |
|
145 |
# ifdef BYTE_ORDER |
|
146 |
# if BYTE_ORDER == LITTLE_ENDIAN |
|
147 |
# define __BYTE_ORDER __LITTLE_ENDIAN |
|
148 |
# else |
|
149 |
# if BYTE_ORDER == BIG_ENDIAN |
|
150 |
# define __BYTE_ORDER __BIG_ENDIAN |
|
151 |
# endif |
|
152 |
# endif |
|
153 |
# endif /* BYTE_ORDER */ |
|
154 |
#endif /* __BYTE_ORDER */ |
|
155 |
||
156 |
||
157 |
||
158 |
||
159 |
||
160 |
#ifdef __BYTE_ORDER |
|
161 |
# if __BYTE_ORDER == __LITTLE_ENDIAN |
|
162 |
||
163 |
/**************************************************************/ |
|
164 |
/* u16 conversion functions to use on little endian platforms */ |
|
165 |
/**************************************************************/ |
|
166 |
||
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
167 |
/* NOTE: The input parameter must be the address |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
168 |
* of an u16 passed as a pointer to u8 |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
169 |
* |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
170 |
* We use u8 *ptr as input parameter and read both (ptr+0) and (ptr+1) |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
171 |
* instead of using u16 *ptr because we sometimes receive data in packtes |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
172 |
* that are not aligned on even addresses, so some compilers recognize that |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
173 |
* the given odd address cannot be used as a pointer to u16 and therefore |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
174 |
* adjust the pointer by (+1) or (-1), basicaly breacking our code! |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
175 |
* So, we revert to u8 pointers... for u16 values. |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
176 |
*/ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
177 |
static inline void mb_hton(u8 *u16_from_ptr, u8 *u16_to_ptr) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
178 |
u16_to_ptr[0] = u16_from_ptr[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
179 |
u16_to_ptr[1] = u16_from_ptr[0]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
180 |
} |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
181 |
#define mb_ntoh(a, b) mb_hton(a, b) |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
182 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
183 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
184 |
static inline void mb_hton_count(u8 *u16_ptr, unsigned count) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
185 |
unsigned i; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
186 |
for (i = 0; i < count*2; i += 2) { |
0 | 187 |
/* swap the bytes around... |
188 |
* a = a ^ b; |
|
189 |
* b = a ^ b; |
|
190 |
* a = a ^ b; |
|
191 |
*/ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
192 |
(u16_ptr+i)[0] ^= (u16_ptr+i)[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
193 |
(u16_ptr+i)[1] ^= (u16_ptr+i)[0]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
194 |
(u16_ptr+i)[0] ^= (u16_ptr+i)[1]; |
0 | 195 |
} |
196 |
} |
|
197 |
#define mb_ntoh_count(w, count) mb_hton_count(w, count) |
|
198 |
||
199 |
||
200 |
||
201 |
# else |
|
202 |
# if __BYTE_ORDER == __BIG_ENDIAN |
|
203 |
/***********************************************************/ |
|
204 |
/* u16 conversion functions to use on big endian platforms */ |
|
205 |
/***********************************************************/ |
|
206 |
||
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
207 |
/* We don't need to swap the bytes around! */ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
208 |
static inline void mb_hton(u8 *u16_from_ptr, u8 *u16_to_ptr) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
209 |
u16_to_ptr[0] = u16_from_ptr[0]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
210 |
u16_to_ptr[1] = u16_from_ptr[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
211 |
} |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
212 |
#define mb_ntoh(a, b) mb_hton(a, b) |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
213 |
|
0 | 214 |
#define mb_hton_count(w, count) /* empty ! */ |
215 |
#define mb_ntoh_count(w, count) /* empty ! */ |
|
216 |
||
217 |
||
218 |
# else |
|
219 |
/********************************************************/ |
|
220 |
/* u16 conversion functions to use on generic platforms */ |
|
221 |
/********************************************************/ |
|
222 |
||
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
223 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
224 |
/* We can't determine endiannes at compile time, so we do it at runtime. |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
225 |
* With any luck the compiler will be able to determine the result of the |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
226 |
* comparison at compile time and end up discarding the non-used code |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
227 |
* and the 'if' itself from the final executable. |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
228 |
*/ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
229 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
230 |
static union {u16 u16; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
231 |
u8 u8[2];} endian_ = 0x0102; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
232 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
233 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
234 |
static inline void mb_hton(u8 *u16_from_ptr, u8 *u16_to_ptr) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
235 |
if (endian_.u8[0] == 0x01) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
236 |
/* machine is big endian -> no swapping */ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
237 |
u16_to_ptr[0] = u16_from_ptr[0]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
238 |
u16_to_ptr[1] = u16_from_ptr[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
239 |
} else { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
240 |
/* machine is little endian -> we swap bytes around */ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
241 |
u16_to_ptr[0] = u16_from_ptr[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
242 |
u16_to_ptr[1] = u16_from_ptr[0]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
243 |
} |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
244 |
} |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
245 |
#define mb_ntoh(a, b) mb_hton(a, b) |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
246 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
247 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
248 |
static inline void mb_hton_count(u8 *u16_ptr, unsigned count) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
249 |
unsigned i; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
250 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
251 |
if (endian_.u8[0] == 0x01) |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
252 |
/* machine is big endian. Nothing to do */ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
253 |
return; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
254 |
|
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
255 |
/* machine is little endian -> we swap bytes around */ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
256 |
for (i = 0; i < count*2; i += 2) { |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
257 |
/* swap the bytes around... |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
258 |
* a = a ^ b; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
259 |
* b = a ^ b; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
260 |
* a = a ^ b; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
261 |
*/ |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
262 |
(u16_ptr+i)[0] ^= (u16_ptr+i)[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
263 |
(u16_ptr+i)[1] ^= (u16_ptr+i)[0]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
264 |
(u16_ptr+i)[0] ^= (u16_ptr+i)[1]; |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
265 |
} |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
266 |
} |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
267 |
#define mb_ntoh_count(w, count) mb_hton_count(w, count) |
0 | 268 |
|
269 |
# endif |
|
270 |
# endif |
|
271 |
#endif /* __BYTE_ORDER */ |
|
272 |
||
273 |
||
274 |
||
275 |
||
276 |
||
277 |
||
278 |
||
279 |
||
280 |
||
281 |
||
282 |
/***********************************************/ |
|
283 |
/***********************************************/ |
|
284 |
/** **/ |
|
285 |
/** Handle requests from master/client **/ |
|
286 |
/** **/ |
|
287 |
/***********************************************/ |
|
288 |
/***********************************************/ |
|
289 |
||
290 |
||
291 |
/* Handle functions 0x01 and 0x02 */ |
|
292 |
typedef int (*read_bits_callback_t)(void *arg, u16 start_addr, u16 bit_count, u8 *data_bytes); |
|
293 |
static int handle_read_bits (u8 *query_packet, |
|
294 |
u8 **resp_packet_ptr, |
|
295 |
u8 *error_code, |
|
296 |
read_bits_callback_t read_bits_callback, |
|
297 |
void *callback_arg |
|
298 |
) { |
|
299 |
u16 start_addr, count; |
|
300 |
int res; |
|
301 |
u8 *resp_packet; |
|
302 |
||
303 |
/* If no callback, handle as if function is not supported... */ |
|
304 |
if (read_bits_callback == NULL) |
|
305 |
{*error_code = ERR_ILLEGAL_FUNCTION; return -1;} |
|
306 |
||
307 |
/* in oprder for the data in this packet to be aligned on even numbered addresses, this |
|
308 |
* response packet will start off at an odd numbered byte... |
|
309 |
* We therefore add 1 to the address where the packet starts. |
|
310 |
*/ |
|
311 |
(*resp_packet_ptr)++; |
|
312 |
resp_packet = *resp_packet_ptr; |
|
313 |
||
314 |
/* NOTE: |
|
315 |
* Modbus uses high level addressing starting off from 1, but |
|
316 |
* this is sent as 0 on the wire! |
|
317 |
* We could expect the user to specify high level addressing |
|
318 |
* starting at 1, and do the conversion to start off at 0 here. |
|
319 |
* However, to do this we would then need to use an u32 data type |
|
320 |
* to correctly hold the address supplied by the user (which could |
|
321 |
* correctly be 65536, which does not fit in an u16), which would |
|
322 |
* in turn require us to check whether the address supplied by the user |
|
323 |
* is correct (i.e. <= 65536). |
|
324 |
* I decided to go with the other option of using an u16, and |
|
325 |
* requiring the user to use addressing starting off at 0! |
|
326 |
*/ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
327 |
mb_ntoh(&(query_packet[2]), (u8 *)&start_addr); |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
328 |
mb_ntoh(&(query_packet[4]), (u8 *)&count); |
0 | 329 |
|
330 |
#ifdef DEBUG |
|
331 |
printf("handle_read_input_bits() called. slave=%d, function=%d, start_addr=%d, count=%d\n", |
|
332 |
query_packet[0], query_packet[1], start_addr, count); |
|
333 |
#endif |
|
334 |
||
335 |
if ((count > MAX_READ_BITS) || (count < 1)) |
|
336 |
{*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;} |
|
337 |
||
338 |
/* Remember, we are using addressing starting off at 0, in the start_addr variable! */ |
|
339 |
/* This means that he highest acceptable address is 65535, when count=1 .... */ |
|
340 |
/* Note the use of 65536 in the comparison will force automatic upgrade of u16 variables! */ |
|
341 |
/* => start_addr + count will nver overflow the u16 type! */ |
|
342 |
if (start_addr + count > 65536) |
|
343 |
{*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
344 |
||
345 |
/* start building response frame... */ |
|
346 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
347 |
resp_packet[1] = query_packet[1]; /* function (either 0x01 or 0x02 ! */ |
|
348 |
resp_packet[2] = (count + 7) / 8; /* number of data bytes = ceil(count/8) */ |
|
349 |
||
350 |
res = read_bits_callback(callback_arg, start_addr, count, &(resp_packet[3])); |
|
351 |
if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
352 |
if (res < 0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;} |
|
353 |
||
354 |
return resp_packet[2] + 3; /* packet size is data length + 3 bytes -> slave, function, count */ |
|
355 |
} |
|
356 |
||
357 |
||
358 |
||
359 |
/* Handle function 0x01 */ |
|
360 |
int handle_read_output_bits (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) |
|
361 |
{return handle_read_bits(query_packet, resp_packet_ptr, error_code, callbacks->read_outbits, callbacks->arg);} |
|
362 |
||
363 |
/* Handle function 0x02 */ |
|
364 |
int handle_read_input_bits (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) |
|
365 |
{return handle_read_bits(query_packet, resp_packet_ptr, error_code, callbacks->read_inbits, callbacks->arg);} |
|
366 |
||
367 |
||
368 |
||
369 |
||
370 |
/* Handle functions 0x03 and 0x04 */ |
|
371 |
typedef int (*read_words_callback_t)(void *arg, u16 start_addr, u16 word_count, u16 *data_words); |
|
372 |
static int handle_read_words (u8 *query_packet, |
|
373 |
u8 **resp_packet_ptr, |
|
374 |
u8 *error_code, |
|
375 |
read_words_callback_t read_words_callback, |
|
376 |
void *callback_arg |
|
377 |
) { |
|
378 |
u16 start_addr, count; |
|
379 |
int res; |
|
380 |
u8 *resp_packet; |
|
381 |
||
382 |
/* If no callback, handle as if function is not supported... */ |
|
383 |
if (read_words_callback == NULL) |
|
384 |
{*error_code = ERR_ILLEGAL_FUNCTION; return -1;} |
|
385 |
||
386 |
/* See equivalent comment in handle_read_bits() */ |
|
387 |
(*resp_packet_ptr)++; |
|
388 |
resp_packet = *resp_packet_ptr; |
|
389 |
||
390 |
/* See equivalent comment in handle_read_bits() */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
391 |
mb_ntoh(&(query_packet[2]), (u8 *)&start_addr); |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
392 |
mb_ntoh(&(query_packet[4]), (u8 *)&count); |
0 | 393 |
|
394 |
#ifdef DEBUG |
|
395 |
printf("handle_read_output_words() called. slave=%d, function=%d, start_addr=%d, count=%d\n", |
|
396 |
query_packet[0], query_packet[1], start_addr, count); |
|
397 |
#endif |
|
398 |
||
399 |
if ((count > MAX_READ_REGS) || (count < 1)) |
|
400 |
{*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;} |
|
401 |
||
402 |
/* See equivalent comment in handle_read_bits() */ |
|
403 |
if (start_addr + count > 65536) |
|
404 |
{*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
405 |
||
406 |
/* start building response frame... */ |
|
407 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
408 |
resp_packet[1] = query_packet[1]; /* function code, either 0x03 or 0x04 !!!*/ |
|
409 |
resp_packet[2] = count * 2; /* number of bytes of data... */ |
|
410 |
||
411 |
res = read_words_callback(callback_arg, start_addr, count, (u16 *)&(resp_packet[3])); |
|
412 |
if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
413 |
if (res < 0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;} |
|
414 |
||
415 |
/* convert all data from host to network byte order. */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
416 |
mb_hton_count(&(resp_packet[3]), count); |
0 | 417 |
|
418 |
return resp_packet[2] + 3; /* packet size is data length + 3 bytes -> slave, function, count */ |
|
419 |
} |
|
420 |
||
421 |
||
422 |
||
423 |
||
424 |
/* Handle function 0x03 */ |
|
425 |
int handle_read_output_words (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) |
|
426 |
{return handle_read_words(query_packet, resp_packet_ptr, error_code, callbacks->read_outwords, callbacks->arg);} |
|
427 |
||
428 |
/* Handle function 0x04 */ |
|
429 |
int handle_read_input_words (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) |
|
430 |
{return handle_read_words(query_packet, resp_packet_ptr, error_code, callbacks->read_inwords, callbacks->arg);} |
|
431 |
||
432 |
||
433 |
||
434 |
/* Handle function 0x05 */ |
|
435 |
int handle_write_output_bit (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) { |
|
436 |
u16 start_addr; |
|
437 |
int res; |
|
438 |
u8 *resp_packet; |
|
439 |
||
440 |
/* If no callback, handle as if function is not supported... */ |
|
441 |
if (callbacks->write_outbits == NULL) |
|
442 |
{*error_code = ERR_ILLEGAL_FUNCTION; return -1;} |
|
443 |
||
444 |
resp_packet = *resp_packet_ptr; |
|
445 |
||
446 |
/* See equivalent comment in handle_read_bits() */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
447 |
mb_ntoh(&(query_packet[2]), (u8 *)&start_addr); |
0 | 448 |
|
449 |
#ifdef DEBUG |
|
450 |
printf("handle_write_output_bit() called. slave=%d, function=%d, start_addr=%d\n", |
|
451 |
query_packet[0], query_packet[1], start_addr); |
|
452 |
#endif |
|
453 |
||
454 |
// byte 5 Must be 0x00, byte 4 must be 0x00 or 0xFF !! |
|
455 |
if ( (query_packet[5] != 0) || |
|
456 |
((query_packet[4] != 0) && (query_packet[4] != 0xFF))) |
|
457 |
{*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;} |
|
458 |
||
459 |
/* Address will always be valid, no need to check! */ |
|
460 |
// if (start_addr > 65535) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
461 |
||
462 |
/* start building response frame... */ |
|
463 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
464 |
resp_packet[1] = query_packet[1]; /* function */ |
|
465 |
resp_packet[2] = query_packet[2]; /* start address - hi byte */ |
|
466 |
resp_packet[3] = query_packet[3]; /* start address - lo byte */ |
|
467 |
resp_packet[4] = query_packet[4]; /* value: 0x00 or 0xFF */ |
|
468 |
resp_packet[5] = query_packet[5]; /* value: must be 0x00 */ |
|
469 |
||
470 |
res = (callbacks->write_outbits)(callbacks->arg, start_addr, 1, &(query_packet[4])); |
|
471 |
if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
472 |
if (res < 0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;} |
|
473 |
||
474 |
return 6; /* response packet size, including slave id in byte 0 */ |
|
475 |
} |
|
476 |
||
477 |
||
478 |
||
479 |
/* Handle function 0x06 */ |
|
480 |
int handle_write_output_word (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) { |
|
481 |
u16 start_addr; |
|
482 |
int res; |
|
483 |
u8 *resp_packet; |
|
484 |
||
485 |
/* If no callback, handle as if function is not supported... */ |
|
486 |
if (callbacks->write_outwords == NULL) |
|
487 |
{*error_code = ERR_ILLEGAL_FUNCTION; return -1;} |
|
488 |
||
489 |
resp_packet = *resp_packet_ptr; |
|
490 |
||
491 |
/* See equivalent comment in handle_read_bits() */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
492 |
mb_ntoh(&(query_packet[2]), (u8 *)&start_addr); |
0 | 493 |
|
494 |
#ifdef DEBUG |
|
495 |
printf("handle_write_output_word() called. slave=%d, function=%d, start_addr=%d\n", |
|
496 |
query_packet[0], query_packet[1], start_addr); |
|
497 |
#endif |
|
498 |
||
499 |
/* Address will always be valid, no need to check! */ |
|
500 |
// if (start_addr > 65535) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
501 |
||
502 |
/* start building response frame... */ |
|
503 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
504 |
resp_packet[1] = query_packet[1]; /* function */ |
|
505 |
resp_packet[2] = query_packet[2]; /* start address - hi byte */ |
|
506 |
resp_packet[3] = query_packet[3]; /* start address - lo byte */ |
|
507 |
resp_packet[4] = query_packet[4]; /* value - hi byte */ |
|
508 |
resp_packet[5] = query_packet[5]; /* value - lo byte */ |
|
509 |
||
510 |
/* convert data from network to host byte order */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
511 |
mb_ntoh_count(&(query_packet[4]), 1); |
0 | 512 |
|
513 |
res = (callbacks->write_outwords)(callbacks->arg, start_addr, 1, (u16 *)&(query_packet[4])); |
|
514 |
if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
515 |
if (res < 0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;} |
|
516 |
||
517 |
return 6; /* packet size is 6 -> slave, function, addr(2), value(2) */ |
|
518 |
} |
|
519 |
||
520 |
||
521 |
||
522 |
/* Handle function 0x0F */ |
|
523 |
int handle_write_output_bits (u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) { |
|
524 |
u16 start_addr, count; |
|
525 |
int res; |
|
526 |
u8 *resp_packet; |
|
527 |
||
528 |
/* If no callback, handle as if function is not supported... */ |
|
529 |
if (callbacks->write_outbits == NULL) |
|
530 |
{*error_code = ERR_ILLEGAL_FUNCTION; return -1;} |
|
531 |
||
532 |
resp_packet = *resp_packet_ptr; |
|
533 |
||
534 |
/* See equivalent comment in handle_read_bits() */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
535 |
mb_ntoh(&(query_packet[2]), (u8 *)&start_addr); |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
536 |
mb_ntoh(&(query_packet[4]), (u8 *)&count); |
0 | 537 |
|
538 |
#ifdef DEBUG |
|
539 |
printf("handle_write_output_bits() called. slave=%d, function=%d, start_addr=%d, count=%d\n", |
|
540 |
query_packet[0], query_packet[1], start_addr, count); |
|
541 |
#endif |
|
542 |
||
543 |
if ((count > MAX_WRITE_COILS) || (count < 1) || ((count+7)/8 != query_packet[6]) ) |
|
544 |
{*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;} |
|
545 |
||
546 |
/* See equivalent comment in handle_read_bits() */ |
|
547 |
if (start_addr + count > 65536) |
|
548 |
{*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
549 |
||
550 |
/* start building response frame... */ |
|
551 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
552 |
resp_packet[1] = query_packet[1]; /* function */ |
|
553 |
resp_packet[2] = query_packet[2]; /* start address - hi byte */ |
|
554 |
resp_packet[3] = query_packet[3]; /* start address - lo byte */ |
|
555 |
resp_packet[4] = query_packet[4]; /* count - hi byte */ |
|
556 |
resp_packet[5] = query_packet[5]; /* count - lo byte */ |
|
557 |
||
558 |
res = (callbacks->write_outbits)(callbacks->arg, start_addr, count, &(query_packet[7])); |
|
559 |
if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
560 |
if (res < 0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;} |
|
561 |
||
562 |
return 6; /* packet size is 6 -> slave, function, addr(2), count(2) */ |
|
563 |
} |
|
564 |
||
565 |
||
566 |
||
567 |
||
568 |
/* Handle function 0x10 */ |
|
569 |
int handle_write_output_words(u8 *query_packet, u8 **resp_packet_ptr, u8 *error_code, mb_slave_callback_t *callbacks) { |
|
570 |
u16 start_addr, count; |
|
571 |
int res; |
|
572 |
u8 *resp_packet; |
|
573 |
||
574 |
/* If no callback, handle as if function is not supported... */ |
|
575 |
if (callbacks->write_outwords == NULL) |
|
576 |
{*error_code = ERR_ILLEGAL_FUNCTION; return -1;} |
|
577 |
||
578 |
resp_packet = *resp_packet_ptr; |
|
579 |
||
580 |
/* See equivalent comment in handle_read_bits() */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
581 |
mb_ntoh(&(query_packet[2]), (u8 *)&start_addr); |
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
582 |
mb_ntoh(&(query_packet[4]), (u8 *)&count); |
0 | 583 |
|
584 |
if ((count > MAX_WRITE_REGS) || (count < 1) || (count*2 != query_packet[6]) ) |
|
585 |
{*error_code = ERR_ILLEGAL_DATA_VALUE; return -1;} |
|
586 |
||
587 |
/* See equivalent comment in handle_read_bits() */ |
|
588 |
if (start_addr + count > 65536) |
|
589 |
{*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
590 |
||
591 |
/* start building response frame... */ |
|
592 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
593 |
resp_packet[1] = query_packet[1]; /* function */ |
|
594 |
resp_packet[2] = query_packet[2]; /* start address - hi byte */ |
|
595 |
resp_packet[3] = query_packet[3]; /* start address - lo byte */ |
|
596 |
resp_packet[4] = query_packet[4]; /* count - hi byte */ |
|
597 |
resp_packet[5] = query_packet[5]; /* count - lo byte */ |
|
598 |
||
599 |
/* convert all data from network to host byte order */ |
|
6
fe4088d5573a
change hton() and ntoh() so we don't use (u8 *) to (u16 *) casts. Some compilers incorrectly change the pointer to force alignment with even addresses.
Mario de Sousa <msousa@fe.up.pt>
parents:
0
diff
changeset
|
600 |
mb_ntoh_count(&(query_packet[7]), count); |
0 | 601 |
|
602 |
res = (callbacks->write_outwords)(callbacks->arg, start_addr, count, (u16 *)&(query_packet[7])); |
|
603 |
if (res == -2) {*error_code = ERR_ILLEGAL_DATA_ADDRESS; return -1;} |
|
604 |
if (res < 0) {*error_code = ERR_SLAVE_DEVICE_FAILURE; return -1;} |
|
605 |
||
606 |
return 6; /* packet size is 6 -> slave, function, addr(2), count(2) */ |
|
607 |
} |
|
608 |
||
609 |
||
610 |
||
611 |
||
612 |
||
613 |
||
614 |
||
615 |
||
616 |
/***********************************************/ |
|
617 |
/***********************************************/ |
|
618 |
/** **/ |
|
619 |
/** initialise / shutdown the library **/ |
|
620 |
/** **/ |
|
621 |
/***********************************************/ |
|
622 |
/***********************************************/ |
|
623 |
||
624 |
int mb_slave_init__(int extra_bytes) { |
|
625 |
buff_extra_bytes_ = extra_bytes; |
|
626 |
return 0; |
|
627 |
} |
|
628 |
||
629 |
||
630 |
int mb_slave_done__(void) |
|
631 |
{return 0;} |
|
632 |
||
633 |
||
634 |
#if 0 |
|
635 |
int mb_slave_init(int nd_count) { |
|
636 |
int extra_bytes; |
|
637 |
||
638 |
#ifdef DEBUG |
|
639 |
fprintf( stderr, "mb_slave_init()\n"); |
|
640 |
fprintf( stderr, "creating %d nodes\n", nd_count); |
|
641 |
#endif |
|
642 |
||
643 |
/* initialise layer 1 library */ |
|
644 |
if (modbus_init(nd_count, DEF_OPTIMIZATION, &extra_bytes) < 0) |
|
645 |
goto error_exit_0; |
|
646 |
||
647 |
/* initialise this library */ |
|
648 |
if (mb_slave_init__(extra_bytes) < 0) |
|
649 |
goto error_exit_1; |
|
650 |
||
651 |
return 0; |
|
652 |
||
653 |
error_exit_1: |
|
654 |
modbus_done(); |
|
655 |
error_exit_0: |
|
656 |
return -1; |
|
657 |
} |
|
658 |
||
659 |
||
660 |
int mb_slave_done(void) { |
|
661 |
mb_slave_done__(void) |
|
662 |
return modbus_done(); |
|
663 |
} |
|
664 |
#endif |
|
665 |
||
666 |
||
667 |
||
668 |
/***********************************************/ |
|
669 |
/***********************************************/ |
|
670 |
/** **/ |
|
671 |
/** open/close slave connection **/ |
|
672 |
/** **/ |
|
673 |
/***********************************************/ |
|
674 |
/***********************************************/ |
|
675 |
||
676 |
/* Create a new slave/server */ |
|
677 |
/* NOTE: We use the lower 2 bits of the returned node id to identify which |
|
678 |
* layer1 implementation to use. |
|
679 |
* 0 -> TCP |
|
680 |
* 1 -> RTU |
|
681 |
* 2 -> ASCII |
|
682 |
* 4 -> unused |
|
683 |
* The node id used by the layer1 is shifted left 2 bits |
|
684 |
* before returning the node id to the caller! |
|
685 |
*/ |
|
686 |
int mb_slave_new(node_addr_t node_addr) { |
|
687 |
int res = -1; |
|
688 |
#ifdef DEBUG |
|
689 |
fprintf( stderr, "mb_slave_connect()\n"); |
|
690 |
#endif |
|
691 |
||
692 |
/* call layer 1 library */ |
|
693 |
switch(node_addr.naf) { |
|
694 |
case naf_tcp: |
|
695 |
res = modbus_tcp_listen(node_addr); |
|
696 |
if (res >= 0) res = res*4 + 0 /* offset into fptr_ with TCP functions */; |
|
697 |
return res; |
|
698 |
case naf_rtu: |
|
699 |
res = modbus_rtu_listen(node_addr); |
|
700 |
if (res >= 0) res = res*4 + 1 /* offset into fptr_ with RTU functions */; |
|
701 |
return res; |
|
702 |
case naf_ascii: |
|
703 |
res = modbus_ascii_listen(node_addr); |
|
704 |
if (res >= 0) res = res*4 + 2 /* offset into fptr_ with ASCII functions */; |
|
705 |
return res; |
|
706 |
} |
|
707 |
||
708 |
return -1; |
|
709 |
} |
|
710 |
||
711 |
||
712 |
||
713 |
||
714 |
int mb_slave_close(int fd) { |
|
715 |
#ifdef DEBUG |
|
716 |
fprintf( stderr, "mb_slave_close(): nd = %d\n", fd); |
|
717 |
#endif |
|
718 |
get_ttyfd(); /* declare the ttyfd variable!! */ |
|
719 |
/* call layer 1 library */ |
|
720 |
/* will call one of modbus_tcp_close(), modbus_rtu_close(), modbus_ascii_close() */ |
|
721 |
return modbus_close(ttyfd); |
|
722 |
} |
|
723 |
||
724 |
||
725 |
||
726 |
||
727 |
||
728 |
/***********************************************/ |
|
729 |
/***********************************************/ |
|
730 |
/** **/ |
|
731 |
/** Run the slave **/ |
|
732 |
/** **/ |
|
733 |
/***********************************************/ |
|
734 |
/***********************************************/ |
|
735 |
||
736 |
/* Execute infinite loop waiting and replying to requests coming from clients/master |
|
737 |
* This function enters an infinite loop wating for new connection requests, |
|
738 |
* and for modbus requests over previoulsy open connections... |
|
739 |
* |
|
740 |
* The frames are read from: |
|
741 |
* - the node descriptor nd, if nd >= 0 |
|
742 |
* When using TCP, if the referenced node nd was created to listen for new connections |
|
743 |
* [mb_slave_listen()], then this function will also reply to Modbus data requests arriving |
|
744 |
* on other nodes that were created as a consequence of accepting connections requests to |
|
745 |
* the referenced node nd. |
|
746 |
* All other nodes are ignored! |
|
747 |
* |
|
748 |
* - any valid and initialised TCP node descriptor, if nd = -1 |
|
749 |
* In this case, will also accept connection requests arriving from a previously |
|
750 |
* created node to listen for new connection requests [mb_slave_listen() ]. |
|
751 |
* NOTE: (only avaliable if using TCP) |
|
752 |
* |
|
753 |
* slaveid identifies the address (RTU and ASCII) or slaveid (TCP) that we implement. |
|
754 |
* Any requests that we receive sent with a slaveid different |
|
755 |
* than the one specified, and also different to 0, will be silently ignored! |
|
756 |
* Whatever the slaveid specified, we always reply to requests |
|
757 |
* to slaveid 0 (the modbus broadcast address). |
|
758 |
* Calling this function with a slaveid of 0 means to ignore this |
|
759 |
* parameter and to reply to all requests (whatever the slaveid |
|
760 |
* used in the request). This should mostly be used by TCP servers... |
|
761 |
*/ |
|
762 |
||
763 |
int mb_slave_run(int fd, mb_slave_callback_t callback_functions, u8 slaveid) { |
|
764 |
int byte_count; |
|
765 |
u16 transaction_id; |
|
766 |
int nd; |
|
767 |
u8 function, error_code = 0; |
|
768 |
int resp_length; |
|
769 |
u8 *query_packet = NULL; |
|
770 |
u8 *resp_packet; |
|
771 |
u8 resp_buffer_[RESP_BUFFER_SIZE]; |
|
772 |
u8 slave; |
|
773 |
||
774 |
get_ttyfd(); /* declare the ttyfd variable!! */ |
|
775 |
||
776 |
#ifdef DEBUG |
|
777 |
fprintf(stderr,"[%lu] mb_slave_run(): Called... fd=%d, ttyfd=%d\n", pthread_self(), fd, ttyfd); |
|
778 |
#endif |
|
779 |
||
780 |
while(1) { |
|
781 |
nd = ttyfd; |
|
782 |
/* will call one of modbus_tcp_read(), modbus_rtu_read(), modbus_ascii_read() */ |
|
783 |
do { |
|
784 |
byte_count = modbus_read(&nd, /* node descriptor */ |
|
785 |
&query_packet, /* u8 **recv_data_ptr, */ |
|
786 |
&transaction_id, /* u16 *transaction_id, */ |
|
787 |
NULL, /* const u8 *send_data, */ |
|
788 |
0, /* int send_length, */ |
|
789 |
NULL /* wait indefenitely */ /* const struct timespec *recv_timeout); */ |
|
790 |
); |
|
791 |
} while (byte_count <= 2); |
|
792 |
||
793 |
#ifdef DEBUG |
|
794 |
{/* display the hex code of each character received */ |
|
795 |
int i; |
|
796 |
printf("[%lu] mb_slave_run() received %d bytes (ptr=%p): \n", pthread_self(), byte_count, query_packet); |
|
797 |
for (i=0; i < byte_count; i++) |
|
798 |
printf("<0x%2X>", query_packet[i]); |
|
799 |
printf("\n"); |
|
800 |
} |
|
801 |
#endif |
|
802 |
||
803 |
slave = query_packet[0]; |
|
804 |
function = query_packet[1]; |
|
805 |
||
806 |
/* We only reply if: |
|
807 |
* - request was sent to broadcast address (slave == 0) |
|
808 |
* OR - we were asked to reply to every request (slaveid == 0) |
|
809 |
* OR - request matches the slaveid we were asked to accept (slave == slaveid) |
|
810 |
* |
|
811 |
* Otherwise, silently ignore the received request!!! |
|
812 |
*/ |
|
813 |
if ((slaveid == 0) || (slave == 0) || (slave == slaveid)) { |
|
814 |
resp_packet = resp_buffer_; |
|
815 |
||
816 |
switch(function) { |
|
817 |
case 0x01: resp_length = handle_read_output_bits (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
818 |
case 0x02: resp_length = handle_read_input_bits (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
819 |
case 0x03: resp_length = handle_read_output_words (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
820 |
case 0x04: resp_length = handle_read_input_words (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
821 |
case 0x05: resp_length = handle_write_output_bit (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
822 |
case 0x06: resp_length = handle_write_output_word (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
823 |
case 0x0F: resp_length = handle_write_output_bits (query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
824 |
case 0x10: resp_length = handle_write_output_words(query_packet, &resp_packet, &error_code, &callback_functions); break; |
|
825 |
/* return exception code 0x01 -> function not supported! */ |
|
826 |
default : resp_length = -1; error_code = 0x01; break; |
|
827 |
}; /* switch(function) */ |
|
828 |
||
829 |
if (resp_length < 0) { |
|
830 |
/* return error... */ |
|
831 |
/* build exception response frame... */ |
|
832 |
resp_packet = resp_buffer_; |
|
833 |
resp_packet[0] = query_packet[0]; /* slave */ |
|
834 |
resp_packet[1] = query_packet[1] | 0x80; /* function code with error bit activated! */ |
|
835 |
resp_packet[2] = error_code; |
|
836 |
resp_length = 3; |
|
837 |
} |
|
838 |
modbus_write(nd, resp_packet, resp_length, transaction_id, NULL /*transmit_timeout*/); |
|
839 |
}; /* if not ignore request */ |
|
840 |
}; /* while(1) */ |
|
841 |
||
842 |
/* humour the compiler... */ |
|
843 |
return 0; |
|
844 |
} |
|
845 |
||
846 |
||
847 |
||
848 |
||
849 |
||
850 |
||
851 |
||
852 |
||
853 |
||
854 |
||
855 |
||
856 |
||
857 |