From a07f1aa1e8de3d030425ccfd1617ee2bf2e3fcf8 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Mon, 19 May 2025 17:58:43 -0400 Subject: [PATCH 01/15] First rough draft --- CMakeLists.txt | 1 + src/CMakeLists.txt | 3 + src/ble/ctrlm_ble_controller.cpp | 2 +- src/ctrlm_utils.cpp | 1 + src/server/CMakeLists.txt | 63 ++++ src/server/ctrlm_fta_caa.h | 54 ++++ src/server/ctrlm_fta_lib.h | 38 +++ src/server/ctrlm_fta_platform.h | 48 +++ src/server/ctrlms_main.c | 113 ++++++++ src/server/ctrlms_utils.c | 15 + src/server/ctrlms_utils.h | 35 +++ src/server/ctrlms_version.c | 56 ++++ src/server/ctrlms_ws.c | 481 +++++++++++++++++++++++++++++++ src/server/ctrlms_ws.h | 46 +++ 14 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 src/server/CMakeLists.txt create mode 100644 src/server/ctrlm_fta_caa.h create mode 100644 src/server/ctrlm_fta_lib.h create mode 100644 src/server/ctrlm_fta_platform.h create mode 100644 src/server/ctrlms_main.c create mode 100644 src/server/ctrlms_utils.c create mode 100644 src/server/ctrlms_utils.h create mode 100644 src/server/ctrlms_version.c create mode 100644 src/server/ctrlms_ws.c create mode 100644 src/server/ctrlms_ws.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fdecdc..c8a6760 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ option(BLE_ENABLED "Enable BLE" OFF) option(BLE_SERVICES "Enable BLE Services" OFF) option(BREAKPAD "Enable BREAKPAD" OFF) option(BUILD_CTRLM_FACTORY "Build Control Factory Test" OFF) +option(BUILD_CTRLM_SERVER "Build Control Server Daemon" ON) option(CPC "Enable CPC" OFF) option(DISABLE_BLE_VOICE "Disable BLE voice" OFF) option(DEEPSLEEP_CLOSE_DB "Deep Sleep Close DB" OFF) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 062dd39..8f6c2fc 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -139,6 +139,9 @@ if(BUILD_FACTORY_TEST) add_subdirectory(factory) endif() +if(BUILD_CTRLM_SERVER) + add_subdirectory(server) +endif() if(IRDB_STUB) target_sources(controlMgr PRIVATE diff --git a/src/ble/ctrlm_ble_controller.cpp b/src/ble/ctrlm_ble_controller.cpp index 3f3dc6a..c9875a0 100644 --- a/src/ble/ctrlm_ble_controller.cpp +++ b/src/ble/ctrlm_ble_controller.cpp @@ -500,7 +500,7 @@ bool ctrlm_obj_controller_ble_t::is_stale(time_t stale_time_threshold) const { } bool ctrlm_obj_controller_ble_t::isVoiceKey(uint16_t key_code) const { - if(key_code == voice_key_code_) { + if(key_code == voice_key_code_ || key_code == KEY_F23) { return true; } return false; diff --git a/src/ctrlm_utils.cpp b/src/ctrlm_utils.cpp index 16af458..3311fc0 100644 --- a/src/ctrlm_utils.cpp +++ b/src/ctrlm_utils.cpp @@ -1299,6 +1299,7 @@ static const map> ctrlm_linux_key_name {KEY_F20, {"Info", "Info"}}, {KEY_F21, {"Guide", "Guide"}}, {KEY_F22, {"Accessibility", "Accessibility"}}, + {KEY_F23, {"Voice Alt", "Voice Alt"}}, {KEY_F8, {"Voice", "Voice"}}, {KEY_ESC, {"Dismiss", "Dismiss"}}, {KEY_F9, {"Quick Access Menu", "Quick Access Menu"}}, diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt new file mode 100644 index 0000000..dda45d0 --- /dev/null +++ b/src/server/CMakeLists.txt @@ -0,0 +1,63 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + + +add_executable(controlServer ctrlms_main.c) + +include_directories( + . + ${CMAKE_SYSROOT}/usr/include/safeclib + ${CMAKE_SYSROOT}/usr/include/libsafec + ${CMAKE_SYSROOT}/usr/include/nopoll + ${CMAKE_SYSROOT}/usr/include +) + +target_sources(controlServer PRIVATE + ctrlms_version.c + ctrlms_utils.c + ctrlms_ws.c +) + +target_link_libraries(controlServer c rdkversion pthread nopoll xr-voice-sdk secure_wrapper) + +#if(CUSTOM_AUTH_LIB) +# add_compile_definitions(PRIVATE CTRLMS_WSS_ENABLED) +# target_link_libraries(controlServer ${CUSTOM_AUTH_LIB}) +#endif() + +#if(EXISTS ${CMAKE_SYSROOT}/usr/lib/libctrlm-hal-certificate.so) +# target_link_libraries(controlServer ctrlm-hal-certificate) +#else() +# if(AUTH_ENABLED) +# message(WARNING "ctrlm-hal-certificate library is not provided, disabling authentication") +# unset(AUTH_ENABLED) +# endif() +#endif() + +if(USE_SAFEC) + find_package(PkgConfig) + pkg_check_modules(SAFEC REQUIRED libsafec) + if(SAFEC_FOUND) + target_link_libraries(controlServer ${SAFEC_LIBRARIES}) + endif() +else() + add_compile_definitions(PUBLIC SAFEC_DUMMY_API) +endif() + +install(TARGETS controlServer DESTINATION bin) diff --git a/src/server/ctrlm_fta_caa.h b/src/server/ctrlm_fta_caa.h new file mode 100644 index 0000000..a682572 --- /dev/null +++ b/src/server/ctrlm_fta_caa.h @@ -0,0 +1,54 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _CTRLM_FTA_CAA_H_ +#define _CTRLM_FTA_CAA_H_ + +#include + +#define MIC_CHANNEL_QTY (3) +#define AUDIO_FRAME_DURATION (20) // in milliseconds +#define SAMPLE_RATE (16000) +#define SAMPLES_PER_FRAME (SAMPLE_RATE * AUDIO_FRAME_DURATION / 1000) +#define SAMPLE_SIZE sizeof(int32_t) + +#define AUDIO_FRAME_SIZE_PER_BEAM (SAMPLE_SIZE * SAMPLES_PER_FRAME) +#define AUDIO_FRAME_SIZE_ALL_BEAMS (AUDIO_FRAME_SIZE_PER_BEAM * MIC_CHANNEL_QTY) + +typedef enum { + CTRLMF_TEST_FACTORY = 0, + CTRLMF_TEST_QTY = 1 +} ctrlms_test_type_t; + +typedef int32_t (*ctrlms_audio_frame_t)[MIC_CHANNEL_QTY][SAMPLES_PER_FRAME]; + +typedef struct { + bool pass; + double snr[MIC_CHANNEL_QTY]; +} ctrlms_test_result_t; + +#ifdef __cplusplus +extern "C" { +#endif + +bool ctrlms_mic_test_audio_analyze(ctrlms_test_type_t test_type, const char *output_filename, uint32_t level, ctrlms_audio_frame_t audio_frames_noise, ctrlms_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, ctrlms_test_result_t *test_result); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/server/ctrlm_fta_lib.h b/src/server/ctrlm_fta_lib.h new file mode 100644 index 0000000..0ce58b1 --- /dev/null +++ b/src/server/ctrlm_fta_lib.h @@ -0,0 +1,38 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _CTRLM_FTA_LIB_H_ +#define _CTRLM_FTA_LIB_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool ctrlms_init(xlog_level_t level); +void ctrlms_term(void); + +bool ctrlms_mic_test_factory(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double *snr_min, double *snr_max, double *snr_var, ctrlms_test_result_t *test_result); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/server/ctrlm_fta_platform.h b/src/server/ctrlm_fta_platform.h new file mode 100644 index 0000000..42119f3 --- /dev/null +++ b/src/server/ctrlm_fta_platform.h @@ -0,0 +1,48 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _CTRLM_FTA_PLATFORM_H_ +#define _CTRLM_FTA_PLATFORM_H_ + +#include + +typedef enum { + CTRLM_FTA_PLATFORM_VOICE_CERT_TYPE_NONE = 0, + CTRLM_FTA_PLATFORM_VOICE_CERT_TYPE_P12 = 1, + CTRLM_FTA_PLATFORM_VOICE_CERT_TYPE_INVALID = 2 +} ctrlm_fta_platform_voice_cert_type_t; + +typedef struct { + ctrlm_fta_platform_voice_cert_type_t type; + char *filename; + char *password; +}ctrlm_fta_platform_cert_info_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +ctrlm_fta_platform_cert_info_t *ctrlm_fta_platform_cert_info_get(bool allow_expired); +void ctrlm_fta_platform_cert_info_free(ctrlm_fta_platform_cert_info_t *cert_info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c new file mode 100644 index 0000000..d754dc2 --- /dev/null +++ b/src/server/ctrlms_main.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include + +#define CTRLMS_VERSION "1.0" + +typedef struct { + bool silent; + bool verbose; +} ctrlms_options_t; + +static bool ctrlms_cmdline_args(int argc, char *argv[]); +static error_t ctrlms_parse_opt(int key, char *arg, struct argp_state *state); + +const char *argp_program_version = "controlServer " CTRLMS_VERSION; +const char *argp_program_bug_address = ""; + +static char doc[] = "controlServer -- a server application"; + +static char args_doc[] = ""; + +static struct argp_option options[] = { + {"verbose", 'v', 0, 0, "Produce verbose output" }, + {"quiet", 'q', 0, 0, "Don't produce any output" }, + { 0 } +}; + +static struct argp argp = { options, ctrlms_parse_opt, args_doc, doc }; + +static ctrlms_options_t g_ctrlms_opts = { .silent = false, + .verbose = false + }; + +int main(int argc, char* argv[]) { + // Parse command line arguments + if(!ctrlms_cmdline_args(argc, argv)) { + return(-1); + } + + xlog_level_t level = XLOG_LEVEL_WARN; + if(g_ctrlms_opts.silent) { + level = XLOG_LEVEL_ERROR; + } else if(g_ctrlms_opts.verbose) { + level = XLOG_LEVEL_INFO; + // TODO Add option to allow debug level logging + //} else if() { + // level = XLOG_LEVEL_DEBUG; + } + + if(!ctrlms_init(level)) { + XLOGD_ERROR("ctrlms_main: init failed"); + } else { + XLOGD_INFO("ctrlms_main: Run main loop"); + + // TODO Start listening for connections + //bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks) { + + + XLOGD_INFO("ctrlms_main: main loop ended"); + } + ctrlms_term(); + XLOGD_INFO("ctrlms_main: return"); + + return(0); +} + +error_t ctrlms_parse_opt(int key, char *arg, struct argp_state *state) { + // Get the input argument from argp_parse, which we know is a pointer to our arguments structure. + ctrlms_options_t *arguments = state->input; + + switch(key) { + case 'q': { + arguments->silent = true; + break; + } + case 'v': { + arguments->verbose = true; + break; + } + case ARGP_KEY_ARG: { + argp_usage(state); + return(ARGP_ERR_UNKNOWN); + } + case ARGP_KEY_END: { + break; + } + default: { + return(ARGP_ERR_UNKNOWN); + } + } + + return(0); +} + +bool ctrlms_cmdline_args(int argc, char *argv[]) { + argp_parse(&argp, argc, argv, 0, 0, &g_ctrlms_opts); + + #if 0 + if() { // Nothing to do + printf("Invalid options specified. Try 'controlServer --help' or 'controlServer --usage' for more information.\n"); + return(false); + } + #endif + + XLOGD_INFO("verbose <%s>", g_ctrlms_opts.verbose ? "YES" : "NO"); + XLOGD_INFO("silent <%s>", g_ctrlms_opts.silent ? "YES" : "NO"); + + return(true); +} diff --git a/src/server/ctrlms_utils.c b/src/server/ctrlms_utils.c new file mode 100644 index 0000000..d51cb10 --- /dev/null +++ b/src/server/ctrlms_utils.c @@ -0,0 +1,15 @@ +#include +#include +#include + +#define CTRLMS_INVALID_STR_LEN (24) + +static char ctrlms_invalid_str[CTRLMS_INVALID_STR_LEN]; + +static const char *ctrlms_invalid_return(int value); + +const char *ctrlms_invalid_return(int value) { + snprintf(ctrlms_invalid_str, sizeof(ctrlms_invalid_str), "INVALID(%d)", value); + ctrlms_invalid_str[sizeof(ctrlms_invalid_str) - 1] = '\0'; + return(ctrlms_invalid_str); +} diff --git a/src/server/ctrlms_utils.h b/src/server/ctrlms_utils.h new file mode 100644 index 0000000..6acf128 --- /dev/null +++ b/src/server/ctrlms_utils.h @@ -0,0 +1,35 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _CTRLMS_UTILS_H_ +#define _CTRLMS_UTILS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool ctrlms_is_initialized(void); +bool ctrlms_is_production(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/server/ctrlms_version.c b/src/server/ctrlms_version.c new file mode 100644 index 0000000..d6f44a8 --- /dev/null +++ b/src/server/ctrlms_version.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + bool initialized; + bool is_production; +} ctrlms_global_t; + +ctrlms_global_t g_ctrlms = { + .initialized = false, + .is_production = true +}; + +bool ctrlms_init(xlog_level_t level) { + rdk_version_info_t info; + int ret_val = rdk_version_parse_version(&info); + + if(ret_val != 0) { + XLOGD_ERROR("parse error <%s>\n", info.parse_error == NULL ? "" : info.parse_error); + return(false); + } + + g_ctrlms.is_production = info.production_build; + + rdk_version_object_free(&info); + + int rc = xlog_init(XLOG_MODULE_ID, NULL, 0); + xlog_level_set_all(level); + + if(rc != 0) { + XLOGD_ERROR("failed to init xlog"); + return(false); + } + + g_ctrlms.initialized = true; + return(true); +} + +void ctrlms_term(void) { + g_ctrlms.initialized = false; + xlog_term(); +} + +bool ctrlms_is_initialized(void) { + return(g_ctrlms.initialized); +} + +bool ctrlms_is_production(void) { + return(g_ctrlms.is_production); +} + diff --git a/src/server/ctrlms_ws.c b/src/server/ctrlms_ws.c new file mode 100644 index 0000000..7bba59a --- /dev/null +++ b/src/server/ctrlms_ws.c @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CTRLMS_WSS_ENABLED +#include +#define CTRLMS_WS_CIPHER_LIST "AES256-SHA256:AES128-GCM-SHA256:AES128-SHA256" +#define CTRLMS_WS_TLS_CERT_KEY_FILE "/tmp/serverXXXXXX" +#define CTRLMS_WS_CERT_NAME_LEN (1024) +#define CTRLMS_WS_CERT_PW_LEN (128) +#endif + +typedef enum { + CTRLMS_WS_MSG_TYPE_TEXT = 0, + CTRLMS_WS_MSG_TYPE_BINARY = 1, + CTRLMS_WS_MSG_TYPE_UNKNOWN = 2 +} ctrlms_ws_msg_type_t; + +typedef struct { + sem_t * semaphore; + uint16_t port; + bool log_enable; + ctrlms_audio_frame_t * audio_frames; + uint32_t * audio_frame_qty; + uint32_t audio_frame_size; + ctrlms_ws_callbacks_t *callbacks; +} ctrlms_ws_thread_params_t; + +typedef struct { + noPollConn * nopoll_conn; + ctrlms_audio_frame_t *audio_frames; + uint32_t * audio_frame_qty; + uint32_t audio_frame_size; + uint32_t audio_byte_cnt; + uint32_t audio_byte_total; + uint8_t * audio_byte_ptr; + ctrlms_ws_callbacks_t callbacks; +} ctrlms_ws_thread_state_t; + +typedef struct { + pthread_t thread_id; + noPollCtx * nopoll_ctx; + ctrlms_audio_frame_t audio_frames; + uint32_t audio_frame_qty; +} ctrlms_ws_global_t; + +static void *ctrlms_ws_main(void *param); + +static nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); +static nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); +static void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data); +static void ctrlms_ws_on_ping(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data); +static void ctrlms_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); +static void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * log_msg, noPollPtr user_data); +#ifdef CTRLMS_WSS_ENABLED +static bool ctrlms_ws_cert_config(FILE *cert_key_fp); +static bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additional_certs); +#endif + +ctrlms_ws_global_t g_ctrlms_ws; + +bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks) { + ctrlms_ws_thread_params_t params; + + sem_t semaphore; + sem_init(&semaphore, 0, 0); + + // Launch thread + params.semaphore = &semaphore; + + params.port = port; + params.log_enable = log_enable; + params.audio_frames = &g_ctrlms_ws.audio_frames; + params.audio_frame_qty = &g_ctrlms_ws.audio_frame_qty; + params.audio_frame_size = audio_frame_size; + params.callbacks = callbacks; + + g_ctrlms_ws.nopoll_ctx = NULL; + g_ctrlms_ws.audio_frames = NULL; + g_ctrlms_ws.audio_frame_qty = 0; + + if(0 != pthread_create(&g_ctrlms_ws.thread_id, NULL, ctrlms_ws_main, ¶ms)) { + XLOGD_ERROR("unable to launch thread"); + return(false); + } + + // Block until initialization is complete or a timeout occurs + XLOGD_INFO("Waiting for thread initialization..."); + sem_wait(&semaphore); + sem_destroy(&semaphore); + return(true); +} + +bool ctrlms_ws_capture_set(ctrlms_audio_frame_t audio_frames, uint32_t audio_frame_qty) { + g_ctrlms_ws.audio_frames = audio_frames; + g_ctrlms_ws.audio_frame_qty = audio_frame_qty; + return(true); +} + +void ctrlms_ws_term(void) { + if(g_ctrlms_ws.nopoll_ctx != NULL) { + nopoll_loop_stop(g_ctrlms_ws.nopoll_ctx); + } + + // Wait for thread to exit + XLOGD_INFO("Waiting for thread to exit"); + void *retval = NULL; + pthread_join(g_ctrlms_ws.thread_id, &retval); + XLOGD_INFO("thread exited."); +} + +void *ctrlms_ws_main(void *param) { + ctrlms_ws_thread_params_t params = *((ctrlms_ws_thread_params_t *)param); + errno_t safec_rc = -1; + + if(params.audio_frames == NULL || params.audio_frame_qty == NULL || params.callbacks == NULL) { + XLOGD_ERROR("invalid params"); + return(NULL); + } + ctrlms_ws_thread_state_t state; + + state.audio_frames = params.audio_frames; + state.audio_frame_qty = params.audio_frame_qty; + state.audio_frame_size = params.audio_frame_size; + state.audio_byte_cnt = 0; + state.audio_byte_ptr = (uint8_t *)params.audio_frames; + state.audio_byte_total = *params.audio_frame_qty * state.audio_frame_size; + state.callbacks = *params.callbacks; + + g_ctrlms_ws.nopoll_ctx = nopoll_ctx_new(); + if(g_ctrlms_ws.nopoll_ctx == NULL) { + XLOGD_ERROR("nopoll context create"); + return(NULL); + } + + #ifdef CTRLMS_WSS_ENABLED + int cert_key_fd = -1; + FILE *cert_key_fp = NULL; + char tmp_cert[32] = {0}; + int err_store; + + safec_rc = sprintf_s(tmp_cert, sizeof(tmp_cert), "%s", CTRLMS_WS_TLS_CERT_KEY_FILE); + if(safec_rc < EOK) { + ERR_CHK(safec_rc); + } + + umask(0600); + cert_key_fd = mkstemp(tmp_cert); + if (cert_key_fd == -1) + { + err_store = errno; + XLOGD_ERROR("mkstemp failed: <%s>", strerror(err_store)); + return(NULL); + } + + cert_key_fp = fdopen(cert_key_fd, "w"); + if(cert_key_fp == NULL) { + err_store = errno; + XLOGD_ERROR("fdopen failed: <%s>", strerror(err_store)); + if(0 != unlink(&tmp_cert[0])) { + err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } + return(NULL); + } + + if(!ctrlms_ws_cert_config(cert_key_fp)) { + XLOGD_ERROR("failed to set cert or key, exit"); + fclose(cert_key_fp); + if(0 != unlink(&tmp_cert[0])) { + err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } + return(NULL); + } + fclose(cert_key_fp); + + // Init OpenSSL + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + #endif + + if(params.log_enable) { + nopoll_log_enable(g_ctrlms_ws.nopoll_ctx, nopoll_true); + nopoll_log_set_handler(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_nopoll_log, NULL); + } + noPollConnOpts *opts = nopoll_conn_opts_new(); + nopoll_ctx_set_on_accept(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_on_accept, &state); + nopoll_ctx_set_on_ready(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_on_ready, &state); + nopoll_ctx_set_on_msg(g_ctrlms_ws.nopoll_ctx, ctrlms_ws_on_message, &state); + + #ifdef CTRLMS_WSS_ENABLED + nopoll_conn_opts_set_ssl_protocol(opts, NOPOLL_METHOD_TLSV1_2); + nopoll_conn_opts_ssl_host_verify(opts, nopoll_false); //localhost will not match host specified in certificate + + if(!nopoll_conn_opts_set_ssl_certs(opts, &tmp_cert[0], &tmp_cert[0], NULL, NULL)) { + XLOGD_ERROR("Failed to add cert/key files to nopoll_conn"); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + nopoll_conn_opts_free(opts); + return(NULL); + } + #endif + + char port[6]; + safec_rc = sprintf_s(port, sizeof(port), "%u", params.port); + if(safec_rc < EOK) { + ERR_CHK(safec_rc); + } + + // Start IPv4/6 listener + #ifdef CTRLMS_WSS_ENABLED + state.nopoll_conn = nopoll_listener_tls_new_opts6(g_ctrlms_ws.nopoll_ctx, opts, "::", port); + #else + state.nopoll_conn = nopoll_listener_new_opts6(g_ctrlms_ws.nopoll_ctx, opts, "::", port); + #endif + if(!nopoll_conn_is_ok(state.nopoll_conn)) { + XLOGD_ERROR("Listener connection IPv6 NOT ok"); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + g_ctrlms_ws.nopoll_ctx = NULL; + return(NULL); + } + + // Unblock the caller that launched this thread + sem_post(params.semaphore); + params.semaphore = NULL; + + XLOGD_INFO("Enter main loop"); + + nopoll_loop_wait(g_ctrlms_ws.nopoll_ctx, 0); + + nopoll_conn_opts_unref(opts); + nopoll_conn_unref(state.nopoll_conn); + nopoll_ctx_unref(g_ctrlms_ws.nopoll_ctx); + g_ctrlms_ws.nopoll_ctx = NULL; + + #ifdef CTRLMS_WSS_ENABLED + if(0 != unlink(tmp_cert)) { + int err_store = errno; + XLOGD_ERROR("failed to remove temp cert <%s>", strerror(err_store)); + } + #endif + + return(NULL); +} + +nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { + ctrlms_ws_thread_state_t *state = (ctrlms_ws_thread_state_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return(nopoll_false); + } + + state->audio_byte_cnt = 0; + state->audio_byte_ptr = (uint8_t *)(*state->audio_frames); + state->audio_byte_total = (*state->audio_frame_qty * state->audio_frame_size); + + // Set ping handler + nopoll_conn_set_on_ping_msg(conn, ctrlms_ws_on_ping, NULL); + + return(nopoll_true); +} + +nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { + ctrlms_ws_thread_state_t *state = (ctrlms_ws_thread_state_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return(nopoll_false); + } + + XLOGD_INFO("Connection established"); + + if(state->callbacks.connected != NULL) { + (*state->callbacks.connected)(state->callbacks.data); + } + + nopoll_conn_set_on_close(conn, ctrlms_ws_on_close, user_data); + + return(nopoll_true); +} + +void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data) { + ctrlms_ws_thread_state_t *state = (ctrlms_ws_thread_state_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return; + } + + int payload_size = nopoll_msg_get_payload_size(msg); + const unsigned char *payload = nopoll_msg_get_payload(msg); + + switch(nopoll_msg_opcode(msg)) { + case NOPOLL_TEXT_FRAME: { + XLOGD_INFO("NOPOLL_TEXT_FRAME"); + break; + } + case NOPOLL_BINARY_FRAME: { + XLOGD_INFO("NOPOLL_BINARY_FRAME size <%d>", payload_size); + if(state->audio_byte_cnt < state->audio_byte_total) { + if(state->audio_byte_ptr != NULL) { + if((state->audio_byte_cnt + payload_size) > state->audio_byte_total) { + memcpy(state->audio_byte_ptr, payload, (state->audio_byte_total - state->audio_byte_cnt)); + state->audio_byte_cnt = state->audio_byte_total; + } else { + memcpy(state->audio_byte_ptr, payload, payload_size); + state->audio_byte_ptr += payload_size; + state->audio_byte_cnt += payload_size; + } + if(state->audio_byte_cnt >= state->audio_byte_total) { + state->audio_byte_ptr = NULL; + } + } + } + if(state->audio_byte_cnt >= state->audio_byte_total) { + const char *reason = "audio buffer filled"; + nopoll_conn_close_ext(conn, 1000, reason, strlen(reason)); + } + break; + } + case NOPOLL_CONTINUATION_FRAME: { + XLOGD_INFO("NOPOLL_CONTINUATION_FRAME"); + break; + } + default: { + XLOGD_INFO("NOPOLL_UNKNOWN"); + break; + } + } +} + +void ctrlms_ws_on_ping(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data) { + ctrlms_ws_thread_state_t *state = (ctrlms_ws_thread_state_t *)user_data; + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return; + } + + XLOGD_INFO("Ping received"); + // Do nothing, we don't care about this event +} + +void ctrlms_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { + ctrlms_ws_thread_state_t *state = (ctrlms_ws_thread_state_t *)user_data; + + if(state == NULL) { + XLOGD_ERROR("invalid params"); + return; + } + XLOGD_INFO(""); + + if(state->callbacks.disconnected != NULL) { + (*state->callbacks.disconnected)(state->callbacks.data); + } +} + + +#ifdef CTRLMS_WSS_ENABLED +bool ctrlms_ws_cert_config(FILE* cert_key_fp) { + + bool ret = false; + ctrlm_fta_platform_cert_info_t *cert_info = NULL; + do { + FILE *device_cert_fp = NULL; + PKCS12 *p12_cert = NULL; + EVP_PKEY *pkey = NULL; + X509 *x509_cert = NULL; + STACK_OF(X509) *additional_certs = NULL; + + cert_info = ctrlm_fta_platform_cert_info_get(false); + if(cert_info == NULL) { + XLOGD_ERROR("unable to get certificate info"); + break; + } + + if(cert_info->type != CTRLM_FTA_PLATFORM_VOICE_CERT_TYPE_P12) { + XLOGD_ERROR("unable to parse certificates that are not of PKCS12 type"); + break; + } + + device_cert_fp = fopen(cert_info->filename, "rb"); + if(device_cert_fp == NULL) { + XLOGD_ERROR("unable to open P12 certificate"); + break; + } + + d2i_PKCS12_fp(device_cert_fp, &p12_cert); + fclose(device_cert_fp); + device_cert_fp = NULL; + + if(p12_cert == NULL) { + XLOGD_ERROR("unable to read P12 certificate"); + break; + } + + if(1 != PKCS12_parse(p12_cert, cert_info->password, &pkey, &x509_cert, &additional_certs)) { + XLOGD_ERROR("unable to parse P12 certificate"); + break; + } + + if(1 != PEM_write_X509(cert_key_fp, x509_cert)) { + XLOGD_ERROR("failed to write temp cert"); + break; + } + + if(!ctrlms_ws_add_chain(cert_key_fp, additional_certs)) { + XLOGD_ERROR("failed to add chain certs"); + break; + } + + if(1 != PEM_write_PrivateKey(cert_key_fp, pkey, NULL, (unsigned char*)cert_info->password, strlen(cert_info->password), NULL, NULL)) { + XLOGD_ERROR("failed to write temp key"); + break; + } + + ret = true; + }while(0); + + if(cert_info != NULL) { + ctrlm_fta_platform_cert_info_free(cert_info); + } + + return ret; +} + +bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additional_certs) { + if(cert_key_fp == NULL) { + XLOGD_ERROR("null file pointer"); + return false; + } + if(additional_certs == NULL) { + XLOGD_ERROR("null certs"); + return false; + } + + for(uint32_t index = 0; index < sk_X509_num(additional_certs); index++) { + X509 *cert = sk_X509_value(additional_certs, index); + if(1 != PEM_write_X509(cert_key_fp, cert)) { + XLOGD_ERROR("failed to write temp cert index %d", index); + return false; + } + } + + return true; +} +#endif + +void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * log_msg, noPollPtr user_data) { + xlog_args_t args; + args.options = XLOG_OPTS_DEFAULT; + args.color = XLOG_COLOR_NONE; + args.function = XLOG_FUNCTION_NONE; + args.line = XLOG_LINE_NONE; + args.id = XLOG_MODULE_ID; + args.size_max = XLOG_BUF_SIZE_DEFAULT; + switch(level) { + case NOPOLL_LEVEL_DEBUG: { args.level = XLOG_LEVEL_DEBUG; break; } + case NOPOLL_LEVEL_INFO: { args.level = XLOG_LEVEL_INFO; break; } + case NOPOLL_LEVEL_WARNING: { args.level = XLOG_LEVEL_WARN; break; } + case NOPOLL_LEVEL_CRITICAL: { args.level = XLOG_LEVEL_ERROR; break; } + default: { args.level = XLOG_LEVEL_INFO; break; } + } + int errsv = errno; + xlog_printf(&args, "%s", log_msg); + errno = errsv; +} diff --git a/src/server/ctrlms_ws.h b/src/server/ctrlms_ws.h new file mode 100644 index 0000000..9b772d1 --- /dev/null +++ b/src/server/ctrlms_ws.h @@ -0,0 +1,46 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _CTRLMF_WS_H_ +#define _CTRLMF_WS_H_ + +#include +#include + +typedef void (*ctrlms_ws_connected_t)(void *data); +typedef void (*ctrlms_ws_disconnected_t)(void *data); + +typedef struct { + ctrlms_ws_connected_t connected; + ctrlms_ws_disconnected_t disconnected; + void * data; +} ctrlms_ws_callbacks_t; + +#ifdef __cplusplus +extern "C" { +#endif + +bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks); +bool ctrlms_ws_capture_set(ctrlms_audio_frame_t audio_frames, uint32_t audio_frame_qty); +void ctrlms_ws_term(void); + +#ifdef __cplusplus +} +#endif + +#endif From 4c4f4f91160357e3af9266dbee34b2b4428efcd9 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Tue, 20 May 2025 16:35:30 -0400 Subject: [PATCH 02/15] Cleaned up a bunch. --- src/server/CMakeLists.txt | 2 +- src/server/ctrlm_fta_caa.h | 54 ------ src/server/ctrlm_server_app.h | 41 +++++ ...fta_platform.h => ctrlm_server_platform.h} | 4 +- src/server/ctrlms_main.c | 13 +- .../{ctrlm_fta_lib.h => ctrlms_version.h} | 8 +- src/server/ctrlms_ws.c | 163 +++++++++++------- src/server/ctrlms_ws.h | 5 +- 8 files changed, 163 insertions(+), 127 deletions(-) delete mode 100644 src/server/ctrlm_fta_caa.h create mode 100644 src/server/ctrlm_server_app.h rename src/server/{ctrlm_fta_platform.h => ctrlm_server_platform.h} (95%) rename src/server/{ctrlm_fta_lib.h => ctrlms_version.h} (75%) diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index dda45d0..2cda8a7 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -34,7 +34,7 @@ target_sources(controlServer PRIVATE ctrlms_ws.c ) -target_link_libraries(controlServer c rdkversion pthread nopoll xr-voice-sdk secure_wrapper) +target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper ${CMAKE_DL_LIBS}) #if(CUSTOM_AUTH_LIB) # add_compile_definitions(PRIVATE CTRLMS_WSS_ENABLED) diff --git a/src/server/ctrlm_fta_caa.h b/src/server/ctrlm_fta_caa.h deleted file mode 100644 index a682572..0000000 --- a/src/server/ctrlm_fta_caa.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * If not stated otherwise in this file or this component's license file the - * following copyright and licenses apply: - * - * Copyright 2014 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ -#ifndef _CTRLM_FTA_CAA_H_ -#define _CTRLM_FTA_CAA_H_ - -#include - -#define MIC_CHANNEL_QTY (3) -#define AUDIO_FRAME_DURATION (20) // in milliseconds -#define SAMPLE_RATE (16000) -#define SAMPLES_PER_FRAME (SAMPLE_RATE * AUDIO_FRAME_DURATION / 1000) -#define SAMPLE_SIZE sizeof(int32_t) - -#define AUDIO_FRAME_SIZE_PER_BEAM (SAMPLE_SIZE * SAMPLES_PER_FRAME) -#define AUDIO_FRAME_SIZE_ALL_BEAMS (AUDIO_FRAME_SIZE_PER_BEAM * MIC_CHANNEL_QTY) - -typedef enum { - CTRLMF_TEST_FACTORY = 0, - CTRLMF_TEST_QTY = 1 -} ctrlms_test_type_t; - -typedef int32_t (*ctrlms_audio_frame_t)[MIC_CHANNEL_QTY][SAMPLES_PER_FRAME]; - -typedef struct { - bool pass; - double snr[MIC_CHANNEL_QTY]; -} ctrlms_test_result_t; - -#ifdef __cplusplus -extern "C" { -#endif - -bool ctrlms_mic_test_audio_analyze(ctrlms_test_type_t test_type, const char *output_filename, uint32_t level, ctrlms_audio_frame_t audio_frames_noise, ctrlms_audio_frame_t audio_frames_signal, uint32_t frame_qty, uint32_t mic_qty, ctrlms_test_result_t *test_result); - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/src/server/ctrlm_server_app.h b/src/server/ctrlm_server_app.h new file mode 100644 index 0000000..048108e --- /dev/null +++ b/src/server/ctrlm_server_app.h @@ -0,0 +1,41 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2014 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#ifndef _CTRLM_SERVER_APP_H_ +#define _CTRLM_SERVER_APP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Audio data received from websocket client +typedef bool (*ctrlms_ws_receive_audio_t)(const unsigned char *payload, int payload_size); + +// Json object received from websocket client +typedef bool (*ctrlms_ws_receive_json_t)(const json_t *json_obj); + +// Json object to send to websocket client +void ctrlms_ws_send_json(const json_t *json_obj); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/server/ctrlm_fta_platform.h b/src/server/ctrlm_server_platform.h similarity index 95% rename from src/server/ctrlm_fta_platform.h rename to src/server/ctrlm_server_platform.h index 42119f3..8342e68 100644 --- a/src/server/ctrlm_fta_platform.h +++ b/src/server/ctrlm_server_platform.h @@ -16,8 +16,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef _CTRLM_FTA_PLATFORM_H_ -#define _CTRLM_FTA_PLATFORM_H_ +#ifndef _CTRLM_SERVER_PLATFORM_H_ +#define _CTRLM_SERVER_PLATFORM_H_ #include diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c index d754dc2..a9de010 100644 --- a/src/server/ctrlms_main.c +++ b/src/server/ctrlms_main.c @@ -5,9 +5,12 @@ #include #include #include +#include #define CTRLMS_VERSION "1.0" +#define CTRLMS_WS_PORT_INT (9881) + typedef struct { bool silent; bool verbose; @@ -54,12 +57,14 @@ int main(int argc, char* argv[]) { if(!ctrlms_init(level)) { XLOGD_ERROR("ctrlms_main: init failed"); } else { - XLOGD_INFO("ctrlms_main: Run main loop"); // TODO Start listening for connections - //bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks) { - - + if(!ctrlms_ws_init(CTRLMS_WS_PORT_INT, true, NULL)) { + XLOGD_ERROR("ctrlms_main: ws init failed"); + } else { + ctrlms_ws_listen(); + ctrlms_ws_term(); + } XLOGD_INFO("ctrlms_main: main loop ended"); } ctrlms_term(); diff --git a/src/server/ctrlm_fta_lib.h b/src/server/ctrlms_version.h similarity index 75% rename from src/server/ctrlm_fta_lib.h rename to src/server/ctrlms_version.h index 0ce58b1..4b64177 100644 --- a/src/server/ctrlm_fta_lib.h +++ b/src/server/ctrlms_version.h @@ -16,11 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef _CTRLM_FTA_LIB_H_ -#define _CTRLM_FTA_LIB_H_ +#ifndef _CTRLMS_VERSION_H_ +#define _CTRLMS_VERSION_H_ #include -#include +#include #ifdef __cplusplus extern "C" { @@ -29,8 +29,6 @@ extern "C" { bool ctrlms_init(xlog_level_t level); void ctrlms_term(void); -bool ctrlms_mic_test_factory(uint32_t duration, const char *output_filename, uint32_t level, const char *audio_filename, double *snr_min, double *snr_max, double *snr_var, ctrlms_test_result_t *test_result); - #ifdef __cplusplus } #endif diff --git a/src/server/ctrlms_ws.c b/src/server/ctrlms_ws.c index 7bba59a..c830c61 100644 --- a/src/server/ctrlms_ws.c +++ b/src/server/ctrlms_ws.c @@ -9,11 +9,14 @@ #include #include #include +#include +#include #include #include #include #include -#include +#include +#include #ifdef CTRLMS_WSS_ENABLED #include @@ -33,31 +36,26 @@ typedef struct { sem_t * semaphore; uint16_t port; bool log_enable; - ctrlms_audio_frame_t * audio_frames; - uint32_t * audio_frame_qty; - uint32_t audio_frame_size; ctrlms_ws_callbacks_t *callbacks; } ctrlms_ws_thread_params_t; typedef struct { - noPollConn * nopoll_conn; - ctrlms_audio_frame_t *audio_frames; - uint32_t * audio_frame_qty; - uint32_t audio_frame_size; - uint32_t audio_byte_cnt; - uint32_t audio_byte_total; - uint8_t * audio_byte_ptr; - ctrlms_ws_callbacks_t callbacks; + noPollConn * nopoll_conn; + ctrlms_ws_callbacks_t callbacks; + void * app_handle; + ctrlms_ws_receive_audio_t app_receive_audio; + ctrlms_ws_receive_json_t app_receive_json; } ctrlms_ws_thread_state_t; typedef struct { pthread_t thread_id; noPollCtx * nopoll_ctx; - ctrlms_audio_frame_t audio_frames; - uint32_t audio_frame_qty; } ctrlms_ws_global_t; static void *ctrlms_ws_main(void *param); +static void *ctrlms_ws_load_app(ctrlms_ws_thread_state_t *state); +static bool ctrlms_ws_receive_audio_stub(const unsigned char *payload, int payload_size); +static bool ctrlms_ws_receive_json_stub(const json_t *json_obj); static nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); static nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); @@ -72,7 +70,7 @@ static bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additi ctrlms_ws_global_t g_ctrlms_ws; -bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks) { +bool ctrlms_ws_init(uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks) { ctrlms_ws_thread_params_t params; sem_t semaphore; @@ -83,14 +81,13 @@ bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, c params.port = port; params.log_enable = log_enable; - params.audio_frames = &g_ctrlms_ws.audio_frames; - params.audio_frame_qty = &g_ctrlms_ws.audio_frame_qty; - params.audio_frame_size = audio_frame_size; - params.callbacks = callbacks; + if(callbacks != NULL) { + params.callbacks = callbacks; + } else { + params.callbacks = NULL; + } g_ctrlms_ws.nopoll_ctx = NULL; - g_ctrlms_ws.audio_frames = NULL; - g_ctrlms_ws.audio_frame_qty = 0; if(0 != pthread_create(&g_ctrlms_ws.thread_id, NULL, ctrlms_ws_main, ¶ms)) { XLOGD_ERROR("unable to launch thread"); @@ -104,9 +101,12 @@ bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, c return(true); } -bool ctrlms_ws_capture_set(ctrlms_audio_frame_t audio_frames, uint32_t audio_frame_qty) { - g_ctrlms_ws.audio_frames = audio_frames; - g_ctrlms_ws.audio_frame_qty = audio_frame_qty; +bool ctrlms_ws_listen(void) { + // Wait for thread to exit + XLOGD_INFO("Waiting until thread exits"); + void *retval = NULL; + pthread_join(g_ctrlms_ws.thread_id, &retval); + XLOGD_INFO("thread exited."); return(true); } @@ -126,19 +126,18 @@ void *ctrlms_ws_main(void *param) { ctrlms_ws_thread_params_t params = *((ctrlms_ws_thread_params_t *)param); errno_t safec_rc = -1; - if(params.audio_frames == NULL || params.audio_frame_qty == NULL || params.callbacks == NULL) { - XLOGD_ERROR("invalid params"); - return(NULL); - } ctrlms_ws_thread_state_t state; - state.audio_frames = params.audio_frames; - state.audio_frame_qty = params.audio_frame_qty; - state.audio_frame_size = params.audio_frame_size; - state.audio_byte_cnt = 0; - state.audio_byte_ptr = (uint8_t *)params.audio_frames; - state.audio_byte_total = *params.audio_frame_qty * state.audio_frame_size; - state.callbacks = *params.callbacks; + if(params.callbacks != NULL) { + state.callbacks = *params.callbacks; + } else { + state.callbacks.connected = NULL; + state.callbacks.disconnected = NULL; + state.callbacks.data = NULL; + } + state.app_receive_audio = NULL; + state.app_receive_json = NULL; + state.app_handle = ctrlms_ws_load_app(&state); g_ctrlms_ws.nopoll_ctx = nopoll_ctx_new(); if(g_ctrlms_ws.nopoll_ctx == NULL) { @@ -254,6 +253,11 @@ void *ctrlms_ws_main(void *param) { } #endif + if(state.app_handle != NULL) { + dlclose(state.app_handle); + state.app_handle = NULL; + } + return(NULL); } @@ -265,10 +269,6 @@ nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user return(nopoll_false); } - state->audio_byte_cnt = 0; - state->audio_byte_ptr = (uint8_t *)(*state->audio_frames); - state->audio_byte_total = (*state->audio_frame_qty * state->audio_frame_size); - // Set ping handler nopoll_conn_set_on_ping_msg(conn, ctrlms_ws_on_ping, NULL); @@ -302,34 +302,34 @@ void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPo return; } + bool close_conn = false; int payload_size = nopoll_msg_get_payload_size(msg); const unsigned char *payload = nopoll_msg_get_payload(msg); switch(nopoll_msg_opcode(msg)) { case NOPOLL_TEXT_FRAME: { - XLOGD_INFO("NOPOLL_TEXT_FRAME"); + XLOGD_INFO("NOPOLL_TEXT_FRAME size <%d>", payload_size); + + json_t *json_obj = json_loads((const char *)payload, 0, NULL); + + if(json_obj == NULL) { + XLOGD_ERROR("Failed to parse JSON object"); + break; + } else { + // Pass the incoming payload to the application + if(state->app_receive_json != NULL) { + close_conn = (*state->app_receive_json)(json_obj); + } + json_decref(json_obj); + } break; } case NOPOLL_BINARY_FRAME: { XLOGD_INFO("NOPOLL_BINARY_FRAME size <%d>", payload_size); - if(state->audio_byte_cnt < state->audio_byte_total) { - if(state->audio_byte_ptr != NULL) { - if((state->audio_byte_cnt + payload_size) > state->audio_byte_total) { - memcpy(state->audio_byte_ptr, payload, (state->audio_byte_total - state->audio_byte_cnt)); - state->audio_byte_cnt = state->audio_byte_total; - } else { - memcpy(state->audio_byte_ptr, payload, payload_size); - state->audio_byte_ptr += payload_size; - state->audio_byte_cnt += payload_size; - } - if(state->audio_byte_cnt >= state->audio_byte_total) { - state->audio_byte_ptr = NULL; - } - } - } - if(state->audio_byte_cnt >= state->audio_byte_total) { - const char *reason = "audio buffer filled"; - nopoll_conn_close_ext(conn, 1000, reason, strlen(reason)); + + // Pass the incoming payload to the application + if(state->app_receive_audio != NULL) { + close_conn = (*state->app_receive_audio)(payload, payload_size); } break; } @@ -342,6 +342,11 @@ void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPo break; } } + + if(close_conn) { + const char *reason = "app closed"; + nopoll_conn_close_ext(conn, 1000, reason, strlen(reason)); + } } void ctrlms_ws_on_ping(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data) { @@ -479,3 +484,45 @@ void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * xlog_printf(&args, "%s", log_msg); errno = errsv; } + +void *ctrlms_ws_load_app(ctrlms_ws_thread_state_t *state) { + void *handle = dlopen("libctrlm_server_app.so", RTLD_NOW); + if(NULL == handle) { + XLOGD_WARN("Failed to load server app plugin <%s>. Using stub implementation.", dlerror()); + + state->app_receive_audio = ctrlms_ws_receive_audio_stub; + state->app_receive_json = ctrlms_ws_receive_json_stub; + return(NULL); + } + + dlerror(); // Clear any existing error + state->app_receive_audio = (ctrlms_ws_receive_audio_t)dlsym(handle, "ctrlms_ws_receive_audio"); + char *error = dlerror(); + + if(error != NULL) { + XLOGD_ERROR("Failed to find plugin method (ctrlms_ws_receive_audio), error <%s>", error); + dlclose(handle); + return(NULL); + } + + state->app_receive_json = (ctrlms_ws_receive_json_t)dlsym(handle, "ctrlms_ws_receive_json"); + error = dlerror(); + + if(error != NULL) { + XLOGD_ERROR("Failed to find plugin method (ctrlms_ws_receive_json), error <%s>", error); + dlclose(handle); + return(NULL); + } + + return(handle); +} + +bool ctrlms_ws_receive_audio_stub(const unsigned char *payload, int payload_size) { + XLOGD_INFO("size <%d>", payload_size); + return(true); +} + +bool ctrlms_ws_receive_json_stub(const json_t *json_obj) { + XLOGD_INFO(""); + return(true); +} diff --git a/src/server/ctrlms_ws.h b/src/server/ctrlms_ws.h index 9b772d1..fd0aa8f 100644 --- a/src/server/ctrlms_ws.h +++ b/src/server/ctrlms_ws.h @@ -20,7 +20,6 @@ #define _CTRLMF_WS_H_ #include -#include typedef void (*ctrlms_ws_connected_t)(void *data); typedef void (*ctrlms_ws_disconnected_t)(void *data); @@ -35,8 +34,8 @@ typedef struct { extern "C" { #endif -bool ctrlms_ws_init(uint32_t audio_frame_size, uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks); -bool ctrlms_ws_capture_set(ctrlms_audio_frame_t audio_frames, uint32_t audio_frame_qty); +bool ctrlms_ws_init(uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks); +bool ctrlms_ws_listen(void); void ctrlms_ws_term(void); #ifdef __cplusplus From 5f6db0a8175c38fcf55e92d143f6ffa3b740c1fb Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Wed, 21 May 2025 15:46:54 -0400 Subject: [PATCH 03/15] added some compile options --- src/server/CMakeLists.txt | 4 ++++ src/server/ctrlms_main.c | 1 + src/server/ctrlms_utils.c | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 2cda8a7..3afcf19 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -17,6 +17,8 @@ # limitations under the License. ########################################################################## +# This is required to allow an executable to export symbols to be used by loadable modules (e.g. plugins using dlopen) +set(CMAKE_ENABLE_EXPORTS ON) add_executable(controlServer ctrlms_main.c) @@ -34,6 +36,8 @@ target_sources(controlServer PRIVATE ctrlms_ws.c ) +target_compile_options(controlServer PUBLIC -fPIC -rdynamic -Wall -Werror) + target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper ${CMAKE_DL_LIBS}) #if(CUSTOM_AUTH_LIB) diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c index a9de010..ed9258e 100644 --- a/src/server/ctrlms_main.c +++ b/src/server/ctrlms_main.c @@ -6,6 +6,7 @@ #include #include #include +#include #define CTRLMS_VERSION "1.0" diff --git a/src/server/ctrlms_utils.c b/src/server/ctrlms_utils.c index d51cb10..800228a 100644 --- a/src/server/ctrlms_utils.c +++ b/src/server/ctrlms_utils.c @@ -2,6 +2,7 @@ #include #include +/* #define CTRLMS_INVALID_STR_LEN (24) static char ctrlms_invalid_str[CTRLMS_INVALID_STR_LEN]; @@ -13,3 +14,4 @@ const char *ctrlms_invalid_return(int value) { ctrlms_invalid_str[sizeof(ctrlms_invalid_str) - 1] = '\0'; return(ctrlms_invalid_str); } +*/ \ No newline at end of file From 50ad1cfaa2c1ca849a976bb3c4060d9f7f6de6c2 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Mon, 26 May 2025 14:43:33 -0400 Subject: [PATCH 04/15] updating app interface to c++ obj --- src/server/CMakeLists.txt | 2 +- src/server/ctrlm_server_app.h | 30 ++++-- src/server/ctrlms_main.c | 2 +- src/server/{ctrlms_ws.c => ctrlms_ws.cpp} | 116 ++++++++++++---------- src/server/ctrlms_ws.h | 11 +- 5 files changed, 85 insertions(+), 76 deletions(-) rename src/server/{ctrlms_ws.c => ctrlms_ws.cpp} (85%) diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 3afcf19..7f21cc8 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -33,7 +33,7 @@ include_directories( target_sources(controlServer PRIVATE ctrlms_version.c ctrlms_utils.c - ctrlms_ws.c + ctrlms_ws.cpp ) target_compile_options(controlServer PUBLIC -fPIC -rdynamic -Wall -Werror) diff --git a/src/server/ctrlm_server_app.h b/src/server/ctrlm_server_app.h index 048108e..919c00f 100644 --- a/src/server/ctrlm_server_app.h +++ b/src/server/ctrlm_server_app.h @@ -16,26 +16,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef _CTRLM_SERVER_APP_H_ -#define _CTRLM_SERVER_APP_H_ +#pragma once #include +#include #include +class ctrlms_app_interface_t +{ + public: + virtual ~ctrlms_app_interface_t() {}; + + virtual void ws_connected(void); + virtual void ws_disconnected(void); + virtual bool ws_receive_audio(const unsigned char *payload, int payload_size); + virtual bool ws_receive_json(const json_t *json_obj); + void ws_send_json(const json_t *json_obj); + void ws_handle_set(void *handle); + + private: + void *ws_handle; +}; + #ifdef __cplusplus extern "C" { #endif -// Audio data received from websocket client -typedef bool (*ctrlms_ws_receive_audio_t)(const unsigned char *payload, int payload_size); - -// Json object received from websocket client -typedef bool (*ctrlms_ws_receive_json_t)(const json_t *json_obj); - -// Json object to send to websocket client -void ctrlms_ws_send_json(const json_t *json_obj); +ctrlms_app_interface_t *ctrlms_app_interface_create(void); #ifdef __cplusplus } #endif -#endif \ No newline at end of file diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c index ed9258e..a091955 100644 --- a/src/server/ctrlms_main.c +++ b/src/server/ctrlms_main.c @@ -60,7 +60,7 @@ int main(int argc, char* argv[]) { } else { // TODO Start listening for connections - if(!ctrlms_ws_init(CTRLMS_WS_PORT_INT, true, NULL)) { + if(!ctrlms_ws_init(CTRLMS_WS_PORT_INT, true)) { XLOGD_ERROR("ctrlms_main: ws init failed"); } else { ctrlms_ws_listen(); diff --git a/src/server/ctrlms_ws.c b/src/server/ctrlms_ws.cpp similarity index 85% rename from src/server/ctrlms_ws.c rename to src/server/ctrlms_ws.cpp index c830c61..680cd92 100644 --- a/src/server/ctrlms_ws.c +++ b/src/server/ctrlms_ws.cpp @@ -36,15 +36,12 @@ typedef struct { sem_t * semaphore; uint16_t port; bool log_enable; - ctrlms_ws_callbacks_t *callbacks; } ctrlms_ws_thread_params_t; typedef struct { noPollConn * nopoll_conn; - ctrlms_ws_callbacks_t callbacks; void * app_handle; - ctrlms_ws_receive_audio_t app_receive_audio; - ctrlms_ws_receive_json_t app_receive_json; + ctrlms_app_interface_t *app_interface; } ctrlms_ws_thread_state_t; typedef struct { @@ -54,8 +51,6 @@ typedef struct { static void *ctrlms_ws_main(void *param); static void *ctrlms_ws_load_app(ctrlms_ws_thread_state_t *state); -static bool ctrlms_ws_receive_audio_stub(const unsigned char *payload, int payload_size); -static bool ctrlms_ws_receive_json_stub(const json_t *json_obj); static nopoll_bool ctrlms_ws_on_accept(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); static nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data); @@ -70,7 +65,7 @@ static bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additi ctrlms_ws_global_t g_ctrlms_ws; -bool ctrlms_ws_init(uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks) { +bool ctrlms_ws_init(uint16_t port, bool log_enable) { ctrlms_ws_thread_params_t params; sem_t semaphore; @@ -81,11 +76,6 @@ bool ctrlms_ws_init(uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callb params.port = port; params.log_enable = log_enable; - if(callbacks != NULL) { - params.callbacks = callbacks; - } else { - params.callbacks = NULL; - } g_ctrlms_ws.nopoll_ctx = NULL; @@ -128,16 +118,8 @@ void *ctrlms_ws_main(void *param) { ctrlms_ws_thread_state_t state; - if(params.callbacks != NULL) { - state.callbacks = *params.callbacks; - } else { - state.callbacks.connected = NULL; - state.callbacks.disconnected = NULL; - state.callbacks.data = NULL; - } - state.app_receive_audio = NULL; - state.app_receive_json = NULL; - state.app_handle = ctrlms_ws_load_app(&state); + state.app_interface = NULL; + state.app_handle = ctrlms_ws_load_app(&state); g_ctrlms_ws.nopoll_ctx = nopoll_ctx_new(); if(g_ctrlms_ws.nopoll_ctx == NULL) { @@ -257,6 +239,10 @@ void *ctrlms_ws_main(void *param) { dlclose(state.app_handle); state.app_handle = NULL; } + if(state.app_interface != NULL) { + delete state.app_interface; + state.app_interface = NULL; + } return(NULL); } @@ -284,10 +270,8 @@ nopoll_bool ctrlms_ws_on_ready(noPollCtx *ctx, noPollConn *conn, noPollPtr user_ } XLOGD_INFO("Connection established"); - - if(state->callbacks.connected != NULL) { - (*state->callbacks.connected)(state->callbacks.data); - } + state->app_interface->ws_handle_set((void *)conn); + state->app_interface->ws_connected(); nopoll_conn_set_on_close(conn, ctrlms_ws_on_close, user_data); @@ -317,9 +301,7 @@ void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPo break; } else { // Pass the incoming payload to the application - if(state->app_receive_json != NULL) { - close_conn = (*state->app_receive_json)(json_obj); - } + close_conn = state->app_interface->ws_receive_json(json_obj); json_decref(json_obj); } break; @@ -328,9 +310,7 @@ void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPo XLOGD_INFO("NOPOLL_BINARY_FRAME size <%d>", payload_size); // Pass the incoming payload to the application - if(state->app_receive_audio != NULL) { - close_conn = (*state->app_receive_audio)(payload, payload_size); - } + close_conn = state->app_interface->ws_receive_audio(payload, payload_size); break; } case NOPOLL_CONTINUATION_FRAME: { @@ -369,9 +349,8 @@ void ctrlms_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { } XLOGD_INFO(""); - if(state->callbacks.disconnected != NULL) { - (*state->callbacks.disconnected)(state->callbacks.data); - } + state->app_interface->ws_disconnected(); + state->app_interface->ws_handle_set(NULL); } @@ -485,44 +464,75 @@ void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * errno = errsv; } +typedef ctrlms_app_interface_t *(*ctrlms_app_interface_create_t)(void); + void *ctrlms_ws_load_app(ctrlms_ws_thread_state_t *state) { void *handle = dlopen("libctrlm_server_app.so", RTLD_NOW); if(NULL == handle) { - XLOGD_WARN("Failed to load server app plugin <%s>. Using stub implementation.", dlerror()); + XLOGD_WARN("failed to load server app plugin <%s>. Using stub implementation.", dlerror()); - state->app_receive_audio = ctrlms_ws_receive_audio_stub; - state->app_receive_json = ctrlms_ws_receive_json_stub; + state->app_interface = new ctrlms_app_interface_t(); return(NULL); } dlerror(); // Clear any existing error - state->app_receive_audio = (ctrlms_ws_receive_audio_t)dlsym(handle, "ctrlms_ws_receive_audio"); + ctrlms_app_interface_create_t app_interface = (ctrlms_app_interface_create_t)dlsym(handle, "ctrlms_app_interface_create"); char *error = dlerror(); if(error != NULL) { - XLOGD_ERROR("Failed to find plugin method (ctrlms_ws_receive_audio), error <%s>", error); + XLOGD_ERROR("failed to find plugin interface, error <%s>", error); dlclose(handle); - return(NULL); - } - state->app_receive_json = (ctrlms_ws_receive_json_t)dlsym(handle, "ctrlms_ws_receive_json"); - error = dlerror(); - - if(error != NULL) { - XLOGD_ERROR("Failed to find plugin method (ctrlms_ws_receive_json), error <%s>", error); - dlclose(handle); + state->app_interface = new ctrlms_app_interface_t(); return(NULL); } + XLOGD_INFO("successfully loaded plugin interface"); + state->app_interface = (*app_interface)(); + return(handle); } -bool ctrlms_ws_receive_audio_stub(const unsigned char *payload, int payload_size) { - XLOGD_INFO("size <%d>", payload_size); - return(true); +void ctrlms_app_interface_t::ws_connected(void) { + XLOGD_INFO("STUB: implement ws_connected"); } -bool ctrlms_ws_receive_json_stub(const json_t *json_obj) { - XLOGD_INFO(""); +void ctrlms_app_interface_t::ws_disconnected(void) { + XLOGD_INFO("STUB: implement ws_disconnected"); +} + +bool ctrlms_app_interface_t::ws_receive_audio(const unsigned char *payload, int payload_size) { + XLOGD_INFO("STUB: audio received size <%d>", payload_size); + return(true); +}; +bool ctrlms_app_interface_t::ws_receive_json(const json_t *json_obj) { + XLOGD_INFO("STUB: json object received"); return(true); } + +void ctrlms_app_interface_t::ws_send_json(const json_t *json_obj) { + if(json_obj == NULL) { + XLOGD_ERROR("json object is NULL"); + return; + } + if(ws_handle == NULL) { + XLOGD_ERROR("ws connection is not established"); + return; + } + char *payload = json_dumps(json_obj, JSON_COMPACT | JSON_ENSURE_ASCII); + if(payload == NULL) { + XLOGD_ERROR("failed to dump JSON object"); + return; + } + + XLOGD_INFO("Sending <%s>", payload); + int rc = nopoll_conn_send_text((noPollConn *)ws_handle, payload, strlen(payload)); + if(rc <= 0) { + XLOGD_ERROR("failed to send message"); + } + free(payload); +} + +void ctrlms_app_interface_t::ws_handle_set(void *handle) { + ws_handle = handle; +} diff --git a/src/server/ctrlms_ws.h b/src/server/ctrlms_ws.h index fd0aa8f..e8e5ba5 100644 --- a/src/server/ctrlms_ws.h +++ b/src/server/ctrlms_ws.h @@ -21,20 +21,11 @@ #include -typedef void (*ctrlms_ws_connected_t)(void *data); -typedef void (*ctrlms_ws_disconnected_t)(void *data); - -typedef struct { - ctrlms_ws_connected_t connected; - ctrlms_ws_disconnected_t disconnected; - void * data; -} ctrlms_ws_callbacks_t; - #ifdef __cplusplus extern "C" { #endif -bool ctrlms_ws_init(uint16_t port, bool log_enable, ctrlms_ws_callbacks_t *callbacks); +bool ctrlms_ws_init(uint16_t port, bool log_enable); bool ctrlms_ws_listen(void); void ctrlms_ws_term(void); From 5c1bcc50f67b57c362947b5ea339e34736469670 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Mon, 26 May 2025 15:37:11 -0400 Subject: [PATCH 05/15] Add emit by key name logic --- src/ble/ctrlm_ble_controller.cpp | 8 +++- src/ble/ctrlm_ble_controller.h | 2 +- src/ble/ctrlm_ble_network.cpp | 11 +++-- src/input_event/ctrlm_input_event_writer.cpp | 19 ++++++++ src/input_event/ctrlm_input_event_writer.h | 1 + src/voice/ctrlm_voice_obj.cpp | 43 +++++++++++++++++++ src/voice/ctrlm_voice_obj.h | 8 +++- .../ctrlm_voice_endpoint_ws_nextgen.cpp | 7 +++ .../ctrlm_voice_endpoint_ws_nextgen.h | 3 +- src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp | 1 - 10 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/ble/ctrlm_ble_controller.cpp b/src/ble/ctrlm_ble_controller.cpp index c9875a0..3f6bd93 100644 --- a/src/ble/ctrlm_ble_controller.cpp +++ b/src/ble/ctrlm_ble_controller.cpp @@ -499,8 +499,12 @@ bool ctrlm_obj_controller_ble_t::is_stale(time_t stale_time_threshold) const { return false; } -bool ctrlm_obj_controller_ble_t::isVoiceKey(uint16_t key_code) const { - if(key_code == voice_key_code_ || key_code == KEY_F23) { +bool ctrlm_obj_controller_ble_t::isVoiceKey(uint16_t key_code, bool &listenForKeyNames) const { + if(key_code == voice_key_code_) { + return true; + } + if(key_code == KEY_F23) { + listenForKeyNames = true; return true; } return false; diff --git a/src/ble/ctrlm_ble_controller.h b/src/ble/ctrlm_ble_controller.h index ea92386..7dc3910 100644 --- a/src/ble/ctrlm_ble_controller.h +++ b/src/ble/ctrlm_ble_controller.h @@ -138,7 +138,7 @@ class ctrlm_obj_controller_ble_t : public ctrlm_obj_controller_t { void print_status(); virtual bool is_stale(time_t stale_time_threshold) const; - bool isVoiceKey(uint16_t key_code) const; + bool isVoiceKey(uint16_t key_code, bool &listenForKeyNames) const; void setPressAndHoldSupport(bool supported); bool getPressAndHoldSupport() const; diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index 6527601..4f6205e 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -2108,8 +2108,9 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { ctrlm_obj_controller_ble_t *controller = controllers_[controller_id]; if (key_status == CTRLM_KEY_STATUS_DOWN) { + bool listenForKeyNames = false; - if (controller->isVoiceKey(dqm->event.code)) { + if (controller->isVoiceKey(dqm->event.code, listenForKeyNames)) { rdkx_timestamp_t keyDownTime; keyDownTime.tv_sec = dqm->event.time.tv_sec; keyDownTime.tv_nsec = dqm->event.time.tv_usec * 1000; @@ -2129,6 +2130,10 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { msg.params = &v_params; req_process_voice_session_begin(&msg, sizeof(msg)); + + if (listenForKeyNames) { + XLOGD_WARN("listening for key names in the voice session"); + } } if (controller->getUpgradeInProgress()) { @@ -2163,8 +2168,8 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { } } else if (key_status == CTRLM_KEY_STATUS_UP) { - - if (controller->isVoiceKey(dqm->event.code)) { + bool listenForKeyNames = false; + if (controller->isVoiceKey(dqm->event.code, listenForKeyNames)) { if(!controller->getPressAndHoldSupport()) { // if the voice session is "Press and Release" then don't end session on voice key up event XLOGD_INFO("------------------------------------------------------------------------"); XLOGD_INFO("CODE_VOICE_KEY button RELEASED event for device: %s (ignored for PAR session)", controller->ieee_address_get().to_string().c_str()); diff --git a/src/input_event/ctrlm_input_event_writer.cpp b/src/input_event/ctrlm_input_event_writer.cpp index c463a78..98b4714 100644 --- a/src/input_event/ctrlm_input_event_writer.cpp +++ b/src/input_event/ctrlm_input_event_writer.cpp @@ -146,6 +146,25 @@ uint16_t ctrlm_input_event_writer::write_event(ctrlm_key_code_t code, ctrlm_key_ return param.key_code; } +uint16_t ctrlm_input_event_writer::write_event_linux(uint16_t code, ctrlm_key_status_t status) { + if (!initialized_) { + XLOGD_ERROR("User input device is not yet initialized!"); + return KEY_RESERVED; + } + + if (ev_key_value_map.find(status) == ev_key_value_map.end()) { + XLOGD_ERROR("Key status <%d> not found in mapping", status); + return KEY_RESERVED; + } + + // TODO the scan code may need to vary based on the key code + if (!write_event_internal(0, code, ev_key_value_map.at(status))) { + return KEY_RESERVED; + } + + return code; +} + bool ctrlm_input_event_writer::get_meta_data(struct stat &file_meta_data) { int ret = fstat(fd_, &file_meta_data); if (ret == -1) { diff --git a/src/input_event/ctrlm_input_event_writer.h b/src/input_event/ctrlm_input_event_writer.h index 487ae57..099496f 100644 --- a/src/input_event/ctrlm_input_event_writer.h +++ b/src/input_event/ctrlm_input_event_writer.h @@ -117,6 +117,7 @@ class ctrlm_input_event_writer { bool init(std::string uinput_name, uint32_t vendor, uint32_t product); void shutdown(void); uint16_t write_event(ctrlm_key_code_t code, ctrlm_key_status_t status); + uint16_t write_event_linux(uint16_t code, ctrlm_key_status_t status); bool get_meta_data(struct stat &file_meta_data); }; diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index ca5deb2..ab80c1e 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "include/ctrlm_ipc.h" #include "include/ctrlm_ipc_voice.h" #include "ctrlm_voice_obj.h" @@ -256,6 +257,8 @@ ctrlm_voice_t::ctrlm_voice_t() { } #endif + init_uinput_writer(); + // Set audio mode to default ctrlm_voice_audio_settings_t settings = CTRLM_VOICE_AUDIO_SETTINGS_INITIALIZER; this->set_audio_mode(&settings); @@ -293,6 +296,8 @@ ctrlm_voice_t::~ctrlm_voice_t() { } } + uinput_writer.shutdown(); + #ifdef BEEP_ON_KWD_ENABLED if(this->sap_opened) { if(!this->obj_sap->close()) { @@ -2356,6 +2361,11 @@ bool ctrlm_voice_t::voice_stb_data_bypass_wuw_verify_failure_get() const { return(this->prefs.vrex_wuw_bypass_failure_flag); } +bool ctrlm_voice_t::voice_stb_data_listen_for_key_names_get() const { + return(true); + //return(this->prefs.vrex_wuw_bypass_failure_flag); +} + void ctrlm_voice_t::voice_stb_data_pii_mask_set(bool mask_pii) { if(this->mask_pii != mask_pii) { this->mask_pii = mask_pii; @@ -3061,6 +3071,26 @@ void ctrlm_voice_t::voice_action_tv_volume_callback(bool up, uint32_t repeat_cou session->status.data.tv_avr.cmd = up ? CTRLM_VOICE_TV_AVR_CMD_VOLUME_UP : CTRLM_VOICE_TV_AVR_CMD_VOLUME_DOWN; session->status.data.tv_avr.ir_repeats = repeat_count; } + +void ctrlm_voice_t::voice_action_key_code_callback(uint16_t key_code) { + + XLOGD_WARN("DAVE emit key code <%s>", ctrlm_linux_key_code_str(key_code, false)); + + uint16_t linux_code = uinput_writer.write_event_linux(key_code, CTRLM_KEY_STATUS_DOWN); + + if(linux_code == KEY_RESERVED) { + XLOGD_ERROR("Something went wrong while trying to inject the %s key DOWN", ctrlm_linux_key_code_str(key_code, false)); + return; + } + + linux_code = uinput_writer.write_event_linux(key_code, CTRLM_KEY_STATUS_UP); + + if(linux_code == KEY_RESERVED) { + XLOGD_ERROR("Something went wrong while trying to inject the %s key UP", ctrlm_linux_key_code_str(key_code, false)); + return; + } +} + void ctrlm_voice_t::voice_action_keyword_verification_callback(const uuid_t uuid, bool success, rdkx_timestamp_t timestamp) { // Get session based on uuid ctrlm_voice_session_t *session = voice_session_from_uuid(uuid); @@ -4293,3 +4323,16 @@ void ctrlm_voice_t::url_hostname_patterns(const std::vector &obj_se this->url_hostname_pattern_add(itr.c_str()); } } + +bool ctrlm_voice_t::init_uinput_writer() { + bool ret = false; + + std::string uinput_name = "ctrlm"; + ret = uinput_writer.init(uinput_name, 0x75F, 0x9); + if (!ret) { + XLOGD_ERROR("failed to initialize a uinput device"); + return ret; + } + + return ret; +} \ No newline at end of file diff --git a/src/voice/ctrlm_voice_obj.h b/src/voice/ctrlm_voice_obj.h index 0bf34a8..dfa2ec3 100644 --- a/src/voice/ctrlm_voice_obj.h +++ b/src/voice/ctrlm_voice_obj.h @@ -36,6 +36,7 @@ #include "ctrlm_rfc.h" #include "xrsr.h" #include "ctrlm_voice_telemetry_events.h" +#include "ctrlm_input_event_writer.h" #ifdef BEEP_ON_KWD_ENABLED #include "ctrlm_thunder_plugin_system_audio_player.h" @@ -528,6 +529,7 @@ class ctrlm_voice_t { bool voice_stb_data_test_get() const; bool voice_stb_data_bypass_wuw_verify_success_get() const; bool voice_stb_data_bypass_wuw_verify_failure_get() const; + bool voice_stb_data_listen_for_key_names_get() const; virtual void voice_stb_data_pii_mask_set(bool mask_pii); bool voice_stb_data_pii_mask_get() const; virtual bool voice_stb_data_device_certificate_set(ctrlm_voice_cert_t &device_cert, bool &ocsp_verify_stapling, bool &ocsp_verify_ca); @@ -606,6 +608,7 @@ class ctrlm_voice_t { virtual void voice_action_tv_mute_callback(bool mute); virtual void voice_action_tv_power_callback(bool power, bool toggle); virtual void voice_action_tv_volume_callback(bool up, uint32_t repeat_count); + virtual void voice_action_key_code_callback(uint16_t key_code); virtual void voice_action_keyword_verification_callback(const uuid_t uuid, bool success, rdkx_timestamp_t timestamp); virtual void voice_server_return_code_callback(const uuid_t uuid, const char *reason, long ret_code); virtual void voice_session_transcription_callback(const uuid_t uuid, const char *transcription); @@ -650,6 +653,8 @@ class ctrlm_voice_t { void voice_device_enable(ctrlm_voice_device_t device, bool db_update, bool *update_routes); void voice_device_disable(ctrlm_voice_device_t device, bool db_update, bool *update_routes); + bool init_uinput_writer(); + protected: // STB Data std::string software_version; @@ -682,7 +687,8 @@ class ctrlm_voice_t { bool device_requires_stb_data[CTRLM_VOICE_DEVICE_INVALID + 1]; std::vector endpoints; std::vector > query_strs_ptt; - + ctrlm_input_event_writer uinput_writer; + private: bool xrsr_opened; ctrlm_voice_ipc_t *voice_ipc; diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp index bc63ae2..27677fd 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp +++ b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.cpp @@ -109,6 +109,7 @@ bool ctrlm_voice_endpoint_ws_nextgen_t::open() { .test_flag = this->voice_obj->voice_stb_data_test_get(), .bypass_wuw_verify_success = this->voice_obj->voice_stb_data_bypass_wuw_verify_success_get(), .bypass_wuw_verify_failure = this->voice_obj->voice_stb_data_bypass_wuw_verify_failure_get(), + .listen_for_key_names = this->voice_obj->voice_stb_data_listen_for_key_names_get(), .mask_pii = ctrlm_is_pii_mask_enabled(), .user_data = (void *)this }; @@ -154,6 +155,7 @@ bool ctrlm_voice_endpoint_ws_nextgen_t::get_handlers(xrsr_handlers_t *handlers) handlers_xrsv.tv_mute = &ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_tv_mute; handlers_xrsv.tv_power = &ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_tv_power; handlers_xrsv.tv_volume = &ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_tv_volume; + handlers_xrsv.key_code = &ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_key_code; handlers_xrsv.msg = &ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_server_message; if(!xrsv_ws_nextgen_handlers(this->xrsv_obj_ws_nextgen, &handlers_xrsv, handlers)) { @@ -628,6 +630,11 @@ void ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_server_me endpoint->voice_obj->server_message(msg, length); } +void ctrlm_voice_endpoint_ws_nextgen_t::ctrlm_voice_handler_ws_nextgen_key_code(uint16_t key_code, void *user_data) { + ctrlm_voice_endpoint_ws_nextgen_t *endpoint = (ctrlm_voice_endpoint_ws_nextgen_t *)user_data; + endpoint->voice_obj->voice_action_key_code_callback(key_code); +} + // End Function Implementations // Static Helper Functions diff --git a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h index 16667f2..0095647 100644 --- a/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h +++ b/src/voice/endpoints/ctrlm_voice_endpoint_ws_nextgen.h @@ -69,7 +69,8 @@ class ctrlm_voice_endpoint_ws_nextgen_t : public ctrlm_voice_endpoint_t { static void ctrlm_voice_handler_ws_nextgen_tv_mute(bool mute, void *user_data); static void ctrlm_voice_handler_ws_nextgen_tv_power(bool power, bool toggle, void *user_data); static void ctrlm_voice_handler_ws_nextgen_tv_volume(bool up, uint32_t repeat_count, void *user_data); - + static void ctrlm_voice_handler_ws_nextgen_key_code(uint16_t key_code, void *user_data); + protected: void *xrsv_obj_ws_nextgen; diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp index c07f22b..e61391f 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_legacy.cpp @@ -167,7 +167,6 @@ bool ctrlm_voice_ipc_iarm_legacy_t::session_end(const ctrlm_voice_ipc_event_sess } bool ctrlm_voice_ipc_iarm_legacy_t::server_message(const char *message, unsigned long size) { - XLOGD_INFO("Not supported"); return(true); } From d9dddd2c6d957d3f3a3049a0db866d7bbaec7e41 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Tue, 27 May 2025 18:07:41 -0400 Subject: [PATCH 06/15] added routing --- include/ctrlm_ipc_voice.h | 1 + src/ble/ctrlm_ble_network.cpp | 3 ++- src/ctrlm_config_default.json | 1 + src/voice/ctrlm_voice_obj.cpp | 25 +++++++++++++------ src/voice/ctrlm_voice_obj.h | 4 ++- src/voice/ctrlm_voice_obj_generic.cpp | 24 +++++++++++++++++- .../ipc/ctrlm_voice_ipc_iarm_thunder.cpp | 2 +- 7 files changed, 48 insertions(+), 12 deletions(-) diff --git a/include/ctrlm_ipc_voice.h b/include/ctrlm_ipc_voice.h index 1a63c32..6c08c0a 100644 --- a/include/ctrlm_ipc_voice.h +++ b/include/ctrlm_ipc_voice.h @@ -208,6 +208,7 @@ typedef struct { ctrlm_controller_id_t controller_id; ///< A unique identifier of the remote unsigned long long ieee_address; ///< IEEE MAC address of the remote ctrlm_iarm_call_result_t result; ///< OUT - The result of the operation. + unsigned char service_id; } ctrlm_voice_iarm_call_voice_session_t; typedef struct { diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index 4f6205e..f0cb5a8 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -494,7 +494,7 @@ void ctrlm_obj_network_ble_t::req_process_voice_session_begin(void *data, int si controllers_[controller_id]->get_model().c_str(), controllers_[controller_id]->get_sw_revision().to_string().c_str(), controllers_[controller_id]->get_hw_revision().to_string().c_str(), 0.0, - false, NULL, NULL, NULL, true, pressAndHoldSupport); + false, NULL, NULL, NULL, true, pressAndHoldSupport, dqm->params->service_id); if (!controllers_[controller_id]->get_capabilities().has_capability(ctrlm_controller_capabilities_t::capability::PAR) && (VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE == voice_status)) { XLOGD_WARN("PAR voice is enabled but not supported by BLE controller treating as normal voice session"); voice_status = VOICE_SESSION_RESPONSE_AVAILABLE; @@ -2123,6 +2123,7 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { ctrlm_voice_iarm_call_voice_session_t v_params; v_params.ieee_address = dqm->ieee_address; + v_params.service_id = listenForKeyNames ? 1 : 0; ctrlm_main_queue_msg_voice_session_t msg; errno_t safec_rc = memset_s(&msg, sizeof(msg), 0, sizeof(msg)); diff --git a/src/ctrlm_config_default.json b/src/ctrlm_config_default.json index 8d536a0..bad418f 100755 --- a/src/ctrlm_config_default.json +++ b/src/ctrlm_config_default.json @@ -237,6 +237,7 @@ "url_src_ptt" : "", "url_src_ff" : "", "url_src_mic_tap" : "", + "url_src_key_listen" : "vrng://localhost:9881/", "vrex_request_timeout" : 30, "enable_sat" : true, "enable_mtls" : true, diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index ab80c1e..85f8d7c 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -82,6 +82,7 @@ ctrlm_voice_t::ctrlm_voice_t() { session->network_type = CTRLM_NETWORK_TYPE_INVALID; session->controller_id = CTRLM_MAIN_CONTROLLER_ID_INVALID; session->voice_device = CTRLM_VOICE_DEVICE_INVALID; + session->service_id = 0; session->format.type = CTRLM_VOICE_FORMAT_INVALID; session->server_ret_code = 0; @@ -148,6 +149,7 @@ ctrlm_voice_t::ctrlm_voice_t() { #ifdef CTRLM_LOCAL_MIC_TAP this->prefs.server_url_src_mic_tap = JSON_STR_VALUE_VOICE_URL_SRC_MIC_TAP; #endif + this->prefs.server_url_src_key_listen = JSON_STR_VALUE_VOICE_URL_SRC_KEY_LISTEN; #ifdef JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_0 this->url_hostname_pattern_add(JSON_ARRAY_VAL_STR_VOICE_SERVER_HOSTS_0); #endif @@ -430,6 +432,7 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * #ifdef CTRLM_LOCAL_MIC_TAP conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_MIC_TAP, this->prefs.server_url_src_mic_tap); #endif + conf.config_value_get(JSON_STR_NAME_VOICE_URL_SRC_KEY_LISTEN, this->prefs.server_url_src_key_listen); conf.config_value_get(JSON_STR_NAME_VOICE_LANGUAGE, this->prefs.guide_language); conf.config_value_get(JSON_INT_NAME_VOICE_MINIMUM_DURATION, this->prefs.utterance_duration_min); if(conf.config_value_get(JSON_BOOL_NAME_VOICE_ENABLE_SAT, this->sat_token_required)) { @@ -541,6 +544,7 @@ bool ctrlm_voice_t::voice_configure_config_file_json(json_t *obj_voice, json_t * #ifdef CTRLM_LOCAL_MIC_TAP ctrlm_sm_voice_url_mic_tap_read(this->prefs.server_url_src_mic_tap); #endif + // TODO ctrlm_sm_voice_url_key_listen_read(this->prefs.server_url_src_key_listen); ctrlm_sm_voice_sat_enable_read(this->sat_token_required); ctrlm_sm_voice_mtls_enable_read(this->mtls_required); ctrlm_sm_voice_secure_url_required_read(this->secure_url_required); @@ -873,6 +877,7 @@ bool ctrlm_voice_t::voice_configure(json_t *settings, bool db_write) { #ifdef CTRLM_LOCAL_MIC_TAP ctrlm_sm_voice_url_mic_tap_write(this->prefs.server_url_src_mic_tap); #endif + // TODO ctrlm_sm_voice_url_key_listen_write(this->prefs.server_url_src_key_listen); } } } @@ -1352,7 +1357,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net ctrlm_voice_device_t device_type, ctrlm_voice_format_t format, voice_session_req_stream_params *stream_params, const char *controller_name, const char *sw_version, const char *hw_version, double voltage, bool command_status, - ctrlm_timestamp_t *timestamp, ctrlm_voice_session_rsp_confirm_t *cb_confirm, void **cb_confirm_param, bool use_external_data_pipe, bool press_and_hold, const char *l_transcription_in, const char *audio_file_in, const uuid_t *uuid, bool low_latency, bool low_cpu_util, int audio_fd) { + ctrlm_timestamp_t *timestamp, ctrlm_voice_session_rsp_confirm_t *cb_confirm, void **cb_confirm_param, bool use_external_data_pipe, bool press_and_hold, uint8_t service_id, const char *l_transcription_in, const char *audio_file_in, const uuid_t *uuid, bool low_latency, bool low_cpu_util, int audio_fd) { ctrlm_voice_session_t *session = &this->voice_session[voice_device_to_session_group(device_type)]; @@ -1424,7 +1429,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.value.text.text = l_transcription_in; xrsr_audio_format_t xrsr_format = { .type = XRSR_AUDIO_FORMAT_NONE}; - if (false == xrsr_session_request(voice_device_to_xrsr(device_type), xrsr_format, request_params, uuid, false, false)) { + if (false == xrsr_session_request(voice_device_to_xrsr(device_type), service_id, xrsr_format, request_params, uuid, false, false)) { XLOGD_ERROR("Failed to acquire the text-only session from the speech router."); return VOICE_SESSION_RESPONSE_BUSY; } @@ -1444,7 +1449,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net xrsr_format.type = XRSR_AUDIO_FORMAT_PCM; } - if (false == xrsr_session_request(voice_device_to_xrsr(device_type), xrsr_format, request_params, uuid, false, false)) { + if (false == xrsr_session_request(voice_device_to_xrsr(device_type), service_id, xrsr_format, request_params, uuid, false, false)) { XLOGD_ERROR("Failed to acquire the audio file session from the speech router."); return VOICE_SESSION_RESPONSE_BUSY; } @@ -1461,7 +1466,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.type = XRSR_SESSION_REQUEST_TYPE_AUDIO_MIC; request_params.value.audio_mic.stream_params_required = this->nsm_voice_session; - if(false == xrsr_session_request(voice_device_to_xrsr(device_type), xrsr_format, request_params, uuid, low_latency, low_cpu_util)) { + if(false == xrsr_session_request(voice_device_to_xrsr(device_type), service_id, xrsr_format, request_params, uuid, low_latency, low_cpu_util)) { XLOGD_ERROR("Failed to acquire the microphone session from the speech router."); return VOICE_SESSION_RESPONSE_BUSY; } @@ -1490,7 +1495,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.value.audio_fd.callback = (create_pipe) ? NULL : ctrlm_voice_data_post_processing_cb; // RF4CE does not use pipe read callback request_params.value.audio_fd.user_data = (create_pipe) ? NULL : (void *)this; - if(false == xrsr_session_request(voice_device_to_xrsr(device_type), voice_format_to_xrsr(format), request_params, uuid, false, false)) { + if(false == xrsr_session_request(voice_device_to_xrsr(device_type), service_id, voice_format_to_xrsr(format), request_params, uuid, false, false)) { XLOGD_TELEMETRY("Failed to acquire voice session"); this->voice_session_notify_abort(network_id, controller_id, 0, CTRLM_VOICE_SESSION_ABORT_REASON_BUSY); if(create_pipe) { @@ -2362,8 +2367,11 @@ bool ctrlm_voice_t::voice_stb_data_bypass_wuw_verify_failure_get() const { } bool ctrlm_voice_t::voice_stb_data_listen_for_key_names_get() const { - return(true); - //return(this->prefs.vrex_wuw_bypass_failure_flag); + const ctrlm_voice_session_t *session = &this->voice_session[VOICE_SESSION_GROUP_DEFAULT]; + if(session->service_id == 1) { + return(true); + } + return(false); } void ctrlm_voice_t::voice_stb_data_pii_mask_set(bool mask_pii) { @@ -4220,6 +4228,7 @@ void ctrlm_voice_t::voice_rfc_retrieved_handler(const ctrlm_rfc_attr_t& attr) { #ifdef CTRLM_LOCAL_MIC_TAP attr.get_rfc_value(JSON_STR_NAME_VOICE_URL_SRC_MIC_TAP, this->prefs.server_url_src_mic_tap); #endif + attr.get_rfc_value(JSON_STR_NAME_VOICE_URL_SRC_KEY_LISTEN, this->prefs.server_url_src_key_listen); // Check if enabled if(!enabled) { for(int i = CTRLM_VOICE_DEVICE_PTT; i < CTRLM_VOICE_DEVICE_INVALID; i++) { @@ -4335,4 +4344,4 @@ bool ctrlm_voice_t::init_uinput_writer() { } return ret; -} \ No newline at end of file +} diff --git a/src/voice/ctrlm_voice_obj.h b/src/voice/ctrlm_voice_obj.h index dfa2ec3..3d98c5b 100644 --- a/src/voice/ctrlm_voice_obj.h +++ b/src/voice/ctrlm_voice_obj.h @@ -285,6 +285,7 @@ typedef struct { #ifdef CTRLM_LOCAL_MIC_TAP std::string server_url_src_mic_tap; #endif + std::string server_url_src_key_listen; std::vector server_hosts; std::string aspect_ratio; std::string guide_language; @@ -412,6 +413,7 @@ typedef struct { double controller_voltage; bool controller_command_status; ctrlm_voice_device_t voice_device; + uint8_t service_id; ctrlm_voice_format_t format; long server_ret_code; std::string server_message; @@ -484,7 +486,7 @@ class ctrlm_voice_t { ctrlm_voice_t(); virtual ~ctrlm_voice_t(); - ctrlm_voice_session_response_status_t voice_session_req(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_voice_device_t device_type, ctrlm_voice_format_t format, voice_session_req_stream_params *stream_params, const char *controller_name, const char *sw_version, const char *hw_version, double voltage, bool command_status=false, ctrlm_timestamp_t *timestamp=NULL, ctrlm_voice_session_rsp_confirm_t *cb_confirm=NULL, void **cb_confirm_param=NULL, bool use_external_data_pipe=false, bool press_and_hold=true, const char *transcription_in=NULL, const char *audio_file_in=NULL, const uuid_t *uuid = NULL, bool low_latency=false, bool low_cpu_util=false, int audio_fd = -1); + ctrlm_voice_session_response_status_t voice_session_req(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, ctrlm_voice_device_t device_type, ctrlm_voice_format_t format, voice_session_req_stream_params *stream_params, const char *controller_name, const char *sw_version, const char *hw_version, double voltage, bool command_status=false, ctrlm_timestamp_t *timestamp=NULL, ctrlm_voice_session_rsp_confirm_t *cb_confirm=NULL, void **cb_confirm_param=NULL, bool use_external_data_pipe=false, bool press_and_hold=true, uint8_t service_id = 0, const char *transcription_in=NULL, const char *audio_file_in=NULL, const uuid_t *uuid = NULL, bool low_latency=false, bool low_cpu_util=false, int audio_fd = -1); void voice_session_rsp_confirm(bool result, signed long long rsp_time, unsigned int rsp_window, const std::string &err_str, ctrlm_timestamp_t *timestamp); bool voice_session_data(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, const char *buffer, long unsigned int length, ctrlm_timestamp_t *timestamp=NULL, uint8_t *lqi=NULL); bool voice_session_data(ctrlm_network_id_t network_id, ctrlm_controller_id_t controller_id, int fd, const uuid_t *uuid=NULL); diff --git a/src/voice/ctrlm_voice_obj_generic.cpp b/src/voice/ctrlm_voice_obj_generic.cpp index 9ad3235..8033182 100644 --- a/src/voice/ctrlm_voice_obj_generic.cpp +++ b/src/voice/ctrlm_voice_obj_generic.cpp @@ -248,8 +248,30 @@ void ctrlm_voice_generic_t::voice_sdk_update_routes() { routes[i].dsts[0].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; } #endif - i++; + XLOGD_INFO("url translation from %s to %s", url->c_str(), urls_translated[translated_index].c_str()); + + if(src == XRSR_SRC_RCU_PTT) { // Key Name Server + routes[i].dst_qty = 2; + std::string url_key_name = this->prefs.server_url_src_key_listen; + urls_translated.push_back("ws" + url_key_name.substr(4)); + translated_index++; + + routes[i].dsts[1].url = urls_translated[translated_index].c_str(); + routes[i].dsts[1].handlers = handlers_xrsr; + routes[i].dsts[1].formats = XRSR_AUDIO_FORMAT_PCM; + routes[i].dsts[1].stream_time_min = 0; + routes[i].dsts[1].stream_from = stream_from; + routes[i].dsts[1].stream_offset = stream_offset; + routes[i].dsts[1].stream_until = stream_until; + #ifdef DEEP_SLEEP_ENABLED + if(src == XRSR_SRC_MICROPHONE) { + routes[i].dsts[1].params[XRSR_POWER_MODE_LOW] = &this->prefs.dst_params_standby; + } + #endif + XLOGD_INFO("url translation from %s to %s", url_key_name.c_str(), urls_translated[translated_index].c_str()); + } + i++; } } else if(url->rfind("aows", 0) == 0) { // Audio only with no server protocol layer over websocket if(url->rfind("aowss", 0) != 0) { diff --git a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp index 7a80ad0..fdd75c7 100644 --- a/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp +++ b/src/voice/ipc/ctrlm_voice_ipc_iarm_thunder.cpp @@ -784,7 +784,7 @@ IARM_Result_t ctrlm_voice_ipc_iarm_thunder_t::voice_session_request(void *data) ctrlm_voice_session_response_status_t voice_status = voice_obj->voice_session_req( CTRLM_MAIN_NETWORK_ID_INVALID, CTRLM_MAIN_CONTROLLER_ID_INVALID, request_config.device, request_config.format, NULL, "APPLICATION", "0.0.0.0", "0.0.0.0", 0.0, - false, NULL, NULL, NULL, (fd >= 0) ? true : false, true, str_transcription.empty() ? NULL : str_transcription.c_str(), str_audio_file.empty() ? NULL : str_audio_file.c_str(), &request_uuid, request_config.low_latency, request_config.low_cpu_util, fd); + false, NULL, NULL, NULL, (fd >= 0) ? true : false, true, 0, str_transcription.empty() ? NULL : str_transcription.c_str(), str_audio_file.empty() ? NULL : str_audio_file.c_str(), &request_uuid, request_config.low_latency, request_config.low_cpu_util, fd); if (voice_status != VOICE_SESSION_RESPONSE_AVAILABLE && voice_status != VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE) { XLOGD_ERROR("Failed opening voice session <%s>", ctrlm_voice_session_response_status_str(voice_status)); From 71d24066286aa561d4484719c7e3e6637fb08db0 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Thu, 29 May 2025 13:43:05 -0400 Subject: [PATCH 07/15] added systemd notify --- src/server/CMakeLists.txt | 2 +- src/server/ctrlms_main.c | 4 ++++ src/server/ctrlms_ws.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 7f21cc8..6382889 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -38,7 +38,7 @@ target_sources(controlServer PRIVATE target_compile_options(controlServer PUBLIC -fPIC -rdynamic -Wall -Werror) -target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper ${CMAKE_DL_LIBS}) +target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper systemd ${CMAKE_DL_LIBS}) #if(CUSTOM_AUTH_LIB) # add_compile_definitions(PRIVATE CTRLMS_WSS_ENABLED) diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c index a091955..c34bd27 100644 --- a/src/server/ctrlms_main.c +++ b/src/server/ctrlms_main.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -63,6 +65,8 @@ int main(int argc, char* argv[]) { if(!ctrlms_ws_init(CTRLMS_WS_PORT_INT, true)) { XLOGD_ERROR("ctrlms_main: ws init failed"); } else { + XLOGD_INFO("Notifying systemd of successful initialization"); + sd_notifyf(0, "READY=1\nSTATUS=ctrlm-server has successfully initialized\nMAINPID=%lu", (unsigned long)getpid()); ctrlms_ws_listen(); ctrlms_ws_term(); } diff --git a/src/server/ctrlms_ws.cpp b/src/server/ctrlms_ws.cpp index 680cd92..d474e23 100644 --- a/src/server/ctrlms_ws.cpp +++ b/src/server/ctrlms_ws.cpp @@ -467,7 +467,7 @@ void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * typedef ctrlms_app_interface_t *(*ctrlms_app_interface_create_t)(void); void *ctrlms_ws_load_app(ctrlms_ws_thread_state_t *state) { - void *handle = dlopen("libctrlm_server_app.so", RTLD_NOW); + void *handle = dlopen("libctrlm-server-app.so", RTLD_NOW); if(NULL == handle) { XLOGD_WARN("failed to load server app plugin <%s>. Using stub implementation.", dlerror()); From 6ffad2fba2dc90ecc7f3fdc1a06d19fde7705ffd Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Thu, 29 May 2025 13:43:05 -0400 Subject: [PATCH 08/15] silenced repeat keys --- src/ctrlm_controller.cpp | 3 +++ src/server/CMakeLists.txt | 2 +- src/server/ctrlms_main.c | 4 ++++ src/server/ctrlms_ws.cpp | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ctrlm_controller.cpp b/src/ctrlm_controller.cpp index e52430f..71522e4 100644 --- a/src/ctrlm_controller.cpp +++ b/src/ctrlm_controller.cpp @@ -176,11 +176,14 @@ void ctrlm_obj_controller_t::process_event_key(ctrlm_key_status_t key_status, ui last_key_code_->set_value((uint64_t)key_code); last_key_time_update(); + // TODO This is a temporary hack to avoid printing REPEAT keys for the secondary voice button + if(key_status != CTRLM_KEY_STATUS_REPEAT) { XLOGD_TELEMETRY("ind_process_keypress: %s - MAC Address <%s>, code = <%d> (%s key), status = <%s>", controller_type_str_get().c_str(), ieee_address_get().to_string().c_str(), mask ? -1 : key_code, ctrlm_linux_key_code_str(key_code, mask), ctrlm_key_status_str(key_status)); + } } ctrlm_controller_capabilities_t ctrlm_obj_controller_t::get_capabilities() const { diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 7f21cc8..6382889 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -38,7 +38,7 @@ target_sources(controlServer PRIVATE target_compile_options(controlServer PUBLIC -fPIC -rdynamic -Wall -Werror) -target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper ${CMAKE_DL_LIBS}) +target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper systemd ${CMAKE_DL_LIBS}) #if(CUSTOM_AUTH_LIB) # add_compile_definitions(PRIVATE CTRLMS_WSS_ENABLED) diff --git a/src/server/ctrlms_main.c b/src/server/ctrlms_main.c index a091955..c34bd27 100644 --- a/src/server/ctrlms_main.c +++ b/src/server/ctrlms_main.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -63,6 +65,8 @@ int main(int argc, char* argv[]) { if(!ctrlms_ws_init(CTRLMS_WS_PORT_INT, true)) { XLOGD_ERROR("ctrlms_main: ws init failed"); } else { + XLOGD_INFO("Notifying systemd of successful initialization"); + sd_notifyf(0, "READY=1\nSTATUS=ctrlm-server has successfully initialized\nMAINPID=%lu", (unsigned long)getpid()); ctrlms_ws_listen(); ctrlms_ws_term(); } diff --git a/src/server/ctrlms_ws.cpp b/src/server/ctrlms_ws.cpp index 680cd92..d474e23 100644 --- a/src/server/ctrlms_ws.cpp +++ b/src/server/ctrlms_ws.cpp @@ -467,7 +467,7 @@ void ctrlms_ws_nopoll_log(noPollCtx * ctx, noPollDebugLevel level, const char * typedef ctrlms_app_interface_t *(*ctrlms_app_interface_create_t)(void); void *ctrlms_ws_load_app(ctrlms_ws_thread_state_t *state) { - void *handle = dlopen("libctrlm_server_app.so", RTLD_NOW); + void *handle = dlopen("libctrlm-server-app.so", RTLD_NOW); if(NULL == handle) { XLOGD_WARN("failed to load server app plugin <%s>. Using stub implementation.", dlerror()); From 0f1292270a5ac5c66361ab21aa99186f3cb90eb3 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 30 May 2025 07:17:56 -0400 Subject: [PATCH 09/15] Added low latency --- src/ble/ctrlm_ble_network.cpp | 2 +- src/voice/ctrlm_voice_obj.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index f0cb5a8..ba4186c 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -494,7 +494,7 @@ void ctrlm_obj_network_ble_t::req_process_voice_session_begin(void *data, int si controllers_[controller_id]->get_model().c_str(), controllers_[controller_id]->get_sw_revision().to_string().c_str(), controllers_[controller_id]->get_hw_revision().to_string().c_str(), 0.0, - false, NULL, NULL, NULL, true, pressAndHoldSupport, dqm->params->service_id); + false, NULL, NULL, NULL, true, pressAndHoldSupport, dqm->params->service_id, NULL, NULL, NULL, (dqm->params->service_id == 1) ? true : false); if (!controllers_[controller_id]->get_capabilities().has_capability(ctrlm_controller_capabilities_t::capability::PAR) && (VOICE_SESSION_RESPONSE_AVAILABLE_PAR_VOICE == voice_status)) { XLOGD_WARN("PAR voice is enabled but not supported by BLE controller treating as normal voice session"); voice_status = VOICE_SESSION_RESPONSE_AVAILABLE; diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index 85f8d7c..e5de304 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -1471,7 +1471,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net return VOICE_SESSION_RESPONSE_BUSY; } } else { - XLOGD_INFO("Requesting the speech router start a session with audio fd <%d>", fds[PIPE_READ]); + XLOGD_INFO("Requesting the speech router start a session with audio fd <%d> low latency <%s>", fds[PIPE_READ], low_latency ? "YES" : "NO"); if(session->state_src == CTRLM_VOICE_STATE_SRC_STREAMING || ((session->state_dst != CTRLM_VOICE_STATE_DST_READY) && (session->state_dst != CTRLM_VOICE_STATE_DST_OPENED))) { // DST_OPENED occurs when the session is in progress and more audio is requested XLOGD_ERROR("unable to accept an audio fd session due to current session - state src <%s> dst <%s>.", ctrlm_voice_state_src_str(session->state_src), ctrlm_voice_state_dst_str(session->state_dst)); return VOICE_SESSION_RESPONSE_BUSY; @@ -1495,7 +1495,7 @@ ctrlm_voice_session_response_status_t ctrlm_voice_t::voice_session_req(ctrlm_net request_params.value.audio_fd.callback = (create_pipe) ? NULL : ctrlm_voice_data_post_processing_cb; // RF4CE does not use pipe read callback request_params.value.audio_fd.user_data = (create_pipe) ? NULL : (void *)this; - if(false == xrsr_session_request(voice_device_to_xrsr(device_type), service_id, voice_format_to_xrsr(format), request_params, uuid, false, false)) { + if(false == xrsr_session_request(voice_device_to_xrsr(device_type), service_id, voice_format_to_xrsr(format), request_params, uuid, low_latency, false)) { XLOGD_TELEMETRY("Failed to acquire voice session"); this->voice_session_notify_abort(network_id, controller_id, 0, CTRLM_VOICE_SESSION_ABORT_REASON_BUSY); if(create_pipe) { From 0b5f26e85a39ba2b1c93c97dd24d4ad7793dd05a Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 30 May 2025 08:06:17 -0400 Subject: [PATCH 10/15] increase pipe size. ignore voice key up --- src/ble/ctrlm_ble_network.cpp | 3 ++- src/ble/hal/blercu/bleservices/gatt/gatt_audiopipe.cpp | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index ba4186c..bcb2ee7 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -2171,7 +2171,8 @@ void ctrlm_obj_network_ble_t::ind_process_keypress(void *data, int size) { } else if (key_status == CTRLM_KEY_STATUS_UP) { bool listenForKeyNames = false; if (controller->isVoiceKey(dqm->event.code, listenForKeyNames)) { - if(!controller->getPressAndHoldSupport()) { // if the voice session is "Press and Release" then don't end session on voice key up event + // TODO This needs to check if the voice session is "Press and Hold" or "Press and Release" based on the key code + if(!controller->getPressAndHoldSupport() || dqm->event.code == KEY_F23) { // if the voice session is "Press and Release" then don't end session on voice key up event XLOGD_INFO("------------------------------------------------------------------------"); XLOGD_INFO("CODE_VOICE_KEY button RELEASED event for device: %s (ignored for PAR session)", controller->ieee_address_get().to_string().c_str()); XLOGD_INFO("------------------------------------------------------------------------"); diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audiopipe.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_audiopipe.cpp index 85009b1..d36a1d0 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audiopipe.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audiopipe.cpp @@ -117,6 +117,14 @@ GattAudioPipe::GattAudioPipe(uint8_t frameSize, uint32_t frameCountMax, cbFrameV m_outputPipeWrFd = fds[1]; } + // Set the pipe size + uint32_t size = 256 * 1024; + + int rc = fcntl(m_outputPipeWrFd, F_SETPIPE_SZ, size); + if(rc < (int)size) { // emit a warning if the kernel returns a pipe size smaller than we requested + XLOGD_WARN("set pipe size failed exp <%u> rxd <%d>", size, rc); + } + } // ----------------------------------------------------------------------------- From b074ea38dc21616f121c85927eb65e655c490595 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 30 May 2025 08:28:39 -0400 Subject: [PATCH 11/15] increase interpacket timeout. print sizes in error message. --- src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp | 2 +- src/ctrlm_config_default.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp index 4a3e359..ec9819d 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp @@ -620,7 +620,7 @@ void GattAudioService::stopStreaming(uint32_t audioDuration, PendingReply<> &&re } if(m_missedSequences >= frameCountMax) { - XLOGD_ERROR("missed frames greater than frame count max"); + XLOGD_ERROR("missed frames <%u> greater than frame count max <%u>", m_missedSequences, frameCountMax); } else { frameCountMax -= m_missedSequences; // compensate for missed frames diff --git a/src/ctrlm_config_default.json b/src/ctrlm_config_default.json index bad418f..0c4755b 100755 --- a/src/ctrlm_config_default.json +++ b/src/ctrlm_config_default.json @@ -246,7 +246,7 @@ "minimum_duration" : 300, "ffv_leading_samples" : 1600, "timeout_packet_initial" : 3200, - "timeout_packet_subsequent" : 250, + "timeout_packet_subsequent" : 1000, "bitrate_minimum" : 64, "time_threshold" : 1000, "timeout_stats" : 3000, From 6a06ebaee5576ee53eabac63f391f68890c29583 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 30 May 2025 11:53:46 -0400 Subject: [PATCH 12/15] Fix timeout packet tag --- src/voice/ctrlm_voice_obj.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index e5de304..be77ce1 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -1693,6 +1693,7 @@ void ctrlm_voice_t::voice_session_data_post_processing(int bytes_sent, const cha if(session->timeout_packet_tag > 0) { g_source_remove(session->timeout_packet_tag); + session->timeout_packet_tag = 0; // QOS timeout handled at end of this function as it depends on time stamps and samples transmitted if(!this->controller_supports_qos(session->voice_device) && bytes_sent != 0) { if(session->network_type == CTRLM_NETWORK_TYPE_IP || session->is_session_by_fifo) { From 80571dc31b1d872c688c7bbf35e70531ac1c7882 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 30 May 2025 14:05:51 -0400 Subject: [PATCH 13/15] Stop audio when session ends. --- src/ble/ctrlm_ble_network.cpp | 11 +++++++++++ src/voice/ctrlm_voice_obj.cpp | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ble/ctrlm_ble_network.cpp b/src/ble/ctrlm_ble_network.cpp index bcb2ee7..434598d 100644 --- a/src/ble/ctrlm_ble_network.cpp +++ b/src/ble/ctrlm_ble_network.cpp @@ -2285,6 +2285,17 @@ void ctrlm_obj_network_ble_t::ind_process_voice_session_end(void *data, int size XLOGD_ERROR("Controller object doesn't exist for controller id %u!", controller_id); return; } + + unsigned long long ieee_address = controllers_[controller_id]->ieee_address_get().get_value();; + + if (ble_rcu_interface_) { + int32_t audioDuration = -1; + if (!ble_rcu_interface_->stopAudioStreaming(ieee_address, audioDuration)) { + XLOGD_ERROR("failed to end voice session for controller id <%u>", controller_id); + } else { + XLOGD_INFO("voice session ended for controller id <%u>", controller_id); + } + } } // ================================================================================================================================================================== diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index be77ce1..94537a4 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -3083,7 +3083,7 @@ void ctrlm_voice_t::voice_action_tv_volume_callback(bool up, uint32_t repeat_cou void ctrlm_voice_t::voice_action_key_code_callback(uint16_t key_code) { - XLOGD_WARN("DAVE emit key code <%s>", ctrlm_linux_key_code_str(key_code, false)); + XLOGD_INFO("emit key code <%s>", ctrlm_linux_key_code_str(key_code, false)); uint16_t linux_code = uinput_writer.write_event_linux(key_code, CTRLM_KEY_STATUS_DOWN); From cceea8a5a56cc77f0ada4d9149f0b60c67affd79 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 30 May 2025 14:46:08 -0400 Subject: [PATCH 14/15] Changed logs to DEBUG --- src/server/ctrlms_ws.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/ctrlms_ws.cpp b/src/server/ctrlms_ws.cpp index d474e23..2c4b8d7 100644 --- a/src/server/ctrlms_ws.cpp +++ b/src/server/ctrlms_ws.cpp @@ -292,7 +292,7 @@ void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPo switch(nopoll_msg_opcode(msg)) { case NOPOLL_TEXT_FRAME: { - XLOGD_INFO("NOPOLL_TEXT_FRAME size <%d>", payload_size); + XLOGD_DEBUG("NOPOLL_TEXT_FRAME size <%d>", payload_size); json_t *json_obj = json_loads((const char *)payload, 0, NULL); @@ -307,7 +307,7 @@ void ctrlms_ws_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPo break; } case NOPOLL_BINARY_FRAME: { - XLOGD_INFO("NOPOLL_BINARY_FRAME size <%d>", payload_size); + XLOGD_DEBUG("NOPOLL_BINARY_FRAME size <%d>", payload_size); // Pass the incoming payload to the application close_conn = state->app_interface->ws_receive_audio(payload, payload_size); From 90098a8bd1d9f1fa8514e2fbede8259aba7f6263 Mon Sep 17 00:00:00 2001 From: dwolav200 Date: Fri, 6 Jun 2025 14:47:20 -0400 Subject: [PATCH 15/15] Updated certificate handling --- src/server/CMakeLists.txt | 19 +++++------------- src/server/ctrlms_ws.cpp | 36 +++++++++++++++++++++-------------- src/voice/ctrlm_voice_obj.cpp | 7 ++----- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 6382889..20d5db9 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -17,9 +17,6 @@ # limitations under the License. ########################################################################## -# This is required to allow an executable to export symbols to be used by loadable modules (e.g. plugins using dlopen) -set(CMAKE_ENABLE_EXPORTS ON) - add_executable(controlServer ctrlms_main.c) include_directories( @@ -40,18 +37,12 @@ target_compile_options(controlServer PUBLIC -fPIC -rdynamic -Wall -Werror) target_link_libraries(controlServer c rdkversion pthread nopoll jansson xr-voice-sdk secure_wrapper systemd ${CMAKE_DL_LIBS}) -#if(CUSTOM_AUTH_LIB) -# add_compile_definitions(PRIVATE CTRLMS_WSS_ENABLED) -# target_link_libraries(controlServer ${CUSTOM_AUTH_LIB}) -#endif() - #if(EXISTS ${CMAKE_SYSROOT}/usr/lib/libctrlm-hal-certificate.so) -# target_link_libraries(controlServer ctrlm-hal-certificate) -#else() -# if(AUTH_ENABLED) -# message(WARNING "ctrlm-hal-certificate library is not provided, disabling authentication") -# unset(AUTH_ENABLED) -# endif() + # TODO This needs to be done in certselector. mountutils recipe with rdkconfig.a static library +# target_link_libraries(controlServer rdkconfig.a) + +# add_compile_definitions(PRIVATE CTRLMS_WSS_ENABLED) +# target_link_libraries(controlServer ctrlm-hal-certificate ssl crypto) #endif() if(USE_SAFEC) diff --git a/src/server/ctrlms_ws.cpp b/src/server/ctrlms_ws.cpp index 2c4b8d7..a0331e0 100644 --- a/src/server/ctrlms_ws.cpp +++ b/src/server/ctrlms_ws.cpp @@ -20,6 +20,7 @@ #ifdef CTRLMS_WSS_ENABLED #include +#include #define CTRLMS_WS_CIPHER_LIST "AES256-SHA256:AES128-GCM-SHA256:AES128-SHA256" #define CTRLMS_WS_TLS_CERT_KEY_FILE "/tmp/serverXXXXXX" #define CTRLMS_WS_CERT_NAME_LEN (1024) @@ -356,9 +357,19 @@ void ctrlms_ws_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) { #ifdef CTRLMS_WSS_ENABLED bool ctrlms_ws_cert_config(FILE* cert_key_fp) { - bool ret = false; - ctrlm_fta_platform_cert_info_t *cert_info = NULL; + + ctrlm_hal_certificate_t *hal_certificate = ctrlm_hal_certificate_get(); + + if(hal_certificate == NULL) { + XLOGD_ERROR("unable to get hal certificate"); + return(false); + } + + ctrlm_voice_cert_t device_cert; + bool ocsp_verify_stapling = false; + bool ocsp_verify_ca = false; + do { FILE *device_cert_fp = NULL; PKCS12 *p12_cert = NULL; @@ -366,18 +377,17 @@ bool ctrlms_ws_cert_config(FILE* cert_key_fp) { X509 *x509_cert = NULL; STACK_OF(X509) *additional_certs = NULL; - cert_info = ctrlm_fta_platform_cert_info_get(false); - if(cert_info == NULL) { - XLOGD_ERROR("unable to get certificate info"); + if(!hal_certificate->device_cert_get(device_cert, ocsp_verify_stapling, ocsp_verify_ca)) { + XLOGD_ERROR("unable to get device certificate"); break; } - if(cert_info->type != CTRLM_FTA_PLATFORM_VOICE_CERT_TYPE_P12) { + if(device_cert.type != CTRLM_VOICE_CERT_TYPE_P12) { XLOGD_ERROR("unable to parse certificates that are not of PKCS12 type"); break; } - device_cert_fp = fopen(cert_info->filename, "rb"); + device_cert_fp = fopen(device_cert.cert.p12.certificate, "rb"); if(device_cert_fp == NULL) { XLOGD_ERROR("unable to open P12 certificate"); break; @@ -392,7 +402,7 @@ bool ctrlms_ws_cert_config(FILE* cert_key_fp) { break; } - if(1 != PKCS12_parse(p12_cert, cert_info->password, &pkey, &x509_cert, &additional_certs)) { + if(1 != PKCS12_parse(p12_cert, device_cert.cert.p12.passphrase, &pkey, &x509_cert, &additional_certs)) { XLOGD_ERROR("unable to parse P12 certificate"); break; } @@ -407,17 +417,15 @@ bool ctrlms_ws_cert_config(FILE* cert_key_fp) { break; } - if(1 != PEM_write_PrivateKey(cert_key_fp, pkey, NULL, (unsigned char*)cert_info->password, strlen(cert_info->password), NULL, NULL)) { + if(1 != PEM_write_PrivateKey(cert_key_fp, pkey, NULL, (unsigned char*)device_cert.cert.p12.passphrase, strlen(device_cert.cert.p12.passphrase), NULL, NULL)) { XLOGD_ERROR("failed to write temp key"); break; - } + } ret = true; }while(0); - if(cert_info != NULL) { - ctrlm_fta_platform_cert_info_free(cert_info); - } + free(hal_certificate); return ret; } @@ -432,7 +440,7 @@ bool ctrlms_ws_add_chain(FILE *cert_key_fp, STACK_OF(X509) *additional_certs) { return false; } - for(uint32_t index = 0; index < sk_X509_num(additional_certs); index++) { + for(int32_t index = 0; index < sk_X509_num(additional_certs); index++) { X509 *cert = sk_X509_value(additional_certs, index); if(1 != PEM_write_X509(cert_key_fp, cert)) { XLOGD_ERROR("failed to write temp cert index %d", index); diff --git a/src/voice/ctrlm_voice_obj.cpp b/src/voice/ctrlm_voice_obj.cpp index 8755c29..b5fb748 100644 --- a/src/voice/ctrlm_voice_obj.cpp +++ b/src/voice/ctrlm_voice_obj.cpp @@ -2368,11 +2368,8 @@ bool ctrlm_voice_t::voice_stb_data_bypass_wuw_verify_failure_get() const { } bool ctrlm_voice_t::voice_stb_data_listen_for_key_names_get() const { - const ctrlm_voice_session_t *session = &this->voice_session[VOICE_SESSION_GROUP_DEFAULT]; - if(session->service_id == 1) { - return(true); - } - return(false); + // Always return true unless the build doesn't support listening for key names + return(true); } void ctrlm_voice_t::voice_stb_data_pii_mask_set(bool mask_pii) {