Commit 49af73debd756be68497a61bd53a07c02673da96

Authored by SZ Lin (林上智)
Committed by Stéphane Raimbault
1 parent 1a503b7f

Fix float endianness issue on big endian architecture.

It converts float values depending on what order they come in.

This patch was modified from rm5248 [1]

[1] https://github.com/synexxus/libmodbus/commit/a511768e7fe7ec52d7bae1d9ae04e33f87a59627
src/modbus-data.c
@@ -123,9 +123,18 @@ float modbus_get_float_abcd(const uint16_t *src) @@ -123,9 +123,18 @@ float modbus_get_float_abcd(const uint16_t *src)
123 { 123 {
124 float f; 124 float f;
125 uint32_t i; 125 uint32_t i;
  126 + uint8_t a, b, c, d;
126 127
127 - i = ntohl(((uint32_t)src[0] << 16) + src[1]);  
128 - memcpy(&f, &i, sizeof(float)); 128 + a = (src[0] >> 8) & 0xFF;
  129 + b = (src[0] >> 0) & 0xFF;
  130 + c = (src[1] >> 8) & 0xFF;
  131 + d = (src[1] >> 0) & 0xFF;
  132 +
  133 + i = (a << 24) |
  134 + (b << 16) |
  135 + (c << 8) |
  136 + (d << 0);
  137 + memcpy(&f, &i, 4);
129 138
130 return f; 139 return f;
131 } 140 }
@@ -135,9 +144,18 @@ float modbus_get_float_dcba(const uint16_t *src) @@ -135,9 +144,18 @@ float modbus_get_float_dcba(const uint16_t *src)
135 { 144 {
136 float f; 145 float f;
137 uint32_t i; 146 uint32_t i;
  147 + uint8_t a, b, c, d;
138 148
139 - i = ntohl(bswap_32((((uint32_t)src[0]) << 16) + src[1]));  
140 - memcpy(&f, &i, sizeof(float)); 149 + a = (src[0] >> 8) & 0xFF;
  150 + b = (src[0] >> 0) & 0xFF;
  151 + c = (src[1] >> 8) & 0xFF;
  152 + d = (src[1] >> 0) & 0xFF;
  153 +
  154 + i = (d << 24) |
  155 + (c << 16) |
  156 + (b << 8) |
  157 + (a << 0);
  158 + memcpy(&f, &i, 4);
141 159
142 return f; 160 return f;
143 } 161 }
@@ -147,9 +165,18 @@ float modbus_get_float_badc(const uint16_t *src) @@ -147,9 +165,18 @@ float modbus_get_float_badc(const uint16_t *src)
147 { 165 {
148 float f; 166 float f;
149 uint32_t i; 167 uint32_t i;
  168 + uint8_t a, b, c, d;
150 169
151 - i = ntohl((uint32_t)(bswap_16(src[0]) << 16) + bswap_16(src[1]));  
152 - memcpy(&f, &i, sizeof(float)); 170 + a = (src[0] >> 8) & 0xFF;
  171 + b = (src[0] >> 0) & 0xFF;
  172 + c = (src[1] >> 8) & 0xFF;
  173 + d = (src[1] >> 0) & 0xFF;
  174 +
  175 + i = (b << 24) |
  176 + (a << 16) |
  177 + (d << 8) |
  178 + (c << 0);
  179 + memcpy(&f, &i, 4);
153 180
154 return f; 181 return f;
155 } 182 }
@@ -159,9 +186,18 @@ float modbus_get_float_cdab(const uint16_t *src) @@ -159,9 +186,18 @@ float modbus_get_float_cdab(const uint16_t *src)
159 { 186 {
160 float f; 187 float f;
161 uint32_t i; 188 uint32_t i;
  189 + uint8_t a, b, c, d;
162 190
163 - i = ntohl((((uint32_t)src[1]) << 16) + src[0]);  
164 - memcpy(&f, &i, sizeof(float)); 191 + a = (src[0] >> 8) & 0xFF;
  192 + b = (src[0] >> 0) & 0xFF;
  193 + c = (src[1] >> 8) & 0xFF;
  194 + d = (src[1] >> 0) & 0xFF;
  195 +
  196 + i = (c << 24) |
  197 + (d << 16) |
  198 + (a << 8) |
  199 + (b << 0);
  200 + memcpy(&f, &i, 4);
165 201
166 return f; 202 return f;
167 } 203 }
@@ -176,50 +212,84 @@ float modbus_get_float(const uint16_t *src) @@ -176,50 +212,84 @@ float modbus_get_float(const uint16_t *src)
176 memcpy(&f, &i, sizeof(float)); 212 memcpy(&f, &i, sizeof(float));
177 213
178 return f; 214 return f;
  215 +
