|
| 1 | +/* common_voltronic-crc.c - Common CRC routines for Voltronic Power devices |
| 2 | + * |
| 3 | + * Copyright (C) |
| 4 | + * 2014 Daniele Pezzini <hyouko@gmail.com> |
| 5 | + * |
| 6 | + * This program is free software; you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License as published by |
| 8 | + * the Free Software Foundation; either version 2 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, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + * GNU General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU General Public License |
| 17 | + * along with this program; if not, write to the Free Software |
| 18 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | + * |
| 20 | + */ |
| 21 | + |
| 22 | +#include "common_voltronic-crc.h" |
| 23 | + |
| 24 | +/* CRC table - filled at runtime by common_voltronic_crc_init() */ |
| 25 | +static unsigned short crc_table[256]; |
| 26 | + |
| 27 | +/* Flag (0/1) just to be sure all is properly initialized */ |
| 28 | +static int initialized = 0; |
| 29 | + |
| 30 | +/* Fill CRC table: this function MUST be called before anything else that needs to perform CRC operations */ |
| 31 | +static void common_voltronic_crc_init(void) |
| 32 | +{ |
| 33 | + unsigned short dividend; |
| 34 | + |
| 35 | + /* Already been here */ |
| 36 | + if (initialized) |
| 37 | + return; |
| 38 | + |
| 39 | + /* Compute remainder of each possible dividend */ |
| 40 | + for (dividend = 0; dividend < 256; dividend++) { |
| 41 | + |
| 42 | + unsigned short bit; |
| 43 | + /* Start with dividend followed by zeros */ |
| 44 | + unsigned long remainder = dividend << 8; |
| 45 | + |
| 46 | + /* Modulo 2 division, a bit at a time */ |
| 47 | + for (bit = 0; bit < 8; bit++) |
| 48 | + /* Try to divide the current data bit */ |
| 49 | + if (remainder & 0x8000) |
| 50 | + remainder = (remainder << 1) ^ 0x1021; |
| 51 | + else |
| 52 | + remainder <<= 1; |
| 53 | + |
| 54 | + /* Store the result into the table */ |
| 55 | + crc_table[dividend] = remainder & 0xffff; |
| 56 | + |
| 57 | + } |
| 58 | + |
| 59 | + /* Ready, now */ |
| 60 | + initialized = 1; |
| 61 | +} |
| 62 | + |
| 63 | +/* See header file for details */ |
| 64 | +unsigned short common_voltronic_crc_compute(const char *input, const size_t len) |
| 65 | +{ |
| 66 | + unsigned short crc, crc_MSB, crc_LSB; |
| 67 | + unsigned long remainder = 0; |
| 68 | + size_t byte; |
| 69 | + |
| 70 | + /* Make sure all is ready */ |
| 71 | + if (!initialized) |
| 72 | + common_voltronic_crc_init(); |
| 73 | + |
| 74 | + /* Divide *input* by the polynomial, a byte at a time */ |
| 75 | + for (byte = 0; byte < len; byte++) |
| 76 | + remainder = (remainder << 8) ^ crc_table[(input[byte] ^ (remainder >> 8)) & 0xff]; |
| 77 | + |
| 78 | + /* The final remainder is the CRC */ |
| 79 | + crc = remainder & 0xffff; |
| 80 | + |
| 81 | + /* Escape characters with a special meaning */ |
| 82 | + |
| 83 | + crc_MSB = (crc >> 8) & 0xff; |
| 84 | + if ( |
| 85 | + crc_MSB == 10 || /* LF */ |
| 86 | + crc_MSB == 13 || /* CR */ |
| 87 | + crc_MSB == 40 /* ( */ |
| 88 | + ) |
| 89 | + crc_MSB++; |
| 90 | + |
| 91 | + crc_LSB = crc & 0xff; |
| 92 | + if ( |
| 93 | + crc_LSB == 10 || /* LF */ |
| 94 | + crc_LSB == 13 || /* CR */ |
| 95 | + crc_LSB == 40 /* ( */ |
| 96 | + ) |
| 97 | + crc_LSB++; |
| 98 | + |
| 99 | + crc = ((crc_MSB & 0xff) << 8) + crc_LSB; |
| 100 | + |
| 101 | + return crc; |
| 102 | +} |
| 103 | + |
| 104 | +/* See header file for details */ |
| 105 | +unsigned short common_voltronic_crc_calc(const char *input, const size_t inputlen) |
| 106 | +{ |
| 107 | + size_t len; |
| 108 | + char *cr = memchr(input, '\r', inputlen); |
| 109 | + |
| 110 | + /* No CR, fall back to string length (and hope *input* doesn't contain inner '\0's) */ |
| 111 | + if (cr == NULL) |
| 112 | + len = strlen(input); |
| 113 | + else |
| 114 | + len = cr - input; |
| 115 | + |
| 116 | + /* At least 1 byte expected */ |
| 117 | + if (!len) |
| 118 | + return -1; |
| 119 | + |
| 120 | + /* Compute (and return) CRC */ |
| 121 | + return common_voltronic_crc_compute(input, len); |
| 122 | +} |
| 123 | + |
| 124 | +/* See header file for details */ |
| 125 | +int common_voltronic_crc_calc_and_add(const char *input, const size_t inputlen, char *output, const size_t outputlen) |
| 126 | +{ |
| 127 | + unsigned short crc, crc_MSB, crc_LSB; |
| 128 | + size_t len; |
| 129 | + char *cr = memchr(input, '\r', inputlen); |
| 130 | + |
| 131 | + /* No CR, fall back to string length (and hope *input* doesn't contain inner '\0's) */ |
| 132 | + if (cr == NULL) |
| 133 | + len = strlen(input); |
| 134 | + else |
| 135 | + len = cr - input; |
<
10000
td data-grid-cell-id="diff-cfcaa8dd3705d59ca49f39e947a95c222109f68e85a8cb92f022d2da22a4bce8-empty-136-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
136 | + |
| 137 | + /* At least 1 byte expected */ |
| 138 | + if (!len) |
| 139 | + return -1; |
| 140 | + |
| 141 | + /* To accomodate CRC, *output* must have space for at least 2 bytes more than the actually used size of *input*. |
| 142 | + * Also, pretend that *input* is a valid null-terminated string and so reserve the final byte in *output* for the terminating '\0'. */ |
| 143 | + if ( |
| 144 | + (cr == NULL && outputlen < len + 3) || /* 2-bytes CRC + 1 byte for terminating '\0' */ |
| 145 | + (cr != NULL && outputlen < len + 4) /* 2-bytes CRC + 1 byte for trailing CR + 1 byte for terminating '\0' */ |
| 146 | + ) |
| 147 | + return -1; |
| 148 | + |
| 149 | + /* Compute CRC */ |
| 150 | + crc = common_voltronic_crc_compute(input, len); |
| 151 | + crc_MSB = (crc >> 8) & 0xff; |
| 152 | + crc_LSB = crc & 0xff; |
| 153 | + |
| 154 | + /* Clear *output* */ |
| 155 | + memset(output, 0, outputlen); |
| 156 | + |
| 157 | + /* Copy *input* to *output* */ |
| 158 | + memcpy(output, input, len); |
| 159 | + |
| 160 | + /* Write CRC to *output* */ |
| 161 | + output[len++] = crc_MSB; |
| 162 | + output[len++] = crc_LSB; |
| 163 | + |
| 164 | + /* Reinstate the trailing CR in *output*, if appropriate */ |
| 165 | + if (cr != NULL) |
| 166 | + output[len++] = '\r'; |
| 167 | + |
| 168 | + return (int)len; |
| 169 | +} |
| 170 | + |
| 171 | +/* See header file for details */ |
| 172 | +int common_voltronic_crc_calc_and_add_m(char *input, const size_t inputlen) |
| 173 | +{ |
| 174 | + int len; |
| 175 | + char buf[inputlen]; |
| 176 | + |
| 177 | + /* Compute CRC and copy *input*, with CRC added to it, to buf */ |
| 178 | + len = common_voltronic_crc_calc_and_add(input, inputlen, buf, inputlen); |
| 179 | + |
| 180 | + /* Failed */ |
| 181 | + if (len == -1) |
| 182 | + return -1; |
| 183 | + |
| 184 | + /* Successfully computed CRC and copied *input*, with CRC added to it, to buf */ |
| 185 | + |
| 186 | + /* Clear *input* */ |
| 187 | + memset(input, 0, inputlen); |
| 188 | + |
| 189 | + /* Copy back buf to *input* */ |
| 190 | + memcpy(input, buf, len); |
| 191 | + |
| 192 | + return len; |
| 193 | +} |
| 194 | + |
| 195 | +/* See header file for details */ |
| 196 | +int common_voltronic_crc_check(const char *input, const size_t inputlen) |
| 197 | +{ |
| 198 | + unsigned short crc, crc_MSB, crc_LSB; |
| 199 | + char *cr = memchr(input, '\r', inputlen); |
| 200 | + size_t len; |
| 201 | + |
| 202 | + /* No CR, fall back to string length (and hope *input* doesn't contain inner '\0's) */ |
| 203 | + if (cr == NULL) |
| 204 | + len = strlen(input); |
| 205 | + else |
| 206 | + len = cr - input; |
| 207 | + |
| 208 | + /* Minimum length: 1 byte + 2 bytes CRC -> 3 */ |
| 209 | + if (len < 3) |
| 210 | + return -1; |
| 211 | + |
| 212 | + /* Compute CRC */ |
| 213 | + crc = common_voltronic_crc_compute(input, len - 2); |
| 214 | + crc_MSB = (crc >> 8) & 0xff; |
| 215 | + crc_LSB = crc & 0xff; |
| 216 | + |
| 217 | + /* Fail */ |
| 218 | + if ( |
| 219 | + crc_MSB != (unsigned char)input[len - 2] || |
| 220 | + crc_LSB != (unsigned char)input[len - 1] |
| 221 | + ) |
| 222 | + return -1; |
| 223 | + |
| 224 | + /* Success */ |
| 225 | + return 0; |
| 226 | +} |
| 227 | + |
| 228 | +/* See header file for details */ |
| 229 | +int common_voltronic_crc_check_and_remove(const char *input, const size_t inputlen, char *output, const size_t outputlen) |
| 230 | +{ |
| 231 | + char *cr; |
| 232 | + size_t len; |
| 233 | + |
| 234 | + /* Failed to check *input* CRC */ |
| 235 | + if (common_voltronic_crc_check(input, inputlen)) |
| 236 | + return -1; |
| 237 | + |
| 238 | + /* *input* successfully validated -> remove CRC bytes */ |
| 239 | + |
| 240 | + cr = memchr(input, '\r', inputlen); |
| 241 | + /* No CR, fall back to string length */ |
| 242 | + if (cr == NULL) |
| 243 | + len = strlen(input); |
| 244 | + else |
| 245 | + len = cr - input; |
| 246 | + |
| 247 | + /* *output* must have space for at least 2 bytes less than the actually used size of *input*. |
| 248 | + * Also, pretend that *input* is a valid null-terminated string and so reserve the final byte in *output* for the terminating '\0'. */ |
| 249 | + len -= 2; /* Consider 2-bytes CRC length */ |
| 250 | + if ( |
| 251 | + (cr == NULL && outputlen < len + 1) || /* 1 byte for terminating '\0' */ |
| 252 | + (cr != NULL && outputlen < len + 2) /* 1 byte for terminating '\0' + 1 byte for trailing CR; 2-byte CRC */ |
| 253 | + ) |
| 254 | + return -1; |
| 255 | + |
| 256 | + /* Clear *output* */ |
| 257 | + memset(output, 0, outputlen); |
| 258 | + |
| 259 | + /* Copy *input* to *output* */ |
| 260 | + memcpy(output, input, len); |
| 261 | + |
| 262 | + /* Reinstate the trailing CR in *output*, if appropriate */ |
| 263 | + if (cr != NULL) |
| 264 | + output[len++] = '\r'; |
| 265 | + |
| 266 | + return (int)len; |
| 267 | +} |
| 268 | + |
| 269 | +/* See header file for details */ |
| 270 | +int common_voltronic_crc_check_and_remove_m(char *input, const size_t inputlen) |
| 271 | +{ |
| 272 | + int len; |
| 273 | + char buf[inputlen]; |
| 274 | + |
| 275 | + /* Check CRC and copy *input*, purged of the CRC, to buf */ |
| 276 | + len = common_voltronic_crc_check_and_remove(input, inputlen, buf, inputlen); |
| 277 | + |
| 278 | + /* Failed */ |
| 279 | + if (len == -1) |
| 280 | + return -1; |
| 281 | + |
| 282 | + /* Successfully checked CRC and copied *input*, purged of the CRC, to buf */ |
| 283 | + |
| 284 | + /* Clear *input* */ |
| 285 | + memset(input, 0, inputlen); |
| 286 | + |
| 287 | + /* Copy back buf to *input* */ |
| 288 | + memcpy(input, buf, len); |
| 289 | + |
| 290 | + return len; |
| 291 | +} |
0 commit comments