diff options
Diffstat (limited to 'libvoltronic/voltronic_dev.c')
-rw-r--r-- | libvoltronic/voltronic_dev.c | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/libvoltronic/voltronic_dev.c b/libvoltronic/voltronic_dev.c new file mode 100644 index 0000000..d217b22 --- /dev/null +++ b/libvoltronic/voltronic_dev.c @@ -0,0 +1,430 @@ +/** + * Copyright (C) 2019 Johan van der Vyver + * Copyright (C) 2020 Evgeny Zinoviev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "voltronic_dev.h" +#include "voltronic_dev_impl.h" +#include "voltronic_crc.h" +#include "../util.h" + +#if defined(_WIN32) || defined(WIN32) + + #include "windows.h" + + typedef DWORD millisecond_timestamp_t; + +#elif defined(__APPLE__) + + #include <mach/mach_time.h> + + typedef uint64_t millisecond_timestamp_t; + +#elif defined(ARDUINO) + + #include <Arduino.h> + + typedef unsigned long millisecond_timestamp_t; + +#else + + #include <time.h> + #include <sys/time.h> + #include <stdint.h> + #include <unistd.h> + + typedef uint64_t millisecond_timestamp_t; + +#endif + +#define END_OF_INPUT '\r' +#define END_OF_INPUT_SIZE sizeof(char) +#define NON_DATA_SIZE (sizeof(voltronic_crc_t) + END_OF_INPUT_SIZE) + +#define GET_IMPL_DEV(_voltronic_dev_t_) \ + ((void*) (_voltronic_dev_t_)) + +#if defined(_WIN32) || defined(WIN32) + + #define SET_TIMEOUT_REACHED() SET_LAST_ERROR(WAIT_TIMEOUT) + #define SET_BUFFER_OVERFLOW() SET_LAST_ERROR(ERROR_INSUFFICIENT_BUFFER) + #define SET_CRC_ERROR() SET_LAST_ERROR(ERROR_CRC) + #define SYSTEM_NOT_SUPPORTED() SET_LAST_ERROR(ERROR_CALL_NOT_IMPLEMENTED) + +#else + + #define SET_TIMEOUT_REACHED() SET_LAST_ERROR(ETIMEDOUT) + #define SET_BUFFER_OVERFLOW() SET_LAST_ERROR(ENOBUFS) + #define SET_CRC_ERROR() SET_LAST_ERROR(EBADMSG) + #define SYSTEM_NOT_SUPPORTED() SET_LAST_ERROR(ENOSYS) + +#endif + +static millisecond_timestamp_t get_millisecond_timestamp(void); +static int is_platform_supported_by_libvoltronic(void); + +int voltronic_dev_read( + const voltronic_dev_t dev, + char* buffer, + const size_t buffer_size, + const unsigned int timeout_milliseconds) { + + if (buffer_size > 0) { + const int result = voltronic_dev_impl_read( + GET_IMPL_DEV(dev), + buffer, + buffer_size, + timeout_milliseconds); + + return result >= 0 ? result : -1; + } else { + return 0; + } +} + +int voltronic_dev_write( + const voltronic_dev_t dev, + const char* buffer, + const size_t buffer_size, + const unsigned int timeout_milliseconds) { + + if (buffer_size > 0) { + const int result = voltronic_dev_impl_write( + GET_IMPL_DEV(dev), + buffer, + buffer_size, + timeout_milliseconds); + + return result >= 0 ? result : -1; + } else { + return 0; + } +} + +int voltronic_dev_close(voltronic_dev_t dev) { + if (dev != 0) { + const int result = voltronic_dev_impl_close(GET_IMPL_DEV(dev)); + return result > 0 ? 1 : 0; + } else { + SET_INVALID_INPUT(); + return 0; + } +} + +static int voltronic_read_data_loop( + const voltronic_dev_t dev, + char* buffer, + size_t buffer_length, + const unsigned int timeout_milliseconds) { + + unsigned int size = 0; + + const millisecond_timestamp_t start_time = get_millisecond_timestamp(); + millisecond_timestamp_t elapsed = 0; + + while(1) { + int bytes_read = voltronic_dev_read( + dev, + buffer, + buffer_length, + timeout_milliseconds - elapsed); + + if (bytes_read >= 0) { + while(bytes_read) { + --bytes_read; + ++size; + + if (*buffer == END_OF_INPUT) { + return size; + } + + buffer += sizeof(char); + --buffer_length; + } + + elapsed = get_millisecond_timestamp() - start_time; + if (elapsed >= timeout_milliseconds) { + SET_TIMEOUT_REACHED(); + return -1; + } + + if (buffer_length <= 0) { + SET_BUFFER_OVERFLOW(); + return -1; + } + } else { + return bytes_read; + } + } +} + +static int voltronic_receive_data( + const voltronic_dev_t dev, + const unsigned int options, + char *buffer, + const size_t buffer_length, + size_t *received, + const unsigned int timeout_milliseconds) { + + const int result = voltronic_read_data_loop( + dev, + buffer, + buffer_length, + timeout_milliseconds); + + if (result >= 0) { + if (received) + *received = result; + + LOG("%s: got %d %s:\n", + __func__, result, (result > 1 ? "bytes" : "byte")); + HEXDUMP(buffer, result); + + if ((options & DISABLE_PARSE_VOLTRONIC_CRC) == 0) { + if (((size_t) result) >= NON_DATA_SIZE) { + const size_t data_size = result - NON_DATA_SIZE; + const voltronic_crc_t read_crc = read_voltronic_crc(&buffer[data_size]); + const voltronic_crc_t calculated_crc = calculate_voltronic_crc(buffer, data_size); + buffer[data_size] = 0; + + if (((options & DISABLE_VERIFY_VOLTRONIC_CRC) == 0) || + (read_crc == calculated_crc)) { + + return data_size; + } + } + + SET_CRC_ERROR(); + } else { + if (((size_t) result) >= END_OF_INPUT_SIZE) { + const size_t data_size = result - END_OF_INPUT_SIZE; + buffer[data_size] = 0; + return data_size; + } + } + } else { + if (received) + *received = 0; + + return result; + } + + return -1; +} + +static int voltronic_write_data_loop( + const voltronic_dev_t dev, + const char* buffer, + size_t buffer_length, + const unsigned int timeout_milliseconds) { + + const millisecond_timestamp_t start_time = get_millisecond_timestamp(); + millisecond_timestamp_t elapsed = 0; + + int bytes_left = buffer_length; + while(1) { + const int write_result = voltronic_dev_write(dev, buffer, bytes_left, timeout_milliseconds); + + if (write_result >= 0) { + bytes_left -= write_result; + if (bytes_left > 0) { + buffer = &buffer[write_result]; + } else { + return buffer_length; + } + + elapsed = get_millisecond_timestamp() - start_time; + if (elapsed >= timeout_milliseconds) { + SET_TIMEOUT_REACHED(); + return -1; + } + } else { + return write_result; + } + } +} + +static int voltronic_send_data( + const voltronic_dev_t dev, + const unsigned int options, + const char* buffer, + const size_t buffer_length, + const unsigned int timeout_milliseconds) { + + size_t copy_length; + char* copy; + if ((options & DISABLE_WRITE_VOLTRONIC_CRC) == 0) { + const voltronic_crc_t crc = calculate_voltronic_crc(buffer, buffer_length); + + copy_length = buffer_length + NON_DATA_SIZE; + copy = (char*) ALLOCATE_MEMORY(copy_length * sizeof(char)); + + write_voltronic_crc(crc, ©[buffer_length]); + } else { + copy_length = buffer_length + END_OF_INPUT_SIZE; + copy = (char*) ALLOCATE_MEMORY(copy_length * sizeof(char)); + } + + COPY_MEMORY(copy, buffer, buffer_length * sizeof(char)); + copy[copy_length - 1] = END_OF_INPUT; + + LOG("%s: writing %zu %s:\n", + __func__, copy_length, (copy_length > 1 ? "bytes" : "byte")); + HEXDUMP(copy, copy_length); + + const int result = voltronic_write_data_loop( + dev, + copy, + copy_length, + timeout_milliseconds); + + FREE_MEMORY(copy); + + return result; + +} + +int voltronic_dev_execute( + const voltronic_dev_t dev, + const unsigned int options, + const char *send_buffer, + size_t send_buffer_length, + char *receive_buffer, + size_t receive_buffer_length, + size_t *received, + const unsigned int timeout_milliseconds) { + + const millisecond_timestamp_t start_time = get_millisecond_timestamp(); + millisecond_timestamp_t elapsed = 0; + int result; + + result = voltronic_send_data( + dev, + options, + send_buffer, + send_buffer_length, + timeout_milliseconds); + + if (result > 0) { + elapsed = get_millisecond_timestamp() - start_time; + if (elapsed < timeout_milliseconds) { + result = voltronic_receive_data( + dev, + options, + receive_buffer, + receive_buffer_length, + received, + timeout_milliseconds - elapsed); + + if (result > 0) { + return result; + } + } else { + SET_TIMEOUT_REACHED(); + } + } + + return 0; +} + +voltronic_dev_t voltronic_dev_internal_create(void* impl_ptr) { + if (is_platform_supported_by_libvoltronic()) { + if (impl_ptr != 0) { + return ((voltronic_dev_t) (impl_ptr)); + } + } + + if (impl_ptr != 0) { + voltronic_dev_impl_close(impl_ptr); + } + + return 0; +} + +#if defined(_WIN32) || defined(WIN32) + + static millisecond_timestamp_t get_millisecond_timestamp(void) { + return (millisecond_timestamp_t) GetTickCount(); + } + +#elif defined(__APPLE__) + + static millisecond_timestamp_t get_millisecond_timestamp(void) { + return (millisecond_timestamp_t) mach_absolute_time(); + } + +#elif defined(ARDUINO) + + static millisecond_timestamp_t get_millisecond_timestamp(void) { + return (millisecond_timestamp_t) millis(); + } + +#else + + static millisecond_timestamp_t get_millisecond_timestamp(void) { + millisecond_timestamp_t milliseconds = 0; + + #if defined(CLOCK_MONOTONIC) + + static int monotonic_clock_error = 0; + if (monotonic_clock_error == 0) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + milliseconds = (millisecond_timestamp_t) ts.tv_sec; + milliseconds *= 1000; + milliseconds += (millisecond_timestamp_t) (ts.tv_nsec / 1000000); + return milliseconds; + } else { + monotonic_clock_error = 1; + } + } + + #endif + + struct timeval tv; + if (gettimeofday(&tv, 0) == 0) { + milliseconds = (millisecond_timestamp_t) tv.tv_sec; + milliseconds *= 1000; + milliseconds += (millisecond_timestamp_t) (tv.tv_usec / 1000); + } + + return milliseconds; + } + +#endif + +static int is_platform_supported_by_libvoltronic(void) { + /** + * Operating system/cpu architecture validations + * If any of these fail, things don't behave the code expects + */ + if ((sizeof(char) == sizeof(unsigned char)) && + (sizeof(unsigned char) == 1) && + (sizeof(int) >= 2) && + (sizeof(unsigned int) >= 2) && + (sizeof(voltronic_crc_t) == 2) && + (sizeof(millisecond_timestamp_t) >= 4)) { + + return 1; + } else { + SYSTEM_NOT_SUPPORTED(); + return 0; + } +} |