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