diff --git a/.gitignore b/.gitignore index 6a5b347a..550dcb07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /*.user /build /build-x86 +.DS_Store + diff --git a/CMakeLists.txt b/CMakeLists.txt index 34aec49e..e8a7f674 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV}) option(ENABLE_V4L2 "Enable the V4L2 grabber" ON) message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2}) +option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" OFF) +message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE}) + # Createt the configuration file # configure a header file to pass some of the CMake settings # to the source code @@ -62,6 +65,10 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD) find_package(libusb-1.0 REQUIRED) find_package(Threads REQUIRED) +if (ENABLE_TINKERFORGE) + #find_package(libtinkerforge-1.0 REQUIRED) +endif (ENABLE_TINKERFORGE) + include(${QT_USE_FILE}) add_definitions(${QT_DEFINITIONS}) # TODO[TvdZ]: This linking directory should only be added if we are cross compiling diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index 14500c71..17f040c0 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -8,3 +8,6 @@ // Define to enable the spi-device #cmakedefine ENABLE_SPIDEV + +// Define to enable the spi-device +#cmakedefine ENABLE_TINKERFORGE diff --git a/dependencies/build/CMakeLists.txt b/dependencies/build/CMakeLists.txt index 88f6ca5f..e931ea79 100644 --- a/dependencies/build/CMakeLists.txt +++ b/dependencies/build/CMakeLists.txt @@ -1,5 +1,6 @@ -add_subdirectory(jsoncpp) add_subdirectory(getoptPlusPlus) -add_subdirectory(serial) add_subdirectory(hidapi) +add_subdirectory(jsoncpp) +add_subdirectory(serial) +add_subdirectory(tinkerforge) diff --git a/dependencies/build/tinkerforge/CMakeLists.txt b/dependencies/build/tinkerforge/CMakeLists.txt new file mode 100644 index 00000000..f1f82f5e --- /dev/null +++ b/dependencies/build/tinkerforge/CMakeLists.txt @@ -0,0 +1,14 @@ +project(tinkerforge) + +# define the current source/header path +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/dependencies/include/tinkerforge) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/dependencies/build/tinkerforge) + +include_directories(${CURRENT_HEADER_DIR}) + +add_library(tinkerforge + ${CURRENT_HEADER_DIR}/bricklet_led_strip.h + ${CURRENT_HEADER_DIR}/ip_connection.h + + ${CURRENT_SOURCE_DIR}/bricklet_led_strip.c + ${CURRENT_SOURCE_DIR}/ip_connection.c) diff --git a/dependencies/build/tinkerforge/bricklet_led_strip.c b/dependencies/build/tinkerforge/bricklet_led_strip.c new file mode 100644 index 00000000..a67486fb --- /dev/null +++ b/dependencies/build/tinkerforge/bricklet_led_strip.c @@ -0,0 +1,373 @@ +/* *********************************************************** + * This file was automatically generated on 2013-12-19. * + * * + * Bindings Version 2.0.13 * + * * + * If you have a bugfix for this file and want to commit it, * + * please fix the bug in the generator. You can find a link * + * to the generator git on tinkerforge.com * + *************************************************************/ + + +#define IPCON_EXPOSE_INTERNALS + +#include "bricklet_led_strip.h" + +#include + + + +typedef void (*FrameRenderedCallbackFunction)(uint16_t, void *); + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(push) + #pragma pack(1) + #define ATTRIBUTE_PACKED +#elif defined __GNUC__ + #ifdef _WIN32 + // workaround struct packing bug in GCC 4.7 on Windows + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 + #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) + #else + #define ATTRIBUTE_PACKED __attribute__((packed)) + #endif +#else + #error unknown compiler, do not know how to enable struct packing +#endif + +typedef struct { + PacketHeader header; + uint16_t index; + uint8_t length; + uint8_t r[16]; + uint8_t g[16]; + uint8_t b[16]; +} ATTRIBUTE_PACKED SetRGBValues_; + +typedef struct { + PacketHeader header; + uint16_t index; + uint8_t length; +} ATTRIBUTE_PACKED GetRGBValues_; + +typedef struct { + PacketHeader header; + uint8_t r[16]; + uint8_t g[16]; + uint8_t b[16]; +} ATTRIBUTE_PACKED GetRGBValuesResponse_; + +typedef struct { + PacketHeader header; + uint16_t duration; +} ATTRIBUTE_PACKED SetFrameDuration_; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetFrameDuration_; + +typedef struct { + PacketHeader header; + uint16_t duration; +} ATTRIBUTE_PACKED GetFrameDurationResponse_; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetSupplyVoltage_; + +typedef struct { + PacketHeader header; + uint16_t voltage; +} ATTRIBUTE_PACKED GetSupplyVoltageResponse_; + +typedef struct { + PacketHeader header; + uint16_t length; +} ATTRIBUTE_PACKED FrameRenderedCallback_; + +typedef struct { + PacketHeader header; + uint32_t frequency; +} ATTRIBUTE_PACKED SetClockFrequency_; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetClockFrequency_; + +typedef struct { + PacketHeader header; + uint32_t frequency; +} ATTRIBUTE_PACKED GetClockFrequencyResponse_; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetIdentity_; + +typedef struct { + PacketHeader header; + char uid[8]; + char connected_uid[8]; + char position; + uint8_t hardware_version[3]; + uint8_t firmware_version[3]; + uint16_t device_identifier; +} ATTRIBUTE_PACKED GetIdentityResponse_; + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(pop) +#endif +#undef ATTRIBUTE_PACKED + +static void led_strip_callback_wrapper_frame_rendered(DevicePrivate *device_p, Packet *packet) { + FrameRenderedCallbackFunction callback_function; + void *user_data = device_p->registered_callback_user_data[LED_STRIP_CALLBACK_FRAME_RENDERED]; + FrameRenderedCallback_ *callback = (FrameRenderedCallback_ *)packet; + *(void **)(&callback_function) = device_p->registered_callbacks[LED_STRIP_CALLBACK_FRAME_RENDERED]; + + if (callback_function == NULL) { + return; + } + + callback->length = leconvert_uint16_from(callback->length); + + callback_function(callback->length, user_data); +} + +void led_strip_create(LEDStrip *led_strip, const char *uid, IPConnection *ipcon) { + DevicePrivate *device_p; + + device_create(led_strip, uid, ipcon->p, 2, 0, 1); + + device_p = led_strip->p; + + device_p->response_expected[LED_STRIP_FUNCTION_SET_RGB_VALUES] = DEVICE_RESPONSE_EXPECTED_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_RGB_VALUES] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_SET_FRAME_DURATION] = DEVICE_RESPONSE_EXPECTED_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_FRAME_DURATION] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_SUPPLY_VOLTAGE] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_CALLBACK_FRAME_RENDERED] = DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY] = DEVICE_RESPONSE_EXPECTED_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_IDENTITY] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + + device_p->callback_wrappers[LED_STRIP_CALLBACK_FRAME_RENDERED] = led_strip_callback_wrapper_frame_rendered; +} + +void led_strip_destroy(LEDStrip *led_strip) { + device_destroy(led_strip); +} + +int led_strip_get_response_expected(LEDStrip *led_strip, uint8_t function_id, bool *ret_response_expected) { + return device_get_response_expected(led_strip->p, function_id, ret_response_expected); +} + +int led_strip_set_response_expected(LEDStrip *led_strip, uint8_t function_id, bool response_expected) { + return device_set_response_expected(led_strip->p, function_id, response_expected); +} + +int led_strip_set_response_expected_all(LEDStrip *led_strip, bool response_expected) { + return device_set_response_expected_all(led_strip->p, response_expected); +} + +void led_strip_register_callback(LEDStrip *led_strip, uint8_t id, void *callback, void *user_data) { + device_register_callback(led_strip->p, id, callback, user_data); +} + +int led_strip_get_api_version(LEDStrip *led_strip, uint8_t ret_api_version[3]) { + return device_get_api_version(led_strip->p, ret_api_version); +} + +int led_strip_set_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t r[16], uint8_t g[16], uint8_t b[16]) { + DevicePrivate *device_p = led_strip->p; + SetRGBValues_ request; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_RGB_VALUES, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + request.index = leconvert_uint16_to(index); + request.length = length; + memcpy(request.r, r, 16 * sizeof(uint8_t)); + memcpy(request.g, g, 16 * sizeof(uint8_t)); + memcpy(request.b, b, 16 * sizeof(uint8_t)); + + ret = device_send_request(device_p, (Packet *)&request, NULL); + + + return ret; +} + +int led_strip_get_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t ret_r[16], uint8_t ret_g[16], uint8_t ret_b[16]) { + DevicePrivate *device_p = led_strip->p; + GetRGBValues_ request; + GetRGBValuesResponse_ response; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_RGB_VALUES, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + request.index = leconvert_uint16_to(index); + request.length = length; + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + + if (ret < 0) { + return ret; + } + memcpy(ret_r, response.r, 16 * sizeof(uint8_t)); + memcpy(ret_g, response.g, 16 * sizeof(uint8_t)); + memcpy(ret_b, response.b, 16 * sizeof(uint8_t)); + + + + return ret; +} + +int led_strip_set_frame_duration(LEDStrip *led_strip, uint16_t duration) { + DevicePrivate *device_p = led_strip->p; + SetFrameDuration_ request; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_FRAME_DURATION, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + request.duration = leconvert_uint16_to(duration); + + ret = device_send_request(device_p, (Packet *)&request, NULL); + + + return ret; +} + +int led_strip_get_frame_duration(LEDStrip *led_strip, uint16_t *ret_duration) { + DevicePrivate *device_p = led_strip->p; + GetFrameDuration_ request; + GetFrameDurationResponse_ response; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_FRAME_DURATION, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + + if (ret < 0) { + return ret; + } + *ret_duration = leconvert_uint16_from(response.duration); + + + + return ret; +} + +int led_strip_get_supply_voltage(LEDStrip *led_strip, uint16_t *ret_voltage) { + DevicePrivate *device_p = led_strip->p; + GetSupplyVoltage_ request; + GetSupplyVoltageResponse_ response; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_SUPPLY_VOLTAGE, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + + if (ret < 0) { + return ret; + } + *ret_voltage = leconvert_uint16_from(response.voltage); + + + + return ret; +} + +int led_strip_set_clock_frequency(LEDStrip *led_strip, uint32_t frequency) { + DevicePrivate *device_p = led_strip->p; + SetClockFrequency_ request; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + request.frequency = leconvert_uint32_to(frequency); + + ret = device_send_request(device_p, (Packet *)&request, NULL); + + + return ret; +} + +int led_strip_get_clock_frequency(LEDStrip *led_strip, uint32_t *ret_frequency) { + DevicePrivate *device_p = led_strip->p; + GetClockFrequency_ request; + GetClockFrequencyResponse_ response; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + + if (ret < 0) { + return ret; + } + *ret_frequency = leconvert_uint32_from(response.frequency); + + + + return ret; +} + +int led_strip_get_identity(LEDStrip *led_strip, char ret_uid[8], char ret_connected_uid[8], char *ret_position, uint8_t ret_hardware_version[3], uint8_t ret_firmware_version[3], uint16_t *ret_device_identifier) { + DevicePrivate *device_p = led_strip->p; + GetIdentity_ request; + GetIdentityResponse_ response; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_IDENTITY, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + + if (ret < 0) { + return ret; + } + strncpy(ret_uid, response.uid, 8); + strncpy(ret_connected_uid, response.connected_uid, 8); + *ret_position = response.position; + memcpy(ret_hardware_version, response.hardware_version, 3 * sizeof(uint8_t)); + memcpy(ret_firmware_version, response.firmware_version, 3 * sizeof(uint8_t)); + *ret_device_identifier = leconvert_uint16_from(response.device_identifier); + + + + return ret; +} diff --git a/dependencies/build/tinkerforge/ip_connection.c b/dependencies/build/tinkerforge/ip_connection.c new file mode 100644 index 00000000..31cf4aee --- /dev/null +++ b/dependencies/build/tinkerforge/ip_connection.c @@ -0,0 +1,2013 @@ +/* + * Copyright (C) 2012-2013 Matthias Bolte + * Copyright (C) 2011 Olaf Lüke + * + * Redistribution and use in source and binary forms of this file, + * with or without modification, are permitted. + */ + +#ifndef _WIN32 + #define _BSD_SOURCE // for usleep from unistd.h +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include +#else + #include + #include + #include // gettimeofday + #include // connect + #include + #include // TCP_NO_DELAY + #include // gethostbyname + #include // struct sockaddr_in +#endif + +#define IPCON_EXPOSE_INTERNALS + +#include "ip_connection.h" + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(push) + #pragma pack(1) + #define ATTRIBUTE_PACKED +#elif defined __GNUC__ + #ifdef _WIN32 + // workaround struct packing bug in GCC 4.7 on Windows + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 + #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) + #else + #define ATTRIBUTE_PACKED __attribute__((packed)) + #endif +#else + #error unknown compiler, do not know how to enable struct packing +#endif + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED Enumerate; + +typedef struct { + PacketHeader header; + char uid[8]; + char connected_uid[8]; + char position; + uint8_t hardware_version[3]; + uint8_t firmware_version[3]; + uint16_t device_identifier; + uint8_t enumeration_type; +} ATTRIBUTE_PACKED EnumerateCallback; + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(pop) +#endif +#undef ATTRIBUTE_PACKED + +#ifndef __cplusplus + #ifdef __GNUC__ + #ifndef __GNUC_PREREQ + #define __GNUC_PREREQ(major, minor) \ + ((((__GNUC__) << 16) + (__GNUC_MINOR__)) >= (((major) << 16) + (minor))) + #endif + #if __GNUC_PREREQ(4, 6) + #define STATIC_ASSERT(condition, message) \ + _Static_assert(condition, message) + #else + #define STATIC_ASSERT(condition, message) // FIXME + #endif + #else + #define STATIC_ASSERT(condition, message) // FIXME + #endif + + STATIC_ASSERT(sizeof(PacketHeader) == 8, "PacketHeader has invalid size"); + STATIC_ASSERT(sizeof(Packet) == 80, "Packet has invalid size"); + STATIC_ASSERT(sizeof(EnumerateCallback) == 34, "EnumerateCallback has invalid size"); +#endif + +/***************************************************************************** + * + * BASE58 + * + *****************************************************************************/ + +#define BASE58_MAX_STR_SIZE 13 + +static const char BASE58_ALPHABET[] = \ + "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; + +#if 0 +static void base58_encode(uint64_t value, char *str) { + uint32_t mod; + char reverse_str[BASE58_MAX_STR_SIZE] = {'\0'}; + int i = 0; + int k = 0; + + while (value >= 58) { + mod = value % 58; + reverse_str[i] = BASE58_ALPHABET[mod]; + value = value / 58; + ++i; + } + + reverse_str[i] = BASE58_ALPHABET[value]; + + for (k = 0; k <= i; k++) { + str[k] = reverse_str[i - k]; + } + + for (; k < BASE58_MAX_STR_SIZE; k++) { + str[k] = '\0'; + } +} +#endif + +static uint64_t base58_decode(const char *str) { + int i; + int k; + uint64_t value = 0; + uint64_t base = 1; + + for (i = 0; i < BASE58_MAX_STR_SIZE; i++) { + if (str[i] == '\0') { + break; + } + } + + --i; + + for (; i >= 0; i--) { + if (str[i] == '\0') { + continue; + } + + for (k = 0; k < 58; k++) { + if (BASE58_ALPHABET[k] == str[i]) { + break; + } + } + + value += k * base; + base *= 58; + } + + return value; +} + +/***************************************************************************** + * + * Socket + * + *****************************************************************************/ + +struct _Socket { +#ifdef _WIN32 + SOCKET handle; +#else + int handle; +#endif + Mutex send_mutex; // used to serialize socket_send calls +}; + +#ifdef _WIN32 + +static int socket_create(Socket *socket_, int domain, int type, int protocol) { + BOOL flag = 1; + + socket_->handle = socket(domain, type, protocol); + + if (socket_->handle == INVALID_SOCKET) { + return -1; + } + + if (setsockopt(socket_->handle, IPPROTO_TCP, TCP_NODELAY, + (const char *)&flag, sizeof(flag)) == SOCKET_ERROR) { + closesocket(socket_->handle); + + return -1; + } + + mutex_create(&socket_->send_mutex); + + return 0; +} + +static void socket_destroy(Socket *socket) { + mutex_destroy(&socket->send_mutex); + + closesocket(socket->handle); +} + +static int socket_connect(Socket *socket, struct sockaddr_in *address, int length) { + return connect(socket->handle, (struct sockaddr *)address, length) == SOCKET_ERROR ? -1 : 0; +} + +static void socket_shutdown(Socket *socket) { + shutdown(socket->handle, SD_BOTH); +} + +static int socket_receive(Socket *socket, void *buffer, int length) { + length = recv(socket->handle, (char *)buffer, length, 0); + + if (length == SOCKET_ERROR) { + length = -1; + + if (WSAGetLastError() == WSAEINTR) { + errno = EINTR; + } else { + errno = EFAULT; + } + } + + return length; +} + +static int socket_send(Socket *socket, void *buffer, int length) { + mutex_lock(&socket->send_mutex); + + length = send(socket->handle, (const char *)buffer, length, 0); + + mutex_unlock(&socket->send_mutex); + + if (length == SOCKET_ERROR) { + length = -1; + } + + return length; +} + +#else + +static int socket_create(Socket *socket_, int domain, int type, int protocol) { + int flag = 1; + + socket_->handle = socket(domain, type, protocol); + + if (socket_->handle < 0) { + return -1; + } + + if (setsockopt(socket_->handle, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, + sizeof(flag)) < 0) { + close(socket_->handle); + + return -1; + } + + mutex_create(&socket_->send_mutex); + + return 0; +} + +static void socket_destroy(Socket *socket) { + mutex_destroy(&socket->send_mutex); + + close(socket->handle); +} + +static int socket_connect(Socket *socket, struct sockaddr_in *address, int length) { + return connect(socket->handle, (struct sockaddr *)address, length); +} + +static void socket_shutdown(Socket *socket) { + shutdown(socket->handle, SHUT_RDWR); +} + +static int socket_receive(Socket *socket, void *buffer, int length) { + return recv(socket->handle, buffer, length, 0); +} + +static int socket_send(Socket *socket, void *buffer, int length) { + int rc; + + mutex_lock(&socket->send_mutex); + + rc = send(socket->handle, buffer, length, 0); + + mutex_unlock(&socket->send_mutex); + + return rc; +} + +#endif + +/***************************************************************************** + * + * Mutex + * + *****************************************************************************/ + +#ifdef _WIN32 + +void mutex_create(Mutex *mutex) { + InitializeCriticalSection(&mutex->handle); +} + +void mutex_destroy(Mutex *mutex) { + DeleteCriticalSection(&mutex->handle); +} + +void mutex_lock(Mutex *mutex) { + EnterCriticalSection(&mutex->handle); +} + +void mutex_unlock(Mutex *mutex) { + LeaveCriticalSection(&mutex->handle); +} + +#else + +void mutex_create(Mutex *mutex) { + pthread_mutex_init(&mutex->handle, NULL); +} + +void mutex_destroy(Mutex *mutex) { + pthread_mutex_destroy(&mutex->handle); +} + +void mutex_lock(Mutex *mutex) { + pthread_mutex_lock(&mutex->handle); +} + +void mutex_unlock(Mutex *mutex) { + pthread_mutex_unlock(&mutex->handle); +} +#endif + +/***************************************************************************** + * + * Event + * + *****************************************************************************/ + +#ifdef _WIN32 + +static void event_create(Event *event) { + event->handle = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +static void event_destroy(Event *event) { + CloseHandle(event->handle); +} + +static void event_set(Event *event) { + SetEvent(event->handle); +} + +static void event_reset(Event *event) { + ResetEvent(event->handle); +} + +static int event_wait(Event *event, uint32_t timeout) { // in msec + return WaitForSingleObject(event->handle, timeout) == WAIT_OBJECT_0 ? 0 : -1; +} + +#else + +static void event_create(Event *event) { + pthread_mutex_init(&event->mutex, NULL); + pthread_cond_init(&event->condition, NULL); + + event->flag = false; +} + +static void event_destroy(Event *event) { + pthread_mutex_destroy(&event->mutex); + pthread_cond_destroy(&event->condition); +} + +static void event_set(Event *event) { + pthread_mutex_lock(&event->mutex); + + event->flag = true; + + pthread_cond_signal(&event->condition); + pthread_mutex_unlock(&event->mutex); +} + +static void event_reset(Event *event) { + pthread_mutex_lock(&event->mutex); + + event->flag = false; + + pthread_mutex_unlock(&event->mutex); +} + +static int event_wait(Event *event, uint32_t timeout) { // in msec + struct timeval tp; + struct timespec ts; + int ret = 0; + + gettimeofday(&tp, NULL); + + ts.tv_sec = tp.tv_sec + timeout / 1000; + ts.tv_nsec = (tp.tv_usec + (timeout % 1000) * 1000) * 1000; + + while (ts.tv_nsec >= 1000000000L) { + ts.tv_sec += 1; + ts.tv_nsec -= 1000000000L; + } + + pthread_mutex_lock(&event->mutex); + + while (!event->flag) { + ret = pthread_cond_timedwait(&event->condition, &event->mutex, &ts); + + if (ret != 0) { + ret = -1; + break; + } + } + + pthread_mutex_unlock(&event->mutex); + + return ret; +} + +#endif + +/***************************************************************************** + * + * Semaphore + * + *****************************************************************************/ + +#ifdef _WIN32 + +static int semaphore_create(Semaphore *semaphore) { + semaphore->handle = CreateSemaphore(NULL, 0, INT32_MAX, NULL); + + return semaphore->handle == NULL ? -1 : 0; +} + +static void semaphore_destroy(Semaphore *semaphore) { + CloseHandle(semaphore->handle); +} + +static int semaphore_acquire(Semaphore *semaphore) { + return WaitForSingleObject(semaphore->handle, INFINITE) != WAIT_OBJECT_0 ? -1 : 0; +} + +static void semaphore_release(Semaphore *semaphore) { + ReleaseSemaphore(semaphore->handle, 1, NULL); +} + +#else + +static int semaphore_create(Semaphore *semaphore) { +#ifdef __APPLE__ + // Mac OS X does not support unnamed semaphores, so we fake them. Unlink + // first to ensure that there is no existing semaphore with that name. + // Then open the semaphore to create a new one. Finally unlink it again to + // avoid leaking the name. The semaphore will work fine without a name. + char name[100]; + + snprintf(name, sizeof(name), "tf-ipcon-%p", semaphore); + + sem_unlink(name); + semaphore->pointer = sem_open(name, O_CREAT | O_EXCL, S_IRWXU, 0); + sem_unlink(name); + + if (semaphore->pointer == SEM_FAILED) { + return -1; + } +#else + semaphore->pointer = &semaphore->object; + + if (sem_init(semaphore->pointer, 0, 0) < 0) { + return -1; + } +#endif + + return 0; +} + +static void semaphore_destroy(Semaphore *semaphore) { +#ifdef __APPLE__ + sem_close(semaphore->pointer); +#else + sem_destroy(semaphore->pointer); +#endif +} + +static int semaphore_acquire(Semaphore *semaphore) { + return sem_wait(semaphore->pointer) < 0 ? -1 : 0; +} + +static void semaphore_release(Semaphore *semaphore) { + sem_post(semaphore->pointer); +} + +#endif + +/***************************************************************************** + * + * Thread + * + *****************************************************************************/ + +#ifdef _WIN32 + +static DWORD WINAPI thread_wrapper(void *opaque) { + Thread *thread = (Thread *)opaque; + + thread->function(thread->opaque); + + return 0; +} + +static int thread_create(Thread *thread, ThreadFunction function, void *opaque) { + thread->function = function; + thread->opaque = opaque; + + thread->handle = CreateThread(NULL, 0, thread_wrapper, thread, 0, &thread->id); + + return thread->handle == NULL ? -1 : 0; +} + +static void thread_destroy(Thread *thread) { + CloseHandle(thread->handle); +} + +static bool thread_is_current(Thread *thread) { + return thread->id == GetCurrentThreadId(); +} + +static void thread_join(Thread *thread) { + WaitForSingleObject(thread->handle, INFINITE); +} + +static void thread_sleep(int msec) { + Sleep(msec); +} + +#else + +static void *thread_wrapper(void *opaque) { + Thread *thread = (Thread *)opaque; + + thread->function(thread->opaque); + + return NULL; +} + +static int thread_create(Thread *thread, ThreadFunction function, void *opaque) { + thread->function = function; + thread->opaque = opaque; + + return pthread_create(&thread->handle, NULL, thread_wrapper, thread); +} + +static void thread_destroy(Thread *thread) { + (void)thread; +} + +static bool thread_is_current(Thread *thread) { + return pthread_equal(thread->handle, pthread_self()) ? true : false; +} + +static void thread_join(Thread *thread) { + pthread_join(thread->handle, NULL); +} + +static void thread_sleep(int msec) { + usleep(msec * 1000); +} + +#endif + +/***************************************************************************** + * + * Table + * + *****************************************************************************/ + +static void table_create(Table *table) { + mutex_create(&table->mutex); + + table->used = 0; + table->allocated = 16; + table->keys = (uint32_t *)malloc(sizeof(uint32_t) * table->allocated); + table->values = (void **)malloc(sizeof(void *) * table->allocated); +} + +static void table_destroy(Table *table) { + free(table->keys); + free(table->values); + + mutex_destroy(&table->mutex); +} + +static void table_insert(Table *table, uint32_t key, void *value) { + int i; + + mutex_lock(&table->mutex); + + for (i = 0; i < table->used; ++i) { + if (table->keys[i] == key) { + table->values[i] = value; + + mutex_unlock(&table->mutex); + + return; + } + } + + if (table->allocated <= table->used) { + table->allocated += 16; + table->keys = (uint32_t *)realloc(table->keys, sizeof(uint32_t) * table->allocated); + table->values = (void **)realloc(table->values, sizeof(void *) * table->allocated); + } + + table->keys[table->used] = key; + table->values[table->used] = value; + + ++table->used; + + mutex_unlock(&table->mutex); +} + +static void table_remove(Table *table, uint32_t key) { + int i; + int tail; + + mutex_lock(&table->mutex); + + for (i = 0; i < table->used; ++i) { + if (table->keys[i] == key) { + tail = table->used - i - 1; + + if (tail > 0) { + memmove(table->keys + i, table->keys + i + 1, sizeof(uint32_t) * tail); + memmove(table->values + i, table->values + i + 1, sizeof(void *) * tail); + } + + --table->used; + + break; + } + } + + mutex_unlock(&table->mutex); +} + +static void *table_get(Table *table, uint32_t key) { + int i; + void *value = NULL; + + mutex_lock(&table->mutex); + + for (i = 0; i < table->used; ++i) { + if (table->keys[i] == key) { + value = table->values[i]; + + break; + } + } + + mutex_unlock(&table->mutex); + + return value; +} + +/***************************************************************************** + * + * Queue + * + *****************************************************************************/ + +enum { + QUEUE_KIND_EXIT = 0, + QUEUE_KIND_META, + QUEUE_KIND_PACKET +}; + +typedef struct { + uint8_t function_id; + uint8_t parameter; + uint64_t socket_id; +} Meta; + +static void queue_create(Queue *queue) { + queue->head = NULL; + queue->tail = NULL; + + mutex_create(&queue->mutex); + semaphore_create(&queue->semaphore); +} + +static void queue_destroy(Queue *queue) { + QueueItem *item = queue->head; + QueueItem *next; + + while (item != NULL) { + next = item->next; + + free(item->data); + free(item); + + item = next; + } + + mutex_destroy(&queue->mutex); + semaphore_destroy(&queue->semaphore); +} + +static void queue_put(Queue *queue, int kind, void *data, int length) { + QueueItem *item = (QueueItem *)malloc(sizeof(QueueItem)); + + item->next = NULL; + item->kind = kind; + item->data = NULL; + item->length = length; + + if (data != NULL) { + item->data = malloc(length); + memcpy(item->data, data, length); + } + + mutex_lock(&queue->mutex); + + if (queue->tail == NULL) { + queue->head = item; + queue->tail = item; + } else { + queue->tail->next = item; + queue->tail = item; + } + + mutex_unlock(&queue->mutex); + semaphore_release(&queue->semaphore); +} + +static int queue_get(Queue *queue, int *kind, void **data, int *length) { + QueueItem *item; + + if (semaphore_acquire(&queue->semaphore) < 0) { + return -1; + } + + mutex_lock(&queue->mutex); + + if (queue->head == NULL) { + mutex_unlock(&queue->mutex); + + return -1; + } + + item = queue->head; + queue->head = item->next; + item->next = NULL; + + if (queue->tail == item) { + queue->head = NULL; + queue->tail = NULL; + } + + mutex_unlock(&queue->mutex); + + *kind = item->kind; + *data = item->data; + *length = item->length; + + free(item); + + return 0; +} + +/***************************************************************************** + * + * Device + * + *****************************************************************************/ + +enum { + IPCON_FUNCTION_ENUMERATE = 254 +}; + +static int ipcon_send_request(IPConnectionPrivate *ipcon_p, Packet *request); + +void device_create(Device *device, const char *uid_str, + IPConnectionPrivate *ipcon_p, uint8_t api_version_major, + uint8_t api_version_minor, uint8_t api_version_release) { + DevicePrivate *device_p; + uint64_t uid; + uint32_t value1; + uint32_t value2; + int i; + + device_p = (DevicePrivate *)malloc(sizeof(DevicePrivate)); + device->p = device_p; + + uid = base58_decode(uid_str); + + if (uid > 0xFFFFFFFF) { + // convert from 64bit to 32bit + value1 = uid & 0xFFFFFFFF; + value2 = (uid >> 32) & 0xFFFFFFFF; + + uid = (value1 & 0x00000FFF); + uid |= (value1 & 0x0F000000) >> 12; + uid |= (value2 & 0x0000003F) << 16; + uid |= (value2 & 0x000F0000) << 6; + uid |= (value2 & 0x3F000000) << 2; + } + + device_p->uid = uid & 0xFFFFFFFF; + + device_p->ipcon_p = ipcon_p; + + device_p->api_version[0] = api_version_major; + device_p->api_version[1] = api_version_minor; + device_p->api_version[2] = api_version_release; + + // request + mutex_create(&device_p->request_mutex); + + // response + device_p->expected_response_function_id = 0; + device_p->expected_response_sequence_number = 0; + + mutex_create(&device_p->response_mutex); + + memset(&device_p->response_packet, 0, sizeof(Packet)); + + event_create(&device_p->response_event); + + for (i = 0; i < DEVICE_NUM_FUNCTION_IDS; i++) { + device_p->response_expected[i] = DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID; + } + + device_p->response_expected[IPCON_FUNCTION_ENUMERATE] = DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE; + device_p->response_expected[IPCON_CALLBACK_ENUMERATE] = DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE; + + // callbacks + for (i = 0; i < DEVICE_NUM_FUNCTION_IDS; i++) { + device_p->registered_callbacks[i] = NULL; + device_p->registered_callback_user_data[i] = NULL; + device_p->callback_wrappers[i] = NULL; + } + + // add to IPConnection + table_insert(&ipcon_p->devices, device_p->uid, device_p); +} + +void device_destroy(Device *device) { + DevicePrivate *device_p = device->p; + + table_remove(&device_p->ipcon_p->devices, device_p->uid); + + event_destroy(&device_p->response_event); + + mutex_destroy(&device_p->response_mutex); + + mutex_destroy(&device_p->request_mutex); + + free(device_p); +} + +int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, + bool *ret_response_expected) { + int flag = device_p->response_expected[function_id]; + + if (flag == DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID) { + return E_INVALID_PARAMETER; + } + + if (flag == DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE || + flag == DEVICE_RESPONSE_EXPECTED_TRUE) { + *ret_response_expected = true; + } else { + *ret_response_expected = false; + } + + return E_OK; +} + +int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id, + bool response_expected) { + int current_flag = device_p->response_expected[function_id]; + + if (current_flag != DEVICE_RESPONSE_EXPECTED_TRUE && + current_flag != DEVICE_RESPONSE_EXPECTED_FALSE) { + return E_INVALID_PARAMETER; + } + + device_p->response_expected[function_id] = + response_expected ? DEVICE_RESPONSE_EXPECTED_TRUE + : DEVICE_RESPONSE_EXPECTED_FALSE; + + return E_OK; +} + +int device_set_response_expected_all(DevicePrivate *device_p, bool response_expected) { + int flag = response_expected ? DEVICE_RESPONSE_EXPECTED_TRUE + : DEVICE_RESPONSE_EXPECTED_FALSE; + int i; + + for (i = 0; i < DEVICE_NUM_FUNCTION_IDS; ++i) { + if (device_p->response_expected[i] == DEVICE_RESPONSE_EXPECTED_TRUE || + device_p->response_expected[i] == DEVICE_RESPONSE_EXPECTED_FALSE) { + device_p->response_expected[i] = flag; + } + } + + return E_OK; +} + +void device_register_callback(DevicePrivate *device_p, uint8_t id, void *callback, + void *user_data) { + device_p->registered_callbacks[id] = callback; + device_p->registered_callback_user_data[id] = user_data; +} + +int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]) { + ret_api_version[0] = device_p->api_version[0]; + ret_api_version[1] = device_p->api_version[1]; + ret_api_version[2] = device_p->api_version[2]; + + return E_OK; +} + +int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response) { + int ret = E_OK; + uint8_t sequence_number = packet_header_get_sequence_number(&request->header); + uint8_t response_expected = packet_header_get_response_expected(&request->header); + uint8_t error_code; + + if (response_expected) { + mutex_lock(&device_p->request_mutex); + + event_reset(&device_p->response_event); + + device_p->expected_response_function_id = request->header.function_id; + device_p->expected_response_sequence_number = sequence_number; + } + + ret = ipcon_send_request(device_p->ipcon_p, request); + + if (ret != E_OK) { + if (response_expected) { + mutex_unlock(&device_p->request_mutex); + } + + return ret; + } + + if (response_expected) { + if (event_wait(&device_p->response_event, device_p->ipcon_p->timeout) < 0) { + ret = E_TIMEOUT; + } + + device_p->expected_response_function_id = 0; + device_p->expected_response_sequence_number = 0; + + event_reset(&device_p->response_event); + + if (ret == E_OK) { + mutex_lock(&device_p->response_mutex); + + error_code = packet_header_get_error_code(&device_p->response_packet.header); + + if (device_p->response_packet.header.function_id != request->header.function_id || + packet_header_get_sequence_number(&device_p->response_packet.header) != sequence_number) { + ret = E_TIMEOUT; + } else if (error_code == 0) { + // no error + if (response != NULL) { + memcpy(response, &device_p->response_packet, + device_p->response_packet.header.length); + } + } else if (error_code == 1) { + ret = E_INVALID_PARAMETER; + } else if (error_code == 2) { + ret = E_NOT_SUPPORTED; + } else { + ret = E_UNKNOWN_ERROR_CODE; + } + + mutex_unlock(&device_p->response_mutex); + } + + mutex_unlock(&device_p->request_mutex); + } + + return ret; +} + +/***************************************************************************** + * + * IPConnection + * + *****************************************************************************/ + +struct _CallbackContext { + IPConnectionPrivate *ipcon_p; + Queue queue; + Thread thread; + Mutex mutex; + bool packet_dispatch_allowed; +}; + +static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_reconnect); +static void ipcon_disconnect_unlocked(IPConnectionPrivate *ipcon_p); + +static void ipcon_dispatch_meta(IPConnectionPrivate *ipcon_p, Meta *meta) { + ConnectedCallbackFunction connected_callback_function; + DisconnectedCallbackFunction disconnected_callback_function; + void *user_data; + bool retry; + + if (meta->function_id == IPCON_CALLBACK_CONNECTED) { + if (ipcon_p->registered_callbacks[IPCON_CALLBACK_CONNECTED] != NULL) { + *(void **)(&connected_callback_function) = ipcon_p->registered_callbacks[IPCON_CALLBACK_CONNECTED]; + user_data = ipcon_p->registered_callback_user_data[IPCON_CALLBACK_CONNECTED]; + + connected_callback_function(meta->parameter, user_data); + } + } else if (meta->function_id == IPCON_CALLBACK_DISCONNECTED) { + // need to do this here, the receive loop is not allowed to + // hold the socket mutex because this could cause a deadlock + // with a concurrent call to the (dis-)connect function + if (meta->parameter != IPCON_DISCONNECT_REASON_REQUEST) { + mutex_lock(&ipcon_p->socket_mutex); + + // don't close the socket if it got disconnected or + // reconnected in the meantime + if (ipcon_p->socket != NULL && ipcon_p->socket_id == meta->socket_id) { + // destroy disconnect probe thread + event_set(&ipcon_p->disconnect_probe_event); + thread_join(&ipcon_p->disconnect_probe_thread); + thread_destroy(&ipcon_p->disconnect_probe_thread); + + // destroy socket + socket_destroy(ipcon_p->socket); + free(ipcon_p->socket); + ipcon_p->socket = NULL; + } + + mutex_unlock(&ipcon_p->socket_mutex); + } + + // FIXME: wait a moment here, otherwise the next connect + // attempt will succeed, even if there is no open server + // socket. the first receive will then fail directly + thread_sleep(100); + + if (ipcon_p->registered_callbacks[IPCON_CALLBACK_DISCONNECTED] != NULL) { + *(void **)(&disconnected_callback_function) = ipcon_p->registered_callbacks[IPCON_CALLBACK_DISCONNECTED]; + user_data = ipcon_p->registered_callback_user_data[IPCON_CALLBACK_DISCONNECTED]; + + disconnected_callback_function(meta->parameter, user_data); + } + + if (meta->parameter != IPCON_DISCONNECT_REASON_REQUEST && + ipcon_p->auto_reconnect && ipcon_p->auto_reconnect_allowed) { + ipcon_p->auto_reconnect_pending = true; + retry = true; + + // block here until reconnect. this is okay, there is no + // callback to deliver when there is no connection + while (retry) { + retry = false; + + mutex_lock(&ipcon_p->socket_mutex); + + if (ipcon_p->auto_reconnect_allowed && ipcon_p->socket == NULL) { + if (ipcon_connect_unlocked(ipcon_p, true) < 0) { + retry = true; + } + } else { + ipcon_p->auto_reconnect_pending = false; + } + + mutex_unlock(&ipcon_p->socket_mutex); + + if (retry) { + // wait a moment to give another thread a chance to + // interrupt the auto-reconnect + thread_sleep(100); + } + } + } + } +} + +static void ipcon_dispatch_packet(IPConnectionPrivate *ipcon_p, Packet *packet) { + EnumerateCallbackFunction enumerate_callback_function; + void *user_data; + EnumerateCallback *enumerate_callback; + DevicePrivate *device_p; + CallbackWrapperFunction callback_wrapper_function; + + if (packet->header.function_id == IPCON_CALLBACK_ENUMERATE) { + if (ipcon_p->registered_callbacks[IPCON_CALLBACK_ENUMERATE] != NULL) { + *(void **)(&enumerate_callback_function) = ipcon_p->registered_callbacks[IPCON_CALLBACK_ENUMERATE]; + user_data = ipcon_p->registered_callback_user_data[IPCON_CALLBACK_ENUMERATE]; + enumerate_callback = (EnumerateCallback *)packet; + + enumerate_callback_function(enumerate_callback->uid, + enumerate_callback->connected_uid, + enumerate_callback->position, + enumerate_callback->hardware_version, + enumerate_callback->firmware_version, + leconvert_uint16_from(enumerate_callback->device_identifier), + enumerate_callback->enumeration_type, + user_data); + } + } else { + device_p = (DevicePrivate *)table_get(&ipcon_p->devices, packet->header.uid); + + if (device_p == NULL) { + return; + } + + callback_wrapper_function = device_p->callback_wrappers[packet->header.function_id]; + + if (callback_wrapper_function == NULL) { + return; + } + + callback_wrapper_function(device_p, packet); + } +} + +static void ipcon_callback_loop(void *opaque) { + CallbackContext *callback = (CallbackContext *)opaque; + int kind; + void *data; + int length; + + while (true) { + if (queue_get(&callback->queue, &kind, &data, &length) < 0) { + // FIXME: what to do here? try again? exit? + break; + } + + // FIXME: cannot lock callback mutex here because this can + // deadlock due to an ordering problem with the socket mutex + //mutex_lock(&callback->mutex); + + if (kind == QUEUE_KIND_EXIT) { + //mutex_unlock(&callback->mutex); + break; + } else if (kind == QUEUE_KIND_META) { + ipcon_dispatch_meta(callback->ipcon_p, (Meta *)data); + } else if (kind == QUEUE_KIND_PACKET) { + // don't dispatch callbacks when the receive thread isn't running + if (callback->packet_dispatch_allowed) { + ipcon_dispatch_packet(callback->ipcon_p, (Packet *)data); + } + } + + //mutex_unlock(&callback->mutex); + + free(data); + } + + // cleanup + mutex_destroy(&callback->mutex); + queue_destroy(&callback->queue); + thread_destroy(&callback->thread); + + free(callback); +} + +// NOTE: assumes that socket_mutex is locked if disconnect_immediately is true +static void ipcon_handle_disconnect_by_peer(IPConnectionPrivate *ipcon_p, + uint8_t disconnect_reason, + uint64_t socket_id, + bool disconnect_immediately) { + Meta meta; + + ipcon_p->auto_reconnect_allowed = true; + + if (disconnect_immediately) { + ipcon_disconnect_unlocked(ipcon_p); + } + + meta.function_id = IPCON_CALLBACK_DISCONNECTED; + meta.parameter = disconnect_reason; + meta.socket_id = socket_id; + + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_META, &meta, sizeof(meta)); +} + +enum { + IPCON_DISCONNECT_PROBE_INTERVAL = 5000 +}; + +enum { + IPCON_FUNCTION_DISCONNECT_PROBE = 128 +}; + +// NOTE: the disconnect probe loop is not allowed to hold the socket_mutex at any +// time because it is created and joined while the socket_mutex is locked +static void ipcon_disconnect_probe_loop(void *opaque) { + IPConnectionPrivate *ipcon_p = (IPConnectionPrivate *)opaque; + PacketHeader disconnect_probe; + + packet_header_create(&disconnect_probe, sizeof(PacketHeader), + IPCON_FUNCTION_DISCONNECT_PROBE, ipcon_p, NULL); + + while (event_wait(&ipcon_p->disconnect_probe_event, + IPCON_DISCONNECT_PROBE_INTERVAL) < 0) { + if (ipcon_p->disconnect_probe_flag) { + // FIXME: this might block + if (socket_send(ipcon_p->socket, &disconnect_probe, + disconnect_probe.length) < 0) { + ipcon_handle_disconnect_by_peer(ipcon_p, IPCON_DISCONNECT_REASON_ERROR, + ipcon_p->socket_id, false); + break; + } + } else { + ipcon_p->disconnect_probe_flag = true; + } + } +} + +static void ipcon_handle_response(IPConnectionPrivate *ipcon_p, Packet *response) { + DevicePrivate *device_p; + uint8_t sequence_number = packet_header_get_sequence_number(&response->header); + + ipcon_p->disconnect_probe_flag = false; + + response->header.uid = leconvert_uint32_from(response->header.uid); + + if (sequence_number == 0 && + response->header.function_id == IPCON_CALLBACK_ENUMERATE) { + if (ipcon_p->registered_callbacks[IPCON_CALLBACK_ENUMERATE] != NULL) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_PACKET, response, + response->header.length); + } + + return; + } + + device_p = (DevicePrivate *)table_get(&ipcon_p->devices, response->header.uid); + + if (device_p == NULL) { + // ignoring response for an unknown device + return; + } + + if (sequence_number == 0) { + if (device_p->registered_callbacks[response->header.function_id] != NULL) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_PACKET, response, + response->header.length); + } + + return; + } + + if (device_p->expected_response_function_id == response->header.function_id && + device_p->expected_response_sequence_number == sequence_number) { + mutex_lock(&device_p->response_mutex); + memcpy(&device_p->response_packet, response, response->header.length); + mutex_unlock(&device_p->response_mutex); + + event_set(&device_p->response_event); + return; + } + + // response seems to be OK, but can't be handled +} + +// NOTE: the receive loop is now allowed to hold the socket_mutex at any time +// because it is created and joined while the socket_mutex is locked +static void ipcon_receive_loop(void *opaque) { + IPConnectionPrivate *ipcon_p = (IPConnectionPrivate *)opaque; + uint64_t socket_id = ipcon_p->socket_id; + Packet pending_data[10]; + int pending_length = 0; + int length; + uint8_t disconnect_reason; + + while (ipcon_p->receive_flag) { + length = socket_receive(ipcon_p->socket, (uint8_t *)pending_data + pending_length, + sizeof(pending_data) - pending_length); + + if (!ipcon_p->receive_flag) { + return; + } + + if (length <= 0) { + if (length < 0 && errno == EINTR) { + continue; + } + + if (length == 0) { + disconnect_reason = IPCON_DISCONNECT_REASON_SHUTDOWN; + } else { + disconnect_reason = IPCON_DISCONNECT_REASON_ERROR; + } + + ipcon_handle_disconnect_by_peer(ipcon_p, disconnect_reason, socket_id, false); + return; + } + + pending_length += length; + + while (ipcon_p->receive_flag) { + if (pending_length < 8) { + // wait for complete header + break; + } + + length = pending_data[0].header.length; + + if (pending_length < length) { + // wait for complete packet + break; + } + + ipcon_handle_response(ipcon_p, pending_data); + + memmove(pending_data, (uint8_t *)pending_data + length, + pending_length - length); + pending_length -= length; + } + } +} + +// NOTE: assumes that socket_mutex is locked +static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_reconnect) { + struct hostent *entity; + struct sockaddr_in address; + uint8_t connect_reason; + Meta meta; + + // create callback queue and thread + if (ipcon_p->callback == NULL) { + ipcon_p->callback = (CallbackContext *)malloc(sizeof(CallbackContext)); + + ipcon_p->callback->ipcon_p = ipcon_p; + ipcon_p->callback->packet_dispatch_allowed = false; + + queue_create(&ipcon_p->callback->queue); + mutex_create(&ipcon_p->callback->mutex); + + if (thread_create(&ipcon_p->callback->thread, ipcon_callback_loop, + ipcon_p->callback) < 0) { + mutex_destroy(&ipcon_p->callback->mutex); + queue_destroy(&ipcon_p->callback->queue); + + free(ipcon_p->callback); + ipcon_p->callback = NULL; + + return E_NO_THREAD; + } + } + + // create and connect socket + entity = gethostbyname(ipcon_p->host); + + if (entity == NULL) { + // destroy callback thread + if (!is_auto_reconnect) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_EXIT, NULL, 0); + + if (!thread_is_current(&ipcon_p->callback->thread)) { + thread_join(&ipcon_p->callback->thread); + } + + ipcon_p->callback = NULL; + } + + return E_HOSTNAME_INVALID; + } + + memset(&address, 0, sizeof(struct sockaddr_in)); + memcpy(&address.sin_addr, entity->h_addr_list[0], entity->h_length); + + address.sin_family = AF_INET; + address.sin_port = htons(ipcon_p->port); + + ipcon_p->socket = (Socket *)malloc(sizeof(Socket)); + + if (socket_create(ipcon_p->socket, AF_INET, SOCK_STREAM, 0) < 0) { + // destroy callback thread + if (!is_auto_reconnect) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_EXIT, NULL, 0); + + if (!thread_is_current(&ipcon_p->callback->thread)) { + thread_join(&ipcon_p->callback->thread); + } + + ipcon_p->callback = NULL; + } + + // destroy socket + free(ipcon_p->socket); + ipcon_p->socket = NULL; + + return E_NO_STREAM_SOCKET; + } + + if (socket_connect(ipcon_p->socket, &address, sizeof(address)) < 0) { + // destroy callback thread + if (!is_auto_reconnect) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_EXIT, NULL, 0); + + if (!thread_is_current(&ipcon_p->callback->thread)) { + thread_join(&ipcon_p->callback->thread); + } + + ipcon_p->callback = NULL; + } + + // destroy socket + socket_destroy(ipcon_p->socket); + free(ipcon_p->socket); + ipcon_p->socket = NULL; + + return E_NO_CONNECT; + } + + ++ipcon_p->socket_id; + + // create disconnect probe thread + ipcon_p->disconnect_probe_flag = true; + + event_reset(&ipcon_p->disconnect_probe_event); + + if (thread_create(&ipcon_p->disconnect_probe_thread, + ipcon_disconnect_probe_loop, ipcon_p) < 0) { + // destroy callback thread + if (!is_auto_reconnect) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_EXIT, NULL, 0); + + if (!thread_is_current(&ipcon_p->callback->thread)) { + thread_join(&ipcon_p->callback->thread); + } + + ipcon_p->callback = NULL; + } + + // destroy socket + socket_destroy(ipcon_p->socket); + free(ipcon_p->socket); + ipcon_p->socket = NULL; + + return E_NO_THREAD; + } + + // create receive thread + ipcon_p->receive_flag = true; + ipcon_p->callback->packet_dispatch_allowed = true; + + if (thread_create(&ipcon_p->receive_thread, ipcon_receive_loop, ipcon_p) < 0) { + ipcon_disconnect_unlocked(ipcon_p); + + // destroy callback thread + if (!is_auto_reconnect) { + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_EXIT, NULL, 0); + + if (!thread_is_current(&ipcon_p->callback->thread)) { + thread_join(&ipcon_p->callback->thread); + } + + ipcon_p->callback = NULL; + } + + return E_NO_THREAD; + } + + ipcon_p->auto_reconnect_allowed = false; + ipcon_p->auto_reconnect_pending = false; + + // trigger connected callback + if (is_auto_reconnect) { + connect_reason = IPCON_CONNECT_REASON_AUTO_RECONNECT; + } else { + connect_reason = IPCON_CONNECT_REASON_REQUEST; + } + + meta.function_id = IPCON_CALLBACK_CONNECTED; + meta.parameter = connect_reason; + meta.socket_id = 0; + + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_META, &meta, sizeof(meta)); + + return E_OK; +} + +// NOTE: assumes that socket_mutex is locked +static void ipcon_disconnect_unlocked(IPConnectionPrivate *ipcon_p) { + // destroy disconnect probe thread + event_set(&ipcon_p->disconnect_probe_event); + thread_join(&ipcon_p->disconnect_probe_thread); + thread_destroy(&ipcon_p->disconnect_probe_thread); + + // stop dispatching packet callbacks before ending the receive + // thread to avoid timeout exceptions due to callback functions + // trying to call getters + if (!thread_is_current(&ipcon_p->callback->thread)) { + // FIXME: cannot lock callback mutex here because this can + // deadlock due to an ordering problem with the socket mutex + //mutex_lock(&ipcon->callback->mutex); + + ipcon_p->callback->packet_dispatch_allowed = false; + + //mutex_unlock(&ipcon->callback->mutex); + } else { + ipcon_p->callback->packet_dispatch_allowed = false; + } + + // destroy receive thread + if (ipcon_p->receive_flag) { + ipcon_p->receive_flag = false; + + socket_shutdown(ipcon_p->socket); + + thread_join(&ipcon_p->receive_thread); + thread_destroy(&ipcon_p->receive_thread); + } + + // destroy socket + socket_destroy(ipcon_p->socket); + free(ipcon_p->socket); + ipcon_p->socket = NULL; +} + +static int ipcon_send_request(IPConnectionPrivate *ipcon_p, Packet *request) { + int ret = E_OK; + + mutex_lock(&ipcon_p->socket_mutex); + + if (ipcon_p->socket == NULL) { + ret = E_NOT_CONNECTED; + } + + if (ret == E_OK) { + if (socket_send(ipcon_p->socket, request, request->header.length) < 0) { + ipcon_handle_disconnect_by_peer(ipcon_p, IPCON_DISCONNECT_REASON_ERROR, + 0, true); + + ret = E_NOT_CONNECTED; + } else { + ipcon_p->disconnect_probe_flag = false; + } + } + + mutex_unlock(&ipcon_p->socket_mutex); + + return ret; +} + +void ipcon_create(IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p; + int i; + + ipcon_p = (IPConnectionPrivate *)malloc(sizeof(IPConnectionPrivate)); + ipcon->p = ipcon_p; + +#ifdef _WIN32 + ipcon_p->wsa_startup_done = false; +#endif + + ipcon_p->host = NULL; + ipcon_p->port = 0; + + ipcon_p->timeout = 2500; + + ipcon_p->auto_reconnect = true; + ipcon_p->auto_reconnect_allowed = false; + ipcon_p->auto_reconnect_pending = false; + + mutex_create(&ipcon_p->sequence_number_mutex); + ipcon_p->next_sequence_number = 0; + + table_create(&ipcon_p->devices); + + for (i = 0; i < IPCON_NUM_CALLBACK_IDS; ++i) { + ipcon_p->registered_callbacks[i] = NULL; + ipcon_p->registered_callback_user_data[i] = NULL; + } + + mutex_create(&ipcon_p->socket_mutex); + ipcon_p->socket = NULL; + ipcon_p->socket_id = 0; + + ipcon_p->receive_flag = false; + + ipcon_p->callback = NULL; + + ipcon_p->disconnect_probe_flag = false; + event_create(&ipcon_p->disconnect_probe_event); + + semaphore_create(&ipcon_p->wait); +} + +void ipcon_destroy(IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p = ipcon->p; + + ipcon_disconnect(ipcon); // FIXME: disable disconnected callback before? + + mutex_destroy(&ipcon_p->sequence_number_mutex); + + table_destroy(&ipcon_p->devices); + + mutex_destroy(&ipcon_p->socket_mutex); + + event_destroy(&ipcon_p->disconnect_probe_event); + + semaphore_destroy(&ipcon_p->wait); + + free(ipcon_p->host); + + free(ipcon_p); +} + +int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port) { + IPConnectionPrivate *ipcon_p = ipcon->p; + int ret; +#ifdef _WIN32 + WSADATA wsa_data; +#endif + + mutex_lock(&ipcon_p->socket_mutex); + +#ifdef _WIN32 + if (!ipcon_p->wsa_startup_done) { + if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { + mutex_unlock(&ipcon_p->socket_mutex); + + return E_NO_STREAM_SOCKET; + } + + ipcon_p->wsa_startup_done = true; + } +#endif + + if (ipcon_p->socket != NULL) { + mutex_unlock(&ipcon_p->socket_mutex); + + return E_ALREADY_CONNECTED; + } + + free(ipcon_p->host); + + ipcon_p->host = strdup(host); + ipcon_p->port = port; + + ret = ipcon_connect_unlocked(ipcon_p, false); + + mutex_unlock(&ipcon_p->socket_mutex); + + return ret; +} + +int ipcon_disconnect(IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p = ipcon->p; + CallbackContext *callback; + Meta meta; + + mutex_lock(&ipcon_p->socket_mutex); + + ipcon_p->auto_reconnect_allowed = false; + + if (ipcon_p->auto_reconnect_pending) { + // abort pending auto-reconnect + ipcon_p->auto_reconnect_pending = false; + } else { + if (ipcon_p->socket == NULL) { + mutex_unlock(&ipcon_p->socket_mutex); + + return E_NOT_CONNECTED; + } + + ipcon_disconnect_unlocked(ipcon_p); + } + + // destroy callback thread + callback = ipcon_p->callback; + ipcon_p->callback = NULL; + + mutex_unlock(&ipcon_p->socket_mutex); + + // do this outside of socket_mutex to allow calling (dis-)connect from + // the callbacks while blocking on the join call here + meta.function_id = IPCON_CALLBACK_DISCONNECTED; + meta.parameter = IPCON_DISCONNECT_REASON_REQUEST; + meta.socket_id = 0; + + queue_put(&callback->queue, QUEUE_KIND_META, &meta, sizeof(meta)); + queue_put(&callback->queue, QUEUE_KIND_EXIT, NULL, 0); + + if (!thread_is_current(&callback->thread)) { + thread_join(&callback->thread); + } + + // NOTE: no further cleanup of the callback queue and thread here, the + // callback thread is doing this on exit + + return E_OK; +} + +int ipcon_get_connection_state(IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p = ipcon->p; + + if (ipcon_p->socket != NULL) { + return IPCON_CONNECTION_STATE_CONNECTED; + } else if (ipcon_p->auto_reconnect_pending) { + return IPCON_CONNECTION_STATE_PENDING; + } else { + return IPCON_CONNECTION_STATE_DISCONNECTED; + } +} + +void ipcon_set_auto_reconnect(IPConnection *ipcon, bool auto_reconnect) { + IPConnectionPrivate *ipcon_p = ipcon->p; + + ipcon_p->auto_reconnect = auto_reconnect; + + if (!ipcon_p->auto_reconnect) { + // abort potentially pending auto reconnect + ipcon_p->auto_reconnect_allowed = false; + } +} + +bool ipcon_get_auto_reconnect(IPConnection *ipcon) { + return ipcon->p->auto_reconnect; +} + +void ipcon_set_timeout(IPConnection *ipcon, uint32_t timeout) { // in msec + ipcon->p->timeout = timeout; +} + +uint32_t ipcon_get_timeout(IPConnection *ipcon) { // in msec + return ipcon->p->timeout; +} + +int ipcon_enumerate(IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p = ipcon->p; + Enumerate enumerate; + int ret; + + ret = packet_header_create(&enumerate.header, sizeof(Enumerate), + IPCON_FUNCTION_ENUMERATE, ipcon_p, NULL); + + if (ret < 0) { + return ret; + } + + return ipcon_send_request(ipcon_p, (Packet *)&enumerate); +} + +void ipcon_wait(IPConnection *ipcon) { + semaphore_acquire(&ipcon->p->wait); +} + +void ipcon_unwait(IPConnection *ipcon) { + semaphore_release(&ipcon->p->wait); +} + +void ipcon_register_callback(IPConnection *ipcon, uint8_t id, void *callback, + void *user_data) { + IPConnectionPrivate *ipcon_p = ipcon->p; + + ipcon_p->registered_callbacks[id] = callback; + ipcon_p->registered_callback_user_data[id] = user_data; +} + +int packet_header_create(PacketHeader *header, uint8_t length, + uint8_t function_id, IPConnectionPrivate *ipcon_p, + DevicePrivate *device_p) { + uint8_t sequence_number; + bool response_expected = false; + int ret = E_OK; + + mutex_lock(&ipcon_p->sequence_number_mutex); + + sequence_number = ipcon_p->next_sequence_number + 1; + ipcon_p->next_sequence_number = sequence_number % 15; + + mutex_unlock(&ipcon_p->sequence_number_mutex); + + memset(header, 0, sizeof(PacketHeader)); + + if (device_p != NULL) { + header->uid = leconvert_uint32_to(device_p->uid); + } + + header->length = length; + header->function_id = function_id; + packet_header_set_sequence_number(header, sequence_number); + + if (device_p != NULL) { + ret = device_get_response_expected(device_p, function_id, &response_expected); + packet_header_set_response_expected(header, response_expected ? 1 : 0); + } + + return ret; +} + +uint8_t packet_header_get_sequence_number(PacketHeader *header) { + return (header->sequence_number_and_options >> 4) & 0x0F; +} + +void packet_header_set_sequence_number(PacketHeader *header, + uint8_t sequence_number) { + header->sequence_number_and_options |= (sequence_number << 4) & 0xF0; +} + +uint8_t packet_header_get_response_expected(PacketHeader *header) { + return (header->sequence_number_and_options >> 3) & 0x01; +} + +void packet_header_set_response_expected(PacketHeader *header, + uint8_t response_expected) { + header->sequence_number_and_options |= (response_expected << 3) & 0x08; +} + +uint8_t packet_header_get_error_code(PacketHeader *header) { + return (header->error_code_and_future_use >> 6) & 0x03; +} + +// undefine potential defines from /usr/include/endian.h +#undef LITTLE_ENDIAN +#undef BIG_ENDIAN + +#define LITTLE_ENDIAN 0x03020100ul +#define BIG_ENDIAN 0x00010203ul + +static const union { + uint8_t bytes[4]; + uint32_t value; +} native_endian = { + { 0, 1, 2, 3 } +}; + +static void *leconvert_swap16(void *data) { + uint8_t *s = (uint8_t *)data; + uint8_t d[2]; + + d[0] = s[1]; + d[1] = s[0]; + + s[0] = d[0]; + s[1] = d[1]; + + return data; +} + +static void *leconvert_swap32(void *data) { + uint8_t *s = (uint8_t *)data; + uint8_t d[4]; + + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + + s[0] = d[0]; + s[1] = d[1]; + s[2] = d[2]; + s[3] = d[3]; + + return data; +} + +static void *leconvert_swap64(void *data) { + uint8_t *s = (uint8_t *)data; + uint8_t d[8]; + + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + + s[0] = d[0]; + s[1] = d[1]; + s[2] = d[2]; + s[3] = d[3]; + s[4] = d[4]; + s[5] = d[5]; + s[6] = d[6]; + s[7] = d[7]; + + return data; +} + +int16_t leconvert_int16_to(int16_t native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(int16_t *)leconvert_swap16(&native); + } +} + +uint16_t leconvert_uint16_to(uint16_t native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(uint16_t *)leconvert_swap16(&native); + } +} + +int32_t leconvert_int32_to(int32_t native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(int32_t *)leconvert_swap32(&native); + } +} + +uint32_t leconvert_uint32_to(uint32_t native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(uint32_t *)leconvert_swap32(&native); + } +} + +int64_t leconvert_int64_to(int64_t native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(int64_t *)leconvert_swap64(&native); + } +} + +uint64_t leconvert_uint64_to(uint64_t native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(uint64_t *)leconvert_swap64(&native); + } +} + +float leconvert_float_to(float native) { + if (native_endian.value == LITTLE_ENDIAN) { + return native; + } else { + return *(float *)leconvert_swap32(&native); + } +} + +int16_t leconvert_int16_from(int16_t little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(int16_t *)leconvert_swap16(&little); + } +} + +uint16_t leconvert_uint16_from(uint16_t little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(uint16_t *)leconvert_swap16(&little); + } +} + +int32_t leconvert_int32_from(int32_t little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(int32_t *)leconvert_swap32(&little); + } +} + +uint32_t leconvert_uint32_from(uint32_t little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(uint32_t *)leconvert_swap32(&little); + } +} + +int64_t leconvert_int64_from(int64_t little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(int64_t *)leconvert_swap64(&little); + } +} + +uint64_t leconvert_uint64_from(uint64_t little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(uint64_t *)leconvert_swap64(&little); + } +} + +float leconvert_float_from(float little) { + if (native_endian.value == LITTLE_ENDIAN) { + return little; + } else { + return *(float *)leconvert_swap32(&little); + } +} diff --git a/dependencies/include/tinkerforge/bricklet_led_strip.h b/dependencies/include/tinkerforge/bricklet_led_strip.h new file mode 100644 index 00000000..11d608c7 --- /dev/null +++ b/dependencies/include/tinkerforge/bricklet_led_strip.h @@ -0,0 +1,301 @@ +/* *********************************************************** + * This file was automatically generated on 2013-12-19. * + * * + * Bindings Version 2.0.13 * + * * + * If you have a bugfix for this file and want to commit it, * + * please fix the bug in the generator. You can find a link * + * to the generator git on tinkerforge.com * + *************************************************************/ + +#ifndef BRICKLET_LED_STRIP_H +#define BRICKLET_LED_STRIP_H + +#include "ip_connection.h" + +/** + * \defgroup BrickletLEDStrip LEDStrip Bricklet + */ + +/** + * \ingroup BrickletLEDStrip + * + * Device to control up to 320 RGB LEDs + */ +typedef Device LEDStrip; + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_SET_RGB_VALUES 1 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_RGB_VALUES 2 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_SET_FRAME_DURATION 3 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_FRAME_DURATION 4 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_SUPPLY_VOLTAGE 5 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY 7 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY 8 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_IDENTITY 255 + +/** + * \ingroup BrickletLEDStrip + * + * Signature: \code void callback(uint16_t length, void *user_data) \endcode + * + * This callback is triggered directly after a new frame is rendered. + * + * You should send the data for the next frame directly after this callback + * was triggered. + * + * For an explanation of the general approach see {@link led_strip_set_rgb_values}. + */ +#define LED_STRIP_CALLBACK_FRAME_RENDERED 6 + + +/** + * \ingroup BrickletLEDStrip + * + * This constant is used to identify a LEDStrip Bricklet. + * + * The {@link led_strip_get_identity} function and the + * {@link IPCON_CALLBACK_ENUMERATE} callback of the IP Connection have a + * \c device_identifier parameter to specify the Brick's or Bricklet's type. + */ +#define LED_STRIP_DEVICE_IDENTIFIER 231 + +/** + * \ingroup BrickletLEDStrip + * + * Creates the device object \c led_strip with the unique device ID \c uid and adds + * it to the IPConnection \c ipcon. + */ +void led_strip_create(LEDStrip *led_strip, const char *uid, IPConnection *ipcon); + +/** + * \ingroup BrickletLEDStrip + * + * Removes the device object \c led_strip from its IPConnection and destroys it. + * The device object cannot be used anymore afterwards. + */ +void led_strip_destroy(LEDStrip *led_strip); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the response expected flag for the function specified by the + * \c function_id parameter. It is *true* if the function is expected to + * send a response, *false* otherwise. + * + * For getter functions this is enabled by default and cannot be disabled, + * because those functions will always send a response. For callback + * configuration functions it is enabled by default too, but can be disabled + * via the led_strip_set_response_expected function. For setter functions it is + * disabled by default and can be enabled. + * + * Enabling the response expected flag for a setter function allows to + * detect timeouts and other error conditions calls of this setter as well. + * The device will then send a response for this purpose. If this flag is + * disabled for a setter function then no response is send and errors are + * silently ignored, because they cannot be detected. + */ +int led_strip_get_response_expected(LEDStrip *led_strip, uint8_t function_id, bool *ret_response_expected); + +/** + * \ingroup BrickletLEDStrip + * + * Changes the response expected flag of the function specified by the + * \c function_id parameter. This flag can only be changed for setter + * (default value: *false*) and callback configuration functions + * (default value: *true*). For getter functions it is always enabled and + * callbacks it is always disabled. + * + * Enabling the response expected flag for a setter function allows to detect + * timeouts and other error conditions calls of this setter as well. The device + * will then send a response for this purpose. If this flag is disabled for a + * setter function then no response is send and errors are silently ignored, + * because they cannot be detected. + */ +int led_strip_set_response_expected(LEDStrip *led_strip, uint8_t function_id, bool response_expected); + +/** + * \ingroup BrickletLEDStrip + * + * Changes the response expected flag for all setter and callback configuration + * functions of this device at once. + */ +int led_strip_set_response_expected_all(LEDStrip *led_strip, bool response_expected); + +/** + * \ingroup BrickletLEDStrip + * + * Registers a callback with ID \c id to the function \c callback. The + * \c user_data will be given as a parameter of the callback. + */ +void led_strip_register_callback(LEDStrip *led_strip, uint8_t id, void *callback, void *user_data); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the API version (major, minor, release) of the bindings for this + * device. + */ +int led_strip_get_api_version(LEDStrip *led_strip, uint8_t ret_api_version[3]); + +/** + * \ingroup BrickletLEDStrip + * + * Sets the *rgb* values for the LEDs with the given *length* starting + * from *index*. + * + * The maximum length is 16, the index goes from 0 to 319 and the rgb values + * have 8 bits each. + * + * Example: If you set + * + * * index to 5, + * * length to 3, + * * r to [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + * * g to [0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] and + * * b to [0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + * + * the LED with index 5 will be red, 6 will be green and 7 will be blue. + * + * The colors will be transfered to actual LEDs when the next + * frame duration ends, see {@link led_strip_set_frame_duration}. + * + * Generic approach: + * + * * Set the frame duration to a value that represents + * the number of frames per second you want to achieve. + * * Set all of the LED colors for one frame. + * * Wait for the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback. + * * Set all of the LED colors for next frame. + * * Wait for the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback. + * * and so on. + * + * This approach ensures that you can change the LED colors with + * a fixed frame rate. + * + * The actual number of controllable LEDs depends on the number of free + * Bricklet ports. See :ref:`here ` for more + * information. A call of {@link led_strip_set_rgb_values} with index + length above the + * bounds is ignored completely. + */ +int led_strip_set_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t r[16], uint8_t g[16], uint8_t b[16]); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the rgb with the given *length* starting from the + * given *index*. + * + * The values are the last values that were set by {@link led_strip_set_rgb_values}. + */ +int led_strip_get_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t ret_r[16], uint8_t ret_g[16], uint8_t ret_b[16]); + +/** + * \ingroup BrickletLEDStrip + * + * Sets the frame duration in ms. + * + * Example: If you want to achieve 20 frames per second, you should + * set the frame duration to 50ms (50ms * 20 = 1 second). + * + * For an explanation of the general approach see {@link led_strip_set_rgb_values}. + * + * Default value: 100ms (10 frames per second). + */ +int led_strip_set_frame_duration(LEDStrip *led_strip, uint16_t duration); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the frame duration as set by {@link led_strip_set_frame_duration}. + */ +int led_strip_get_frame_duration(LEDStrip *led_strip, uint16_t *ret_duration); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the current supply voltage of the LEDs. The voltage is given in mV. + */ +int led_strip_get_supply_voltage(LEDStrip *led_strip, uint16_t *ret_voltage); + +/** + * \ingroup BrickletLEDStrip + * + * Sets the frequency of the clock in Hz. The range is 10000Hz (10kHz) up to + * 2000000Hz (2MHz). + * + * The Bricklet will choose the nearest achievable frequency, which may + * be off by a few Hz. You can get the exact frequency that is used by + * calling {@link led_strip_get_clock_frequency}. + * + * If you have problems with flickering LEDs, they may be bits flipping. You + * can fix this by either making the connection between the LEDs and the + * Bricklet shorter or by reducing the frequency. + * + * With a decreasing frequency your maximum frames per second will decrease + * too. + * + * The default value is 1.66MHz. + * + * \note + * The frequency in firmware version 2.0.0 is fixed at 2MHz. + * + * .. versionadded:: 2.0.1~(Plugin) + */ +int led_strip_set_clock_frequency(LEDStrip *led_strip, uint32_t frequency); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the currently used clock frequency. + * + * .. versionadded:: 2.0.1~(Plugin) + */ +int led_strip_get_clock_frequency(LEDStrip *led_strip, uint32_t *ret_frequency); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the UID, the UID where the Bricklet is connected to, + * the position, the hardware and firmware version as well as the + * device identifier. + * + * The position can be 'a', 'b', 'c' or 'd'. + * + * The device identifiers can be found :ref:`here `. + * + * .. versionadded:: 2.0.0~(Plugin) + */ +int led_strip_get_identity(LEDStrip *led_strip, char ret_uid[8], char ret_connected_uid[8], char *ret_position, uint8_t ret_hardware_version[3], uint8_t ret_firmware_version[3], uint16_t *ret_device_identifier); + +#endif diff --git a/dependencies/include/tinkerforge/ip_connection.h b/dependencies/include/tinkerforge/ip_connection.h new file mode 100644 index 00000000..5369bf76 --- /dev/null +++ b/dependencies/include/tinkerforge/ip_connection.h @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2012-2013 Matthias Bolte + * Copyright (C) 2011 Olaf Lüke + * + * Redistribution and use in source and binary forms of this file, + * with or without modification, are permitted. + */ + +#ifndef IP_CONNECTION_H +#define IP_CONNECTION_H + +/** + * \defgroup IPConnection IP Connection + */ + +#ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS +#endif +#include +#include +#include + +#if !defined __cplusplus && defined __GNUC__ + #include +#endif + +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include +#else + #include + #include +#endif + +enum { + E_OK = 0, + E_TIMEOUT = -1, + E_NO_STREAM_SOCKET = -2, + E_HOSTNAME_INVALID = -3, + E_NO_CONNECT = -4, + E_NO_THREAD = -5, + E_NOT_ADDED = -6, // unused since v2.0 + E_ALREADY_CONNECTED = -7, + E_NOT_CONNECTED = -8, + E_INVALID_PARAMETER = -9, // error response from device + E_NOT_SUPPORTED = -10, // error response from device + E_UNKNOWN_ERROR_CODE = -11 // error response from device +}; + +#ifdef IPCON_EXPOSE_INTERNALS + +typedef struct _Socket Socket; + +typedef struct { +#ifdef _WIN32 + CRITICAL_SECTION handle; +#else + pthread_mutex_t handle; +#endif +} Mutex; + +void mutex_create(Mutex *mutex); + +void mutex_destroy(Mutex *mutex); + +void mutex_lock(Mutex *mutex); + +void mutex_unlock(Mutex *mutex); + +typedef struct { +#ifdef _WIN32 + HANDLE handle; +#else + pthread_cond_t condition; + pthread_mutex_t mutex; + bool flag; +#endif +} Event; + +typedef struct { +#ifdef _WIN32 + HANDLE handle; +#else + sem_t object; + sem_t *pointer; +#endif +} Semaphore; + +typedef void (*ThreadFunction)(void *opaque); + +typedef struct { +#ifdef _WIN32 + HANDLE handle; + DWORD id; +#else + pthread_t handle; +#endif + ThreadFunction function; + void *opaque; +} Thread; + +typedef struct { + Mutex mutex; + int used; + int allocated; + uint32_t *keys; + void **values; +} Table; + +typedef struct _QueueItem { + struct _QueueItem *next; + int kind; + void *data; + int length; +} QueueItem; + +typedef struct { + Mutex mutex; + Semaphore semaphore; + QueueItem *head; + QueueItem *tail; +} Queue; + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(push) + #pragma pack(1) + #define ATTRIBUTE_PACKED +#elif defined __GNUC__ + #ifdef _WIN32 + // workaround struct packing bug in GCC 4.7 on Windows + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 + #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) + #else + #define ATTRIBUTE_PACKED __attribute__((packed)) + #endif +#else + #error unknown compiler, do not know how to enable struct packing +#endif + +typedef struct { + uint32_t uid; + uint8_t length; + uint8_t function_id; + uint8_t sequence_number_and_options; + uint8_t error_code_and_future_use; +} ATTRIBUTE_PACKED PacketHeader; + +typedef struct { + PacketHeader header; + uint8_t payload[64]; + uint8_t optional_data[8]; +} ATTRIBUTE_PACKED Packet; + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(pop) +#endif +#undef ATTRIBUTE_PACKED + +#endif // IPCON_EXPOSE_INTERNALS + +typedef struct _IPConnection IPConnection; +typedef struct _IPConnectionPrivate IPConnectionPrivate; +typedef struct _Device Device; +typedef struct _DevicePrivate DevicePrivate; + +#ifdef IPCON_EXPOSE_INTERNALS + +typedef struct _CallbackContext CallbackContext; + +#endif + +typedef void (*EnumerateCallbackFunction)(const char *uid, + const char *connected_uid, + char position, + uint8_t hardware_version[3], + uint8_t firmware_version[3], + uint16_t device_identifier, + uint8_t enumeration_type, + void *user_data); +typedef void (*ConnectedCallbackFunction)(uint8_t connect_reason, + void *user_data); +typedef void (*DisconnectedCallbackFunction)(uint8_t disconnect_reason, + void *user_data); + +#ifdef IPCON_EXPOSE_INTERNALS + +typedef void (*CallbackWrapperFunction)(DevicePrivate *device_p, Packet *packet); + +#endif + +/** + * \internal + */ +struct _Device { + DevicePrivate *p; +}; + +#ifdef IPCON_EXPOSE_INTERNALS + +#define DEVICE_NUM_FUNCTION_IDS 256 + +/** + * \internal + */ +struct _DevicePrivate { + uint32_t uid; + + IPConnectionPrivate *ipcon_p; + + uint8_t api_version[3]; + + Mutex request_mutex; + + uint8_t expected_response_function_id; // protected by request_mutex + uint8_t expected_response_sequence_number; // protected by request_mutex + Mutex response_mutex; + Packet response_packet; // protected by response_mutex + Event response_event; + int response_expected[DEVICE_NUM_FUNCTION_IDS]; + + void *registered_callbacks[DEVICE_NUM_FUNCTION_IDS]; + void *registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS]; + CallbackWrapperFunction callback_wrappers[DEVICE_NUM_FUNCTION_IDS]; +}; + +/** + * \internal + */ +enum { + DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID = 0, + DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE, // getter + DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE, // callback + DEVICE_RESPONSE_EXPECTED_TRUE, // setter + DEVICE_RESPONSE_EXPECTED_FALSE // setter, default +}; + +/** + * \internal + */ +void device_create(Device *device, const char *uid, + IPConnectionPrivate *ipcon_p, uint8_t api_version_major, + uint8_t api_version_minor, uint8_t api_version_release); + +/** + * \internal + */ +void device_destroy(Device *device); + +/** + * \internal + */ +int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, + bool *ret_response_expected); + +/** + * \internal + */ +int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id, + bool response_expected); + +/** + * \internal + */ +int device_set_response_expected_all(DevicePrivate *device_p, bool response_expected); + +/** + * \internal + */ +void device_register_callback(DevicePrivate *device_p, uint8_t id, void *callback, + void *user_data); + +/** + * \internal + */ +int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]); + +/** + * \internal + */ +int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response); + +#endif // IPCON_EXPOSE_INTERNALS + +/** + * \ingroup IPConnection + * + * Possible IDs for ipcon_register_callback. + */ +enum { + IPCON_CALLBACK_ENUMERATE = 253, + IPCON_CALLBACK_CONNECTED = 0, + IPCON_CALLBACK_DISCONNECTED = 1 +}; + +/** + * \ingroup IPConnection + * + * Possible values for enumeration_type parameter of EnumerateCallback. + */ +enum { + IPCON_ENUMERATION_TYPE_AVAILABLE = 0, + IPCON_ENUMERATION_TYPE_CONNECTED = 1, + IPCON_ENUMERATION_TYPE_DISCONNECTED = 2 +}; + +/** + * \ingroup IPConnection + * + * Possible values for connect_reason parameter of ConnectedCallback. + */ +enum { + IPCON_CONNECT_REASON_REQUEST = 0, + IPCON_CONNECT_REASON_AUTO_RECONNECT = 1 +}; + +/** + * \ingroup IPConnection + * + * Possible values for disconnect_reason parameter of DisconnectedCallback. + */ +enum { + IPCON_DISCONNECT_REASON_REQUEST = 0, + IPCON_DISCONNECT_REASON_ERROR = 1, + IPCON_DISCONNECT_REASON_SHUTDOWN = 2 +}; + +/** + * \ingroup IPConnection + * + * Possible return values of ipcon_get_connection_state. + */ +enum { + IPCON_CONNECTION_STATE_DISCONNECTED = 0, + IPCON_CONNECTION_STATE_CONNECTED = 1, + IPCON_CONNECTION_STATE_PENDING = 2 // auto-reconnect in progress +}; + +/** + * \internal + */ +struct _IPConnection { + IPConnectionPrivate *p; +}; + +#ifdef IPCON_EXPOSE_INTERNALS + +#define IPCON_NUM_CALLBACK_IDS 256 + +/** + * \internal + */ +struct _IPConnectionPrivate { +#ifdef _WIN32 + bool wsa_startup_done; // protected by socket_mutex +#endif + + char *host; + uint16_t port; + + uint32_t timeout; // in msec + + bool auto_reconnect; + bool auto_reconnect_allowed; + bool auto_reconnect_pending; + + Mutex sequence_number_mutex; + uint8_t next_sequence_number; // protected by sequence_number_mutex + + Table devices; + + void *registered_callbacks[IPCON_NUM_CALLBACK_IDS]; + void *registered_callback_user_data[IPCON_NUM_CALLBACK_IDS]; + + Mutex socket_mutex; + Socket *socket; // protected by socket_mutex + uint64_t socket_id; // protected by socket_mutex + + bool receive_flag; + Thread receive_thread; // protected by socket_mutex + + CallbackContext *callback; + + bool disconnect_probe_flag; + Thread disconnect_probe_thread; // protected by socket_mutex + Event disconnect_probe_event; + + Semaphore wait; +}; + +#endif // IPCON_EXPOSE_INTERNALS + +/** + * \ingroup IPConnection + * + * Creates an IP Connection object that can be used to enumerate the available + * devices. It is also required for the constructor of Bricks and Bricklets. + */ +void ipcon_create(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Destroys the IP Connection object. Calls ipcon_disconnect internally. + * The connection to the Brick Daemon gets closed and the threads of the + * IP Connection are terminated. + */ +void ipcon_destroy(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Creates a TCP/IP connection to the given \c host and c\ port. The host and + * port can point to a Brick Daemon or to a WIFI/Ethernet Extension. + * + * Devices can only be controlled when the connection was established + * successfully. + * + * Blocks until the connection is established and returns an error code if + * there is no Brick Daemon or WIFI/Ethernet Extension listening at the given + * host and port. + */ +int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port); + +/** + * \ingroup IPConnection + * + * Disconnects the TCP/IP connection from the Brick Daemon or the WIFI/Ethernet + * Extension. + */ +int ipcon_disconnect(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Can return the following states: + * + * - IPCON_CONNECTION_STATE_DISCONNECTED: No connection is established. + * - IPCON_CONNECTION_STATE_CONNECTED: A connection to the Brick Daemon or + * the WIFI/Ethernet Extension is established. + * - IPCON_CONNECTION_STATE_PENDING: IP Connection is currently trying to + * connect. + */ +int ipcon_get_connection_state(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Enables or disables auto-reconnect. If auto-reconnect is enabled, + * the IP Connection will try to reconnect to the previously given + * host and port, if the connection is lost. + * + * Default value is *true*. + */ +void ipcon_set_auto_reconnect(IPConnection *ipcon, bool auto_reconnect); + +/** + * \ingroup IPConnection + * + * Returns *true* if auto-reconnect is enabled, *false* otherwise. + */ +bool ipcon_get_auto_reconnect(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Sets the timeout in milliseconds for getters and for setters for which the + * response expected flag is activated. + * + * Default timeout is 2500. + */ +void ipcon_set_timeout(IPConnection *ipcon, uint32_t timeout); + +/** + * \ingroup IPConnection + * + * Returns the timeout as set by ipcon_set_timeout. + */ +uint32_t ipcon_get_timeout(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Broadcasts an enumerate request. All devices will respond with an enumerate + * callback. + */ +int ipcon_enumerate(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Stops the current thread until ipcon_unwait is called. + * + * This is useful if you rely solely on callbacks for events, if you want + * to wait for a specific callback or if the IP Connection was created in + * a thread. + * + * ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release" + * of a semaphore. + */ +void ipcon_wait(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Unwaits the thread previously stopped by ipcon_wait. + * + * ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release" + * of a semaphore. + */ +void ipcon_unwait(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Registers a callback for a given ID. + */ +void ipcon_register_callback(IPConnection *ipcon, uint8_t id, + void *callback, void *user_data); + +#ifdef IPCON_EXPOSE_INTERNALS + +/** + * \internal + */ +int packet_header_create(PacketHeader *header, uint8_t length, + uint8_t function_id, IPConnectionPrivate *ipcon_p, + DevicePrivate *device_p); + +/** + * \internal + */ +uint8_t packet_header_get_sequence_number(PacketHeader *header); + +/** + * \internal + */ +void packet_header_set_sequence_number(PacketHeader *header, + uint8_t sequence_number); + +/** + * \internal + */ +uint8_t packet_header_get_response_expected(PacketHeader *header); + +/** + * \internal + */ +void packet_header_set_response_expected(PacketHeader *header, + uint8_t response_expected); + +/** + * \internal + */ +uint8_t packet_header_get_error_code(PacketHeader *header); + +/** + * \internal + */ +int16_t leconvert_int16_to(int16_t native); + +/** + * \internal + */ +uint16_t leconvert_uint16_to(uint16_t native); + +/** + * \internal + */ +int32_t leconvert_int32_to(int32_t native); + +/** + * \internal + */ +uint32_t leconvert_uint32_to(uint32_t native); + +/** + * \internal + */ +int64_t leconvert_int64_to(int64_t native); + +/** + * \internal + */ +uint64_t leconvert_uint64_to(uint64_t native); + +/** + * \internal + */ +float leconvert_float_to(float native); + +/** + * \internal + */ +int16_t leconvert_int16_from(int16_t little); + +/** + * \internal + */ +uint16_t leconvert_uint16_from(uint16_t little); + +/** + * \internal + */ +int32_t leconvert_int32_from(int32_t little); + +/** + * \internal + */ +uint32_t leconvert_uint32_from(uint32_t little); + +/** + * \internal + */ +int64_t leconvert_int64_from(int64_t little); + +/** + * \internal + */ +uint64_t leconvert_uint64_from(uint64_t little); + +/** + * \internal + */ +float leconvert_float_from(float little); + +#endif // IPCON_EXPOSE_INTERNALS + +#endif diff --git a/dependencies/tinkerforge_c_bindings_2_0_13.zip b/dependencies/tinkerforge_c_bindings_2_0_13.zip new file mode 100644 index 00000000..198d9688 Binary files /dev/null and b/dependencies/tinkerforge_c_bindings_2_0_13.zip differ diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 6bfc2fe8..6e2acfdc 100644 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -8,8 +8,8 @@ find_package(libusb-1.0 REQUIRED) find_package(Threads REQUIRED) include_directories( - ../../include/hidapi - ${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device + ../../include/hidapi + ${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device # Group the headers that go through the MOC compiler SET(Leddevice_QT_HEADERS @@ -67,6 +67,17 @@ if(ENABLE_SPIDEV) ) endif(ENABLE_SPIDEV) +if(ENABLE_TINKERFORGE) + SET(Leddevice_HEADERS + ${Leddevice_HEADERS} + ${CURRENT_SOURCE_DIR}/LedDeviceTinkerforge.h + ) + SET(Leddevice_SOURCES + ${Leddevice_SOURCES} + ${CURRENT_SOURCE_DIR}/LedDeviceTinkerforge.cpp + ) +endif(ENABLE_TINKERFORGE) + QT4_WRAP_CPP(Leddevice_HEADERS_MOC ${Leddevice_QT_HEADERS}) @@ -78,12 +89,17 @@ add_library(leddevice ) target_link_libraries(leddevice - hyperion-utils - serialport - ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev - ${CMAKE_THREAD_LIBS_INIT} - ${QT_LIBRARIES} + hyperion-utils + serialport + ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev + ${CMAKE_THREAD_LIBS_INIT} + ${QT_LIBRARIES} ) + +if(ENABLE_TINKERFORGE) + target_link_libraries(leddevice tinkerforge) +endif() + if(APPLE) target_link_libraries(leddevice hidapi-mac) else() diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 2a9aeb4e..4c596e88 100644 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -16,6 +16,10 @@ #include "LedDeviceWs2801.h" #endif +#ifdef ENABLE_TINKERFORGE + #include "LedDeviceTinkerforge.h" +#endif + #include "LedDeviceAdalight.h" #include "LedDeviceLightpack.h" #include "LedDeviceMultiLightpack.h" @@ -87,6 +91,22 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) device = deviceWs2801; } #endif + +#ifdef ENABLE_TINKERFORGE + else if (type=="tinkerforge") + { + const std::string host = deviceConfig.get("output", "127.0.0.1").asString(); + const uint16_t port = deviceConfig.get("port", 4223).asInt(); + const std::string uid = deviceConfig["uid"].asString(); + const unsigned rate = deviceConfig["rate"].asInt(); + + LedDeviceTinkerforge* deviceTinkerforge = new LedDeviceTinkerforge(host, port, uid, rate); + deviceTinkerforge->open(); + + device = deviceTinkerforge; + } +#endif + // else if (type == "ws2811") // { // const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/leddevice/LedDeviceTinkerforge.cpp b/libsrc/leddevice/LedDeviceTinkerforge.cpp new file mode 100644 index 00000000..e99f482f --- /dev/null +++ b/libsrc/leddevice/LedDeviceTinkerforge.cpp @@ -0,0 +1,143 @@ + +// STL includes +#include +#include + +// Local LedDevice includes +#include "LedDeviceTinkerforge.h" + +static const unsigned MAX_NUM_LEDS = 320; +static const unsigned MAX_NUM_LEDS_SETTABLE = 16; + +LedDeviceTinkerforge::LedDeviceTinkerforge(const std::string & host, uint16_t port, const std::string & uid, const unsigned interval) : + LedDevice(), + _host(host), + _port(port), + _uid(uid), + _interval(interval), + _ipConnection(nullptr), + _ledStrip(nullptr), + _colorChannelSize(0) +{ + // empty +} + +LedDeviceTinkerforge::~LedDeviceTinkerforge() +{ + // Close the device (if it is opened) + if (_ipConnection != nullptr && _ledStrip != nullptr) + { + switchOff(); + } + + // Clean up claimed resources + delete _ipConnection; + delete _ledStrip; +} + +int LedDeviceTinkerforge::open() +{ + // Check if connection is already createds + if (_ipConnection != nullptr) + { + std::cout << "Attempt to open existing connection; close before opening" << std::endl; + return -1; + } + + // Initialise a new connection + _ipConnection = new IPConnection; + ipcon_create(_ipConnection); + + int connectionStatus = ipcon_connect(_ipConnection, _host.c_str(), _port); + if (connectionStatus < 0) + { + std::cerr << "Attempt to connect to master brick (" << _host << ":" << _port << ") failed with status " << connectionStatus << std::endl; + return -1; + } + + // Create the 'LedStrip' + _ledStrip = new LEDStrip; + led_strip_create(_ledStrip, _uid.c_str(), _ipConnection); + + int frameStatus = led_strip_set_frame_duration(_ledStrip, _interval); + if (frameStatus < 0) + { + std::cerr << "Attempt to connect to led strip bricklet (led_strip_set_frame_duration()) failed with status " << frameStatus << std::endl; + return -1; + } + + return 0; +} + +int LedDeviceTinkerforge::write(const std::vector &ledValues) +{ + unsigned nrLedValues = ledValues.size(); + + if (nrLedValues > MAX_NUM_LEDS) + { + std::cerr << "Invalid attempt to write led values. Not more than " << MAX_NUM_LEDS << " leds are allowed." << std::endl; + return -1; + } + + if (_colorChannelSize < nrLedValues) + { + _redChannel.resize(nrLedValues, uint8_t(0)); + _greenChannel.resize(nrLedValues, uint8_t(0)); + _blueChannel.resize(nrLedValues, uint8_t(0)); + } + _colorChannelSize = nrLedValues; + + auto redIt = _redChannel.begin(); + auto greenIt = _greenChannel.begin(); + auto blueIt = _blueChannel.begin(); + + for (const ColorRgb &ledValue : ledValues) + { + *redIt = ledValue.red; + ++redIt; + *greenIt = ledValue.green; + ++greenIt; + *blueIt = ledValue.blue; + ++blueIt; + } + + return transferLedData(_ledStrip, 0, _colorChannelSize, _redChannel.data(), _greenChannel.data(), _blueChannel.data()); +} + +int LedDeviceTinkerforge::switchOff() +{ + std::cerr << "Switchoff" << std::endl; + std::fill(_redChannel.begin(), _redChannel.end(), 0); + std::fill(_greenChannel.begin(), _greenChannel.end(), 0); + std::fill(_blueChannel.begin(), _blueChannel.end(), 0); + + return transferLedData(_ledStrip, 0, _colorChannelSize, _redChannel.data(), _greenChannel.data(), _blueChannel.data()); +} + +int LedDeviceTinkerforge::transferLedData(LEDStrip *ledStrip, unsigned index, unsigned length, uint8_t *redChannel, uint8_t *greenChannel, uint8_t *blueChannel) +{ + if (length == 0 || index >= length || length > MAX_NUM_LEDS) + { + return E_INVALID_PARAMETER; + } + + uint8_t * redPtr = redChannel; + uint8_t * greenPtr = greenChannel; + uint8_t * bluePtr = blueChannel; + for (unsigned i=index; i length) ? length - i : MAX_NUM_LEDS_SETTABLE; + const int status = led_strip_set_rgb_values(ledStrip, i, copyLength, redPtr, greenPtr, bluePtr); + redPtr += copyLength; + greenPtr += copyLength; + bluePtr += copyLength; + + if (status != E_OK) + { + std::cerr << "Setting led values failed with status " << status << std::endl; + return status; + } + } + + return E_OK; +} diff --git a/libsrc/leddevice/LedDeviceTinkerforge.h b/libsrc/leddevice/LedDeviceTinkerforge.h new file mode 100644 index 00000000..95f43332 --- /dev/null +++ b/libsrc/leddevice/LedDeviceTinkerforge.h @@ -0,0 +1,82 @@ + +#pragma once + +// STL includes +#include + +// Hyperion-Leddevice includes +#include + + +extern "C" { + #include + #include +} + +class LedDeviceTinkerforge : public LedDevice +{ +public: + + LedDeviceTinkerforge(const std::string &host, uint16_t port, const std::string &uid, const unsigned interval); + + virtual ~LedDeviceTinkerforge(); + + /// + /// Attempts to open a connection to the master bricklet and the led strip bricklet. + /// + /// @return Zero on succes else negative + /// + int open(); + + /// + /// Writes the colors to the led strip bricklet + /// + /// @param ledValues The color value for each led + /// + /// @return Zero on success else negative + /// + virtual int write(const std::vector &ledValues); + + /// + /// Switches off the leds + /// + /// @return Zero on success else negative + /// + virtual int switchOff(); + +private: + /// + /// Writes the data to the led strip blicklet + int transferLedData(LEDStrip *ledstrip, unsigned int index, unsigned int length, uint8_t *redChannel, uint8_t *greenChannel, uint8_t *blueChannel); + + /// The host of the master brick + const std::string _host; + + /// The port of the master brick + const uint16_t _port; + + /// The uid of the led strip bricklet + const std::string _uid; + + /// The interval/rate + const unsigned _interval; + + /// ip connection handle + IPConnection *_ipConnection; + + /// led strip handle + LEDStrip *_ledStrip; + + /// buffer for red channel led data + std::vector _redChannel; + + /// buffer for red channel led data + std::vector _greenChannel; + + /// buffer for red channel led data + std::vector _blueChannel; + + /// buffer size of the color channels + unsigned int _colorChannelSize; + +};