diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 5880abb9..35fee629 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -34,7 +34,6 @@ elif [[ $RUNNER_OS == "Windows" ]]; then elif [[ "$RUNNER_OS" == 'Linux' ]]; then echo "Docker arguments used: DOCKER_IMAGE=${DOCKER_IMAGE}, DOCKER_TAG=${DOCKER_TAG}, TARGET_ARCH=${TARGET_ARCH}" # verification bypass of external dependencies - git config --global --add safe.directory "${GITHUB_WORKSPACE}/dependencies/external/*" # set GitHub Container Registry url REGISTRY_URL="ghcr.io/hyperion-project/${DOCKER_IMAGE}" # take ownership of deploy dir diff --git a/.github/workflows/qt5_6.yml b/.github/workflows/qt5_6.yml index 046f22ba..25f3c3af 100644 --- a/.github/workflows/qt5_6.yml +++ b/.github/workflows/qt5_6.yml @@ -41,25 +41,16 @@ jobs: fail-fast: false matrix: os: [ - { distribution: debian, codename: buster, description: Debian Buster (x86_64), architecture: [ amd64, linux/amd64 ] }, - { distribution: debian, codename: bullseye, description: Debian Bullseye (x86_64), architecture: [ amd64, linux/amd64 ] }, - { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 1/ZERO), architecture: [ armv6, linux/arm/v5 ] }, - { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, - { distribution: debian, codename: bullseye, description: Debian Bullseye (Raspberry Pi 2/3/4), architecture: [ armv7, linux/arm/v7 ] }, - { distribution: debian, codename: buster, description: Debian Buster (Generic AARCH64), architecture: [ arm64, linux/arm64 ] }, - { distribution: debian, codename: bullseye, description: Debian Bullseye (Generic AARCH64), architecture: [ arm64, linux/arm64 ] } + { distribution: debian, codename: buster, description: Debian Buster (x86_64), architecture: [ amd64, linux/amd64 ], platform: x11 }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (x86_64), architecture: [ amd64, linux/amd64 ], platform: x11 }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 1 & Zero), architecture: [ armv6, linux/arm/v5 ], platform: rpi }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi 2), architecture: [ armv7, linux/arm/v7 ], platform: rpi }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (Raspberry Pi 2), architecture: [ armv7, linux/arm/v7 ], platform: rpi }, + { distribution: debian, codename: buster, description: Debian Buster (Raspberry Pi Zero 2 & 3/4), architecture: [ arm64, linux/arm64 ], platform: rpi }, + { distribution: debian, codename: bullseye, description: Debian Bullseye (Raspberry Pi Zero 2 & 3/4), architecture: [ arm64, linux/arm64 ], platform: rpi } ] isQt5: - ${{ inputs.qt_version == '5' }} - include: - - os.architecture[0]: amd64 - platform: x11 - - os.architecture[0]: armv6 - platform: rpi - - os.architecture[0]: armv7 - platform: rpi - - os.architecture[0]: arm64 - platform: amlogic exclude: - isQt5: true os: { distribution: debian, codename: bullseye } @@ -88,7 +79,7 @@ jobs: env: DOCKER_IMAGE: ${{ matrix.os.distribution }} DOCKER_TAG: ${{ matrix.os.codename }}${{ inputs.qt_version == '6' && '-qt6' || '' }} - PLATFORM: ${{ matrix.platform }} + PLATFORM: ${{ matrix.os.platform }} TARGET_ARCH: ${{ matrix.os.architecture[1] }} - name: 📦 Upload diff --git a/CHANGELOG.md b/CHANGELOG.md index f4cde2a7..dcdf81a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,7 +76,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen ### Technical - Changed default build from Stretch to Buster -- Support Qt 6.7, Update to Protobuf 23.4.0, Update mbedTLS to v3.4.0, Update flatbuffers to v23.5.26 +- Support Qt 6.7, Update to Protobuf 25.1, Update mbedTLS to v3.4.0, Update flatbuffers to v23.5.26 - Use C++17 standard as default - Added Pull Request (PR) installation script, allowing users to test development builds savely on Linux - Fixed missing include limits in QJsonSchemaChecker - Thanks @Portisch @@ -111,6 +111,7 @@ To allow segment streaming, enable "Realtime - Use main segment only" in WLED's - REST API - Increased default timeout to address "Operation cancelled" errors - LED Devices: Allow to differentiate between recoverable/unrecoverable errors - Renamed LED area assignment naming to provide clarity on the processing algorithms +- Updated SEDU default baud rates ### Fixed diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index 82a098b7..53c214c1 100755 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -249,13 +249,13 @@ log "---> DEPLOY_PATH = ${DEPLOY_PATH}" # cleanup deploy folder, create folder for ownership sudo rm -fr "${DEPLOY_PATH}" >/dev/null 2>&1 -mkdir -p "${DEPLOY_PATH}" >/dev/null 2>&1 +sudo mkdir -p "${DEPLOY_PATH}" >/dev/null 2>&1 #Remove previous build area, if no incremental build if [ ${BUILD_INCREMENTAL} != true ]; then sudo rm -fr "${BUILD_PATH}" >/dev/null 2>&1 fi -mkdir -p "${BUILD_PATH}" >/dev/null 2>&1 +sudo mkdir -p "${BUILD_PATH}" >/dev/null 2>&1 echo "---> Compiling Hyperion from source code at ${CODE_PATH}" @@ -291,7 +291,7 @@ if [ ${DOCKERRC} == 0 ]; then if [ ${BUILD_PACKAGES} == "true" ]; then echo "---> Copying packages to host folder: ${DEPLOY_PATH}" && - cp -v ${BUILD_PATH}/Hyperion-* ${DEPLOY_PATH} 2>/dev/null + sudo cp -v ${BUILD_PATH}/Hyperion-* ${DEPLOY_PATH} 2>/dev/null echo "---> Find deployment packages in: ${DEPLOY_PATH}" sudo chown -fR $(stat -c "%U:%G" ${BASE_PATH}) ${DEPLOY_PATH} fi diff --git a/cmake/FindGitVersion.cmake b/cmake/FindGitVersion.cmake index 4e8db794..6c0597ff 100644 --- a/cmake/FindGitVersion.cmake +++ b/cmake/FindGitVersion.cmake @@ -1,4 +1,4 @@ -execute_process( COMMAND git config --global --add safe.directory ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ERROR_QUIET ) +execute_process( COMMAND git config --global --add safe.directory "*" ERROR_QUIET ) execute_process( COMMAND git log -1 --format=%cn-%t/%h-%ct WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE BUILD_ID ERROR_QUIET ) execute_process( COMMAND sh -c "git branch | grep '^*' | sed 's;^*;;g' " WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE VERSION_ID ERROR_QUIET ) execute_process( COMMAND sh -c "git remote --verbose | grep origin | grep fetch | cut -f2 | cut -d' ' -f1" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_REMOTE_PATH ERROR_QUIET ) diff --git a/cmake/desktop/hyperion.desktop b/cmake/desktop/hyperion.desktop index 67d02cbd..6bfe24b3 100644 --- a/cmake/desktop/hyperion.desktop +++ b/cmake/desktop/hyperion.desktop @@ -8,4 +8,3 @@ TryExec=hyperiond Exec=hyperiond Type=Application StartupNotify=false -Categories=Application; diff --git a/cmake/package-scripts/postinst b/cmake/package-scripts/postinst index e1b85c12..d1832220 100644 --- a/cmake/package-scripts/postinst +++ b/cmake/package-scripts/postinst @@ -105,26 +105,28 @@ ln -fs $BINSP/scripts/updateHyperionUser.sh $BINTP/updateHyperionUser 2>/dev/nul if [ "$IS_UPGRADE" = false ]; then if [ ! -z "${DISPLAY}" ] || [ ! -z "${WAYLAND_DISPLAY}" ] || [ ! -z "${XDG_CURRENT_DESKTOP}" ]; then echo "---> Install Hyperion desktop icons" - cp -R /usr/share/hyperion/icons /usr/share/icons/hicolor 2>/dev/null + cp -R /usr/share/hyperion/icons/* /usr/share/icons/hicolor/ 2>/dev/null cp /usr/share/hyperion/desktop/hyperion.metainfo.xml /usr/share/metainfo 2>/dev/null - if hash desktop-file-install 2>/dev/null; then + if [ -x "$(command -v desktop-file-install )" ]; then desktop-file-install /usr/share/hyperion/desktop/hyperion.desktop 2>/dev/null else # On some systems this directory doesn't exist by default mkdir -p /usr/share/applications cp /usr/share/hyperion/desktop/hyperion.desktop /usr/share/applications - # Update application icons - if hash update-desktop-database 2>/dev/null; then - update-desktop-database -q /usr/share/applications - update-desktop-database -q /usr/share/icons/ - fi + fi + + # update desktop-database (if exists) + if [ -x "$(command -v update-desktop-database)" ]; then + update-desktop-database -q /usr/share/applications + update-desktop-database -q /usr/share/icons/hicolor fi fi fi -# cleanup desktop icons +# cleanup desktop and icons rm -r /usr/share/hyperion/desktop 2>/dev/null +rm -r /usr/share/hyperion/icons 2>/dev/null #Check, if dtparam=spi=on is in place if [ $CPU_RPI -eq 1 ]; then diff --git a/cmake/package-scripts/prerm b/cmake/package-scripts/prerm index 92b86679..55f6cd5e 100644 --- a/cmake/package-scripts/prerm +++ b/cmake/package-scripts/prerm @@ -59,29 +59,18 @@ fi # In case we don't use a service kill all instances killall hyperiond 2> /dev/null +echo "---> Remove Hyperion desktop icons" # remove desktop/appstream file rm -v /usr/share/applications/hyperion* 2> /dev/null rm -v /usr/share/metainfo/hyperion* 2> /dev/null -# update desktop-database (if exists) -if [ -x /usr/bin/update-desktop-database ]; then - update-desktop-database -q /usr/share/applications -fi - # remove Hyperion icons -for i in 16x16 22x22 24x24 32x32 36x36 48x48 64x64 72x72 96x96 128x128 192x192 256x256 512x512; do - rm -v usr/share/icons/hicolor/$i/apps/hyperion.png 2> /dev/null -done +rm -v /usr/share/icons/hicolor/*/apps/hyperion.png 2> /dev/null -# update icon-cache -if [ -e /usr/share/icons/hicolor/icon-theme.cache ] ; then - # touch it, just in case we cannot find the binary... - touch --no-create /usr/share/icons/hicolor - if hash gtk-update-icon-cache 2>/dev/null; then - gtk-update-icon-cache /usr/share/icons/hicolor - fi - # ignore errors - true +# update desktop-database (if exists) +if [ -x "$(command -v update-desktop-database)" ]; then + update-desktop-database -q /usr/share/applications + update-desktop-database -q /usr/share/icons/hicolor/ fi exit 0 diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 572667fb..18fc9dc9 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -97,7 +97,6 @@ "blueSignalThreshold": 0, "signalDetection": false, "noSignalCounterThreshold": 200, - "cecDetection": false, "sDVOffsetMin": 0.1, "sDVOffsetMax": 0.9, "sDHOffsetMin": 0.4, diff --git a/dependencies/build/tinkerforge/bricklet_led_strip.c b/dependencies/build/tinkerforge/bricklet_led_strip.c index a67486fb..2993adca 100644 --- a/dependencies/build/tinkerforge/bricklet_led_strip.c +++ b/dependencies/build/tinkerforge/bricklet_led_strip.c @@ -1,11 +1,11 @@ /* *********************************************************** - * This file was automatically generated on 2013-12-19. * + * This file was automatically generated on 2023-11-30. * * * - * Bindings Version 2.0.13 * + * C/C++ Bindings Version 2.1.33 * * * * 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 * + * to the generators git repository on tinkerforge.com * *************************************************************/ @@ -15,9 +15,13 @@ #include +#ifdef __cplusplus +extern "C" { +#endif -typedef void (*FrameRenderedCallbackFunction)(uint16_t, void *); + +typedef void (*FrameRendered_CallbackFunction)(uint16_t length, void *user_data); #if defined _MSC_VER || defined __BORLANDC__ #pragma pack(push) @@ -26,7 +30,7 @@ typedef void (*FrameRenderedCallbackFunction)(uint16_t, void *); #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 + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) #else #define ATTRIBUTE_PACKED __attribute__((packed)) @@ -42,66 +46,135 @@ typedef struct { uint8_t r[16]; uint8_t g[16]; uint8_t b[16]; -} ATTRIBUTE_PACKED SetRGBValues_; +} ATTRIBUTE_PACKED SetRGBValues_Request; typedef struct { PacketHeader header; uint16_t index; uint8_t length; -} ATTRIBUTE_PACKED GetRGBValues_; +} ATTRIBUTE_PACKED GetRGBValues_Request; typedef struct { PacketHeader header; uint8_t r[16]; uint8_t g[16]; uint8_t b[16]; -} ATTRIBUTE_PACKED GetRGBValuesResponse_; +} ATTRIBUTE_PACKED GetRGBValues_Response; typedef struct { PacketHeader header; uint16_t duration; -} ATTRIBUTE_PACKED SetFrameDuration_; +} ATTRIBUTE_PACKED SetFrameDuration_Request; typedef struct { PacketHeader header; -} ATTRIBUTE_PACKED GetFrameDuration_; +} ATTRIBUTE_PACKED GetFrameDuration_Request; typedef struct { PacketHeader header; uint16_t duration; -} ATTRIBUTE_PACKED GetFrameDurationResponse_; +} ATTRIBUTE_PACKED GetFrameDuration_Response; typedef struct { PacketHeader header; -} ATTRIBUTE_PACKED GetSupplyVoltage_; +} ATTRIBUTE_PACKED GetSupplyVoltage_Request; typedef struct { PacketHeader header; uint16_t voltage; -} ATTRIBUTE_PACKED GetSupplyVoltageResponse_; +} ATTRIBUTE_PACKED GetSupplyVoltage_Response; typedef struct { PacketHeader header; uint16_t length; -} ATTRIBUTE_PACKED FrameRenderedCallback_; +} ATTRIBUTE_PACKED FrameRendered_Callback; typedef struct { PacketHeader header; uint32_t frequency; -} ATTRIBUTE_PACKED SetClockFrequency_; +} ATTRIBUTE_PACKED SetClockFrequency_Request; typedef struct { PacketHeader header; -} ATTRIBUTE_PACKED GetClockFrequency_; +} ATTRIBUTE_PACKED GetClockFrequency_Request; typedef struct { PacketHeader header; uint32_t frequency; -} ATTRIBUTE_PACKED GetClockFrequencyResponse_; +} ATTRIBUTE_PACKED GetClockFrequency_Response; typedef struct { PacketHeader header; -} ATTRIBUTE_PACKED GetIdentity_; + uint16_t chip; +} ATTRIBUTE_PACKED SetChipType_Request; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetChipType_Request; + +typedef struct { + PacketHeader header; + uint16_t chip; +} ATTRIBUTE_PACKED GetChipType_Response; + +typedef struct { + PacketHeader header; + uint16_t index; + uint8_t length; + uint8_t r[12]; + uint8_t g[12]; + uint8_t b[12]; + uint8_t w[12]; +} ATTRIBUTE_PACKED SetRGBWValues_Request; + +typedef struct { + PacketHeader header; + uint16_t index; + uint8_t length; +} ATTRIBUTE_PACKED GetRGBWValues_Request; + +typedef struct { + PacketHeader header; + uint8_t r[12]; + uint8_t g[12]; + uint8_t b[12]; + uint8_t w[12]; +} ATTRIBUTE_PACKED GetRGBWValues_Response; + +typedef struct { + PacketHeader header; + uint8_t mapping; +} ATTRIBUTE_PACKED SetChannelMapping_Request; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetChannelMapping_Request; + +typedef struct { + PacketHeader header; + uint8_t mapping; +} ATTRIBUTE_PACKED GetChannelMapping_Response; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED EnableFrameRenderedCallback_Request; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED DisableFrameRenderedCallback_Request; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED IsFrameRenderedCallbackEnabled_Request; + +typedef struct { + PacketHeader header; + uint8_t enabled; +} ATTRIBUTE_PACKED IsFrameRenderedCallbackEnabled_Response; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED GetIdentity_Request; typedef struct { PacketHeader header; @@ -111,7 +184,7 @@ typedef struct { uint8_t hardware_version[3]; uint8_t firmware_version[3]; uint16_t device_identifier; -} ATTRIBUTE_PACKED GetIdentityResponse_; +} ATTRIBUTE_PACKED GetIdentity_Response; #if defined _MSC_VER || defined __BORLANDC__ #pragma pack(pop) @@ -119,10 +192,18 @@ typedef struct { #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]; + FrameRendered_CallbackFunction callback_function; + void *user_data; + FrameRendered_Callback *callback; + + if (packet->header.length != sizeof(FrameRendered_Callback)) { + return; // silently ignoring callback with wrong length + } + + callback_function = (FrameRendered_CallbackFunction)device_p->registered_callbacks[DEVICE_NUM_FUNCTION_IDS + LED_STRIP_CALLBACK_FRAME_RENDERED]; + user_data = device_p->registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS + LED_STRIP_CALLBACK_FRAME_RENDERED]; + callback = (FrameRendered_Callback *)packet; + (void)callback; // avoid unused variable warning if (callback_function == NULL) { return; @@ -134,9 +215,10 @@ static void led_strip_callback_wrapper_frame_rendered(DevicePrivate *device_p, P } void led_strip_create(LEDStrip *led_strip, const char *uid, IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p = ipcon->p; DevicePrivate *device_p; - device_create(led_strip, uid, ipcon->p, 2, 0, 1); + device_create(led_strip, uid, ipcon_p, 2, 0, 3, LED_STRIP_DEVICE_IDENTIFIER); device_p = led_strip->p; @@ -145,16 +227,26 @@ void led_strip_create(LEDStrip *led_strip, const char *uid, IPConnection *ipcon) 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_SET_CHIP_TYPE] = DEVICE_RESPONSE_EXPECTED_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_CHIP_TYPE] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_SET_RGBW_VALUES] = DEVICE_RESPONSE_EXPECTED_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_RGBW_VALUES] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_SET_CHANNEL_MAPPING] = DEVICE_RESPONSE_EXPECTED_FALSE; + device_p->response_expected[LED_STRIP_FUNCTION_GET_CHANNEL_MAPPING] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_ENABLE_FRAME_RENDERED_CALLBACK] = DEVICE_RESPONSE_EXPECTED_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_DISABLE_FRAME_RENDERED_CALLBACK] = DEVICE_RESPONSE_EXPECTED_TRUE; + device_p->response_expected[LED_STRIP_FUNCTION_IS_FRAME_RENDERED_CALLBACK_ENABLED] = 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; + + ipcon_add_device(ipcon_p, device_p); } void led_strip_destroy(LEDStrip *led_strip) { - device_destroy(led_strip); + device_release(led_strip->p); } int led_strip_get_response_expected(LEDStrip *led_strip, uint8_t function_id, bool *ret_response_expected) { @@ -169,8 +261,8 @@ int led_strip_set_response_expected_all(LEDStrip *led_strip, bool response_expec 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); +void led_strip_register_callback(LEDStrip *led_strip, int16_t callback_id, void (*function)(void), void *user_data) { + device_register_callback(led_strip->p, callback_id, function, user_data); } int led_strip_get_api_version(LEDStrip *led_strip, uint8_t ret_api_version[3]) { @@ -179,9 +271,15 @@ int led_strip_get_api_version(LEDStrip *led_strip, uint8_t ret_api_version[3]) { 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; + SetRGBValues_Request request; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_RGB_VALUES, device_p->ipcon_p, device_p); if (ret < 0) { @@ -194,18 +292,23 @@ int led_strip_set_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length 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); - + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); 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; + GetRGBValues_Request request; + GetRGBValues_Response response; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_RGB_VALUES, device_p->ipcon_p, device_p); if (ret < 0) { @@ -215,25 +318,30 @@ int led_strip_get_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length request.index = leconvert_uint16_to(index); request.length = length; - ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(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; + SetFrameDuration_Request request; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_FRAME_DURATION, device_p->ipcon_p, device_p); if (ret < 0) { @@ -242,67 +350,80 @@ int led_strip_set_frame_duration(LEDStrip *led_strip, uint16_t duration) { request.duration = leconvert_uint16_to(duration); - ret = device_send_request(device_p, (Packet *)&request, NULL); - + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); 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; + GetFrameDuration_Request request; + GetFrameDuration_Response response; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return 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); + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(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; + GetSupplyVoltage_Request request; + GetSupplyVoltage_Response response; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return 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); + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(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; + SetClockFrequency_Request request; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY, device_p->ipcon_p, device_p); if (ret < 0) { @@ -311,41 +432,287 @@ int led_strip_set_clock_frequency(LEDStrip *led_strip, uint32_t frequency) { request.frequency = leconvert_uint32_to(frequency); - ret = device_send_request(device_p, (Packet *)&request, NULL); - + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); 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; + GetClockFrequency_Request request; + GetClockFrequency_Response response; int ret; + ret = device_check_validity(device_p); + + if (ret < 0) { + return 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); + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); if (ret < 0) { return ret; } + *ret_frequency = leconvert_uint32_from(response.frequency); + return ret; +} +int led_strip_set_chip_type(LEDStrip *led_strip, uint16_t chip) { + DevicePrivate *device_p = led_strip->p; + SetChipType_Request request; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_CHIP_TYPE, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + request.chip = leconvert_uint16_to(chip); + + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); + + return ret; +} + +int led_strip_get_chip_type(LEDStrip *led_strip, uint16_t *ret_chip) { + DevicePrivate *device_p = led_strip->p; + GetChipType_Request request; + GetChipType_Response response; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_CHIP_TYPE, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); + + if (ret < 0) { + return ret; + } + + *ret_chip = leconvert_uint16_from(response.chip); + + return ret; +} + +int led_strip_set_rgbw_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t r[12], uint8_t g[12], uint8_t b[12], uint8_t w[12]) { + DevicePrivate *device_p = led_strip->p; + SetRGBWValues_Request request; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_RGBW_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, 12 * sizeof(uint8_t)); + memcpy(request.g, g, 12 * sizeof(uint8_t)); + memcpy(request.b, b, 12 * sizeof(uint8_t)); + memcpy(request.w, w, 12 * sizeof(uint8_t)); + + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); + + return ret; +} + +int led_strip_get_rgbw_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t ret_r[12], uint8_t ret_g[12], uint8_t ret_b[12], uint8_t ret_w[12]) { + DevicePrivate *device_p = led_strip->p; + GetRGBWValues_Request request; + GetRGBWValues_Response response; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_RGBW_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, sizeof(response)); + + if (ret < 0) { + return ret; + } + + memcpy(ret_r, response.r, 12 * sizeof(uint8_t)); + memcpy(ret_g, response.g, 12 * sizeof(uint8_t)); + memcpy(ret_b, response.b, 12 * sizeof(uint8_t)); + memcpy(ret_w, response.w, 12 * sizeof(uint8_t)); + + return ret; +} + +int led_strip_set_channel_mapping(LEDStrip *led_strip, uint8_t mapping) { + DevicePrivate *device_p = led_strip->p; + SetChannelMapping_Request request; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_CHANNEL_MAPPING, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + request.mapping = mapping; + + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); + + return ret; +} + +int led_strip_get_channel_mapping(LEDStrip *led_strip, uint8_t *ret_mapping) { + DevicePrivate *device_p = led_strip->p; + GetChannelMapping_Request request; + GetChannelMapping_Response response; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_CHANNEL_MAPPING, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); + + if (ret < 0) { + return ret; + } + + *ret_mapping = response.mapping; + + return ret; +} + +int led_strip_enable_frame_rendered_callback(LEDStrip *led_strip) { + DevicePrivate *device_p = led_strip->p; + EnableFrameRenderedCallback_Request request; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_ENABLE_FRAME_RENDERED_CALLBACK, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); + + return ret; +} + +int led_strip_disable_frame_rendered_callback(LEDStrip *led_strip) { + DevicePrivate *device_p = led_strip->p; + DisableFrameRenderedCallback_Request request; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_DISABLE_FRAME_RENDERED_CALLBACK, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); + + return ret; +} + +int led_strip_is_frame_rendered_callback_enabled(LEDStrip *led_strip, bool *ret_enabled) { + DevicePrivate *device_p = led_strip->p; + IsFrameRenderedCallbackEnabled_Request request; + IsFrameRenderedCallbackEnabled_Response response; + int ret; + + ret = device_check_validity(device_p); + + if (ret < 0) { + return ret; + } + + ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_IS_FRAME_RENDERED_CALLBACK_ENABLED, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); + + if (ret < 0) { + return ret; + } + + *ret_enabled = response.enabled != 0; 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; + GetIdentity_Request request; + GetIdentity_Response response; int ret; ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_IDENTITY, device_p->ipcon_p, device_p); @@ -354,20 +721,22 @@ int led_strip_get_identity(LEDStrip *led_strip, char ret_uid[8], char ret_connec return ret; } - - ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response); + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); if (ret < 0) { return ret; } - strncpy(ret_uid, response.uid, 8); - strncpy(ret_connected_uid, response.connected_uid, 8); + + memcpy(ret_uid, response.uid, 8); + memcpy(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; } + +#ifdef __cplusplus +} +#endif diff --git a/dependencies/build/tinkerforge/ip_connection.c b/dependencies/build/tinkerforge/ip_connection.c index 0e496542..9cb7c86d 100644 --- a/dependencies/build/tinkerforge/ip_connection.c +++ b/dependencies/build/tinkerforge/ip_connection.c @@ -1,13 +1,22 @@ /* - * Copyright (C) 2012-2013 Matthias Bolte + * Copyright (C) 2012-2016, 2019-2020 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. + * with or without modification, are permitted. See the Creative + * Commons Zero (CC0 1.0) License for more details. */ #ifndef _WIN32 - #define _DEFAULT_SOURCE // for usleep from unistd.h + #ifndef _BSD_SOURCE + #define _BSD_SOURCE // for usleep from unistd.h + #endif + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #ifndef _DEFAULT_SOURCE + #define _DEFAULT_SOURCE + #endif #endif #include @@ -19,21 +28,45 @@ #ifdef _WIN32 #include + #include + #include + #include #else + #include #include #include - #include // gettimeofday #include // connect #include + #include #include // TCP_NO_DELAY #include // gethostbyname #include // struct sockaddr_in #endif +#ifdef _MSC_VER + // replace getpid with GetCurrentProcessId + #define getpid GetCurrentProcessId + + // avoid warning from MSVC about deprecated POSIX name + #define strdup _strdup +#else + #include // gettimeofday +#endif + +#if defined _MSC_VER && _MSC_VER < 1900 + // snprintf is not available in older MSVC versions + #define snprintf _snprintf +#endif + #define IPCON_EXPOSE_INTERNALS +#define IPCON_EXPOSE_MILLISLEEP #include "ip_connection.h" +#ifdef __cplusplus +extern "C" { +#endif + #if defined _MSC_VER || defined __BORLANDC__ #pragma pack(push) #pragma pack(1) @@ -41,7 +74,7 @@ #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 + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) #else #define ATTRIBUTE_PACKED __attribute__((packed)) @@ -52,7 +85,7 @@ typedef struct { PacketHeader header; -} ATTRIBUTE_PACKED Enumerate; +} ATTRIBUTE_PACKED DeviceEnumerate_Broadcast; typedef struct { PacketHeader header; @@ -63,7 +96,36 @@ typedef struct { uint8_t firmware_version[3]; uint16_t device_identifier; uint8_t enumeration_type; -} ATTRIBUTE_PACKED EnumerateCallback; +} ATTRIBUTE_PACKED DeviceEnumerate_Callback; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED DeviceGetIdentity_Request; + +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 DeviceGetIdentity_Response; + +typedef struct { + PacketHeader header; +} ATTRIBUTE_PACKED BrickDaemonGetAuthenticationNonce_Request; + +typedef struct { + PacketHeader header; + uint8_t server_nonce[4]; +} ATTRIBUTE_PACKED BrickDaemonGetAuthenticationNonce_Response; + +typedef struct { + PacketHeader header; + uint8_t client_nonce[4]; + uint8_t digest[20]; +} ATTRIBUTE_PACKED BrickDaemonAuthenticate_Request; #if defined _MSC_VER || defined __BORLANDC__ #pragma pack(pop) @@ -78,31 +140,384 @@ typedef struct { #endif #if __GNUC_PREREQ(4, 6) #define STATIC_ASSERT(condition, message) \ - _Static_assert(condition, message) + _Static_assert(condition, message); #else - #define STATIC_ASSERT(condition, message) + #define STATIC_ASSERT(condition, message) // FIXME #endif #else - #define STATIC_ASSERT(condition, message) + #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"); + STATIC_ASSERT(sizeof(PacketHeader) == 8, "PacketHeader has invalid size") + STATIC_ASSERT(sizeof(Packet) == 80, "Packet has invalid size") + STATIC_ASSERT(sizeof(DeviceEnumerate_Broadcast) == 8, "DeviceEnumerate_Broadcast has invalid size") + STATIC_ASSERT(sizeof(DeviceEnumerate_Callback) == 34, "DeviceEnumerate_Callback has invalid size") + STATIC_ASSERT(sizeof(DeviceGetIdentity_Request) == 8, "DeviceGetIdentity_Request has invalid size") + STATIC_ASSERT(sizeof(DeviceGetIdentity_Response) == 33, "DeviceGetIdentity_Response has invalid size") + STATIC_ASSERT(sizeof(BrickDaemonGetAuthenticationNonce_Request) == 8, "BrickDaemonGetAuthenticationNonce_Request has invalid size") + STATIC_ASSERT(sizeof(BrickDaemonGetAuthenticationNonce_Response) == 12, "BrickDaemonGetAuthenticationNonce_Response has invalid size") + STATIC_ASSERT(sizeof(BrickDaemonAuthenticate_Request) == 32, "BrickDaemonAuthenticate_Request has invalid size") #endif +void millisleep(uint32_t msec) { +#ifdef _WIN32 + Sleep(msec); +#else + if (msec >= 1000) { + sleep(msec / 1000); + + msec %= 1000; + } + + usleep(msec * 1000); +#endif +} + +/***************************************************************************** + * + * SHA1 + * + *****************************************************************************/ + +/* + * Based on the SHA-1 C implementation by Steve Reid + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + +#define SHA1_BLOCK_LENGTH 64 +#define SHA1_DIGEST_LENGTH 20 + +typedef struct { + uint32_t state[5]; + uint64_t count; + uint8_t buffer[SHA1_BLOCK_LENGTH]; +} SHA1; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +// blk0() and blk() perform the initial expand. blk0() deals with host endianess +#define blk0(i) (block[i] = htonl(block[i])) +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15]^block[(i+2)&15]^block[i&15],1)) + +// (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30) +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30) +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30) +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30) +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30) + +// hash a single 512-bit block. this is the core of the algorithm +static uint32_t sha1_transform(SHA1 *sha1, const uint8_t buffer[SHA1_BLOCK_LENGTH]) { + uint32_t a, b, c, d, e; + uint32_t block[SHA1_BLOCK_LENGTH / 4]; + + memcpy(&block, buffer, SHA1_BLOCK_LENGTH); + + // copy sha1->state[] to working variables + a = sha1->state[0]; + b = sha1->state[1]; + c = sha1->state[2]; + d = sha1->state[3]; + e = sha1->state[4]; + + // 4 rounds of 20 operations each (loop unrolled) + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + // add the working variables back into sha1->state[] + sha1->state[0] += a; + sha1->state[1] += b; + sha1->state[2] += c; + sha1->state[3] += d; + sha1->state[4] += e; + + // wipe variables + a = b = c = d = e = 0; + + return a + b + c + d + e; // return to avoid dead-store warning from clang static analyzer +} + +static void sha1_init(SHA1 *sha1) { + sha1->state[0] = 0x67452301; + sha1->state[1] = 0xEFCDAB89; + sha1->state[2] = 0x98BADCFE; + sha1->state[3] = 0x10325476; + sha1->state[4] = 0xC3D2E1F0; + sha1->count = 0; +} + +static void sha1_update(SHA1 *sha1, const uint8_t *data, size_t length) { + size_t i, j; + + j = (size_t)((sha1->count >> 3) & 63); + sha1->count += (uint64_t)length << 3; + + if ((j + length) > 63) { + i = 64 - j; + + memcpy(&sha1->buffer[j], data, i); + sha1_transform(sha1, sha1->buffer); + + for (; i + 63 < length; i += 64) { + sha1_transform(sha1, &data[i]); + } + + j = 0; + } else { + i = 0; + } + + memcpy(&sha1->buffer[j], &data[i], length - i); +} + +static void sha1_final(SHA1 *sha1, uint8_t digest[SHA1_DIGEST_LENGTH]) { + uint32_t i; + uint8_t count[8]; + + for (i = 0; i < 8; i++) { + // this is endian independent + count[i] = (uint8_t)((sha1->count >> ((7 - (i & 7)) * 8)) & 255); + } + + sha1_update(sha1, (uint8_t *)"\200", 1); + + while ((sha1->count & 504) != 448) { + sha1_update(sha1, (uint8_t *)"\0", 1); + } + + sha1_update(sha1, count, 8); + + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + digest[i] = (uint8_t)((sha1->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + + memset(sha1, 0, sizeof(*sha1)); +} + +#undef rol +#undef blk0 +#undef blk +#undef R0 +#undef R1 +#undef R2 +#undef R3 +#undef R4 + +/***************************************************************************** + * + * Utils + * + *****************************************************************************/ + +static int string_length(const char *s, int max_length) { + const char *p = s; + int n = 0; + + while (*p != '\0' && n < max_length) { + ++p; + ++n; + } + + return n; +} + +#ifdef _MSC_VER + +// difference between Unix epoch and January 1, 1601 in 100-nanoseconds +#define DELTA_EPOCH 116444736000000000ULL + +typedef void (WINAPI *GETSYSTEMTIMEPRECISEASFILETIME)(LPFILETIME); + +// implement gettimeofday based on GetSystemTime(Precise)AsFileTime +static int gettimeofday(struct timeval *tv, struct timezone *tz) { + GETSYSTEMTIMEPRECISEASFILETIME ptr_GetSystemTimePreciseAsFileTime = NULL; + FILETIME ft; + uint64_t t; + + (void)tz; + + if (tv != NULL) { +#pragma warning(push) +#pragma warning(disable: 4191) // stop MSVC from warning about casting FARPROC + ptr_GetSystemTimePreciseAsFileTime = + (GETSYSTEMTIMEPRECISEASFILETIME)GetProcAddress(GetModuleHandleA("kernel32"), + "GetSystemTimePreciseAsFileTime"); +#pragma warning(pop) + + if (ptr_GetSystemTimePreciseAsFileTime != NULL) { + ptr_GetSystemTimePreciseAsFileTime(&ft); + } else { + GetSystemTimeAsFileTime(&ft); + } + + t = ((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime; + t = (t - DELTA_EPOCH) / 10; // 100-nanoseconds to microseconds + + tv->tv_sec = (long)(t / 1000000UL); + tv->tv_usec = (long)(t % 1000000UL); + } + + return 0; +} + +#endif + +#ifndef _WIN32 + +static int read_uint32_non_blocking(const char *filename, uint32_t *value) { + int fd = open(filename, O_NONBLOCK); + int rc; + + if (fd < 0) { + return -1; + } + + rc = (int)read(fd, value, sizeof(uint32_t)); + + close(fd); + + return rc != sizeof(uint32_t) ? -1 : 0; +} + +#endif + +// this function is not meant to be called often, +// this function is meant to provide a good random seed value +static uint32_t get_random_uint32(void) { + uint32_t r = 0; + struct timeval tv; + uint32_t seconds; + uint32_t microseconds; +#ifdef _WIN32 + HCRYPTPROV hprovider; + + if (!CryptAcquireContext(&hprovider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + goto fallback; + } + + if (!CryptGenRandom(hprovider, sizeof(r), (BYTE *)&r)) { + CryptReleaseContext(hprovider, 0); + + goto fallback; + } + + CryptReleaseContext(hprovider, 0); +#else + // try /dev/urandom first, if not available or a read would + // block then fall back to /dev/random + if (read_uint32_non_blocking("/dev/urandom", &r) < 0) { + if (read_uint32_non_blocking("/dev/random", &r) < 0) { + goto fallback; + } + } +#endif + + return r; + +fallback: + // if no other random source is available fall back to the current time + if (gettimeofday(&tv, NULL) < 0) { + seconds = (uint32_t)time(NULL); + microseconds = 0; + } else { + seconds = (uint32_t)tv.tv_sec; + microseconds = tv.tv_usec; + } + + return (seconds << 26 | seconds >> 6) + microseconds + getpid(); // overflow is intended +} + +static void hmac_sha1(uint8_t *secret, int secret_length, + uint8_t *data, int data_length, + uint8_t digest[SHA1_DIGEST_LENGTH]) { + SHA1 sha1; + uint8_t secret_digest[SHA1_DIGEST_LENGTH]; + uint8_t inner_digest[SHA1_DIGEST_LENGTH]; + uint8_t ipad[SHA1_BLOCK_LENGTH]; + uint8_t opad[SHA1_BLOCK_LENGTH]; + int i; + + if (secret_length > SHA1_BLOCK_LENGTH) { + sha1_init(&sha1); + sha1_update(&sha1, secret, secret_length); + sha1_final(&sha1, secret_digest); + + secret = secret_digest; + secret_length = SHA1_DIGEST_LENGTH; + } + + // inner digest + for (i = 0; i < secret_length; ++i) { + ipad[i] = secret[i] ^ 0x36; + } + + for (i = secret_length; i < SHA1_BLOCK_LENGTH; ++i) { + ipad[i] = 0x36; + } + + sha1_init(&sha1); + sha1_update(&sha1, ipad, SHA1_BLOCK_LENGTH); + sha1_update(&sha1, data, data_length); + sha1_final(&sha1, inner_digest); + + // outer digest + for (i = 0; i < secret_length; ++i) { + opad[i] = secret[i] ^ 0x5C; + } + + for (i = secret_length; i < SHA1_BLOCK_LENGTH; ++i) { + opad[i] = 0x5C; + } + + sha1_init(&sha1); + sha1_update(&sha1, opad, SHA1_BLOCK_LENGTH); + sha1_update(&sha1, inner_digest, SHA1_DIGEST_LENGTH); + sha1_final(&sha1, digest); +} + /***************************************************************************** * * BASE58 * *****************************************************************************/ -#define BASE58_MAX_STR_SIZE 13 - static const char BASE58_ALPHABET[] = \ "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; #if 0 + +#define BASE58_MAX_STR_SIZE 13 + static void base58_encode(uint64_t value, char *str) { uint32_t mod; char reverse_str[BASE58_MAX_STR_SIZE] = {'\0'}; @@ -126,38 +541,80 @@ static void base58_encode(uint64_t value, char *str) { str[k] = '\0'; } } + #endif -static uint64_t base58_decode(const char *str) { - int i; +// https://www.fefe.de/intof.html +static bool uint64_add(uint64_t a, uint64_t b, uint64_t *c) { + if (UINT64_MAX - a < b) { + return false; + } + + *c = a + b; + + return true; +} + +static bool uint64_multiply(uint64_t a, uint64_t b, uint64_t *c) { + uint64_t a0 = a & UINT32_MAX; + uint64_t a1 = a >> 32; + uint64_t b0 = b & UINT32_MAX; + uint64_t b1 = b >> 32; + uint64_t c0; + uint64_t c1; + + if (a1 > 0 && b1 > 0) { + return false; + } + + c1 = a1 * b0 + a0 * b1; + + if (c1 > UINT32_MAX) { + return false; + } + + c0 = a0 * b0; + c1 <<= 32; + + return uint64_add(c1, c0, c); +} + +static bool base58_decode(const char *str, uint64_t *ret_value) { + int i = strlen(str) - 1; int k; + uint64_t next; uint64_t value = 0; uint64_t base = 1; - for (i = 0; i < BASE58_MAX_STR_SIZE; i++) { - if (str[i] == '\0') { - break; - } - } + *ret_value = 0; - --i; - - for (; i >= 0; i--) { - if (str[i] == '\0') { - continue; - } - - for (k = 0; k < 58; k++) { + for (; i >= 0; --i) { + for (k = 0; k < 58; ++k) { if (BASE58_ALPHABET[k] == str[i]) { break; } } - value += k * base; - base *= 58; + if (k == 58) { + return false; // invalid char + } + + if (!uint64_multiply(k, base, &next)) { + return false; // overflow + } + + if (!uint64_add(value, next, &value)) { + return false; // overflow + } + + if (i > 0 && !uint64_multiply(base, 58, &base)) { + return false; // overflow + } } - return value; + *ret_value = value; + + return true; } /***************************************************************************** @@ -187,7 +644,7 @@ static int socket_create(Socket *socket_, int domain, int type, int protocol) { } if (setsockopt(socket_->handle, IPPROTO_TCP, TCP_NODELAY, - (const char *)&flag, sizeof(flag)) == SOCKET_ERROR) { + (const char *)&flag, sizeof(flag)) == SOCKET_ERROR) { closesocket(socket_->handle); return -1; @@ -204,8 +661,8 @@ static void socket_destroy(Socket *socket) { 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 int socket_connect(Socket *socket, struct sockaddr *address, int length) { + return connect(socket->handle, address, length) == SOCKET_ERROR ? -1 : 0; } static void socket_shutdown(Socket *socket) { @@ -228,7 +685,7 @@ static int socket_receive(Socket *socket, void *buffer, int length) { return length; } -static int socket_send(Socket *socket, void *buffer, int length) { +static int socket_send(Socket *socket, const void *buffer, int length) { mutex_lock(&socket->send_mutex); length = send(socket->handle, (const char *)buffer, length, 0); @@ -254,7 +711,7 @@ static int socket_create(Socket *socket_, int domain, int type, int protocol) { } if (setsockopt(socket_->handle, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, - sizeof(flag)) < 0) { + sizeof(flag)) < 0) { close(socket_->handle); return -1; @@ -271,8 +728,8 @@ static void socket_destroy(Socket *socket) { close(socket->handle); } -static int socket_connect(Socket *socket, struct sockaddr_in *address, int length) { - return connect(socket->handle, (struct sockaddr *)address, length); +static int socket_connect(Socket *socket, struct sockaddr *address, int length) { + return connect(socket->handle, address, length); } static void socket_shutdown(Socket *socket) { @@ -280,15 +737,15 @@ static void socket_shutdown(Socket *socket) { } static int socket_receive(Socket *socket, void *buffer, int length) { - return recv(socket->handle, buffer, length, 0); + return (int)recv(socket->handle, buffer, length, 0); } -static int socket_send(Socket *socket, void *buffer, int length) { +static int socket_send(Socket *socket, const void *buffer, int length) { int rc; mutex_lock(&socket->send_mutex); - rc = send(socket->handle, buffer, length, 0); + rc = (int)send(socket->handle, buffer, length, 0); mutex_unlock(&socket->send_mutex); @@ -387,7 +844,7 @@ static void event_set(Event *event) { event->flag = true; - pthread_cond_signal(&event->condition); + pthread_cond_broadcast(&event->condition); pthread_mutex_unlock(&event->mutex); } @@ -402,7 +859,7 @@ static void event_reset(Event *event) { static int event_wait(Event *event, uint32_t timeout) { // in msec struct timeval tp; struct timespec ts; - int ret = 0; + int ret = E_OK; gettimeofday(&tp, NULL); @@ -420,7 +877,7 @@ static int event_wait(Event *event, uint32_t timeout) { // in msec ret = pthread_cond_timedwait(&event->condition, &event->mutex, &ts); if (ret != 0) { - ret = -1; + ret = E_TIMEOUT; break; } } @@ -440,10 +897,8 @@ static int event_wait(Event *event, uint32_t timeout) { // in msec #ifdef _WIN32 -static int semaphore_create(Semaphore *semaphore) { +static void semaphore_create(Semaphore *semaphore) { semaphore->handle = CreateSemaphore(NULL, 0, INT32_MAX, NULL); - - return semaphore->handle == NULL ? -1 : 0; } static void semaphore_destroy(Semaphore *semaphore) { @@ -460,7 +915,7 @@ static void semaphore_release(Semaphore *semaphore) { #else -static int semaphore_create(Semaphore *semaphore) { +static void 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. @@ -473,19 +928,11 @@ static int semaphore_create(Semaphore *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; - } + sem_init(semaphore->pointer, 0, 0); #endif - - return 0; } static void semaphore_destroy(Semaphore *semaphore) { @@ -543,10 +990,6 @@ 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) { @@ -576,10 +1019,6 @@ static void thread_join(Thread *thread) { pthread_join(thread->handle, NULL); } -static void thread_sleep(int msec) { - usleep(msec * 1000); -} - #endif /***************************************************************************** @@ -604,18 +1043,20 @@ static void table_destroy(Table *table) { mutex_destroy(&table->mutex); } -static void table_insert(Table *table, uint32_t key, void *value) { +static void *table_insert(Table *table, uint32_t key, void *value) { int i; + void *replaced_value; mutex_lock(&table->mutex); for (i = 0; i < table->used; ++i) { if (table->keys[i] == key) { + replaced_value = table->values[i]; table->values[i] = value; mutex_unlock(&table->mutex); - return; + return replaced_value; } } @@ -631,6 +1072,8 @@ static void table_insert(Table *table, uint32_t key, void *value) { ++table->used; mutex_unlock(&table->mutex); + + return NULL; } static void table_remove(Table *table, uint32_t key) { @@ -684,6 +1127,7 @@ static void *table_get(Table *table, uint32_t key) { enum { QUEUE_KIND_EXIT = 0, + QUEUE_KIND_DESTROY_AND_EXIT, QUEUE_KIND_META, QUEUE_KIND_PACKET }; @@ -719,18 +1163,12 @@ static void queue_destroy(Queue *queue) { semaphore_destroy(&queue->semaphore); } -static void queue_put(Queue *queue, int kind, void *data, int length) { +static void queue_put(Queue *queue, int kind, void *data) { 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); - } + item->data = data; mutex_lock(&queue->mutex); @@ -746,7 +1184,7 @@ static void queue_put(Queue *queue, int kind, void *data, int length) { semaphore_release(&queue->semaphore); } -static int queue_get(Queue *queue, int *kind, void **data, int *length) { +static int queue_get(Queue *queue, int *kind, void **data) { QueueItem *item; if (semaphore_acquire(&queue->semaphore) < 0) { @@ -774,7 +1212,6 @@ static int queue_get(Queue *queue, int *kind, void **data, int *length) { *kind = item->kind; *data = item->data; - *length = item->length; free(item); @@ -788,14 +1225,41 @@ static int queue_get(Queue *queue, int *kind, void **data, int *length) { *****************************************************************************/ enum { - IPCON_FUNCTION_ENUMERATE = 254 + DEVICE_FUNCTION_ENUMERATE = 254, + DEVICE_FUNCTION_GET_IDENTITY = 255 }; static int ipcon_send_request(IPConnectionPrivate *ipcon_p, Packet *request); +// NOTE: assumes device_p->ref_count == 0 +static void device_destroy(DevicePrivate *device_p) { + int i; + + if (!device_p->replaced && device_p->uid_valid) { + table_remove(&device_p->ipcon_p->devices, device_p->uid); + } + + for (i = 0; i < DEVICE_NUM_FUNCTION_IDS; i++) { + free(device_p->high_level_callbacks[i].data); + } + + mutex_destroy(&device_p->stream_mutex); + + event_destroy(&device_p->response_event); + + mutex_destroy(&device_p->response_mutex); + + mutex_destroy(&device_p->request_mutex); + + mutex_destroy(&device_p->device_identifier_mutex); + + free(device_p); +} + 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) { + IPConnectionPrivate *ipcon_p, uint8_t api_version_major, + uint8_t api_version_minor, uint8_t api_version_release, + uint16_t device_identifier) { DevicePrivate *device_p; uint64_t uid; uint32_t value1; @@ -805,9 +1269,11 @@ void device_create(Device *device, const char *uid_str, device_p = (DevicePrivate *)malloc(sizeof(DevicePrivate)); device->p = device_p; - uid = base58_decode(uid_str); + device_p->replaced = false; - if (uid > 0xFFFFFFFF) { + device_p->uid_valid = base58_decode(uid_str, &uid); + + if (device_p->uid_valid && uid > 0xFFFFFFFF) { // convert from 64bit to 32bit value1 = uid & 0xFFFFFFFF; value2 = (uid >> 32) & 0xFFFFFFFF; @@ -819,7 +1285,13 @@ void device_create(Device *device, const char *uid_str, uid |= (value2 & 0x3F000000) << 2; } - device_p->uid = uid & 0xFFFFFFFF; + if (uid == 0) { + device_p->uid_valid = false; // broadcast UID is forbidden + } + + device_p->ref_count = 1; + + device_p->uid = (uint32_t)uid; device_p->ipcon_p = ipcon_p; @@ -827,6 +1299,13 @@ void device_create(Device *device, const char *uid_str, device_p->api_version[1] = api_version_minor; device_p->api_version[2] = api_version_release; + // device identifier + device_p->device_identifier = device_identifier; + + mutex_create(&device_p->device_identifier_mutex); + + device_p->device_identifier_check = DEVICE_IDENTIFIER_CHECK_PENDING; + // request mutex_create(&device_p->request_mutex); @@ -844,36 +1323,39 @@ void device_create(Device *device, const char *uid_str, 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; + // stream + mutex_create(&device_p->stream_mutex); // callbacks - for (i = 0; i < DEVICE_NUM_FUNCTION_IDS; i++) { + for (i = 0; i < DEVICE_NUM_FUNCTION_IDS * 2; 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); + for (i = 0; i < DEVICE_NUM_FUNCTION_IDS; i++) { + device_p->callback_wrappers[i] = NULL; + device_p->high_level_callbacks[i].exists = false; + device_p->high_level_callbacks[i].data = NULL; + device_p->high_level_callbacks[i].length = 0; + } } -void device_destroy(Device *device) { - DevicePrivate *device_p = device->p; +void device_release(DevicePrivate *device_p) { + IPConnectionPrivate *ipcon_p = device_p->ipcon_p; - table_remove(&device_p->ipcon_p->devices, device_p->uid); + mutex_lock(&ipcon_p->devices_ref_mutex); - event_destroy(&device_p->response_event); + --device_p->ref_count; - mutex_destroy(&device_p->response_mutex); + if (device_p->ref_count == 0) { + device_destroy(device_p); + } - mutex_destroy(&device_p->request_mutex); - - free(device_p); + mutex_unlock(&ipcon_p->devices_ref_mutex); } int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, - bool *ret_response_expected) { + bool *ret_response_expected) { int flag = device_p->response_expected[function_id]; if (flag == DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID) { @@ -881,7 +1363,7 @@ int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, } if (flag == DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE || - flag == DEVICE_RESPONSE_EXPECTED_TRUE) { + flag == DEVICE_RESPONSE_EXPECTED_TRUE) { *ret_response_expected = true; } else { *ret_response_expected = false; @@ -891,29 +1373,29 @@ int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, } int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id, - bool response_expected) { + 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) { + 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; + 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; + : 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] == DEVICE_RESPONSE_EXPECTED_FALSE) { device_p->response_expected[i] = flag; } } @@ -921,10 +1403,14 @@ int device_set_response_expected_all(DevicePrivate *device_p, bool response_expe 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; +void device_register_callback(DevicePrivate *device_p, int16_t callback_id, + void (*function)(void), void *user_data) { + if (callback_id <= -DEVICE_NUM_FUNCTION_IDS || callback_id >= DEVICE_NUM_FUNCTION_IDS) { + return; + } + + device_p->registered_callbacks[DEVICE_NUM_FUNCTION_IDS + callback_id] = function; + device_p->registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS + callback_id] = user_data; } int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]) { @@ -935,7 +1421,9 @@ int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]) return E_OK; } -int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response) { +// NOTE: assumes that device_check_validity was successful +int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response, + int expected_response_length) { 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); @@ -976,13 +1464,19 @@ int device_send_request(DevicePrivate *device_p, Packet *request, Packet *respon 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) { + 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) { + if (expected_response_length == 0) { + // setter with response-expected enabled + expected_response_length = sizeof(PacketHeader); + } + + if (device_p->response_packet.header.length != expected_response_length) { + ret = E_WRONG_RESPONSE_LENGTH; + } else if (response != NULL) { memcpy(response, &device_p->response_packet, - device_p->response_packet.header.length); + device_p->response_packet.header.length); } } else if (error_code == 1) { ret = E_INVALID_PARAMETER; @@ -1001,6 +1495,134 @@ int device_send_request(DevicePrivate *device_p, Packet *request, Packet *respon return ret; } +int device_check_validity(DevicePrivate *device_p) { + DeviceGetIdentity_Request request; + DeviceGetIdentity_Response response; + uint16_t device_identifier; + int ret; + + if (device_p->replaced) { + return E_DEVICE_REPLACED; + } + + if (!device_p->uid_valid) { + return E_INVALID_UID; + } + + if (device_p->device_identifier_check == DEVICE_IDENTIFIER_CHECK_PENDING) { + mutex_lock(&device_p->device_identifier_mutex); + + if (device_p->device_identifier_check == DEVICE_IDENTIFIER_CHECK_PENDING) { + ret = packet_header_create(&request.header, sizeof(request), DEVICE_FUNCTION_GET_IDENTITY, device_p->ipcon_p, device_p); + + if (ret < 0) { + mutex_unlock(&device_p->device_identifier_mutex); + + return ret; + } + + // initialize to 0 to stop the clang static analyzer from warning about accessing + // uninitialized memory when accessing the device_identifier member later on + memset(&response, 0, sizeof(response)); + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); + + if (ret < 0) { + mutex_unlock(&device_p->device_identifier_mutex); + + return ret; + } + + device_identifier = leconvert_uint16_from(response.device_identifier); + + if (device_identifier == device_p->device_identifier) { + device_p->device_identifier_check = DEVICE_IDENTIFIER_CHECK_MATCH; + } else { + device_p->device_identifier_check = DEVICE_IDENTIFIER_CHECK_MISMATCH; + } + } + + mutex_unlock(&device_p->device_identifier_mutex); + } + + if (device_p->device_identifier_check == DEVICE_IDENTIFIER_CHECK_MISMATCH) { + return E_WRONG_DEVICE_TYPE; + } + + return E_OK; // DEVICE_IDENTIFIER_CHECK_MATCH +} + +/***************************************************************************** + * + * Brick Daemon + * + *****************************************************************************/ + +enum { + BRICK_DAEMON_FUNCTION_GET_AUTHENTICATION_NONCE = 1, + BRICK_DAEMON_FUNCTION_AUTHENTICATE = 2 +}; + +static void brickd_create(BrickDaemon *brickd, const char *uid, IPConnection *ipcon) { + IPConnectionPrivate *ipcon_p = ipcon->p; + DevicePrivate *device_p; + + device_create(brickd, uid, ipcon_p, 2, 0, 0, 0); + + device_p = brickd->p; + + device_p->response_expected[BRICK_DAEMON_FUNCTION_GET_AUTHENTICATION_NONCE] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE; + device_p->response_expected[BRICK_DAEMON_FUNCTION_AUTHENTICATE] = DEVICE_RESPONSE_EXPECTED_TRUE; + + ipcon_add_device(ipcon_p, device_p); +} + +static void brickd_destroy(BrickDaemon *brickd) { + device_release(brickd->p); +} + +static int brickd_get_authentication_nonce(BrickDaemon *brickd, uint8_t ret_server_nonce[4]) { + DevicePrivate *device_p = brickd->p; + BrickDaemonGetAuthenticationNonce_Request request; + BrickDaemonGetAuthenticationNonce_Response response; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), BRICK_DAEMON_FUNCTION_GET_AUTHENTICATION_NONCE, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response, sizeof(response)); + + if (ret < 0) { + return ret; + } + + memcpy(ret_server_nonce, response.server_nonce, 4 * sizeof(uint8_t)); + + return ret; +} + +static int brickd_authenticate(BrickDaemon *brickd, uint8_t client_nonce[4], uint8_t digest[20]) { + DevicePrivate *device_p = brickd->p; + BrickDaemonAuthenticate_Request request; + int ret; + + ret = packet_header_create(&request.header, sizeof(request), BRICK_DAEMON_FUNCTION_AUTHENTICATE, device_p->ipcon_p, device_p); + + if (ret < 0) { + return ret; + } + + memcpy(request.client_nonce, client_nonce, 4 * sizeof(uint8_t)); + memcpy(request.digest, digest, 20 * sizeof(uint8_t)); + + ret = device_send_request(device_p, (Packet *)&request, NULL, 0); + + return ret; +} + /***************************************************************************** * * IPConnection @@ -1010,14 +1632,34 @@ int device_send_request(DevicePrivate *device_p, Packet *request, Packet *respon struct _CallbackContext { IPConnectionPrivate *ipcon_p; Queue queue; - Thread thread; Mutex mutex; + Thread thread; 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 DevicePrivate *ipcon_acquire_device(IPConnectionPrivate *ipcon_p, uint32_t uid) { + DevicePrivate *device_p; + + if (uid == 0) { + return NULL; + } + + mutex_lock(&ipcon_p->devices_ref_mutex); + + device_p = (DevicePrivate *)table_get(&ipcon_p->devices, uid); + + if (device_p != NULL) { + ++device_p->ref_count; + } + + mutex_unlock(&ipcon_p->devices_ref_mutex); + + return device_p; +} + static void ipcon_dispatch_meta(IPConnectionPrivate *ipcon_p, Meta *meta) { ConnectedCallbackFunction connected_callback_function; DisconnectedCallbackFunction disconnected_callback_function; @@ -1026,7 +1668,7 @@ static void ipcon_dispatch_meta(IPConnectionPrivate *ipcon_p, Meta *meta) { 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]; + connected_callback_function = (ConnectedCallbackFunction)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); @@ -1055,13 +1697,13 @@ static void ipcon_dispatch_meta(IPConnectionPrivate *ipcon_p, Meta *meta) { mutex_unlock(&ipcon_p->socket_mutex); } - // NOTE: wait a moment here, otherwise the next connect + // 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); + millisleep(100); if (ipcon_p->registered_callbacks[IPCON_CALLBACK_DISCONNECTED] != NULL) { - *(void **)(&disconnected_callback_function) = ipcon_p->registered_callbacks[IPCON_CALLBACK_DISCONNECTED]; + disconnected_callback_function = (DisconnectedCallbackFunction)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); @@ -1092,7 +1734,7 @@ static void ipcon_dispatch_meta(IPConnectionPrivate *ipcon_p, Meta *meta) { if (retry) { // wait a moment to give another thread a chance to // interrupt the auto-reconnect - thread_sleep(100); + millisleep(100); } } } @@ -1102,27 +1744,31 @@ static void ipcon_dispatch_meta(IPConnectionPrivate *ipcon_p, Meta *meta) { static void ipcon_dispatch_packet(IPConnectionPrivate *ipcon_p, Packet *packet) { EnumerateCallbackFunction enumerate_callback_function; void *user_data; - EnumerateCallback *enumerate_callback; + DeviceEnumerate_Callback *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]; + if (packet->header.length != sizeof(DeviceEnumerate_Callback)) { + return; // silently ignoring callback with wrong length + } + + enumerate_callback_function = (EnumerateCallbackFunction)ipcon_p->registered_callbacks[IPCON_CALLBACK_ENUMERATE]; user_data = ipcon_p->registered_callback_user_data[IPCON_CALLBACK_ENUMERATE]; - enumerate_callback = (EnumerateCallback *)packet; + enumerate_callback = (DeviceEnumerate_Callback *)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); + 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); + device_p = ipcon_acquire_device(ipcon_p, packet->header.uid); if (device_p == NULL) { return; @@ -1131,10 +1777,40 @@ static void ipcon_dispatch_packet(IPConnectionPrivate *ipcon_p, Packet *packet) callback_wrapper_function = device_p->callback_wrappers[packet->header.function_id]; if (callback_wrapper_function == NULL) { + device_release(device_p); + return; } + if (device_check_validity(device_p) < 0) { + device_release(device_p); + + return; // silently ignoring callback for invalid device + } + callback_wrapper_function(device_p, packet); + + device_release(device_p); + } +} + +static void ipcon_destroy_callback_context(CallbackContext *callback) { + thread_destroy(&callback->thread); + mutex_destroy(&callback->mutex); + queue_destroy(&callback->queue); + + free(callback); +} + +static void ipcon_exit_callback_thread(CallbackContext *callback) { + if (!thread_is_current(&callback->thread)) { + queue_put(&callback->queue, QUEUE_KIND_EXIT, NULL); + + thread_join(&callback->thread); + + ipcon_destroy_callback_context(callback); + } else { + queue_put(&callback->queue, QUEUE_KIND_DESTROY_AND_EXIT, NULL); } } @@ -1142,22 +1818,25 @@ 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) { - // NOTE: what to do here? try again? exit? + if (queue_get(&callback->queue, &kind, &data) < 0) { + // FIXME: what to do here? try again? exit? -> yes try again, see https://github.com/Tinkerforge/brickd/issues/21 break; } - // NOTE: cannot lock callback mutex here because this can + if (kind == QUEUE_KIND_EXIT) { + break; + } else if (kind == QUEUE_KIND_DESTROY_AND_EXIT) { + ipcon_destroy_callback_context(callback); + 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) { + 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 @@ -1170,21 +1849,14 @@ static void ipcon_callback_loop(void *opaque) { 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; + uint8_t disconnect_reason, + uint64_t socket_id, + bool disconnect_immediately) { + Meta *meta; ipcon_p->auto_reconnect_allowed = true; @@ -1192,11 +1864,12 @@ static void ipcon_handle_disconnect_by_peer(IPConnectionPrivate *ipcon_p, ipcon_disconnect_unlocked(ipcon_p); } - meta.function_id = IPCON_CALLBACK_DISCONNECTED; - meta.parameter = disconnect_reason; - meta.socket_id = socket_id; + meta = (Meta *)malloc(sizeof(Meta)); + 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)); + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_META, meta); } enum { @@ -1214,16 +1887,16 @@ static void ipcon_disconnect_probe_loop(void *opaque) { PacketHeader disconnect_probe; packet_header_create(&disconnect_probe, sizeof(PacketHeader), - IPCON_FUNCTION_DISCONNECT_PROBE, ipcon_p, NULL); + IPCON_FUNCTION_DISCONNECT_PROBE, ipcon_p, NULL); while (event_wait(&ipcon_p->disconnect_probe_event, - IPCON_DISCONNECT_PROBE_INTERVAL) < 0) { + IPCON_DISCONNECT_PROBE_INTERVAL) < 0) { if (ipcon_p->disconnect_probe_flag) { - // TODO: this might block + // FIXME: this might block if (socket_send(ipcon_p->socket, &disconnect_probe, - disconnect_probe.length) < 0) { + disconnect_probe.length) < 0) { ipcon_handle_disconnect_by_peer(ipcon_p, IPCON_DISCONNECT_REASON_ERROR, - ipcon_p->socket_id, false); + ipcon_p->socket_id, false); break; } } else { @@ -1235,22 +1908,25 @@ static void ipcon_disconnect_probe_loop(void *opaque) { static void ipcon_handle_response(IPConnectionPrivate *ipcon_p, Packet *response) { DevicePrivate *device_p; uint8_t sequence_number = packet_header_get_sequence_number(&response->header); + Packet *callback; 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) { + 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); + callback = (Packet *)malloc(response->header.length); + + memcpy(callback, response, response->header.length); + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_PACKET, callback); } return; } - device_p = (DevicePrivate *)table_get(&ipcon_p->devices, response->header.uid); + device_p = ipcon_acquire_device(ipcon_p, response->header.uid); if (device_p == NULL) { // ignoring response for an unknown device @@ -1258,24 +1934,34 @@ static void ipcon_handle_response(IPConnectionPrivate *ipcon_p, Packet *response } 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); + if (device_p->registered_callbacks[DEVICE_NUM_FUNCTION_IDS + response->header.function_id] != NULL || + device_p->high_level_callbacks[response->header.function_id].exists) { + callback = (Packet *)malloc(response->header.length); + + memcpy(callback, response, response->header.length); + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_PACKET, callback); } + device_release(device_p); + return; } if (device_p->expected_response_function_id == response->header.function_id && - device_p->expected_response_sequence_number == sequence_number) { + 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); + + device_release(device_p); + return; } + device_release(device_p); + // response seems to be OK, but can't be handled } @@ -1291,7 +1977,7 @@ static void ipcon_receive_loop(void *opaque) { while (ipcon_p->receive_flag) { length = socket_receive(ipcon_p->socket, (uint8_t *)pending_data + pending_length, - sizeof(pending_data) - pending_length); + sizeof(pending_data) - pending_length); if (!ipcon_p->receive_flag) { return; @@ -1315,7 +2001,7 @@ static void ipcon_receive_loop(void *opaque) { pending_length += length; while (ipcon_p->receive_flag) { - if (pending_length < 8) { + if (pending_length < (int)sizeof(PacketHeader)) { // wait for complete header break; } @@ -1330,18 +2016,20 @@ static void ipcon_receive_loop(void *opaque) { ipcon_handle_response(ipcon_p, pending_data); memmove(pending_data, (uint8_t *)pending_data + length, - pending_length - length); + pending_length - length); pending_length -= length; } } } -// NOTE: assumes that socket_mutex is locked +// NOTE: assumes that socket is NULL and socket_mutex is locked static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_reconnect) { - struct hostent *entity; - struct sockaddr_in address; + char service[32]; + struct addrinfo hints; + struct addrinfo *resolved = NULL; + Socket *tmp; uint8_t connect_reason; - Meta meta; + Meta *meta; // create callback queue and thread if (ipcon_p->callback == NULL) { @@ -1354,7 +2042,7 @@ static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_rec mutex_create(&ipcon_p->callback->mutex); if (thread_create(&ipcon_p->callback->thread, ipcon_callback_loop, - ipcon_p->callback) < 0) { + ipcon_p->callback) < 0) { mutex_destroy(&ipcon_p->callback->mutex); queue_destroy(&ipcon_p->callback->queue); @@ -1366,70 +2054,59 @@ static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_rec } // create and connect socket - entity = gethostbyname(ipcon_p->host); + snprintf(service, sizeof(service), "%u", ipcon_p->port); - if (entity == NULL) { + memset(&hints, 0, sizeof(hints)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(ipcon_p->host, service, &hints, &resolved) != 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_exit_callback_thread(ipcon_p->callback); 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); + tmp = (Socket *)malloc(sizeof(Socket)); - 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) { + if (socket_create(tmp, resolved->ai_family, resolved->ai_socktype, + resolved->ai_protocol) < 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_exit_callback_thread(ipcon_p->callback); ipcon_p->callback = NULL; } // destroy socket - free(ipcon_p->socket); - ipcon_p->socket = NULL; + free(tmp); + freeaddrinfo(resolved); return E_NO_STREAM_SOCKET; } - if (socket_connect(ipcon_p->socket, &address, sizeof(address)) < 0) { + if (socket_connect(tmp, resolved->ai_addr, resolved->ai_addrlen) < 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_exit_callback_thread(ipcon_p->callback); ipcon_p->callback = NULL; } // destroy socket - socket_destroy(ipcon_p->socket); - free(ipcon_p->socket); - ipcon_p->socket = NULL; + socket_destroy(tmp); + free(tmp); + freeaddrinfo(resolved); return E_NO_CONNECT; } + freeaddrinfo(resolved); + + ipcon_p->socket = tmp; ++ipcon_p->socket_id; // create disconnect probe thread @@ -1438,15 +2115,10 @@ static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_rec event_reset(&ipcon_p->disconnect_probe_event); if (thread_create(&ipcon_p->disconnect_probe_thread, - ipcon_disconnect_probe_loop, ipcon_p) < 0) { + 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_exit_callback_thread(ipcon_p->callback); ipcon_p->callback = NULL; } @@ -1463,16 +2135,14 @@ static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_rec ipcon_p->callback->packet_dispatch_allowed = true; if (thread_create(&ipcon_p->receive_thread, ipcon_receive_loop, ipcon_p) < 0) { + ipcon_p->receive_flag = false; + + // destroy socket 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_exit_callback_thread(ipcon_p->callback); ipcon_p->callback = NULL; } @@ -1489,16 +2159,17 @@ static int ipcon_connect_unlocked(IPConnectionPrivate *ipcon_p, bool is_auto_rec connect_reason = IPCON_CONNECT_REASON_REQUEST; } - meta.function_id = IPCON_CALLBACK_CONNECTED; - meta.parameter = connect_reason; - meta.socket_id = 0; + meta = (Meta *)malloc(sizeof(Meta)); + 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)); + queue_put(&ipcon_p->callback->queue, QUEUE_KIND_META, meta); return E_OK; } -// NOTE: assumes that socket_mutex is locked +// NOTE: assumes that socket is not NULL and socket_mutex is locked static void ipcon_disconnect_unlocked(IPConnectionPrivate *ipcon_p) { // destroy disconnect probe thread event_set(&ipcon_p->disconnect_probe_event); @@ -1509,7 +2180,7 @@ static void ipcon_disconnect_unlocked(IPConnectionPrivate *ipcon_p) { // thread to avoid timeout exceptions due to callback functions // trying to call getters if (!thread_is_current(&ipcon_p->callback->thread)) { - // NOTE: cannot lock callback mutex here because this can + // FIXME: cannot lock callback mutex here because this can // deadlock due to an ordering problem with the socket mutex //mutex_lock(&ipcon->callback->mutex); @@ -1547,8 +2218,7 @@ static int ipcon_send_request(IPConnectionPrivate *ipcon_p, Packet *request) { 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); + ipcon_handle_disconnect_by_peer(ipcon_p, IPCON_DISCONNECT_REASON_ERROR, 0, true); ret = E_NOT_CONNECTED; } else { @@ -1584,6 +2254,10 @@ void ipcon_create(IPConnection *ipcon) { mutex_create(&ipcon_p->sequence_number_mutex); ipcon_p->next_sequence_number = 0; + mutex_create(&ipcon_p->authentication_mutex); + ipcon_p->next_authentication_nonce = 0; + + mutex_create(&ipcon_p->devices_ref_mutex); table_create(&ipcon_p->devices); for (i = 0; i < IPCON_NUM_CALLBACK_IDS; ++i) { @@ -1603,16 +2277,23 @@ void ipcon_create(IPConnection *ipcon) { event_create(&ipcon_p->disconnect_probe_event); semaphore_create(&ipcon_p->wait); + + brickd_create(&ipcon_p->brickd, "2", ipcon); } void ipcon_destroy(IPConnection *ipcon) { IPConnectionPrivate *ipcon_p = ipcon->p; - ipcon_disconnect(ipcon); // NOTE: disable disconnected callback before? + ipcon_disconnect(ipcon); // FIXME: disable disconnected callback before? + + brickd_destroy(&ipcon_p->brickd); + + mutex_destroy(&ipcon_p->authentication_mutex); mutex_destroy(&ipcon_p->sequence_number_mutex); - table_destroy(&ipcon_p->devices); + table_destroy(&ipcon_p->devices); // FIXME: destroy all devices? + mutex_destroy(&ipcon_p->devices_ref_mutex); mutex_destroy(&ipcon_p->socket_mutex); @@ -1667,7 +2348,7 @@ int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port) { int ipcon_disconnect(IPConnection *ipcon) { IPConnectionPrivate *ipcon_p = ipcon->p; CallbackContext *callback; - Meta meta; + Meta *meta; mutex_lock(&ipcon_p->socket_mutex); @@ -1694,19 +2375,62 @@ int ipcon_disconnect(IPConnection *ipcon) { // 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; + meta = (Meta *)malloc(sizeof(Meta)); + 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); + queue_put(&callback->queue, QUEUE_KIND_META, meta); - if (!thread_is_current(&callback->thread)) { - thread_join(&callback->thread); + ipcon_exit_callback_thread(callback); + + return E_OK; +} + +int ipcon_authenticate(IPConnection *ipcon, const char *secret) { + IPConnectionPrivate *ipcon_p = ipcon->p; + int ret; + uint32_t nonces[2]; // server, client + uint8_t digest[SHA1_DIGEST_LENGTH]; + int i; + int secret_length; + + secret_length = string_length(secret, IPCON_MAX_SECRET_LENGTH); + + for (i = 0; i < secret_length; ++i) { + if ((secret[i] & 0x80) != 0) { + return E_NON_ASCII_CHAR_IN_SECRET; + } } - // NOTE: no further cleanup of the callback queue and thread here, the - // callback thread is doing this on exit + mutex_lock(&ipcon_p->authentication_mutex); + + if (ipcon_p->next_authentication_nonce == 0) { + ipcon_p->next_authentication_nonce = get_random_uint32(); + } + + ret = brickd_get_authentication_nonce(&ipcon_p->brickd, (uint8_t *)nonces); + + if (ret < 0) { + mutex_unlock(&ipcon_p->authentication_mutex); + + return ret; + } + + nonces[1] = ipcon_p->next_authentication_nonce++; + + hmac_sha1((uint8_t *)secret, secret_length, + (uint8_t *)nonces, sizeof(nonces), digest); + + ret = brickd_authenticate(&ipcon_p->brickd, (uint8_t *)&nonces[1], digest); + + if (ret < 0) { + mutex_unlock(&ipcon_p->authentication_mutex); + + return ret; + } + + mutex_unlock(&ipcon_p->authentication_mutex); return E_OK; } @@ -1748,11 +2472,11 @@ uint32_t ipcon_get_timeout(IPConnection *ipcon) { // in msec int ipcon_enumerate(IPConnection *ipcon) { IPConnectionPrivate *ipcon_p = ipcon->p; - Enumerate enumerate; + DeviceEnumerate_Broadcast enumerate; int ret; - ret = packet_header_create(&enumerate.header, sizeof(Enumerate), - IPCON_FUNCTION_ENUMERATE, ipcon_p, NULL); + ret = packet_header_create(&enumerate.header, sizeof(DeviceEnumerate_Broadcast), + DEVICE_FUNCTION_ENUMERATE, ipcon_p, NULL); if (ret < 0) { return ret; @@ -1769,17 +2493,33 @@ void ipcon_unwait(IPConnection *ipcon) { semaphore_release(&ipcon->p->wait); } -void ipcon_register_callback(IPConnection *ipcon, uint8_t id, void *callback, - void *user_data) { +void ipcon_register_callback(IPConnection *ipcon, int16_t callback_id, + void (*function)(void), void *user_data) { IPConnectionPrivate *ipcon_p = ipcon->p; - ipcon_p->registered_callbacks[id] = callback; - ipcon_p->registered_callback_user_data[id] = user_data; + if (callback_id <= -1 || callback_id >= IPCON_NUM_CALLBACK_IDS) { + return; + } + + ipcon_p->registered_callbacks[callback_id] = function; + ipcon_p->registered_callback_user_data[callback_id] = user_data; +} + +void ipcon_add_device(IPConnectionPrivate *ipcon_p, DevicePrivate *device_p) { + DevicePrivate *replaced_device_p; + + if (device_p->uid_valid) { + replaced_device_p = (DevicePrivate *)table_insert(&ipcon_p->devices, device_p->uid, device_p); + + if (replaced_device_p != NULL) { + replaced_device_p->replaced = true; + } + } } int packet_header_create(PacketHeader *header, uint8_t length, - uint8_t function_id, IPConnectionPrivate *ipcon_p, - DevicePrivate *device_p) { + uint8_t function_id, IPConnectionPrivate *ipcon_p, + DevicePrivate *device_p) { uint8_t sequence_number; bool response_expected = false; int ret = E_OK; @@ -1803,7 +2543,7 @@ int packet_header_create(PacketHeader *header, uint8_t length, 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); + packet_header_set_response_expected(header, response_expected); } return ret; @@ -1813,8 +2553,8 @@ 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) { +void packet_header_set_sequence_number(PacketHeader *header, uint8_t sequence_number) { + header->sequence_number_and_options &= ~0xF0; header->sequence_number_and_options |= (sequence_number << 4) & 0xF0; } @@ -1822,192 +2562,156 @@ 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; +void packet_header_set_response_expected(PacketHeader *header, bool response_expected) { + if (response_expected) { + header->sequence_number_and_options |= 0x01 << 3; + } else { + header->sequence_number_and_options &= ~(0x01 << 3); + } } 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); - } + return leconvert_uint16_to(native); } uint16_t leconvert_uint16_to(uint16_t native) { - if (native_endian.value == LITTLE_ENDIAN) { - return native; - } else { - return *(uint16_t *)leconvert_swap16(&native); - } + union { + uint8_t bytes[2]; + uint16_t little; + } c; + + c.bytes[0] = (native >> 0) & 0xFF; + c.bytes[1] = (native >> 8) & 0xFF; + + return c.little; } int32_t leconvert_int32_to(int32_t native) { - if (native_endian.value == LITTLE_ENDIAN) { - return native; - } else { - return *(int32_t *)leconvert_swap32(&native); - } + return leconvert_uint32_to(native); } uint32_t leconvert_uint32_to(uint32_t native) { - if (native_endian.value == LITTLE_ENDIAN) { - return native; - } else { - return *(uint32_t *)leconvert_swap32(&native); - } + union { + uint8_t bytes[4]; + uint32_t little; + } c; + + c.bytes[0] = (native >> 0) & 0xFF; + c.bytes[1] = (native >> 8) & 0xFF; + c.bytes[2] = (native >> 16) & 0xFF; + c.bytes[3] = (native >> 24) & 0xFF; + + return c.little; } int64_t leconvert_int64_to(int64_t native) { - if (native_endian.value == LITTLE_ENDIAN) { - return native; - } else { - return *(int64_t *)leconvert_swap64(&native); - } + return leconvert_uint64_to(native); } uint64_t leconvert_uint64_to(uint64_t native) { - if (native_endian.value == LITTLE_ENDIAN) { - return native; - } else { - return *(uint64_t *)leconvert_swap64(&native); - } + union { + uint8_t bytes[8]; + uint64_t little; + } c; + + c.bytes[0] = (native >> 0) & 0xFF; + c.bytes[1] = (native >> 8) & 0xFF; + c.bytes[2] = (native >> 16) & 0xFF; + c.bytes[3] = (native >> 24) & 0xFF; + c.bytes[4] = (native >> 32) & 0xFF; + c.bytes[5] = (native >> 40) & 0xFF; + c.bytes[6] = (native >> 48) & 0xFF; + c.bytes[7] = (native >> 56) & 0xFF; + + return c.little; } float leconvert_float_to(float native) { - if (native_endian.value == LITTLE_ENDIAN) { - return native; - } else { - return *(float *)leconvert_swap32(&native); - } + union { + uint32_t u; + float f; + } c; + + c.f = native; + c.u = leconvert_uint32_to(c.u); + + return c.f; } int16_t leconvert_int16_from(int16_t little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(int16_t *)leconvert_swap16(&little); - } + return leconvert_uint16_from(little); } uint16_t leconvert_uint16_from(uint16_t little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(uint16_t *)leconvert_swap16(&little); - } + uint8_t *bytes = (uint8_t *)&little; + + return ((uint16_t)bytes[1] << 8) | + (uint16_t)bytes[0]; } int32_t leconvert_int32_from(int32_t little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(int32_t *)leconvert_swap32(&little); - } + return leconvert_uint32_from(little); } uint32_t leconvert_uint32_from(uint32_t little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(uint32_t *)leconvert_swap32(&little); - } + uint8_t *bytes = (uint8_t *)&little; + + return ((uint32_t)bytes[3] << 24) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[1] << 8) | + (uint32_t)bytes[0]; } int64_t leconvert_int64_from(int64_t little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(int64_t *)leconvert_swap64(&little); - } + return leconvert_uint64_from(little); } uint64_t leconvert_uint64_from(uint64_t little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(uint64_t *)leconvert_swap64(&little); - } + uint8_t *bytes = (uint8_t *)&little; + + return ((uint64_t)bytes[7] << 56) | + ((uint64_t)bytes[6] << 48) | + ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[4] << 32) | + ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[2] << 16) | + ((uint64_t)bytes[1] << 8) | + (uint64_t)bytes[0]; } float leconvert_float_from(float little) { - if (native_endian.value == LITTLE_ENDIAN) { - return little; - } else { - return *(float *)leconvert_swap32(&little); - } + union { + uint32_t u; + float f; + } c; + + c.f = little; + c.u = leconvert_uint32_from(c.u); + + return c.f; } + +char *string_copy(char *dest, const char *src, size_t n) { + size_t idx = 0; + + while(src[idx] != '\0' && idx < n) { + dest[idx] = src[idx]; + ++idx; + } + + while (idx < n) { + dest[idx] = '\0'; + ++idx; + } + + return dest; +} + + +#ifdef __cplusplus +} +#endif diff --git a/dependencies/external/protobuf b/dependencies/external/protobuf index 2c5fa078..7f94235e 160000 --- a/dependencies/external/protobuf +++ b/dependencies/external/protobuf @@ -1 +1 @@ -Subproject commit 2c5fa078d8e86e5f4bd34e6f4c9ea9e8d7d4d44a +Subproject commit 7f94235e552599141950d7a4a3eaf93bc87d1b22 diff --git a/dependencies/include/tinkerforge/bricklet_led_strip.h b/dependencies/include/tinkerforge/bricklet_led_strip.h index 11d608c7..f9ea8fdc 100644 --- a/dependencies/include/tinkerforge/bricklet_led_strip.h +++ b/dependencies/include/tinkerforge/bricklet_led_strip.h @@ -1,11 +1,11 @@ /* *********************************************************** - * This file was automatically generated on 2013-12-19. * + * This file was automatically generated on 2023-11-30. * * * - * Bindings Version 2.0.13 * + * C/C++ Bindings Version 2.1.33 * * * * 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 * + * to the generators git repository on tinkerforge.com * *************************************************************/ #ifndef BRICKLET_LED_STRIP_H @@ -13,14 +13,18 @@ #include "ip_connection.h" +#ifdef __cplusplus +extern "C" { +#endif + /** - * \defgroup BrickletLEDStrip LEDStrip Bricklet + * \defgroup BrickletLEDStrip LED Strip Bricklet */ /** * \ingroup BrickletLEDStrip * - * Device to control up to 320 RGB LEDs + * Controls up to 320 RGB LEDs */ typedef Device LEDStrip; @@ -59,6 +63,51 @@ typedef Device LEDStrip; */ #define LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY 8 +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_SET_CHIP_TYPE 9 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_CHIP_TYPE 10 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_SET_RGBW_VALUES 11 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_RGBW_VALUES 12 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_SET_CHANNEL_MAPPING 13 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_GET_CHANNEL_MAPPING 14 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_ENABLE_FRAME_RENDERED_CALLBACK 15 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_DISABLE_FRAME_RENDERED_CALLBACK 16 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_FUNCTION_IS_FRAME_RENDERED_CALLBACK_ENABLED 17 + /** * \ingroup BrickletLEDStrip */ @@ -68,21 +117,197 @@ typedef Device LEDStrip; * \ingroup BrickletLEDStrip * * Signature: \code void callback(uint16_t length, void *user_data) \endcode - * - * This callback is triggered directly after a new frame is rendered. - * + * + * This callback is triggered directly after a new frame is rendered. The + * parameter is the number of RGB or RGBW LEDs in that frame. + * * 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 + */ +#define LED_STRIP_CHIP_TYPE_WS2801 2801 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHIP_TYPE_WS2811 2811 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHIP_TYPE_WS2812 2812 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHIP_TYPE_LPD8806 8806 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHIP_TYPE_APA102 102 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RGB 6 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RBG 9 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BRG 33 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BGR 36 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GRB 18 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GBR 24 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RGBW 27 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RGWB 30 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RBGW 39 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RBWG 45 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RWGB 54 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_RWBG 57 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GRWB 78 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GRBW 75 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GBWR 108 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GBRW 99 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GWBR 120 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_GWRB 114 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BRGW 135 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BRWG 141 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BGRW 147 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BGWR 156 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BWRG 177 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_BWGR 180 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_WRBG 201 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_WRGB 198 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_WGBR 216 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_WGRB 210 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_WBGR 228 + +/** + * \ingroup BrickletLEDStrip + */ +#define LED_STRIP_CHANNEL_MAPPING_WBRG 225 + /** * \ingroup BrickletLEDStrip * - * This constant is used to identify a LEDStrip Bricklet. + * This constant is used to identify a LED Strip Bricklet. * * The {@link led_strip_get_identity} function and the * {@link IPCON_CALLBACK_ENUMERATE} callback of the IP Connection have a @@ -90,6 +315,13 @@ typedef Device LEDStrip; */ #define LED_STRIP_DEVICE_IDENTIFIER 231 +/** + * \ingroup BrickletLEDStrip + * + * This constant represents the display name of a LED Strip Bricklet. + */ +#define LED_STRIP_DEVICE_DISPLAY_NAME "LED Strip Bricklet" + /** * \ingroup BrickletLEDStrip * @@ -122,7 +354,7 @@ void led_strip_destroy(LEDStrip *led_strip); * 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 + * disabled for a setter function then no response is sent 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); @@ -133,13 +365,12 @@ int led_strip_get_response_expected(LEDStrip *led_strip, uint8_t function_id, bo * 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. + * (default value: *true*). For getter functions it is always 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, + * setter function then no response is sent 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); @@ -155,10 +386,10 @@ int led_strip_set_response_expected_all(LEDStrip *led_strip, bool response_expec /** * \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. + * Registers the given \c function with the given \c callback_id. The + * \c user_data will be passed as the last parameter to the \c function. */ -void led_strip_register_callback(LEDStrip *led_strip, uint8_t id, void *callback, void *user_data); +void led_strip_register_callback(LEDStrip *led_strip, int16_t callback_id, void (*function)(void), void *user_data); /** * \ingroup BrickletLEDStrip @@ -171,38 +402,40 @@ 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. - * + * Sets *length* RGB values for the LEDs starting from *index*. + * + * To make the colors show correctly you need to configure the chip type + * ({@link led_strip_set_chip_type}) and a 3-channel channel mapping ({@link led_strip_set_channel_mapping}) + * according to the connected LEDs. + * * 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. - * + * + * \note Depending on the LED circuitry colors can be permuted. + * * The colors will be transfered to actual LEDs when the next * frame duration ends, see {@link led_strip_set_frame_duration}. - * - * Generic approach: - * + * + * Generic approach: + * * * Set the frame duration to a value that represents - * the number of frames per second you want to achieve. + * 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 @@ -213,9 +446,9 @@ int led_strip_set_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length /** * \ingroup BrickletLEDStrip * - * Returns the rgb with the given *length* starting from the - * given *index*. - * + * Returns *length* R, G and B values starting from the + * given LED *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]); @@ -223,14 +456,12 @@ int led_strip_get_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length /** * \ingroup BrickletLEDStrip * - * Sets the frame duration in ms. - * + * Sets the frame duration. + * * Example: If you want to achieve 20 frames per second, you should - * set the frame duration to 50ms (50ms * 20 = 1 second). - * + * 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); @@ -244,58 +475,223 @@ 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. + * Returns the current supply voltage of the LEDs. */ 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). - * + * Sets the frequency of the clock. + * * 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) + * + * .. versionadded:: 2.0.1$nbsp;(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) + * Returns the currently used clock frequency as set by {@link led_strip_set_clock_frequency}. + * + * .. versionadded:: 2.0.1$nbsp;(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, + * Sets the type of the LED driver chip. We currently support the chips + * + * * WS2801, + * * WS2811, + * * WS2812 / SK6812 / NeoPixel RGB, + * * SK6812RGBW / NeoPixel RGBW (Chip Type = WS2812), + * * LPD8806 and + * * APA102 / DotStar. + * + * .. versionadded:: 2.0.2$nbsp;(Plugin) + */ +int led_strip_set_chip_type(LEDStrip *led_strip, uint16_t chip); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the currently used chip type as set by {@link led_strip_set_chip_type}. + * + * .. versionadded:: 2.0.2$nbsp;(Plugin) + */ +int led_strip_get_chip_type(LEDStrip *led_strip, uint16_t *ret_chip); + +/** + * \ingroup BrickletLEDStrip + * + * Sets *length* RGBW values for the LEDs starting from *index*. + * + * To make the colors show correctly you need to configure the chip type + * ({@link led_strip_set_chip_type}) and a 4-channel channel mapping ({@link led_strip_set_channel_mapping}) + * according to the connected LEDs. + * + * The maximum length is 12, the index goes from 0 to 239 and the rgbw values + * have 8 bits each. + * + * Example: If you set + * + * * index to 5, + * * length to 4, + * * r to [255, 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], + * * b to [0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0] and + * * w to [0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0] + * + * the LED with index 5 will be red, 6 will be green, 7 will be blue and 8 will be white. + * + * \note Depending on the LED circuitry colors can be permuted. + * + * 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_rgbw_values} with index + length above the + * bounds is ignored completely. + * + * The LPD8806 LED driver chips have 7-bit channels for RGB. Internally the LED + * Strip Bricklets divides the 8-bit values set using this function by 2 to make + * them 7-bit. Therefore, you can just use the normal value range (0-255) for + * LPD8806 LEDs. + * + * The brightness channel of the APA102 LED driver chips has 5-bit. Internally the + * LED Strip Bricklets divides the 8-bit values set using this function by 8 to make + * them 5-bit. Therefore, you can just use the normal value range (0-255) for + * the brightness channel of APA102 LEDs. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_set_rgbw_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t r[12], uint8_t g[12], uint8_t b[12], uint8_t w[12]); + +/** + * \ingroup BrickletLEDStrip + * + * Returns *length* RGBW values starting from the given *index*. + * + * The values are the last values that were set by {@link led_strip_set_rgbw_values}. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_get_rgbw_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t ret_r[12], uint8_t ret_g[12], uint8_t ret_b[12], uint8_t ret_w[12]); + +/** + * \ingroup BrickletLEDStrip + * + * Sets the channel mapping for the connected LEDs. + * + * {@link led_strip_set_rgb_values} and {@link led_strip_set_rgbw_values} take the data in RGB(W) order. + * But the connected LED driver chips might have their 3 or 4 channels in a + * different order. For example, the WS2801 chips typically use BGR order, the + * WS2812 chips typically use GRB order and the APA102 chips typically use WBGR + * order. + * + * The APA102 chips are special. They have three 8-bit channels for RGB + * and an additional 5-bit channel for the overall brightness of the RGB LED + * making them 4-channel chips. Internally the brightness channel is the first + * channel, therefore one of the Wxyz channel mappings should be used. Then + * the W channel controls the brightness. + * + * If a 3-channel mapping is selected then {@link led_strip_set_rgb_values} has to be used. + * Calling {@link led_strip_set_rgbw_values} with a 3-channel mapping will produce incorrect + * results. Vice-versa if a 4-channel mapping is selected then + * {@link led_strip_set_rgbw_values} has to be used. Calling {@link led_strip_set_rgb_values} with a + * 4-channel mapping will produce incorrect results. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_set_channel_mapping(LEDStrip *led_strip, uint8_t mapping); + +/** + * \ingroup BrickletLEDStrip + * + * Returns the currently used channel mapping as set by {@link led_strip_set_channel_mapping}. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_get_channel_mapping(LEDStrip *led_strip, uint8_t *ret_mapping); + +/** + * \ingroup BrickletLEDStrip + * + * Enables the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback. + * + * By default the callback is enabled. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_enable_frame_rendered_callback(LEDStrip *led_strip); + +/** + * \ingroup BrickletLEDStrip + * + * Disables the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback. + * + * By default the callback is enabled. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_disable_frame_rendered_callback(LEDStrip *led_strip); + +/** + * \ingroup BrickletLEDStrip + * + * Returns *true* if the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback is enabled, *false* otherwise. + * + * .. versionadded:: 2.0.6$nbsp;(Plugin) + */ +int led_strip_is_frame_rendered_callback_enabled(LEDStrip *led_strip, bool *ret_enabled); + +/** + * \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) + * + * The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port). + * A Bricklet connected to an :ref:`Isolator Bricklet ` is always at + * position 'z'. + * + * The device identifier numbers can be found :ref:`here `. + * |device_identifier_constant| */ 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); +#ifdef __cplusplus +} +#endif + #endif diff --git a/dependencies/include/tinkerforge/ip_connection.h b/dependencies/include/tinkerforge/ip_connection.h index bd86940b..84c67542 100644 --- a/dependencies/include/tinkerforge/ip_connection.h +++ b/dependencies/include/tinkerforge/ip_connection.h @@ -1,9 +1,10 @@ /* - * Copyright (C) 2012-2013 Matthias Bolte + * Copyright (C) 2012-2014, 2019-2020 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. + * with or without modification, are permitted. See the Creative + * Commons Zero (CC0 1.0) License for more details. */ #ifndef IP_CONNECTION_H @@ -16,11 +17,12 @@ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif + #include #include #include -#if !defined __cplusplus && defined __GNUC__ +#if !defined __cplusplus && (defined __GNUC__ || (defined _MSC_VER && _MSC_VER >= 1600)) #include #endif @@ -34,6 +36,10 @@ #include #endif +#ifdef __cplusplus +extern "C" { +#endif + enum { E_OK = 0, E_TIMEOUT = -1, @@ -46,9 +52,21 @@ enum { 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 + E_UNKNOWN_ERROR_CODE = -11, // error response from device + E_STREAM_OUT_OF_SYNC = -12, + E_INVALID_UID = -13, + E_NON_ASCII_CHAR_IN_SECRET = -14, + E_WRONG_DEVICE_TYPE = -15, + E_DEVICE_REPLACED = -16, + E_WRONG_RESPONSE_LENGTH = -17 }; +#ifdef IPCON_EXPOSE_MILLISLEEP + +void millisleep(uint32_t msec); + +#endif // IPCON_EXPOSE_MILLISLEEP + #ifdef IPCON_EXPOSE_INTERNALS typedef struct _Socket Socket; @@ -113,7 +131,6 @@ typedef struct _QueueItem { struct _QueueItem *next; int kind; void *data; - int length; } QueueItem; typedef struct { @@ -130,7 +147,7 @@ typedef struct { #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 + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) #else #define ATTRIBUTE_PACKED __attribute__((packed)) @@ -140,7 +157,7 @@ typedef struct { #endif typedef struct { - uint32_t uid; + uint32_t uid; // always little endian uint8_t length; uint8_t function_id; uint8_t sequence_number_and_options; @@ -168,24 +185,35 @@ typedef struct _DevicePrivate DevicePrivate; #ifdef IPCON_EXPOSE_INTERNALS typedef struct _CallbackContext CallbackContext; +typedef struct _HighLevelCallback HighLevelCallback; + +/** + * \internal + */ +struct _HighLevelCallback { + bool exists; + void *data; + size_t length; +}; #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); + 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); + void *user_data); typedef void (*DisconnectedCallbackFunction)(uint8_t disconnect_reason, - void *user_data); + void *user_data); #ifdef IPCON_EXPOSE_INTERNALS +typedef void (*CallbackFunction)(void); typedef void (*CallbackWrapperFunction)(DevicePrivate *device_p, Packet *packet); #endif @@ -201,16 +229,31 @@ struct _Device { #define DEVICE_NUM_FUNCTION_IDS 256 +typedef enum { + DEVICE_IDENTIFIER_CHECK_PENDING = 0, + DEVICE_IDENTIFIER_CHECK_MATCH = 1, + DEVICE_IDENTIFIER_CHECK_MISMATCH = 2 +} DeviceIdentifierCheck; + /** * \internal */ struct _DevicePrivate { - uint32_t uid; + int ref_count; + + bool replaced; + + uint32_t uid; // always host endian + bool uid_valid; IPConnectionPrivate *ipcon_p; uint8_t api_version[3]; + uint16_t device_identifier; + Mutex device_identifier_mutex; + DeviceIdentifierCheck device_identifier_check; // protected by device_identifier_mutex + Mutex request_mutex; uint8_t expected_response_function_id; // protected by request_mutex @@ -220,9 +263,12 @@ struct _DevicePrivate { 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]; + Mutex stream_mutex; + + CallbackFunction registered_callbacks[DEVICE_NUM_FUNCTION_IDS * 2]; + void *registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS * 2]; CallbackWrapperFunction callback_wrappers[DEVICE_NUM_FUNCTION_IDS]; + HighLevelCallback high_level_callbacks[DEVICE_NUM_FUNCTION_IDS]; }; /** @@ -231,7 +277,6 @@ struct _DevicePrivate { 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 }; @@ -240,25 +285,26 @@ enum { * \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); + IPConnectionPrivate *ipcon_p, uint8_t api_version_major, + uint8_t api_version_minor, uint8_t api_version_release, + uint16_t device_identifier); /** * \internal */ -void device_destroy(Device *device); +void device_release(DevicePrivate *device_p); /** * \internal */ int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, - bool *ret_response_expected); + bool *ret_response_expected); /** * \internal */ int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id, - bool response_expected); + bool response_expected); /** * \internal @@ -268,8 +314,8 @@ int device_set_response_expected_all(DevicePrivate *device_p, bool response_expe /** * \internal */ -void device_register_callback(DevicePrivate *device_p, uint8_t id, void *callback, - void *user_data); +void device_register_callback(DevicePrivate *device_p, int16_t callback_id, + void (*function)(void), void *user_data); /** * \internal @@ -279,7 +325,13 @@ 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); +int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response, + int expected_response_length); + +/** + * \internal + */ +int device_check_validity(DevicePrivate *device_p); #endif // IPCON_EXPOSE_INTERNALS @@ -347,6 +399,12 @@ struct _IPConnection { #ifdef IPCON_EXPOSE_INTERNALS #define IPCON_NUM_CALLBACK_IDS 256 +#define IPCON_MAX_SECRET_LENGTH 64 + +/** + * \internal + */ +typedef Device BrickDaemon; /** * \internal @@ -368,9 +426,13 @@ struct _IPConnectionPrivate { Mutex sequence_number_mutex; uint8_t next_sequence_number; // protected by sequence_number_mutex + Mutex authentication_mutex; // protects authentication handshake + uint32_t next_authentication_nonce; // protected by authentication_mutex + + Mutex devices_ref_mutex; // protects DevicePrivate.ref_count Table devices; - void *registered_callbacks[IPCON_NUM_CALLBACK_IDS]; + CallbackFunction registered_callbacks[IPCON_NUM_CALLBACK_IDS]; void *registered_callback_user_data[IPCON_NUM_CALLBACK_IDS]; Mutex socket_mutex; @@ -387,6 +449,8 @@ struct _IPConnectionPrivate { Event disconnect_probe_event; Semaphore wait; + + BrickDaemon brickd; }; #endif // IPCON_EXPOSE_INTERNALS @@ -431,6 +495,21 @@ int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port); */ int ipcon_disconnect(IPConnection *ipcon); +/** + * \ingroup IPConnection + * + * Performs an authentication handshake with the connected Brick Daemon or + * WIFI/Ethernet Extension. If the handshake succeeds the connection switches + * from non-authenticated to authenticated state and communication can + * continue as normal. If the handshake fails then the connection gets closed. + * Authentication can fail if the wrong secret was used or if authentication + * is not enabled at all on the Brick Daemon or the WIFI/Ethernet Extension. + * + * For more information about authentication see + * https://www.tinkerforge.com/en/doc/Tutorials/Tutorial_Authentication/Tutorial.html + */ +int ipcon_authenticate(IPConnection *ipcon, const char *secret); + /** * \ingroup IPConnection * @@ -514,19 +593,25 @@ void ipcon_unwait(IPConnection *ipcon); /** * \ingroup IPConnection * - * Registers a callback for a given ID. + * Registers the given \c function with the given \c callback_id. The + * \c user_data will be passed as the last parameter to the \c function. */ -void ipcon_register_callback(IPConnection *ipcon, uint8_t id, - void *callback, void *user_data); +void ipcon_register_callback(IPConnection *ipcon, int16_t callback_id, + void (*function)(void), void *user_data); #ifdef IPCON_EXPOSE_INTERNALS +/** + * \internal + */ +void ipcon_add_device(IPConnectionPrivate *ipcon_p, DevicePrivate *device_p); + /** * \internal */ int packet_header_create(PacketHeader *header, uint8_t length, - uint8_t function_id, IPConnectionPrivate *ipcon_p, - DevicePrivate *device_p); + uint8_t function_id, IPConnectionPrivate *ipcon_p, + DevicePrivate *device_p); /** * \internal @@ -536,8 +621,7 @@ uint8_t packet_header_get_sequence_number(PacketHeader *header); /** * \internal */ -void packet_header_set_sequence_number(PacketHeader *header, - uint8_t sequence_number); +void packet_header_set_sequence_number(PacketHeader *header, uint8_t sequence_number); /** * \internal @@ -547,8 +631,7 @@ uint8_t packet_header_get_response_expected(PacketHeader *header); /** * \internal */ -void packet_header_set_response_expected(PacketHeader *header, - uint8_t response_expected); +void packet_header_set_response_expected(PacketHeader *header, bool response_expected); /** * \internal @@ -625,6 +708,15 @@ uint64_t leconvert_uint64_from(uint64_t little); */ float leconvert_float_from(float little); +/** + * \internal + */ +char *string_copy(char *dest, const char *src, size_t n); + #endif // IPCON_EXPOSE_INTERNALS +#ifdef __cplusplus +} +#endif + #endif diff --git a/include/cec/CECHandler.h b/include/cec/CECHandler.h index 3d118a88..d3a19c11 100644 --- a/include/cec/CECHandler.h +++ b/include/cec/CECHandler.h @@ -82,6 +82,7 @@ private: CECConfig _cecConfig {}; bool _isInitialised; + bool _isOpen; bool _isEnabled; int _buttonReleaseDelayMs; diff --git a/include/utils/PixelFormat.h b/include/utils/PixelFormat.h index b639faf5..584ffc20 100644 --- a/include/utils/PixelFormat.h +++ b/include/utils/PixelFormat.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef PIXELFORMAT_H +#define PIXELFORMAT_H #include @@ -14,9 +15,7 @@ enum class PixelFormat { BGR32, NV12, I420, -#ifdef HAVE_TURBO_JPEG MJPEG, -#endif NO_CHANGE }; @@ -57,12 +56,10 @@ inline PixelFormat parsePixelFormat(const QString& pixelFormat) { return PixelFormat::NV12; } -#ifdef HAVE_TURBO_JPEG else if (format.compare("mjpeg") == 0) { return PixelFormat::MJPEG; } -#endif // return the default NO_CHANGE return PixelFormat::NO_CHANGE; @@ -103,12 +100,10 @@ inline QString pixelFormatToString(const PixelFormat& pixelFormat) { return "NV12"; } -#ifdef HAVE_TURBO_JPEG else if (pixelFormat == PixelFormat::MJPEG) { return "MJPEG"; } -#endif // return the default NO_CHANGE return "NO_CHANGE"; @@ -166,3 +161,5 @@ inline QString flipModeToString(const FlipMode& flipMode) // return the default NO_CHANGE return "NO_CHANGE"; } + +#endif // PIXELFORMAT_H diff --git a/libsrc/cec/CECHandler.cpp b/libsrc/cec/CECHandler.cpp index ddceb9ec..438dd087 100644 --- a/libsrc/cec/CECHandler.cpp +++ b/libsrc/cec/CECHandler.cpp @@ -8,8 +8,8 @@ #include #include +#include #include -#include #include /* Enable to turn on detailed CEC logs */ @@ -19,6 +19,7 @@ CECHandler::CECHandler(const QJsonDocument& config, QObject * parent) : QObject(parent) , _config(config) , _isInitialised(false) + , _isOpen(false) , _isEnabled(false) , _buttonReleaseDelayMs(CEC_BUTTON_TIMEOUT) , _buttonRepeatRateMs(0) @@ -126,62 +127,71 @@ void CECHandler::stop() bool CECHandler::enable() { - bool opened {false}; if (_isInitialised) { - const auto adapters = getAdapters(); - if (adapters.isEmpty()) + if (!_isOpen) { - Error(_logger, "Failed to find any CEC adapter. CEC event handling will be disabled."); - _cecAdapter->Close(); - return false; - } - - Info(_logger, "Auto detecting CEC adapter"); - for (const auto & adapter : adapters) - { - printAdapter(adapter); - - if (!opened && openAdapter(adapter)) + const auto adapters = getAdapters(); + if (adapters.isEmpty()) { - Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType)); - opened = true; - break; + Error(_logger, "Failed to find any CEC adapter. CEC event handling will be disabled."); + _cecAdapter->Close(); + return false; + } + + Info(_logger, "Auto detecting CEC adapter"); + bool opened {false}; + for (const auto & adapter : adapters) + { + printAdapter(adapter); + if (!opened) + { + if (openAdapter(adapter)) + { + + Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType)); + opened = true; + + break; + } + } + } + + if (!opened) + { + Error(_logger, "Could not initialize any CEC adapter."); + _cecAdapter->Close(); + } + else + { + _isOpen=true; + QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); +#ifdef VERBOSE_CEC + std::cout << "Found Devices: " << scan().toStdString() << std::endl; +#endif } } -#ifdef VERBOSE_CEC - std::cout << "Found Devices: " << scan().toStdString() << std::endl; -#endif - } - - if (!opened) - { - Error(_logger, "Could not initialize any CEC adapter."); - _cecAdapter->Close(); - } - else - { - if (!_cecAdapter->SetConfiguration(&_cecConfig)) + if (_isOpen && !_cecAdapter->SetConfiguration(&_cecConfig)) { Error(_logger, "Failed setting remote button press timing parameters"); } - QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); - Info(_logger, "CEC handler started"); + + Info(_logger, "CEC handler enabled"); + } - return opened; + return _isOpen; } void CECHandler::disable() { if (_isInitialised) { - Info(_logger, "Stopping CEC handler"); - QObject::disconnect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); - _cecAdapter->Close(); + _isOpen=false; + Info(_logger, "CEC handler disabled"); } } @@ -227,7 +237,9 @@ QVector CECHandler::getAdapters() const bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor) { if (_cecAdapter == nullptr) + { return false; + } if(!_cecAdapter->Open(descriptor.strComName)) { @@ -301,9 +313,9 @@ QString CECHandler::scan() const void CECHandler::triggerAction(const QString& cecEvent) { Event action = _cecEventActionMap.value(cecEvent, Event::Unknown); - Debug(_logger, "CEC-Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(cecEvent), eventToString(action) ); if ( action != Event::Unknown ) { + Debug(_logger, "CEC-Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(cecEvent), eventToString(action) ); emit signalEvent(action); } } diff --git a/libsrc/grabber/video/CMakeLists.txt b/libsrc/grabber/video/CMakeLists.txt index c4c2f896..ab447738 100644 --- a/libsrc/grabber/video/CMakeLists.txt +++ b/libsrc/grabber/video/CMakeLists.txt @@ -28,7 +28,7 @@ target_link_libraries(${PROJECT_NAME} hyperion) if(ENABLE_V4L2 OR ENABLE_MF) find_package(TurboJPEG) if(TURBOJPEG_FOUND) - add_definitions(-DHAVE_TURBO_JPEG) + target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_TURBO_JPEG) message(STATUS "Using Turbo JPEG library: ${TurboJPEG_LIBRARY}") target_link_libraries(${PROJECT_NAME} ${TurboJPEG_LIBRARY}) target_include_directories(${PROJECT_NAME} PUBLIC ${TurboJPEG_INCLUDE_DIRS}) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index c0441294..539b823c 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -674,7 +674,7 @@ void Hyperion::update() if (_ledString.hasBlackListedLeds()) { - for (int id : _ledString.blacklistedLedIds()) + for (unsigned long id : _ledString.blacklistedLedIds()) { if (id > _ledBuffer.size()-1) { diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index c8d4d657..06edc498 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -72,7 +72,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion , _enabled(false) , _enabledSystemCfg(false) , _smoothingType(SmoothingType::Linear) - , tempValues(std::vector(0, 0L)) + , tempValues(std::vector()) { QString subComponent = hyperion->property("instance").toString(); _log= Logger::getInstance("SMOOTHING", subComponent); @@ -87,7 +87,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion // add pause on cfg 1 SmoothingCfg cfg {true, 0, 0}; - _cfgList.append(cfg); + _cfgList.append(std::move(cfg)); // listen for comp changes connect(_hyperion, &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange); @@ -592,7 +592,7 @@ unsigned LinearColorSmoothing::addConfig(int settlingTime_ms, double ledUpdateFr ledUpdateFrequency_hz, updateDelay }; - _cfgList.append(cfg); + _cfgList.append(std::move(cfg)); DebugIf(verbose && _enabled, _log,"%s", QSTRING_CSTR(getConfig(_cfgList.count()-1))); diff --git a/libsrc/hyperion/schema/schema-cecEvents.json b/libsrc/hyperion/schema/schema-cecEvents.json index 1ccc1aab..a5790a36 100644 --- a/libsrc/hyperion/schema/schema-cecEvents.json +++ b/libsrc/hyperion/schema/schema-cecEvents.json @@ -65,7 +65,7 @@ "enum": [ "standby", "set stream path", - "F1(blue)", + "F1 (blue)", "F2 (red)", "F3 (green)", "F4 (yellow)" diff --git a/libsrc/leddevice/schemas/schema-sedu.json b/libsrc/leddevice/schemas/schema-sedu.json index ba7978eb..18266697 100644 --- a/libsrc/leddevice/schemas/schema-sedu.json +++ b/libsrc/leddevice/schemas/schema-sedu.json @@ -11,11 +11,11 @@ "rateList": { "type": "string", "title":"edt_dev_spec_baudrate_title", - "enum": [ "CUSTOM","9600","14400","19200","28800","33600","38400","56000","57600","76800","115200","128000","153600","230400","256000","307200","460800","921600","1000000","1500000","2000000","3000000","4000000" ], + "enum": [ "CUSTOM","250000","500000","1000000" ], "options": { "enum_titles": [ "edt_conf_enum_custom" ] }, - "default": "1000000", + "default": "500000", "access": "advanced", "propertyOrder" : 2 }, diff --git a/libsrc/python/PythonInit.cpp b/libsrc/python/PythonInit.cpp index 10560f1c..5684c24d 100644 --- a/libsrc/python/PythonInit.cpp +++ b/libsrc/python/PythonInit.cpp @@ -58,7 +58,11 @@ PythonInit::PythonInit() if (QFile(py_file).exists() || QDir(py_path).exists() || QDir(py_framework).exists()) { - Py_NoSiteFlag++; +#if (PY_VERSION_HEX >= 0x030C0000) + config.site_import = 0; +#else + Py_NoSiteFlag++; +#endif if (QFile(py_file).exists()) // Windows { #if (PY_VERSION_HEX >= 0x03080000) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 64001881..0416aa70 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -92,8 +92,8 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo , _osxGrabber(nullptr) , _qtGrabber(nullptr) , _dxGrabber(nullptr) - , _audioGrabber(nullptr) , _ssdp(nullptr) + , _audioGrabber(nullptr) , _eventHandler(nullptr) , _osEventHandler(nullptr) , _eventScheduler(nullptr) diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index f324fa4d..8134d423 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -211,8 +211,8 @@ private: OsxWrapper* _osxGrabber; QtWrapper* _qtGrabber; DirectXWrapper* _dxGrabber; - SSDPHandler* _ssdp; AudioWrapper* _audioGrabber; + SSDPHandler* _ssdp; EventHandler* _eventHandler; OsEventHandler* _osEventHandler; EventScheduler* _eventScheduler; diff --git a/test/jsonchecks/jsonschema.py b/test/jsonchecks/jsonschema.py index db0c144e..a708a7d6 100644 --- a/test/jsonchecks/jsonschema.py +++ b/test/jsonchecks/jsonschema.py @@ -224,7 +224,7 @@ class ValidatorMixin(object): properties is dispatched to ``validate_property`` methods. E.g., to implement a validator for a ``maximum`` property, create a ``validate_maximum`` method. Validator methods should yield zero or more - :exc:`ValidationError``\s to signal failed validation. + :exc:`ValidationError``\\s to signal failed validation. """ @@ -1028,7 +1028,7 @@ if hasattr(socket, "inet_pton"): @_checks_drafts(draft3="host-name", draft4="hostname") def is_host_name(instance): - pattern = "^[A-Za-z0-9][A-Za-z0-9\.\-]{1,255}$" + pattern = r"^[A-Za-z0-9][A-Za-z0-9.-]{1,255}$" if not re.match(pattern, instance): return False components = instance.split(".")