179 } 216 }
180 217
181 /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */ 218 /* Set a float to 4 bytes for Modbus w/o any conversion (ABCD) */
182 void modbus_set_float_abcd(float f, uint16_t *dest) 219 void modbus_set_float_abcd(float f, uint16_t *dest)
183 { 220 {
184 uint32_t i; 221 uint32_t i;
  222 + uint8_t *out = (uint8_t*) dest;
  223 + uint8_t a, b, c, d;
185 224
186 memcpy(&i, &f, sizeof(uint32_t)); 225 memcpy(&i, &f, sizeof(uint32_t));
187 - i = htonl(i);  
188 - dest[0] = (uint16_t)(i >> 16);  
189 - dest[1] = (uint16_t)i; 226 + a = (i >> 24) & 0xFF;
  227 + b = (i >> 16) & 0xFF;
  228 + c = (i >> 8) & 0xFF;
  229 + d = (i >> 0) & 0xFF;
  230 +
  231 + out[0] = a;
  232 + out[1] = b;
  233 + out[2] = c;
  234 + out[3] = d;
190 } 235 }
191 236
192 /* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */ 237 /* Set a float to 4 bytes for Modbus with byte and word swap conversion (DCBA) */
193 void modbus_set_float_dcba(float f, uint16_t *dest) 238 void modbus_set_float_dcba(float f, uint16_t *dest)
194 { 239 {
195 uint32_t i; 240 uint32_t i;
  241 + uint8_t *out = (uint8_t*) dest;
  242 + uint8_t a, b, c, d;
196 243
197 memcpy(&i, &f, sizeof(uint32_t)); 244 memcpy(&i, &f, sizeof(uint32_t));
198 - i = bswap_32(htonl(i));  
199 - dest[0] = (uint16_t)(i >> 16);  
200 - dest[1] = (uint16_t)i; 245 + a = (i >> 24) & 0xFF;
  246 + b = (i >> 16) & 0xFF;
  247 + c = (i >> 8) & 0xFF;
  248 + d = (i >> 0) & 0xFF;
  249 +
  250 + out[0] = d;
  251 + out[1] = c;
  252 + out[2] = b;
  253 + out[3] = a;
  254 +
201 } 255 }
202 256
203 /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */ 257 /* Set a float to 4 bytes for Modbus with byte swap conversion (BADC) */
204 void modbus_set_float_badc(float f, uint16_t *dest) 258 void modbus_set_float_badc(float f, uint16_t *dest)
205 { 259 {
206 uint32_t i; 260 uint32_t i;
  261 + uint8_t *out = (uint8_t*) dest;
  262 + uint8_t a, b, c, d;
207 263
208 memcpy(&i, &f, sizeof(uint32_t)); 264 memcpy(&i, &f, sizeof(uint32_t));
209 - i = htonl(i);  
210 - dest[0] = (uint16_t)bswap_16(i >> 16);  
211 - dest[1] = (uint16_t)bswap_16(i & 0xFFFF); 265 + a = (i >> 24) & 0xFF;
  266 + b = (i >> 16) & 0xFF;
  267 + c = (i >> 8) & 0xFF;
  268 + d = (i >> 0) & 0xFF;
  269 +
  270 + out[0] = b;
  271 + out[1] = a;
  272 + out[2] = d;
  273 + out[3] = c;
212 } 274 }
213 275
214 /* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */ 276 /* Set a float to 4 bytes for Modbus with word swap conversion (CDAB) */
215 void modbus_set_float_cdab(float f, uint16_t *dest) 277 void modbus_set_float_cdab(float f, uint16_t *dest)
216 { 278 {
217 uint32_t i; 279 uint32_t i;
  280 + uint8_t *out = (uint8_t*) dest;
  281 + uint8_t a, b, c, d;
218 282
219 memcpy(&i, &f, sizeof(uint32_t)); 283 memcpy(&i, &f, sizeof(uint32_t));
220 - i = htonl(i);  
221 - dest[0] = (uint16_t)i;  
222 - dest[1] = (uint16_t)(i >> 16); 284 + a = (i >> 24) & 0xFF;
  285 + b = (i >> 16) & 0xFF;
  286 + c = (i >> 8) & 0xFF;
  287 + d = (i >> 0) & 0xFF;
  288 +
  289 + out[0] = c;
  290 + out[1] = d;
  291 + out[2] = a;
  292 + out[3] = b;
223 } 293 }
224 294
225 /* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */ 295 /* DEPRECATED - Set a float to 4 bytes in a sort of Modbus format! */
tests/unit-test-client.c
@@ -27,6 +27,7 @@ int send_crafted_request(modbus_t *ctx, int function, @@ -27,6 +27,7 @@ int send_crafted_request(modbus_t *ctx, int function,
27 uint16_t max_value, uint16_t bytes, 27 uint16_t max_value, uint16_t bytes,
28 int backend_length, int backend_offset); 28 int backend_length, int backend_offset);
29 int equal_dword(uint16_t *tab_reg, const uint32_t value); 29 int equal_dword(uint16_t *tab_reg, const uint32_t value);
  30 +int is_memory_equal(const void *s1, const void *s2, size_t size);
