diff options
Diffstat (limited to 'lib/avtp_pipeline/avdecc_msg/openavb_avdecc_msg_client.c')
-rw-r--r-- | lib/avtp_pipeline/avdecc_msg/openavb_avdecc_msg_client.c | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/lib/avtp_pipeline/avdecc_msg/openavb_avdecc_msg_client.c b/lib/avtp_pipeline/avdecc_msg/openavb_avdecc_msg_client.c new file mode 100644 index 00000000..01c827f3 --- /dev/null +++ b/lib/avtp_pipeline/avdecc_msg/openavb_avdecc_msg_client.c @@ -0,0 +1,723 @@ +/************************************************************************************************************* +Copyright (c) 2012-2015, Symphony Teleca Corporation, a Harman International Industries, Incorporated company +Copyright (c) 2016-2017, Harman International Industries, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS LISTED "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS LISTED BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Attributions: The inih library portion of the source code is licensed from +Brush Technology and Ben Hoyt - Copyright (c) 2009, Brush Technology and Copyright (c) 2009, Ben Hoyt. +Complete license and copyright information can be found at +https://github.com/benhoyt/inih/commit/74d2ca064fb293bc60a77b0bd068075b293cf175. +*************************************************************************************************************/ + +/* +* MODULE SUMMARY : +* +* Stream clients (talkers or listeners) must connect to the central +* "avdecc_msg" process to support AVDECC control. +* This code provides the means for them to do so. +* +* It provides proxy functions for the process to call. The arguments +* for those calls are packed into messages, which are unpacked in the +* avdecc_msg process and then used to call the real functions. +* +* Current IPC uses unix sockets. Can change this by creating a new +* implementations in openavb_avdecc_msg_client.c and openavb_avdecc_msg_server.c +*/ + +#include <stdlib.h> +#include <string.h> + +#include "openavb_platform.h" + +#define AVB_LOG_COMPONENT "AVDECC Msg" +#include "openavb_pub.h" +#include "openavb_log.h" + +#include "openavb_trace.h" +#include "openavb_avdecc_msg_client.h" +#include "openavb_tl.h" +#include "openavb_talker.h" +#include "openavb_listener.h" + +// forward declarations +static bool openavbAvdeccMsgClntReceiveFromServer(int avdeccMsgHandle, openavbAvdeccMessage_t *msg); + +// OSAL specific functions +#include "openavb_avdecc_msg_client_osal.c" + +// AvdeccMsgStateListGet() support. +#include "openavb_avdecc_msg.c" + +static bool openavbAvdeccMsgClntReceiveFromServer(int avdeccMsgHandle, openavbAvdeccMessage_t *msg) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + if (!msg || avdeccMsgHandle == AVB_AVDECC_MSG_HANDLE_INVALID) { + AVB_LOG_ERROR("Client receive; invalid argument passed"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return FALSE; + } + + switch(msg->type) { + case OPENAVB_AVDECC_MSG_VERSION_CALLBACK: + AVB_LOG_DEBUG("Message received: OPENAVB_AVDECC_MSG_VERSION_CALLBACK"); + openavbAvdeccMsgClntCheckVerMatchesSrvr(avdeccMsgHandle, ntohl(msg->params.versionCallback.AVBVersion)); + break; + case OPENAVB_AVDECC_MSG_LISTENER_STREAM_ID: + AVB_LOG_DEBUG("Message received: OPENAVB_AVDECC_MSG_LISTENER_STREAM_ID"); + openavbAvdeccMsgClntHndlListenerStreamIDFromServer(avdeccMsgHandle, + msg->params.listenerStreamID.sr_class, msg->params.listenerStreamID.stream_src_mac, ntohs(msg->params.listenerStreamID.stream_uid), + msg->params.listenerStreamID.stream_dest_mac, ntohs(msg->params.listenerStreamID.stream_vlan_id)); + break; + case OPENAVB_AVDECC_MSG_S2C_TALKER_STREAM_ID: + AVB_LOG_DEBUG("Message received: OPENAVB_AVDECC_MSG_S2C_TALKER_STREAM_ID"); + openavbAvdeccMsgClntHndlTalkerStreamIDFromServer(avdeccMsgHandle, + msg->params.s2cTalkerStreamID.sr_class, + msg->params.s2cTalkerStreamID.stream_id_valid, + msg->params.s2cTalkerStreamID.stream_src_mac, + ntohs(msg->params.s2cTalkerStreamID.stream_uid), + msg->params.s2cTalkerStreamID.stream_dest_valid, + msg->params.s2cTalkerStreamID.stream_dest_mac, + msg->params.s2cTalkerStreamID.stream_vlan_id_valid, + ntohs(msg->params.s2cTalkerStreamID.stream_vlan_id)); + break; + case OPENAVB_AVDECC_MSG_CLIENT_CHANGE_REQUEST: + AVB_LOG_DEBUG("Message received: OPENAVB_AVDECC_MSG_CLIENT_CHANGE_REQUEST"); + openavbAvdeccMsgClntHndlChangeRequestFromServer(avdeccMsgHandle, (openavbAvdeccMsgStateType_t) msg->params.clientChangeRequest.desired_state); + break; + default: + AVB_LOG_ERROR("Client receive: unexpected message"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return FALSE; + } + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return TRUE; +} + +bool openavbAvdeccMsgClntRequestVersionFromServer(int avdeccMsgHandle) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + openavbAvdeccMessage_t msgBuf; + + memset(&msgBuf, 0, OPENAVB_AVDECC_MSG_LEN); + msgBuf.type = OPENAVB_AVDECC_MSG_VERSION_REQUEST; + bool ret = openavbAvdeccMsgClntSendToServer(avdeccMsgHandle, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return ret; +} + +void openavbAvdeccMsgClntCheckVerMatchesSrvr(int avdeccMsgHandle, U32 AVBVersion) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + if (AVBVersion == AVB_CORE_VER_FULL) { + pState->verState = OPENAVB_AVDECC_MSG_VER_VALID; + AVB_LOG_DEBUG("AVDECC Versions Match"); + } + else { + pState->verState = OPENAVB_AVDECC_MSG_VER_INVALID; + AVB_LOG_WARNING("AVDECC Versions Do Not Match"); + } + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); +} + +bool openavbAvdeccMsgClntInitIdentify(int avdeccMsgHandle, const char * friendly_name, U8 talker) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + openavbAvdeccMessage_t msgBuf; + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + memset(&msgBuf, 0, OPENAVB_AVDECC_MSG_LEN); + msgBuf.type = OPENAVB_AVDECC_MSG_CLIENT_INIT_IDENTIFY; + openavbAvdeccMsgParams_ClientInitIdentify_t * pParams = + &(msgBuf.params.clientInitIdentify); + strncpy(pParams->friendly_name, friendly_name, sizeof(pParams->friendly_name)); + pParams->talker = talker; + bool ret = openavbAvdeccMsgClntSendToServer(avdeccMsgHandle, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return ret; +} + +bool openavbAvdeccMsgClntTalkerStreamID(int avdeccMsgHandle, U8 sr_class, const U8 stream_src_mac[6], U16 stream_uid, const U8 stream_dest_mac[6], U16 stream_vlan_id) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + openavbAvdeccMessage_t msgBuf; + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + // Send a default stream_vlan_id value if none specified. + if (stream_vlan_id == 0) { + stream_vlan_id = 2; // SR Class default VLAN Id values per IEEE 802.1Q-2011 Table 9-2 + } + + // Send the stream information to the server. + memset(&msgBuf, 0, OPENAVB_AVDECC_MSG_LEN); + msgBuf.type = OPENAVB_AVDECC_MSG_C2S_TALKER_STREAM_ID; + openavbAvdeccMsgParams_C2S_TalkerStreamID_t * pParams = + &(msgBuf.params.c2sTalkerStreamID); + pParams->sr_class = sr_class; + memcpy(pParams->stream_src_mac, stream_src_mac, 6); + pParams->stream_uid = htons(stream_uid); + memcpy(pParams->stream_dest_mac, stream_dest_mac, 6); + pParams->stream_vlan_id = htons(stream_vlan_id); + bool ret = openavbAvdeccMsgClntSendToServer(avdeccMsgHandle, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return ret; +} + +bool openavbAvdeccMsgClntHndlListenerStreamIDFromServer(int avdeccMsgHandle, U8 sr_class, const U8 stream_src_mac[6], U16 stream_uid, const U8 stream_dest_mac[6], U16 stream_vlan_id) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + if (!pState->pTLState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d state not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + // Determine if the supplied information differs from the current settings. + openavb_tl_cfg_t * pCfg = &(pState->pTLState->cfg); + if (pCfg->sr_class != sr_class || + memcmp(pCfg->stream_addr.buffer.ether_addr_octet, stream_src_mac, 6) != 0 || + pCfg->stream_uid != stream_uid || + memcmp(pCfg->dest_addr.buffer.ether_addr_octet, stream_dest_mac, 6) != 0 || + pCfg->vlan_id != stream_vlan_id) { + // If the Listener is running, stop the Listener before updating the information. + if (pState->pTLState->bRunning) { + AVB_LOG_DEBUG("Forcing Listener to Stop to change streaming settings"); + openavbAvdeccMsgClntHndlChangeRequestFromServer(avdeccMsgHandle, OPENAVB_AVDECC_MSG_STOPPED); + } + + // Update the stream information supplied by the server. + pCfg->sr_class = sr_class; + memcpy(pCfg->stream_addr.buffer.ether_addr_octet, stream_src_mac, 6); + pCfg->stream_addr.mac = &(pCfg->stream_addr.buffer); // Indicate that the MAC Address is valid. + pCfg->stream_uid = stream_uid; + memcpy(pCfg->dest_addr.buffer.ether_addr_octet, stream_dest_mac, 6); + pCfg->dest_addr.mac = &(pCfg->dest_addr.buffer); // Indicate that the MAC Address is valid. + pCfg->vlan_id = stream_vlan_id; + } + + AVB_LOGF_DEBUG("AVDECC-supplied (Listener) sr_class: %u", pCfg->sr_class); + AVB_LOGF_DEBUG("AVDECC-supplied (Listener) stream_id: " ETH_FORMAT "/%u", + ETH_OCTETS(pCfg->stream_addr.buffer.ether_addr_octet), pCfg->stream_uid); + AVB_LOGF_DEBUG("AVDECC-supplied (Listener) dest_addr: " ETH_FORMAT, + ETH_OCTETS(pCfg->dest_addr.buffer.ether_addr_octet)); + AVB_LOGF_DEBUG("AVDECC-supplied (Listener) vlan_id: %u", pCfg->vlan_id); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return true; +} + +bool openavbAvdeccMsgClntHndlTalkerStreamIDFromServer(int avdeccMsgHandle, + U8 sr_class, U8 stream_id_valid, const U8 stream_src_mac[6], U16 stream_uid, U8 stream_dest_valid, const U8 stream_dest_mac[6], U8 stream_vlan_id_valid, U16 stream_vlan_id) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + if (!pState->pTLState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d state not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + // Update the stream information supplied by the server. + openavb_tl_cfg_t * pCfg = &(pState->pTLState->cfg); + pCfg->sr_class = sr_class; + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) sr_class: %u", pCfg->sr_class); + if (stream_id_valid) { + if (memcmp(stream_src_mac, "\x00\x00\x00\x00\x00\x00", 6) == 0 && stream_uid == 0) { + // Restore from the backup value. + if (pCfg->backup_stream_id_valid) { + memcpy(pCfg->stream_addr.buffer.ether_addr_octet, pCfg->backup_stream_addr, 6); + pCfg->stream_addr.mac = &(pCfg->stream_addr.buffer); // Indicate that the MAC Address is valid. + pCfg->stream_uid = pCfg->backup_stream_uid; + pCfg->backup_stream_id_valid = FALSE; + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) reverted to default stream_id: " ETH_FORMAT "/%u", + ETH_OCTETS(pCfg->stream_addr.buffer.ether_addr_octet), pCfg->stream_uid); + } + } + else { + // Backup the current value. + if (!pCfg->backup_stream_id_valid) { + memcpy(pCfg->backup_stream_addr, pCfg->stream_addr.buffer.ether_addr_octet, 6); + pCfg->backup_stream_uid = pCfg->stream_uid; + pCfg->backup_stream_id_valid = TRUE; + } + + // Save the new value. + memcpy(pCfg->stream_addr.buffer.ether_addr_octet, stream_src_mac, 6); + pCfg->stream_addr.mac = &(pCfg->stream_addr.buffer); // Indicate that the MAC Address is valid. + pCfg->stream_uid = stream_uid; + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) stream_id: " ETH_FORMAT "/%u", + ETH_OCTETS(pCfg->stream_addr.buffer.ether_addr_octet), pCfg->stream_uid); + } + } + if (stream_dest_valid) { + if (memcmp(stream_dest_mac, "\x00\x00\x00\x00\x00\x00", 6) == 0) { + // Restore from the backup value. + if (pCfg->backup_dest_addr_valid) { + memcpy(pCfg->dest_addr.buffer.ether_addr_octet, pCfg->backup_dest_addr, 6); + pCfg->dest_addr.mac = &(pCfg->dest_addr.buffer); // Indicate that the MAC Address is valid. + pCfg->backup_dest_addr_valid = FALSE; + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) reverted to default dest_addr: " ETH_FORMAT, + ETH_OCTETS(pCfg->dest_addr.buffer.ether_addr_octet)); + } + } + else { + // Backup the current value. + if (!pCfg->backup_dest_addr_valid) { + memcpy(pCfg->backup_dest_addr, pCfg->dest_addr.buffer.ether_addr_octet, 6); + pCfg->backup_dest_addr_valid = TRUE; + } + + // Save the new value. + memcpy(pCfg->dest_addr.buffer.ether_addr_octet, stream_dest_mac, 6); + pCfg->dest_addr.mac = &(pCfg->dest_addr.buffer); // Indicate that the MAC Address is valid. + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) dest_addr: " ETH_FORMAT, + ETH_OCTETS(pCfg->dest_addr.buffer.ether_addr_octet)); + } + } + if (stream_vlan_id_valid) { + if (stream_vlan_id == 0) { + // Restore from the backup value. + if (pCfg->backup_vlan_id_valid) { + pCfg->vlan_id = pCfg->backup_vlan_id; + pCfg->backup_vlan_id_valid = FALSE; + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) reverted to default vlan_id: %u", pCfg->vlan_id); + } + } + else { + // Backup the current value. + if (!pCfg->backup_vlan_id_valid) { + pCfg->backup_vlan_id_valid = pCfg->vlan_id; + pCfg->backup_vlan_id_valid = TRUE; + } + + // Save the new value. + pCfg->vlan_id = stream_vlan_id; + AVB_LOGF_DEBUG("AVDECC-supplied (Talker) vlan_id: %u", pCfg->vlan_id); + } + } + + // Notify AVDECC of the Talker values to be used after the update. + openavbAvdeccMsgClntTalkerStreamID(avdeccMsgHandle, pCfg->sr_class, pCfg->stream_addr.buffer.ether_addr_octet, pCfg->stream_uid, pCfg->dest_addr.buffer.ether_addr_octet, pCfg->vlan_id); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return true; +} + +bool openavbAvdeccMsgClntHndlChangeRequestFromServer(int avdeccMsgHandle, openavbAvdeccMsgStateType_t desiredState) +{ + bool ret = false; + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState || !pState->pTLState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + switch (desiredState) { + case OPENAVB_AVDECC_MSG_STOPPED: + // Stop requested. + AVB_LOGF_DEBUG("Stop state requested for client %d", avdeccMsgHandle); + if (pState->pTLState->bRunning) { + if (openavbTLStop((tl_handle_t) pState->pTLState)) { + // NOTE: openavbTLStop() call will cause client change notification if successful. + AVB_LOGF_INFO("Client %d state changed to Stopped", avdeccMsgHandle); + ret = true; + } else { + // Notify server of issues. + AVB_LOGF_ERROR("Unable to change client %d state to Stopped", avdeccMsgHandle); + openavbAvdeccMsgClntChangeNotification(avdeccMsgHandle, OPENAVB_AVDECC_MSG_UNKNOWN); + } + } else { + // Notify server we are already in this state. + AVB_LOGF_INFO("Client %d state is already at Stopped", avdeccMsgHandle); + openavbAvdeccMsgClntChangeNotification(avdeccMsgHandle, OPENAVB_AVDECC_MSG_STOPPED); + ret = true; + } + break; + + case OPENAVB_AVDECC_MSG_RUNNING: + // Run requested. + AVB_LOGF_DEBUG("Run state requested for client %d", avdeccMsgHandle); + if (!(pState->pTLState->bRunning)) { + if (openavbTLRun((tl_handle_t) pState->pTLState)) { + // NOTE: openavbTLRun() call will cause client change notification if successful. + AVB_LOGF_INFO("Client %d state changed to Running", avdeccMsgHandle); + ret = true; + } else { + // Notify server of issues. + AVB_LOGF_ERROR("Unable to change client %d state to Running", avdeccMsgHandle); + openavbAvdeccMsgClntChangeNotification(avdeccMsgHandle, OPENAVB_AVDECC_MSG_UNKNOWN); + } + } + else if (pState->pTLState->bRunning && pState->pTLState->bPaused) { + if (pState->pTLState->cfg.role == AVB_ROLE_TALKER) { + openavbTLPauseTalker(pState->pTLState, FALSE); + // NOTE: openavbTLPauseTalker() call will cause Talker change notification. + AVB_LOGF_INFO("Talker %d state changed from Paused to Running", avdeccMsgHandle); + ret = true; + } else { + openavbTLPauseListener(pState->pTLState, FALSE); + // NOTE: openavbTLPauseListener() call will cause Listener change notification. + AVB_LOGF_INFO("Listener %d state changed from Paused to Running", avdeccMsgHandle); + ret = true; + } + } + else { + // Notify server we are already in this state. + AVB_LOGF_INFO("Client %d state is already at Running", avdeccMsgHandle); + openavbAvdeccMsgClntChangeNotification(avdeccMsgHandle, OPENAVB_AVDECC_MSG_RUNNING); + ret = true; + } + break; + + case OPENAVB_AVDECC_MSG_PAUSED: + // Running with Pause requested. + AVB_LOGF_DEBUG("Paused state requested for client %d", avdeccMsgHandle); + if (!(pState->pTLState->bRunning)) { + // Notify server of issues. + AVB_LOGF_ERROR("Client %d attempted to pause the stream while not Running.", avdeccMsgHandle); + openavbAvdeccMsgClntChangeNotification(avdeccMsgHandle, OPENAVB_AVDECC_MSG_UNKNOWN); + } + else if (pState->pTLState->bRunning && !(pState->pTLState->bPaused)) { + if (pState->pTLState->cfg.role == AVB_ROLE_TALKER) { + openavbTLPauseTalker(pState->pTLState, TRUE); + // NOTE: openavbTLPauseTalker() call will cause Talker change notification. + AVB_LOGF_INFO("Talker %d state changed from Running to Paused", avdeccMsgHandle); + ret = true; + } else { + openavbTLPauseListener(pState->pTLState, TRUE); + // NOTE: openavbTLPauseListener() call will cause Listener change notification. + AVB_LOGF_INFO("Listener %d state changed from Running to Paused", avdeccMsgHandle); + ret = true; + } + } + else { + // Notify server we are already in this state. + AVB_LOGF_INFO("Client %d state is already at Paused", avdeccMsgHandle); + openavbAvdeccMsgClntChangeNotification(avdeccMsgHandle, OPENAVB_AVDECC_MSG_PAUSED); + ret = true; + } + break; + + default: + AVB_LOGF_ERROR("openavbAvdeccMsgClntHndlChangeRequestFromServer invalid state %d", desiredState); + break; + } + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return ret; +} + +bool openavbAvdeccMsgClntChangeNotification(int avdeccMsgHandle, openavbAvdeccMsgStateType_t currentState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + openavbAvdeccMessage_t msgBuf; + + avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); + if (!pState) { + AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return false; + } + + memset(&msgBuf, 0, OPENAVB_AVDECC_MSG_LEN); + msgBuf.type = OPENAVB_AVDECC_MSG_CLIENT_CHANGE_NOTIFICATION; + openavbAvdeccMsgParams_ClientChangeNotification_t * pParams = + &(msgBuf.params.clientChangeNotification); + pParams->current_state = (U8) currentState; + bool ret = openavbAvdeccMsgClntSendToServer(avdeccMsgHandle, &msgBuf); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return ret; +} + + +// Called from openavbAvdeccMsgThreadFn() which is started from openavbTLRun() +void openavbAvdeccMsgRunTalker(avdecc_msg_state_t *pState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + if (!pState) { + AVB_LOG_ERROR("Invalid AVDECC Msg State"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + openavb_tl_cfg_t * cfg = &(pState->pTLState->cfg); + if (cfg->role != AVB_ROLE_TALKER) { + AVB_LOG_ERROR("openavbAvdeccMsgRunTalker() passed a non-Talker state"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + if (!pState->bConnected) { + AVB_LOG_WARNING("Failed to connect to AVDECC Msg"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Let the AVDECC Msg server know our identity. + if (!openavbAvdeccMsgClntInitIdentify(pState->avdeccMsgHandle, cfg->friendly_name, true)) { + AVB_LOG_ERROR("openavbAvdeccMsgClntInitIdentify() failed"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Let the AVDECC Msg server know our stream ID. + if (!openavbAvdeccMsgClntTalkerStreamID(pState->avdeccMsgHandle, + cfg->sr_class, cfg->stream_addr.buffer.ether_addr_octet, cfg->stream_uid, + cfg->dest_addr.buffer.ether_addr_octet, cfg->vlan_id)) { + AVB_LOG_ERROR("openavbAvdeccMsgClntTalkerStreamID() failed"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Let the AVDECC Msg server know our current state. + if (!openavbAvdeccMsgClntChangeNotification(pState->avdeccMsgHandle, + (pState->pTLState->bRunning ? + (pState->pTLState->bPaused ? OPENAVB_AVDECC_MSG_PAUSED : OPENAVB_AVDECC_MSG_RUNNING ) : + OPENAVB_AVDECC_MSG_STOPPED))) { + AVB_LOG_ERROR("Initial openavbAvdeccMsgClntChangeNotification() failed"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Do until we are stopped or lose connection to AVDECC Msg server. + while (pState->pTLState->bAvdeccMsgRunning && pState->bConnected) { + + // Look for messages from AVDECC Msg. + if (!openavbAvdeccMsgClntService(pState->avdeccMsgHandle, 1000)) { + AVB_LOG_WARNING("Lost connection to AVDECC Msg"); + pState->bConnected = FALSE; + pState->avdeccMsgHandle = AVB_AVDECC_MSG_HANDLE_INVALID; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); +} + +// Called from openavbAvdeccMsgThreadFn() which is started from openavbTLRun() +void openavbAvdeccMsgRunListener(avdecc_msg_state_t *pState) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + + if (!pState && !pState->pTLState) { + AVB_LOG_ERROR("Invalid AVDECC Msg State"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + openavb_tl_cfg_t * cfg = &(pState->pTLState->cfg); + if (cfg->role != AVB_ROLE_LISTENER) { + AVB_LOG_ERROR("openavbAvdeccMsgRunListener() passed a non-Listener state"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + if (!pState->bConnected) { + AVB_LOG_WARNING("Failed to connect to AVDECC Msg"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Let the AVDECC Msg server know our identity. + if (!openavbAvdeccMsgClntInitIdentify(pState->avdeccMsgHandle, cfg->friendly_name, false)) { + AVB_LOG_ERROR("openavbAvdeccMsgClntInitIdentify() failed"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Let the AVDECC Msg server know our current state. + if (!openavbAvdeccMsgClntChangeNotification(pState->avdeccMsgHandle, + (pState->pTLState->bRunning ? + (pState->pTLState->bPaused ? OPENAVB_AVDECC_MSG_PAUSED : OPENAVB_AVDECC_MSG_RUNNING ) : + OPENAVB_AVDECC_MSG_STOPPED))) { + AVB_LOG_ERROR("Initial openavbAvdeccMsgClntChangeNotification() failed"); + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return; + } + + // Do until we are stopped or lose connection to AVDECC Msg. + while (pState->pTLState->bAvdeccMsgRunning && pState->bConnected) { + + // Look for messages from AVDECC Msg. + if (!openavbAvdeccMsgClntService(pState->avdeccMsgHandle, 1000)) { + AVB_LOG_WARNING("Lost connection to AVDECC Msg"); + pState->bConnected = FALSE; + pState->avdeccMsgHandle = AVB_AVDECC_MSG_HANDLE_INVALID; + } + } + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); +} + +// AVDECC Msg thread function +void* openavbAvdeccMsgThreadFn(void *pv) +{ + AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); + avdecc_msg_state_t avdeccMsgState; + + // Perform the base initialization. + openavbAvdeccMsgInitialize(); + + // Initialize our local state structure. + memset(&avdeccMsgState, 0, sizeof(avdeccMsgState)); + avdeccMsgState.pTLState = (tl_state_t *)pv; + + avdeccMsgState.avdeccMsgHandle = + avdeccMsgState.pTLState->avdeccMsgHandle = AVB_AVDECC_MSG_HANDLE_INVALID; + while (avdeccMsgState.pTLState->bAvdeccMsgRunning) { + AVB_TRACE_LINE(AVB_TRACE_AVDECC_MSG_DETAIL); + + if (avdeccMsgState.avdeccMsgHandle == AVB_AVDECC_MSG_HANDLE_INVALID) { + avdeccMsgState.avdeccMsgHandle = openavbAvdeccMsgClntOpenSrvrConnection(); + if (avdeccMsgState.avdeccMsgHandle == AVB_AVDECC_MSG_HANDLE_INVALID) { + // error connecting to AVDECC Msg, already logged + SLEEP(1); + continue; + } + avdeccMsgState.pTLState->avdeccMsgHandle = avdeccMsgState.avdeccMsgHandle; + } + AvdeccMsgStateListAdd(&avdeccMsgState); + + // Validate the AVB version for client and server are the same before continuing + avdeccMsgState.verState = OPENAVB_AVDECC_MSG_VER_UNKNOWN; + avdeccMsgState.bConnected = openavbAvdeccMsgClntRequestVersionFromServer(avdeccMsgState.avdeccMsgHandle); + if (avdeccMsgState.pTLState->bAvdeccMsgRunning && avdeccMsgState.bConnected && avdeccMsgState.verState == OPENAVB_AVDECC_MSG_VER_UNKNOWN) { + // Check for AVDECC Msg version message. Timeout in 500 msec. + if (!openavbAvdeccMsgClntService(avdeccMsgState.avdeccMsgHandle, 500)) { + AVB_LOG_WARNING("Lost connection to AVDECC Msg, will retry"); + avdeccMsgState.bConnected = FALSE; + avdeccMsgState.avdeccMsgHandle = + avdeccMsgState.pTLState->avdeccMsgHandle = AVB_AVDECC_MSG_HANDLE_INVALID; + } + } + if (avdeccMsgState.verState == OPENAVB_AVDECC_MSG_VER_UNKNOWN) { + AVB_LOG_ERROR("AVB core version not reported by AVDECC Msg server. Will reconnect to AVDECC Msg and check again."); + } else if (avdeccMsgState.verState == OPENAVB_AVDECC_MSG_VER_INVALID) { + AVB_LOG_ERROR("AVB core version is different than AVDECC Msg AVB core version. Will reconnect to AVDECC Msg and check again."); + } else { + AVB_LOG_DEBUG("AVB core version matches AVDECC Msg AVB core version."); + } + + if (avdeccMsgState.bConnected && avdeccMsgState.verState == OPENAVB_AVDECC_MSG_VER_VALID) { + if (avdeccMsgState.pTLState->cfg.role == AVB_ROLE_TALKER) { + openavbAvdeccMsgRunTalker(&avdeccMsgState); + } + else { + openavbAvdeccMsgRunListener(&avdeccMsgState); + } + } + + // Close the AVDECC Msg connection. + AvdeccMsgStateListRemove(&avdeccMsgState); + openavbAvdeccMsgClntCloseSrvrConnection(avdeccMsgState.avdeccMsgHandle); + avdeccMsgState.bConnected = FALSE; + avdeccMsgState.avdeccMsgHandle = + avdeccMsgState.pTLState->avdeccMsgHandle = AVB_AVDECC_MSG_HANDLE_INVALID; + + if (avdeccMsgState.pTLState->bAvdeccMsgRunning && avdeccMsgState.verState == OPENAVB_AVDECC_MSG_VER_VALID) { + SLEEP(1); + } + } + + avdeccMsgState.pTLState = NULL; + + // Perform the base cleanup. + openavbAvdeccMsgCleanup(); + + THREAD_JOINABLE(avdeccMsgState.pTLState->avdeccMsgThread); + + AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); + return NULL; +} + + +// Client-side helper function. +bool openavbAvdeccMsgClntNotifyCurrentState(tl_state_t *pTLState) +{ + if (!pTLState) { return FALSE; } + if (!(pTLState->bAvdeccMsgRunning)) { return FALSE; } + + // Find the AVDECC Msg for the supplied tl_state_t pointer. + int i; + for (i = 0; i < MAX_AVDECC_MSG_CLIENTS; ++i) { + avdecc_msg_state_t * pAvdeccMsgState = AvdeccMsgStateListGetIndex(i); + if (!pAvdeccMsgState) { + // Out of items. + break; + } + if (pAvdeccMsgState->pTLState == pTLState) { + // Notify the server regarding the current state. + if (pTLState->bRunning) { + openavbAvdeccMsgClntChangeNotification(pAvdeccMsgState->avdeccMsgHandle, (pTLState->bPaused ? OPENAVB_AVDECC_MSG_PAUSED : OPENAVB_AVDECC_MSG_RUNNING)); + } else { + openavbAvdeccMsgClntChangeNotification(pAvdeccMsgState->avdeccMsgHandle, OPENAVB_AVDECC_MSG_STOPPED); + } + return TRUE; + } + } + + return FALSE; +} |