Exportable version of WizziLab's modem driver.
Diff: src/kal_codec.cpp
- Revision:
- 41:6f83174ffed4
diff -r 62b7f6d25772 -r 6f83174ffed4 src/kal_codec.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/kal_codec.cpp Mon Nov 26 16:42:16 2018 +0000 @@ -0,0 +1,581 @@ +/// @copyright +/// ========================================================================={{{ +/// Copyright (c) 20XX WizziLab / +/// All rights reserved / +/// / +/// IMPORTANT: This Software may not be modified, copied or distributed unless / +/// embedded on a WizziLab product. Other than for the foregoing purpose, this / +/// Software and/or its documentation may not be used, reproduced, copied, / +/// prepared derivative works of, modified, performed, distributed, displayed / +/// or sold for any purpose. For the sole purpose of embedding this Software / +/// on a WizziLab product, copy, modification and distribution of this / +/// Software is granted provided that the following conditions are respected: / +/// / +/// * Redistributions of source code must retain the above copyright notice, / +/// this list of conditions and the following disclaimer / +/// / +/// * Redistributions in binary form must reproduce the above copyright / +/// notice, this list of conditions and the following disclaimer in the / +/// documentation and/or other materials provided with the distribution. / +/// / +/// * The name of WizziLab can not be used to endorse or promote products / +/// derived from this software without specific prior written permission. / +/// / +/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS / +/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED / +/// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR / +/// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR / +/// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, / +/// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, / +/// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, / +/// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY / +/// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING / +/// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS / +/// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. / +/// WIZZILAB HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, / +/// ENHANCEMENTS OR MODIFICATIONS. / +/// / +/// Should you have any questions regarding your right to use this Software, / +/// contact WizziLab at www.wizzilab.com. / +/// / +/// =========================================================================}}} +/// @endcopyright +/// +/// ======================================================================= +/// +/// @file kal_codec.c +/// @brief Codec Utilities +/// +/// ======================================================================= + +#include <string.h> +#include "hal_types.h" +#include "kal_codec.h" + + +// ====================================================================== +// +// +// ASCII to Binary Codec Toolkits +// +// +// ====================================================================== + +const char k_nybble_chars[] = "0123456789ABCDEF"; + +//====================================================================== +// hex2ascii +//---------------------------------------------------------------------- +/// @brief Convert hexadecimal number to ascii +/// @param char* in input buffer (hex) +/// @param char* out output buffer (ascii) +/// @param u16 len length of inoput buffer in bytes +//====================================================================== +void hex2ascii( char* in, char* out, u16 length) +{ + u8 b; + while(length--) { + b = *in++; + *out++ = k_nybble_chars[ ( b >> 4 ) & 0x0F ]; + *out++ = k_nybble_chars[ b & 0x0F ]; + } +} + +//====================================================================== +// itoa +//---------------------------------------------------------------------- +/// @brief Converts an integer value to a null-terminated string using the specified base and stores the result +/// in the array given by result parameter. +/// @param int value integer to convert +/// @param char* result converted string +/// @param int base base used for conversion 2, 8, 10, 16 +/// @return A pointer to the resulting null-terminated string, same as parameter result +//====================================================================== +char* itoa(int value, char* result, int base) +{ + // check that the base if valid + //if (base < 2 || base > 16) { return NULL; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "fedcba9876543210123456789abcdef" [15 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + //*ptr-- = '\0'; + result = ptr--; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +//====================================================================== +// atoitok +//---------------------------------------------------------------------- +/// @brief Extracts from a ASCII string a value (decimal or hex) and +/// convert it to an integer value. +/// - Blank characters are skipped +/// - hexa values must begin with '0x' or '0X' +/// - '-' sign is handled +/// - returns after one value has been parsed or null character +/// or CR character has been found. +/// @param p pointer to pointer to the string to be parsed. The pointer +/// to string is modified: at the end of the call it points to +/// the character following the last parsed one. +//====================================================================== +int atoitok(u8 **p) +{ + int k = 0; + u8 base16=0; + s8 sign = 1; + while ((**p==' ')||(**p=='\t')||(**p==',')) (*p)++; + if (**p=='-') + { + sign = -1; + (*p)++; + } + if ((**p=='0') && (*(*p+1)=='x')) + { + base16 = 1; + *p += 2; + } + while (**p && **p!=' ' && **p!=0xa && **p!=',') { + if (!base16) + { + k = (k<<3)+(k<<1)+(**p)-'0'; + } + else + { + if (**p > '9') + if (**p > 'F') + k = (k<<4)+(10+(**p)-'a'); + else + k = (k<<4)+(10+(**p)-'A'); + else + k = (k<<4)+(**p)-'0'; + } + (*p)++; + } + return sign*k; +} + +//====================================================================== +// kal_atoi +//---------------------------------------------------------------------- +/// @brief Extracts from a ASCII string a decimal value and +/// convert it to an integer value. +/// '-' and '+' signs are handled +/// @param s char* string to convert +/// @retval s32 integer value +//====================================================================== +s32 kal_atoi(u8* s) +{ + s32 k = 0; + s8 sign = 1; + + if (*s == '-') + { + sign = -1; + s++; + } + else if (*s == '+') + { + s++; + } + + while (*s >= '0' && *s <= '9') + { + k = 10 * k + *s - '0'; + s++; + } + + return (sign * k); +} + +//====================================================================== +// kal_atoi_float +//---------------------------------------------------------------------- +/// @brief Extracts from a ASCII string a float value and +/// convert it to an integer value * 10^number_of_decimals. +/// '-' and '+' signs are handled +/// @param s char* string to convert +/// @param d u8 Number of decimals +/// @retval s32 integer value +//====================================================================== +s32 kal_atoi_float(u8* s, u8 d) +{ + s32 k_int = 0; + s32 k_dec = 0; + s8 sign = 1; + u8 i; + + // Check sign + if (*s == '-') + { + sign = -1; + s++; + } + else if (*s == '+') + { + s++; + } + + // Calculate integer part + while (*s >= '0' && *s <= '9') + { + k_int = 10 * k_int + *s - '0'; + s++; + } + + for (i = 0; i < d; i++) + { + k_int *= 10; + } + + // Skip float separator + if (*s == '.') + { + s++; + } + else + { + return (sign * k_int); + } + + // Calculate decimal part + while (*s >= '0' && *s <= '9' && d--) + { + k_dec = 10 * k_dec + *s - '0'; + s++; + } + + // If string is finished but all the decimals aren't there, complete as if they were followed by 0s + for (i = 0; i < d; i++) + { + k_dec *= 10; + } + + return (sign * (k_int + k_dec)); +} +//====================================================================== +// kal_atoi_hex +//---------------------------------------------------------------------- +/// @brief Extracts from a ASCII string a hex value and +/// convert it to an integer value. +/// @param s char* string to convert +/// @retval u32 integer value +//====================================================================== +u32 kal_atoi_hex(u8* s) +{ + u32 k = 0; + + while (*s != '\0') + { + if (*s > '9') + { + k = 16 * k + 10 + (*s) - ((*s > 'F') ? 'a' : 'A'); + } + else + { + k = 16 * k + (*s) - '0'; + } + s++; + } + + return (k); +} + +//====================================================================== +// kal_base64_strlen +//---------------------------------------------------------------------- +/// @brief Real length (excluding padding) of Base64-encoded string +/// Note : the input string always has 4x chars, if not return 0 +/// @param in char* string to decode +/// @retval u16 string length +//====================================================================== +u16 kal_base64_strlen(const char* in) +{ + u16 len, j; + register const char *p; + + // Useful strlen + len = strlen(in); + + // the input string always has 4x chars + if (len % 4) + { + return 0; + } + + // remove padding + for (j = 0, p = in + len - 1; len > 0 && *p == '=' ; j++, p--, len--) {;} + + // there is 0, 1 or 2 padding chars + if (j > 2) + { + return 0; + } + + return len; +} + +//====================================================================== +// kal_base64 codec tables +//====================================================================== +const char k_kal_base64_encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +const u8 k_kal_base64_decode[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + +//====================================================================== +// kal_base64_encode +//---------------------------------------------------------------------- +/// @brief Encode Base64-encoded buffer. The output buffer is supposed to have enough space +/// Beware, the stream encoding is Big Endian. +/// @param out char* string result +/// @param in u8* binary buffer to encode +/// @param len u16 length of the binary buffer in bytes +/// @retval void +//====================================================================== +void kal_base64_encode(char *out, u8* in, u16 len) +{ + u16 i = 0; + register u8 a,b,c; + + if (len > 1) + { + for (; i < len - 2; i += 3) + { + a = *in++; + b = *in++; + c = *in++; + *out++ = k_kal_base64_encode[(((a >> 2) & 0x3F))]; + *out++ = k_kal_base64_encode[(((a << 4) & 0x30) | ((b >> 4) & 0x0F))]; + *out++ = k_kal_base64_encode[(((b << 2) & 0x3C) | ((c >> 6) & 0x03))]; + *out++ = k_kal_base64_encode[(((c >> 0) & 0x3F))]; + } + } + + if (i < len) + { + a = *in++; + *out++ = k_kal_base64_encode[(((a >> 2) & 0x3F))]; + + if (i == (len - 1)) + { + *out++ = k_kal_base64_encode[(((a << 4) & 0x30))]; + *out++ = '='; + } + else + { + b = *in++; + *out++ = k_kal_base64_encode[(((a << 4) & 0x30) | ((b >> 4) & 0x0F))]; + *out++ = k_kal_base64_encode[(((b << 2) & 0x3C))]; + } + + *out++ = '='; + } + + *out++ = '\0'; + return; +} + +//====================================================================== +// kal_base64_decode +//---------------------------------------------------------------------- +/// @brief Decode Base64-encoded buffer. The output buffer is supposed to have enough space +/// @param out u8* binary buffer result +/// @param in char* string to decode +/// @param len u16 string length, excluding padding +/// @retval void +//====================================================================== +void kal_base64_decode(u8 *out, const char* in, u16 len) +{ + register u8 a,b,c,d; + + // Decode + for (; len > 3; len -= 4) + { + a = k_kal_base64_decode[(u8)*in++]; + b = k_kal_base64_decode[(u8)*in++]; + c = k_kal_base64_decode[(u8)*in++]; + d = k_kal_base64_decode[(u8)*in++]; + + *out++ = (a << 2) | (b >> 4); + *out++ = (b << 4) | (c >> 2); + *out++ = (c << 6) | (d >> 0); + } + + // Note len == 1 is not possible + if (len > 1) + { + a = k_kal_base64_decode[(u8)*in++]; + b = k_kal_base64_decode[(u8)*in++]; + *out++ = (a << 2) | (b >> 4); + } + if (len > 2) + { + c = k_kal_base64_decode[(u8)*in++]; + *out++ = (b << 4) | (c >> 2); + } +} + +//====================================================================== +// kal_tolower +//---------------------------------------------------------------------- +/// @brief Changes inplace to lower character a non-constant string +/// @param s u8* String to transform +/// @retval void +//====================================================================== +void kal_tolower(u8* s) +{ + while (*s != '\0') + { + if (*s >= 'A' && *s <= 'Z') + { + *s += (u8)('a' - 'A'); + } + s++; + } +} + + +//====================================================================== +// kal_toupper +//---------------------------------------------------------------------- +/// @brief Changes inplace to upper character a non-constant string +/// @param s u8* String to transform +/// @retval void +//====================================================================== +void kal_toupper(u8* s) +{ + while (*s != '\0') + { + if (*s >= 'a' && *s <= 'z') + { + *s -= (u8)('a' - 'A'); + } + s++; + } +} + +//====================================================================== +// kal_crc8 +//---------------------------------------------------------------------- +/// @brief Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. +/// A table-based algorithm would be faster, but for only a few +/// bytes it isn't worth the code size. +/// @param in u8* input buffer +/// @param len u32 input buffer length in bytes +/// @retval void +//====================================================================== +/* Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A + * table-based algorithm would be faster, but for only a few bytes it isn't + * worth the code size. */ +u8 kal_crc8(u8* in, u32 len) +{ + const u8 *data = in; + u32 crc = 0; + u8 i; + u32 j; + + for (j = len; j; j--, data++) + { + crc ^= (*data << 8); + for(i = 8; i; i--) + { + if (crc & 0x8000) + { + crc ^= (0x1070 << 3); + } + crc <<= 1; + } + } + return (u8)(crc >> 8); +} + +//====================================================================== +// kal_ctf_encode +//---------------------------------------------------------------------- +/// @brief Compress u32 to D7A Compressed Time format (CTF). +/// The ceil flag is used to define rounding, so that +/// kal_ctf_decode(kal_ctf_encode(val, FALSE) <= val (floor) +/// kal_ctf_decode(kal_ctf_encode(val, TRUE) >= val (ceiling) +/// @param val u32 value to encode +/// @param ceil u8 ceil value when TRUE +/// @retval kal_ctf_t compressed value +//====================================================================== +kal_ctf_t kal_ctf_encode(u32 val, u8 ceil) +{ + u8 exp; + u32 tmp = val; + kal_ctf_t ctf; + + // serch best (smallest) exponent + for (exp = 0; tmp > 31; exp++, tmp >>= 2) {}; + + if (exp < 8) + { + ctf.bf.exp = exp; + ctf.bf.mant = tmp; + + // manage floor + if (ceil) + { + if (kal_ctf_decode(ctf) < val) + { + if (ctf.bf.mant < 31) + { + ctf.bf.mant++; + } + else + { + if (ctf.bf.exp < 7) + { + // mant = 31+1 = 4*8 + ctf.bf.exp++; + ctf.bf.mant = 8; + } + else + { + // exp = 7+1 -> overflow + ctf.byte = 0xff; + } + } + } + } + } + else + { + ctf.byte = 0xff; + } + + return ctf; +} + +// ======================================================================= +// kal_ctf_decode +// ----------------------------------------------------------------------- +/// @brief Decompress from Compressed Time Format to u32 +/// @param ctf kal_ctf_t compressed value in CTF +/// @retval u32 decode result +// ======================================================================= +u32 kal_ctf_decode(kal_ctf_t ctf) +{ + return ((1 << (2*ctf.bf.exp)) * ctf.bf.mant); +} +