30 31
31 #define BUG_REPORT(_cond, _format, _args ...) \ 32 #define BUG_REPORT(_cond, _format, _args ...) \
32 printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, # _cond, ## _args) 33 printf("\nLine %d: assertion error for '%s': " _format "\n", __LINE__, # _cond, ## _args)
@@ -40,6 +41,11 @@ int equal_dword(uint16_t *tab_reg, const uint32_t value); @@ -40,6 +41,11 @@ int equal_dword(uint16_t *tab_reg, const uint32_t value);
40 } \ 41 } \
41 }; 42 };
42 43
  44 +int is_memory_equal(const void *s1, const void *s2, size_t size)
  45 +{
  46 + return (memcmp(s1, s2, size) == 0);
  47 +}
  48 +
43 int equal_dword(uint16_t *tab_reg, const uint32_t value) { 49 int equal_dword(uint16_t *tab_reg, const uint32_t value) {
44 return ((tab_reg[0] == (value >> 16)) && (tab_reg[1] == (value & 0xFFFF))); 50 return ((tab_reg[0] == (value >> 16)) && (tab_reg[1] == (value & 0xFFFF)));
45 } 51 }
@@ -287,26 +293,26 @@ int main(int argc, char *argv[]) @@ -287,26 +293,26 @@ int main(int argc, char *argv[])
287 /** FLOAT **/ 293 /** FLOAT **/
288 printf("1/4 Set/get float ABCD: "); 294 printf("1/4 Set/get float ABCD: ");
289 modbus_set_float_abcd(UT_REAL, tab_rp_registers); 295 modbus_set_float_abcd(UT_REAL, tab_rp_registers);
290 - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_ABCD), "FAILED Set float ABCD");  
291 - real = modbus_get_float_abcd(tab_rp_registers); 296 + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_ABCD_SET, 4), "FAILED Set float ABCD");
  297 + real = modbus_get_float_abcd(UT_IREAL_ABCD_GET);
292 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); 298 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL);
293 299
294 printf("2/4 Set/get float DCBA: "); 300 printf("2/4 Set/get float DCBA: ");
295 modbus_set_float_dcba(UT_REAL, tab_rp_registers); 301 modbus_set_float_dcba(UT_REAL, tab_rp_registers);
296 - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_DCBA), "FAILED Set float DCBA");  
297 - real = modbus_get_float_dcba(tab_rp_registers); 302 + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_DCBA_SET, 4), "FAILED Set float DCBA");
  303 + real = modbus_get_float_dcba(UT_IREAL_DCBA_GET);
298 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); 304 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL);
299 305
300 printf("3/4 Set/get float BADC: "); 306 printf("3/4 Set/get float BADC: ");
301 modbus_set_float_badc(UT_REAL, tab_rp_registers); 307 modbus_set_float_badc(UT_REAL, tab_rp_registers);
302 - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_BADC), "FAILED Set float BADC");  
303 - real = modbus_get_float_badc(tab_rp_registers); 308 + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_BADC_SET, 4), "FAILED Set float BADC");
  309 + real = modbus_get_float_badc(UT_IREAL_BADC_GET);
304 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); 310 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL);
305 311
306 printf("4/4 Set/get float CDAB: "); 312 printf("4/4 Set/get float CDAB: ");
307 modbus_set_float_cdab(UT_REAL, tab_rp_registers); 313 modbus_set_float_cdab(UT_REAL, tab_rp_registers);
308 - ASSERT_TRUE(equal_dword(tab_rp_registers, UT_IREAL_CDAB), "FAILED Set float CDAB");  
309 - real = modbus_get_float_cdab(tab_rp_registers); 314 + ASSERT_TRUE(is_memory_equal(tab_rp_registers, UT_IREAL_CDAB_SET, 4), "FAILED Set float CDAB");
  315 + real = modbus_get_float_cdab(UT_IREAL_CDAB_GET);
310 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL); 316 ASSERT_TRUE(real == UT_REAL, "FAILED (%f != %f)\n", real, UT_REAL);
311 317
312 printf("\nAt this point, error messages doesn't mean the test has failed\n"); 318 printf("\nAt this point, error messages doesn't mean the test has failed\n");
tests/unit-test.h.in
@@ -56,12 +56,45 @@ const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x108; @@ -56,12 +56,45 @@ const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x108;
56 const uint16_t UT_INPUT_REGISTERS_NB = 0x1; 56 const uint16_t UT_INPUT_REGISTERS_NB = 0x1;
57 const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; 57 const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A };
58 58
  59 +/*
  60 + * This float value is 0x47F12000 (in big-endian format).
  61 + * In Little-endian(intel) format, it will be stored in memory as follows:
  62 + * 0x00 0x20 0xF1 0x47
  63 + *
  64 + * You can check this with the following code:
  65 +
  66 + float fl = UT_REAL;
  67 + uint8_t *inmem = (uint8_t*)&fl;
  68 + int x;
  69 + for(x = 0; x < 4; x++){
  70 + printf("0x%02X ", inmem[ x ]);
  71 + }
  72 + printf("\n");
  73 + */
59 const float UT_REAL = 123456.00; 74 const float UT_REAL = 123456.00;
60 75
61 -const uint32_t UT_IREAL_ABCD = 0x0020F147;  
62 -const uint32_t UT_IREAL_DCBA = 0x47F12000;  
63 -const uint32_t UT_IREAL_BADC = 0x200047F1;  
64 -const uint32_t UT_IREAL_CDAB = 0xF1470020; 76 +/*
  77 + * The following arrays assume that 'A' is the MSB,
  78 + * and 'D' is the LSB.
  79 + * Thus, the following is the case:
  80 + * A = 0x47
  81 + * B = 0xF1
  82 + * C = 0x20
  83 + * D = 0x00
  84 + *
  85 + * There are two sets of arrays: one to test that the setting is correct,
  86 + * the other to test that the getting is correct.
  87 + * Note that the 'get' values must be constants in processor-endianness,
  88 + * as libmodbus will convert all words to processor-endianness as they come in.
  89 + */
  90 +const uint8_t UT_IREAL_ABCD_SET[] = {0x47, 0xF1, 0x20, 0x00};
  91 +const uint16_t UT_IREAL_ABCD_GET[] = {0x47F1, 0x2000};
  92 +const uint8_t UT_IREAL_DCBA_SET[] = {0x00, 0x20, 0xF1, 0x47};
  93 +const uint16_t UT_IREAL_DCBA_GET[] = {0x0020, 0xF147};
  94 +const uint8_t UT_IREAL_BADC_SET[] = {0xF1, 0x47, 0x00, 0x20};
  95 +const uint16_t UT_IREAL_BADC_GET[] = {0xF147, 0x0020};
  96 +const uint8_t UT_IREAL_CDAB_SET[] = {0x20, 0x00, 0x47, 0xF1};
  97 +const uint16_t UT_IREAL_CDAB_GET[] = {0x2000, 0x47F1};
65 98
66 /* const uint32_t UT_IREAL_ABCD = 0x47F12000); 99 /* const uint32_t UT_IREAL_ABCD = 0x47F12000);
67 const uint32_t UT_IREAL_DCBA = 0x0020F147; 100 const uint32_t UT_IREAL_DCBA = 0x0020F147;