int openavbEptClntOpenSrvrConnection(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); struct sockaddr_un server; server.sun_family = AF_UNIX; snprintf(server.sun_path, UNIX_PATH_MAX, AVB_ENDPOINT_UNIX_PATH); int h = socket(AF_UNIX, SOCK_STREAM, 0); if (h < 0) { AVB_LOGF_DEBUG("Failed to open socket: %s", strerror(errno)); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return AVB_ENDPOINT_HANDLE_INVALID; } AVB_LOGF_DEBUG("Connecting to %s", server.sun_path); int rslt = connect(h, (struct sockaddr*)&server, sizeof(struct sockaddr_un)); if (rslt < 0) { AVB_LOGF_DEBUG("Failed to connect socket: %s", strerror(errno)); socketClose(h); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return AVB_ENDPOINT_HANDLE_INVALID; } AVB_LOG_DEBUG("Connected to endpoint"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return h; }
bool openavbAVDECCGetTalkerStreamInfo(openavb_aem_descriptor_stream_io_t *pDescriptorStreamOutput, U16 configIdx, openavb_acmp_TalkerStreamInfo_t *pTalkerStreamInfo) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); // Sanity tests. if (!pDescriptorStreamOutput) { AVB_LOG_ERROR("openavbAVDECCGetTalkerStreamInfo Invalid descriptor"); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } if (!pTalkerStreamInfo) { AVB_LOG_ERROR("openavbAVDECCGetTalkerStreamInfo Invalid streaminfo"); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } if (!pDescriptorStreamOutput->stream) { AVB_LOG_ERROR("openavbAVDECCGetTalkerStreamInfo Invalid StreamInput descriptor stream"); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } // Get the destination MAC Address. if (!pDescriptorStreamOutput->stream->dest_addr.mac || memcmp(pDescriptorStreamOutput->stream->dest_addr.buffer.ether_addr_octet, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) { AVB_LOG_DEBUG("openavbAVDECCGetTalkerStreamInfo Invalid stream dest_addr"); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } memcpy(pTalkerStreamInfo->stream_dest_mac, pDescriptorStreamOutput->stream->dest_addr.mac, ETH_ALEN); AVB_LOGF_DEBUG("Talker stream_dest_mac: " ETH_FORMAT, ETH_OCTETS(pTalkerStreamInfo->stream_dest_mac)); // Get the Stream ID. if (!pDescriptorStreamOutput->stream->stream_addr.mac || memcmp(pDescriptorStreamOutput->stream->stream_addr.buffer.ether_addr_octet, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) { AVB_LOG_ERROR("openavbAVDECCGetTalkerStreamInfo Invalid stream stream_addr"); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } memcpy(pTalkerStreamInfo->stream_id, pDescriptorStreamOutput->stream->stream_addr.mac, ETH_ALEN); U8 *pStreamUID = pTalkerStreamInfo->stream_id + 6; *(U16 *)(pStreamUID) = htons(pDescriptorStreamOutput->stream->stream_uid); AVB_LOGF_DEBUG("Talker stream_id: " ETH_FORMAT "/%u", ETH_OCTETS(pTalkerStreamInfo->stream_id), (((U16) pTalkerStreamInfo->stream_id[6]) << 8) | (U16) pTalkerStreamInfo->stream_id[7]); // Get the VLAN ID. pTalkerStreamInfo->stream_vlan_id = pDescriptorStreamOutput->stream->vlan_id; AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return TRUE; }
// Delete a connection with saved state // bool openavbAvdeccClearSavedState(const openavb_tl_data_cfg_t *pListener) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); int i; // Delete the saved state matching the supplied one. // If the supplied saved state does not match any of the ones already saved, do nothing and return. for (i = 0; i < 1000; ++i) { const openavb_saved_state_t * pTest = openavbAvdeccGetSavedState(i); if (!pTest) { break; } if (strcmp(pTest->listener_friendly_name, pListener->friendly_name) == 0) { // We found the index for the item to delete. // Delete the item and save the updated file. openavbAvdeccDeleteSavedState(i); AVB_LOGF_DEBUG("Cleared saved state: listener_id=\"%s\"", pListener->friendly_name); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return true; } } AVB_LOGF_WARNING("Unable to find saved state to clear: listener_id=\"%s\"", pListener->friendly_name); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return false; }
// Open a rawsock for TX or RX void *pcapRawsockOpen(pcap_rawsock_t* rawsock, const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); AVB_LOGF_DEBUG("Open, rx=%d, tx=%d, ethertype=%x size=%d, num=%d", rx_mode, tx_mode, ethertype, frame_size, num_frames); baseRawsockOpen(&rawsock->base, ifname, rx_mode, tx_mode, ethertype, frame_size, num_frames); if (tx_mode) { AVB_LOG_DEBUG("pcap rawsock transmit mode will bypass FQTSS"); } rawsock->handle = 0; // Get info about the network device if (!simpleAvbCheckInterface(ifname, &(rawsock->base.ifInfo))) { AVB_LOGF_ERROR("Creating rawsock; bad interface name: %s", ifname); free(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } // Deal with frame size. if (rawsock->base.frameSize == 0) { // use interface MTU as max frames size, if none specified rawsock->base.frameSize = rawsock->base.ifInfo.mtu + ETH_HLEN + VLAN_HLEN; } else if (rawsock->base.frameSize > rawsock->base.ifInfo.mtu + ETH_HLEN + VLAN_HLEN) { AVB_LOG_ERROR("Creating rawsock; requested frame size exceeds MTU"); free(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } char errbuf[PCAP_ERRBUF_SIZE]; rawsock->handle = open_pcap_dev(ifname, rawsock->base.frameSize, errbuf); if (!rawsock->handle) { AVB_LOGF_ERROR("Cannot open device %s: %s", ifname, errbuf); free(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } // fill virtual functions table rawsock_cb_t *cb = &rawsock->base.cb; cb->close = pcapRawsockClose; cb->getTxFrame = pcapRawsockGetTxFrame; cb->txFrameReady = pcapRawsockTxFrameReady; cb->send = pcapRawsockSend; cb->getRxFrame = pcapRawsockGetRxFrame; cb->rxMulticast = pcapRawsockRxMulticast; cb->rxParseHdr = pcapRawsockRxParseHdr; AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return rawsock; }
static bool openavbEptSrvrReceiveFromClient(int h, openavbEndpointMessage_t *msg) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); if (!msg) { AVB_LOG_ERROR("Receiving message; invalid argument passed"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } bool ret = FALSE; switch (msg->type) { case OPENAVB_ENDPOINT_TALKER_REGISTER: AVB_LOGF_DEBUG("TalkerRegister from client uid=%d", msg->streamID.uniqueID); ret = openavbEptSrvrRegisterStream(h, &msg->streamID, msg->params.talkerRegister.destAddr, &msg->params.talkerRegister.tSpec, msg->params.talkerRegister.srClass, msg->params.talkerRegister.srRank, msg->params.talkerRegister.latency); break; case OPENAVB_ENDPOINT_LISTENER_ATTACH: AVB_LOGF_DEBUG("ListenerAttach from client uid=%d", msg->streamID.uniqueID); ret = openavbEptSrvrAttachStream(h, &msg->streamID, msg->params.listenerAttach.lsnrDecl); break; case OPENAVB_ENDPOINT_CLIENT_STOP: AVB_LOGF_DEBUG("Stop from client uid=%d", msg->streamID.uniqueID); ret = openavbEptSrvrStopStream(h, &msg->streamID); break; case OPENAVB_ENDPOINT_VERSION_REQUEST: AVB_LOG_DEBUG("Version request from client"); ret = openavbEptSrvrHndlVerRqstFromClient(h); break; default: AVB_LOG_ERROR("Unexpected message received at server"); break; } AVB_LOGF_VERBOSE("Message handled, ret=%d", ret); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return ret; }
bool openavbAvdeccMsgSrvrHndlTalkerStreamIDFromClient(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; } openavb_tl_data_cfg_t *pCfg = pState->stream; if (!pCfg) { AVB_LOGF_ERROR("avdeccMsgHandle %d stream not valid", avdeccMsgHandle); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } // Update the stream information supplied by the client. 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("Talker-supplied sr_class: %u", pCfg->sr_class); AVB_LOGF_DEBUG("Talker-supplied stream_id: " ETH_FORMAT "/%u", ETH_OCTETS(pCfg->stream_addr.buffer.ether_addr_octet), pCfg->stream_uid); AVB_LOGF_DEBUG("Talker-supplied dest_addr: " ETH_FORMAT, ETH_OCTETS(pCfg->dest_addr.buffer.ether_addr_octet)); AVB_LOGF_DEBUG("Talker-supplied vlan_id: %u", pCfg->vlan_id); // Notify the state machine that we received this information. openavbAcmpSMTalker_updateStreamInfo(pCfg); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return true; }
bool openavbAdpOpenSocket(const char* ifname, U16 vlanID, U8 vlanPCP) { AVB_TRACE_ENTRY(AVB_TRACE_ADP); hdr_info_t hdr; #ifndef UBUNTU // This is the normal case for most of our supported platforms rxSock = openavbRawsockOpen(ifname, TRUE, FALSE, ETHERTYPE_8021Q, ADP_FRAME_LEN, ADP_NUM_BUFFERS); #else rxSock = openavbRawsockOpen(ifname, TRUE, FALSE, ETHERTYPE_AVTP, ADP_FRAME_LEN, ADP_NUM_BUFFERS); #endif txSock = openavbRawsockOpen(ifname, FALSE, TRUE, ETHERTYPE_AVTP, ADP_FRAME_LEN, ADP_NUM_BUFFERS); if (txSock && rxSock && openavbRawsockGetAddr(txSock, ADDR_PTR(&intfAddr)) && ether_aton_r(ADP_PROTOCOL_ADDR, &adpAddr) && openavbRawsockRxMulticast(rxSock, TRUE, ADDR_PTR(&adpAddr))) { if (!openavbRawsockRxAVTPSubtype(rxSock, OPENAVB_ADP_AVTP_SUBTYPE | 0x80)) { AVB_LOG_DEBUG("RX AVTP Subtype not supported"); } memset(&hdr, 0, sizeof(hdr_info_t)); hdr.shost = ADDR_PTR(&intfAddr); hdr.dhost = ADDR_PTR(&adpAddr); hdr.ethertype = ETHERTYPE_AVTP; if (vlanID != 0 || vlanPCP != 0) { hdr.vlan = TRUE; hdr.vlan_pcp = vlanPCP; hdr.vlan_vid = vlanID; AVB_LOGF_DEBUG("VLAN pcp=%d vid=%d", hdr.vlan_pcp, hdr.vlan_vid); } if (!openavbRawsockTxSetHdr(txSock, &hdr)) { AVB_LOG_ERROR("TX socket Header Failure"); openavbAdpCloseSocket(); AVB_TRACE_EXIT(AVB_TRACE_ADP); return false; } AVB_TRACE_EXIT(AVB_TRACE_ADP); return true; } AVB_LOG_ERROR("Invalid socket"); openavbAdpCloseSocket(); AVB_TRACE_EXIT(AVB_TRACE_ADP); return false; }
static void openavbAdpMessageRxFrameReceive(U32 timeoutUsec) { AVB_TRACE_ENTRY(AVB_TRACE_ADP); hdr_info_t hdrInfo; unsigned int offset, len; U8 *pBuf, *pFrame; memset(&hdrInfo, 0, sizeof(hdr_info_t)); pBuf = (U8 *)openavbRawsockGetRxFrame(rxSock, timeoutUsec, &offset, &len); if (pBuf) { pFrame = pBuf + offset; offset = openavbRawsockRxParseHdr(rxSock, pBuf, &hdrInfo); { #ifndef UBUNTU if (hdrInfo.ethertype == ETHERTYPE_8021Q) { // Oh! Need to look past the VLAN tag U16 vlan_bits = ntohs(*(U16 *)(pFrame + offset)); hdrInfo.vlan = TRUE; hdrInfo.vlan_vid = vlan_bits & 0x0FFF; hdrInfo.vlan_pcp = (vlan_bits >> 13) & 0x0007; offset += 2; hdrInfo.ethertype = ntohs(*(U16 *)(pFrame + offset)); offset += 2; } #endif // Make sure that this is an AVTP packet // (Should always be AVTP if it's to our AVTP-specific multicast address) if (hdrInfo.ethertype == ETHERTYPE_AVTP) { // parse the PDU only for ADP messages if (*(pFrame + offset) == (0x80 | OPENAVB_ADP_AVTP_SUBTYPE)) { if (memcmp(hdrInfo.shost, ADDR_PTR(&intfAddr), 6) != 0) { // Not from us! openavbAdpMessageRxFrameParse(pFrame + offset, len - offset, &hdrInfo); } } } else { AVB_LOG_WARNING("Received non-AVTP frame!"); AVB_LOGF_DEBUG("Unexpected packet data (length %d):", len); AVB_LOG_BUFFER(AVB_LOG_LEVEL_DEBUG, pFrame, len, 16); } } // Release the frame openavbRawsockRelRxFrame(rxSock, pBuf); }
// Save the connection to the saved state // bool openavbAvdeccSaveState(const openavb_tl_data_cfg_t *pListener, U16 flags, U16 talker_unique_id, const U8 talker_entity_id[8], const U8 controller_entity_id[8]) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); int i; // Don't add to the saved state list if fast connect support is not enabled. if (!gAvdeccCfg.bFastConnectSupported) { AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return false; } // If the supplied saved state matches one of the ones already saved, do nothing and return. // If the Talker or Controller has changed, delete the old information. for (i = 0; i < 1000; ++i) { const openavb_saved_state_t * pTest = openavbAvdeccGetSavedState(i); if (!pTest) { break; } if (strcmp(pTest->listener_friendly_name, pListener->friendly_name) == 0) { if (pTest->flags == flags && pTest->talker_unique_id == talker_unique_id && memcmp(pTest->talker_entity_id, talker_entity_id, 8) == 0 && memcmp(pTest->controller_entity_id, controller_entity_id, 8) == 0) { // The supplied data is a match for the existing item. Do nothing. AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return true; } // Delete this item. We will create a new item with the updated information. openavbAvdeccDeleteSavedState(i); break; } } // Add the supplied state to the list of states. openavbAvdeccAddSavedState(pListener->friendly_name, flags, talker_unique_id, talker_entity_id, controller_entity_id); AVB_LOGF_DEBUG("New saved state: listener_id=%s, flags=0x%04x, talker_unique_id=0x%04x, talker_entity_id=" ENTITYID_FORMAT ", controller_entity_id=" ENTITYID_FORMAT, pListener->friendly_name, flags, talker_unique_id, ENTITYID_ARGS(talker_entity_id), ENTITYID_ARGS(controller_entity_id)); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return true; }
// Pre-set the ethernet header information that will be used on TX frames bool baseRawsockTxSetHdr(void *pvRawsock, hdr_info_t *pHdr) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); base_rawsock_t *rawsock = (base_rawsock_t*)pvRawsock; if (!VALID_TX_RAWSOCK(rawsock)) { AVB_LOG_ERROR("Setting TX header; invalid argument"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return FALSE; } // source address if (pHdr->shost) { memcpy(&(rawsock->ethHdr.notag.shost), pHdr->shost, ETH_ALEN); } // destination address if (pHdr->dhost) { memcpy(&(rawsock->ethHdr.notag.dhost), pHdr->dhost, ETH_ALEN); } // VLAN tag? if (!pHdr->vlan) { // No, set ethertype in normal location rawsock->ethHdr.notag.ethertype = htons(rawsock->ethertype); // and set ethernet header length rawsock->ethHdrLen = sizeof(eth_hdr_t); } else { // Add VLAN tag AVB_LOGF_DEBUG("VLAN=%d pcp=%d vid=%d", pHdr->vlan_vid, pHdr->vlan_pcp, pHdr->vlan_vid); // Build bitfield with vlan_pcp and vlan_vid. // I think CFI bit is alway 0 u_int16_t bits = 0; bits |= (pHdr->vlan_pcp << 13) & 0xE000; bits |= pHdr->vlan_vid & 0x0FFF; // Create VLAN tag rawsock->ethHdr.tagged.vlan.tpip = htons(ETHERTYPE_VLAN); rawsock->ethHdr.tagged.vlan.bits = htons(bits); rawsock->ethHdr.tagged.ethertype = htons(rawsock->ethertype); // and set ethernet header length rawsock->ethHdrLen = sizeof(eth_vlan_hdr_t); } AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return TRUE; }
bool openavbEndpointServerOpen(void) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); int i; for (i=0; i < POLL_FD_COUNT; i++) { fds[i].fd = SOCK_INVALID; fds[i].events = 0; } lsock = socket(AF_UNIX, SOCK_STREAM, 0); if (lsock < 0) { AVB_LOGF_ERROR("Failed to open socket: %s", strerror(errno)); goto error; } // serverAddr is file static serverAddr.sun_family = AF_UNIX; snprintf(serverAddr.sun_path, UNIX_PATH_MAX, AVB_ENDPOINT_UNIX_PATH); int rslt = bind(lsock, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_un)); if (rslt != 0) { AVB_LOGF_ERROR("Failed to create %s: %s", serverAddr.sun_path, strerror(errno)); AVB_LOG_WARNING("** If endpoint process crashed, run the cleanup script **"); goto error; } rslt = listen(lsock, 5); if (rslt != 0) { AVB_LOGF_ERROR("Failed to listen on socket: %s", strerror(errno)); goto error; } AVB_LOGF_DEBUG("Listening on socket: %s", serverAddr.sun_path); fds[AVB_ENDPOINT_LISTEN_FDS].fd = lsock; fds[AVB_ENDPOINT_LISTEN_FDS].events = POLLIN; AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; error: if (lsock >= 0) { close(lsock); lsock = -1; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; }
// Determine if the connection has a saved state // bool openavbAvdeccGetSaveStateInfo(const openavb_tl_data_cfg_t *pListener, U16 *p_flags, U16 *p_talker_unique_id, U8 (*p_talker_entity_id)[8], U8 (*p_controller_entity_id)[8]) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); int i; // Don't return anything from the saved state list if fast connect support is not enabled. if (!gAvdeccCfg.bFastConnectSupported) { AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return false; } // If the loaded saved state information matches the Listener supplied, return the information for it. for (i = 0; i < 1000; ++i) { const openavb_saved_state_t * pTest = openavbAvdeccGetSavedState(i); if (!pTest) { break; } if (strcmp(pTest->listener_friendly_name, pListener->friendly_name) == 0) { AVB_LOGF_DEBUG("Saved state available for listener_id=%s, flags=0x%04x, talker_unique_id=0x%04x, talker_entity_id=" ENTITYID_FORMAT ", controller_entity_id=" ENTITYID_FORMAT, pListener->friendly_name, pTest->flags, pTest->talker_unique_id, ENTITYID_ARGS(pTest->talker_entity_id), ENTITYID_ARGS(pTest->controller_entity_id)); if (p_flags) { *p_flags = pTest->flags; } if (p_talker_unique_id) { *p_talker_unique_id = pTest->talker_unique_id; } if (p_talker_entity_id) { memcpy(*p_talker_entity_id, pTest->talker_entity_id, 8); } if (p_controller_entity_id) { memcpy(*p_controller_entity_id, pTest->controller_entity_id, 8); } AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return true; } } AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return false; }
/* Find a stream in the list of streams we're handling */ clientStream_t* findStream(AVBStreamID_t *streamID) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); clientStream_t* ps = NULL; // Check for default stream MAC address, and fill it in with // interface MAC so that if the talker didn't fill in // the stream MAC, we use the one that the endpoint is // configured to use. // // Listener should never pass a default MAC in, // because the config code forces the listener to specify MAC in // its configuration. Talker may send a default (empty) MAC in // the stream ID, in which case we fill it in. // // TODO: This is sketchy - it would probably be better to force every // client to send fully populated stream IDs. I think the reason // I didn't do that is that it would cause duplicate configuration // (in the talker and in the endpoint.) Perhaps the filling in of // the MAC could happen in the endpoint function which calls // findstream for the talker. // static const U8 emptyMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; if (memcmp(streamID->addr, emptyMAC, ETH_ALEN) == 0) { memcpy(streamID->addr, x_cfg.ifmac, ETH_ALEN); AVB_LOGF_DEBUG("Replaced default streamID MAC with interface MAC "ETH_FORMAT, ETH_OCTETS(streamID->addr)); } clientStream_t **lpp; for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { if (memcmp(streamID->addr, (*lpp)->streamID.addr, ETH_ALEN) == 0 && streamID->uniqueID == (*lpp)->streamID.uniqueID) { ps = *lpp; break; } } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return ps; }
// Setup the rawsock to receive multicast packets bool pcapRawsockRxMulticast(void *pvRawsock, bool add_membership, const U8 addr[ETH_ALEN]) { pcap_rawsock_t *rawsock = (pcap_rawsock_t*)pvRawsock; struct bpf_program comp_filter_exp; char filter_exp[30]; sprintf(filter_exp, "ether dst %02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); AVB_LOGF_DEBUG("%s %d %s", __func__, (int)add_membership, filter_exp); if (pcap_compile(rawsock->handle, &comp_filter_exp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) < 0) { AVB_LOGF_ERROR("Could not parse filter %s: %s", filter_exp, pcap_geterr(rawsock->handle)); return false; } if (pcap_setfilter(rawsock->handle, &comp_filter_exp) < 0) { AVB_LOGF_ERROR("Could not install filter %s: %s", filter_exp, pcap_geterr(rawsock->handle)); return false; } return true; }
/* Talker client registers a stream */ bool openavbEptSrvrRegisterStream(int h, AVBStreamID_t *streamID, U8 destAddr[], AVBTSpec_t *tSpec, U8 srClass, U8 srRank, U32 latency) { openavbRC rc = OPENAVB_SUCCESS; AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); clientStream_t *ps = findStream(streamID); if (ps && ps->clientHandle != h) { AVB_LOGF_ERROR("Error registering talker; multiple clients for stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } ps = addStream(h, streamID); if (!ps) { AVB_LOGF_ERROR("Error registering talker; unable to add client stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } ps->role = clientTalker; ps->tSpec = *tSpec; ps->srClass = (SRClassIdx_t)srClass; ps->srRank = srRank; ps->latency = latency; ps->fwmark = INVALID_FWMARK; if (memcmp(ps->destAddr, destAddr, ETH_ALEN) == 0) { // no client-supplied address, use MAAP struct ether_addr addr; ps->hndMaap = openavbMaapAllocate(1, &addr); if (ps->hndMaap) { memcpy(ps->destAddr, addr.ether_addr_octet, ETH_ALEN); strmAttachCb((void*)ps, openavbSrp_LDSt_Stream_Info); // Inform talker about MAAP } else { AVB_LOG_ERROR("Error registering talker: MAAP failed to allocate MAC address"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); delStream(ps); return FALSE; } } else { // client-supplied destination MAC address memcpy(ps->destAddr, destAddr, ETH_ALEN); ps->hndMaap = NULL; } // Do SRP talker register AVB_LOGF_DEBUG("REGISTER: ps=%p, streamID=%d, tspec=%d,%d, srClass=%d, srRank=%d, latency=%d, da="ETH_FORMAT"", ps, streamID->uniqueID, tSpec->maxFrameSize, tSpec->maxIntervalFrames, ps->srClass, ps->srRank, ps->latency, ETH_OCTETS(ps->destAddr)); if(x_cfg.noSrp) { // we are operating in a mode supporting preconfigured streams; SRP is not in use, // so, as a proxy for SRP, which would normally make this call after establishing // the stream, call the callback from here strmAttachCb((void*)ps, openavbSrp_LDSt_Ready); } else { // normal SRP operation rc = openavbSrpRegisterStream((void*)ps, &ps->streamID, ps->destAddr, &ps->tSpec, ps->srClass, ps->srRank, ps->latency); if (!IS_OPENAVB_SUCCESS(rc)) { if (ps->hndMaap) openavbMaapRelease(ps->hndMaap); delStream(ps); } } openavbEndPtLogAllStaticStreams(); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return IS_OPENAVB_SUCCESS(rc); }
bool openavbEptClntService(int h, int timeout) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); bool rc = FALSE; if (h == AVB_ENDPOINT_HANDLE_INVALID) { AVB_LOG_ERROR("Client service: invalid socket"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } struct pollfd fds[1]; memset(fds, 0, sizeof(struct pollfd)); fds[0].fd = h; fds[0].events = POLLIN; AVB_LOG_VERBOSE("Waiting for event..."); int pRet = poll(fds, 1, timeout); if (pRet == 0) { AVB_LOG_VERBOSE("Poll timeout"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; } else if (pRet < 0) { if (errno == EINTR) { AVB_LOG_VERBOSE("Poll interrupted"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; } else { AVB_LOGF_ERROR("Poll error: %s", strerror(errno)); } } else { AVB_LOGF_DEBUG("Poll returned %d events", pRet); // only one fd, so it's readable. openavbEndpointMessage_t msgBuf; memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); ssize_t nRead = read(h, &msgBuf, OPENAVB_ENDPOINT_MSG_LEN); if (nRead < OPENAVB_ENDPOINT_MSG_LEN) { // sock closed if (nRead == 0) { AVB_LOG_ERROR("Socket closed unexpectedly"); } else if (nRead < 0) { AVB_LOGF_ERROR("Socket read error: %s", strerror(errno)); } else { AVB_LOG_ERROR("Socket read to short"); } socketClose(h); } else { // got a message if (openavbEptClntReceiveFromServer(h, &msgBuf)) { rc = TRUE; } else { AVB_LOG_ERROR("Invalid message received"); socketClose(h); } } } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return rc; }
// Open a rawsock for TX or RX void* ringRawsockOpen(ring_rawsock_t *rawsock, const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); if (!simpleRawsockOpen((simple_rawsock_t*)rawsock, ifname, rx_mode, tx_mode, ethertype, frame_size, num_frames)) { AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } rawsock->pMem = (void*)(-1); // Use version 2 headers for the MMAP packet stuff - avoids 32/64 // bit problems, gives nanosecond timestamps, and allows rx of vlan id int val = TPACKET_V2; if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)) < 0) { AVB_LOGF_ERROR("Creating rawsock; get PACKET_VERSION: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } // Get the size of the headers in the ring unsigned len = sizeof(val); if (getsockopt(rawsock->sock, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) { AVB_LOGF_ERROR("Creating rawsock; get PACKET_HDRLEN: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } rawsock->bufHdrSize = TPACKET_ALIGN(val); if (rawsock->base.rxMode) { rawsock->bufHdrSize = rawsock->bufHdrSize + TPACKET_ALIGN(sizeof(struct sockaddr_ll)); } rawsock->bufferSize = rawsock->base.frameSize + rawsock->bufHdrSize; rawsock->frameCount = num_frames; AVB_LOGF_DEBUG("frameSize=%d, bufHdrSize=%d(%d+%zu) bufferSize=%d, frameCount=%d", rawsock->base.frameSize, rawsock->bufHdrSize, val, sizeof(struct sockaddr_ll), rawsock->bufferSize, rawsock->frameCount); // Get number of bytes in a memory page. The blocks we ask for // must be a multiple of pagesize. (Actually, it should be // (pagesize * 2^N) to avoid wasting memory.) int pagesize = getpagesize(); rawsock->blockSize = pagesize * 4; AVB_LOGF_DEBUG("pagesize=%d blockSize=%d", pagesize, rawsock->blockSize); // Calculate number of buffers and frames based on blocks int buffersPerBlock = rawsock->blockSize / rawsock->bufferSize; rawsock->blockCount = rawsock->frameCount / buffersPerBlock + 1; rawsock->frameCount = buffersPerBlock * rawsock->blockCount; AVB_LOGF_DEBUG("frameCount=%d, buffersPerBlock=%d, blockCount=%d", rawsock->frameCount, buffersPerBlock, rawsock->blockCount); // Fill in the kernel structure with our calculated values struct tpacket_req s_packet_req; memset(&s_packet_req, 0, sizeof(s_packet_req)); s_packet_req.tp_block_size = rawsock->blockSize; s_packet_req.tp_frame_size = rawsock->bufferSize; s_packet_req.tp_block_nr = rawsock->blockCount; s_packet_req.tp_frame_nr = rawsock->frameCount; // Ask the kernel to create the TX_RING or RX_RING if (rawsock->base.txMode) { if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_TX_RING, (char*)&s_packet_req, sizeof(s_packet_req)) < 0) { AVB_LOGF_ERROR("Creating rawsock; TX_RING: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } AVB_LOGF_DEBUG("PACKET_%s_RING OK", "TX"); } else { if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_RX_RING, (char*)&s_packet_req, sizeof(s_packet_req)) < 0) { AVB_LOGF_ERROR("Creating rawsock, RX_RING: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } AVB_LOGF_DEBUG("PACKET_%s_RING OK", "TX"); } // Call MMAP to get access to the memory used for the ring rawsock->memSize = rawsock->blockCount * rawsock->blockSize; AVB_LOGF_DEBUG("memSize=%zu (%d, %d), sock=%d", rawsock->memSize, rawsock->blockCount, rawsock->blockSize, rawsock->sock); rawsock->pMem = mmap((void*)0, rawsock->memSize, PROT_READ|PROT_WRITE, MAP_SHARED, rawsock->sock, (off_t)0); if (rawsock->pMem == (void*)(-1)) { AVB_LOGF_ERROR("Creating rawsock; MMAP: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } AVB_LOGF_DEBUG("mmap: %p", rawsock->pMem); // Initialize the memory memset(rawsock->pMem, 0, rawsock->memSize); // Initialize the state of the ring rawsock->blockIndex = 0; rawsock->bufferIndex = 0; rawsock->buffersOut = 0; rawsock->buffersReady = 0; // fill virtual functions table rawsock_cb_t *cb = &rawsock->base.cb; cb->close = ringRawsockClose; cb->getTxFrame = ringRawsockGetTxFrame; cb->relTxFrame = ringRawsockRelTxFrame; cb->txFrameReady = ringRawsockTxFrameReady; cb->send = ringRawsockSend; cb->txBufLevel = ringRawsockTxBufLevel; cb->rxBufLevel = ringRawsockRxBufLevel; cb->getRxFrame = ringRawsockGetRxFrame; cb->rxParseHdr = ringRawsockRxParseHdr; cb->relRxFrame = ringRawsockRelRxFrame; cb->getTXOutOfBuffers = ringRawsockGetTXOutOfBuffers; cb->getTXOutOfBuffersCyclic = ringRawsockGetTXOutOfBuffersCyclic; AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return rawsock; }
// Get a buffer from the ring to use for TX U8* ringRawsockGetTxFrame(void *pvRawsock, bool blocking, unsigned int *len) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); ring_rawsock_t *rawsock = (ring_rawsock_t*)pvRawsock; // Displays only warning when buffer busy after second try int bBufferBusyReported = 0; if (!VALID_TX_RAWSOCK(rawsock) || len == NULL) { AVB_LOG_ERROR("Getting TX frame; bad arguments"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } if (rawsock->buffersOut >= rawsock->frameCount) { AVB_LOG_ERROR("Getting TX frame; too many TX buffers in use"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } // Get pointer to next framebuf. volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(rawsock->pMem + (rawsock->blockIndex * rawsock->blockSize) + (rawsock->bufferIndex * rawsock->bufferSize)); // And pointer to portion of buffer to be filled with frame volatile U8 *pBuffer = (U8*)pHdr + rawsock->bufHdrSize; AVB_LOGF_VERBOSE("block=%d, buffer=%d, out=%d, pBuffer=%p, pHdr=%p", rawsock->blockIndex, rawsock->bufferIndex, rawsock->buffersOut, pBuffer, pHdr); // Check if buffer ready for user // In send mode, we want to see TP_STATUS_AVAILABLE while (pHdr->tp_status != TP_STATUS_AVAILABLE) { switch (pHdr->tp_status) { case TP_STATUS_SEND_REQUEST: case TP_STATUS_SENDING: if (blocking) { #if 0 // We should be able to poll on the socket to wait for the buffer to // be ready, but it doesn't work (at least on 2.6.37). // Keep this code, because it may work on newer kernels // poll until tx buffer is ready struct pollfd pfd; pfd.fd = rawsock->sock; pfd.events = POLLWRNORM; pfd.revents = 0; int ret = poll(&pfd, 1, -1); if (ret < 0 && errno != EINTR) { AVB_LOGF_DEBUG("getting TX frame; poll failed: %s", strerror(errno)); } #else // Can't poll, so sleep instead to avoid tight loop if(0 == bBufferBusyReported) { if(!rawsock->txOutOfBuffer) { // Display this info only once just to let know that something like this happened AVB_LOGF_INFO("Getting TX frame (sock=%d): TX buffer busy", rawsock->sock); } ++rawsock->txOutOfBuffer; ++rawsock->txOutOfBufferCyclic; } else if(1 == bBufferBusyReported) { //Display this warning if buffer was busy more than once because it might influence late/lost AVB_LOGF_WARNING("Getting TX frame (sock=%d): TX buffer busy after usleep(50) verify if there are any lost/late frames", rawsock->sock); } ++bBufferBusyReported; usleep(50); #endif } else { AVB_LOG_DEBUG("Non-blocking, return NULL"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } break; case TP_STATUS_WRONG_FORMAT: default: pHdr->tp_status = TP_STATUS_AVAILABLE; break; } } // Remind client how big the frame buffer is if (len) *len = rawsock->base.frameSize; // increment indexes to point to next buffer if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { rawsock->bufferIndex = 0; if (++(rawsock->blockIndex) >= rawsock->blockCount) { rawsock->blockIndex = 0; } } // increment the count of buffers held by client rawsock->buffersOut += 1; // warn if too many are out if (rawsock->buffersOut >= (rawsock->frameCount * 4)/5) { AVB_LOGF_WARNING("Getting TX frame; consider increasing buffers: count=%d, out=%d", rawsock->frameCount, rawsock->buffersOut); } AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return (U8*)pBuffer; }
// callback function - called for each name/value pair by ini parsing library static int openavbIniCfgCallback(void *user, const char *tlSection, const char *name, const char *value) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); openavb_tl_data_cfg_t *pCfg = (openavb_tl_data_cfg_t *)user; AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); bool valOK = FALSE; char *pEnd; if (MATCH(name, "role")) { if (MATCH(value, "talker")) { pCfg->role = AVB_ROLE_TALKER; valOK = TRUE; } else if (MATCH(value, "listener")) { pCfg->role = AVB_ROLE_LISTENER; valOK = TRUE; } } else if (MATCH(name, "initial_state")) { if (MATCH(value, "running")) { pCfg->initial_state = TL_INIT_STATE_RUNNING; valOK = TRUE; } else if (MATCH(value, "stopped")) { pCfg->initial_state = TL_INIT_STATE_STOPPED; valOK = TRUE; } } else if (MATCH(name, "dest_addr")) { valOK = parse_mac(value, &pCfg->dest_addr); } else if (MATCH(name, "stream_addr")) { valOK = parse_mac(value, &pCfg->stream_addr); } else if (MATCH(name, "stream_uid")) { errno = 0; pCfg->stream_uid = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->stream_uid <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "max_interval_frames")) { errno = 0; pCfg->max_interval_frames = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_interval_frames <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "max_frame_size")) { errno = 0; pCfg->max_frame_size = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_interval_frames <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "sr_class")) { if (strlen(value) == 1) { if (tolower(value[0]) == 'a') { pCfg->sr_class = SR_CLASS_A; valOK = TRUE; } else if (tolower(value[0]) == 'b') { pCfg->sr_class = SR_CLASS_B; valOK = TRUE; } } } else if (MATCH(name, "sr_rank")) { if (strlen(value) == 1) { if (value[0] == '1') { pCfg->sr_rank = SR_RANK_REGULAR; valOK = TRUE; } else if (value[0] == '0') { pCfg->sr_rank = SR_RANK_EMERGENCY; valOK = TRUE; } } } else if (MATCH(name, "max_transit_usec")) { errno = 0; pCfg->max_transit_usec = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_transit_usec <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "max_transmit_deficit_usec")) { errno = 0; pCfg->max_transmit_deficit_usec = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_transmit_deficit_usec <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "internal_latency")) { errno = 0; pCfg->internal_latency = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->internal_latency <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "batch_factor")) { errno = 0; pCfg->batch_factor = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->batch_factor > 0 && pCfg->batch_factor <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "max_stale")) { errno = 0; pCfg->max_stale = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_stale >= 0 && pCfg->max_stale <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "raw_tx_buffers")) { errno = 0; pCfg->raw_tx_buffers = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->raw_tx_buffers <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "raw_rx_buffers")) { errno = 0; pCfg->raw_rx_buffers = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->raw_rx_buffers <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "report_seconds")) { errno = 0; pCfg->report_seconds = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && (int)pCfg->report_seconds >= 0 && pCfg->report_seconds <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "start_paused")) { // ignore this item - tl_host doesn't use it because // it pauses before reading any of its streams. errno = 0; long tmp; tmp = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && tmp >= 0 && tmp <= 1) { pCfg->start_paused = (tmp == 1); valOK = TRUE; } } else if (MATCH(name, "vlan_id")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); // vlanID is 12 bit field if (*pEnd == '\0' && errno == 0 && tmp >= 0x0 && tmp <= 0xFFF) { pCfg->vlan_id = tmp; valOK = TRUE; } } else if (MATCH(name, "fixed_timestamp")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->fixed_timestamp = tmp; valOK = TRUE; } } else if (MATCH(name, "spin_wait")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->spin_wait = (tmp == 1); valOK = TRUE; } } else if (MATCH(name, "tx_blocking_in_intf")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->tx_blocking_in_intf = tmp; valOK = TRUE; } } else if (MATCH(name, "thread_rt_priority")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->thread_rt_priority = tmp; valOK = TRUE; } } else if (MATCH(name, "thread_affinity")) { errno = 0; unsigned long tmp; tmp = strtoul(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->thread_affinity = tmp; valOK = TRUE; } } else if (MATCH(name, "friendly_name")) { strncpy(pCfg->friendly_name, value, FRIENDLY_NAME_SIZE - 1); valOK = TRUE; } else if (MATCH(name, "current_sampling_rate")) { errno = 0; pCfg->current_sampling_rate = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->current_sampling_rate <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "sampling_rates")) { errno = 0; memset(pCfg->sampling_rates,0,sizeof(pCfg->sampling_rates)); char *rate, *copy; copy = strdup(value); rate = strtok(copy,","); int i = 0,break_flag = 0; while (rate != NULL ) { pCfg->sampling_rates[i] = strtol(rate,&pEnd, 10); if (*pEnd != '\0' || errno != 0 || pCfg->sampling_rates[i] > UINT32_MAX) { break_flag = 1; break; } rate = strtok(NULL,","); i++; } if (break_flag != 1) { valOK = TRUE; pCfg->sampling_rates_count = i; } } else if (strcmp(name, "intf_nv_audio_rate") == 0) { long int val = strtol(value, &pEnd, 10); if (val >= AVB_AUDIO_RATE_8KHZ && val <= AVB_AUDIO_RATE_192KHZ) { pCfg->audioRate = val; valOK = TRUE; } else { AVB_LOG_ERROR("Invalid audio rate configured for intf_nv_audio_rate."); pCfg->audioRate = AVB_AUDIO_RATE_44_1KHZ; } } else if (strcmp(name, "intf_nv_audio_bit_depth") == 0) { long int val = strtol(value, &pEnd, 10); if (val >= AVB_AUDIO_BIT_DEPTH_1BIT && val <= AVB_AUDIO_BIT_DEPTH_64BIT) { pCfg->audioBitDepth = val; valOK = TRUE; } else { AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_bits."); pCfg->audioBitDepth = AVB_AUDIO_BIT_DEPTH_24BIT; } } else if (strcmp(name, "intf_nv_audio_channels") == 0) { long int val = strtol(value, &pEnd, 10); if (val >= AVB_AUDIO_CHANNELS_1 && val <= AVB_AUDIO_CHANNELS_8) { pCfg->audioChannels = val; valOK = TRUE; } else { AVB_LOG_ERROR("Invalid audio channels configured for intf_nv_audio_channels."); pCfg->audioChannels = AVB_AUDIO_CHANNELS_2; } } else if (MATCH(name, "map_fn")) { errno = 0; memset(pCfg->map_fn,0,sizeof(pCfg->map_fn)); strncpy(pCfg->map_fn,value,sizeof(pCfg->map_fn)-1); valOK = TRUE; } else { // Ignored item. AVB_LOGF_DEBUG("Unhandled configuration item: name=%s, value=%s", name, value); // Don't abort for this item. valOK = TRUE; } if (!valOK) { // bad value AVB_LOGF_ERROR("Invalid value: name=%s, value=%s", name, value); return 0; } AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return 1; // OK }
void openavbEptSrvrService(void) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); struct sockaddr_un addrClient; socklen_t lenAddr; int i, j; int csock; int nfds = POLL_FD_COUNT; int pRet; AVB_LOG_VERBOSE("Waiting for event..."); pRet = poll(fds, nfds, 1000); if (pRet == 0) { AVB_LOG_VERBOSE("poll timeout"); } else if (pRet < 0) { if (errno == EINTR) { AVB_LOG_VERBOSE("Poll interrupted"); } else { AVB_LOGF_ERROR("Poll error: %s", strerror(errno)); } } else { AVB_LOGF_VERBOSE("Poll returned %d events", pRet); for (i=0; i<nfds; i++) { if (fds[i].revents != 0) { AVB_LOGF_VERBOSE("%d sock=%d, event=0x%x, revent=0x%x", i, fds[i].fd, fds[i].events, fds[i].revents); if (i == AVB_ENDPOINT_LISTEN_FDS) { // listen sock - indicates new connection from client lenAddr = sizeof(addrClient); csock = accept(lsock, (struct sockaddr*)&addrClient, &lenAddr); if (csock < 0) { AVB_LOGF_ERROR("Failed to accept connection: %s", strerror(errno)); } else { for (j = 0; j < POLL_FD_COUNT; j++) { if (fds[j].fd == SOCK_INVALID) { fds[j].fd = csock; fds[j].events = POLLIN; break; } } if (j >= POLL_FD_COUNT) { AVB_LOG_ERROR("Too many client connections"); close(csock); } } } else { csock = fds[i].fd; openavbEndpointMessage_t msgBuf; memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); ssize_t nRead = read(csock, &msgBuf, OPENAVB_ENDPOINT_MSG_LEN); AVB_LOGF_VERBOSE("Socket read h=%d,fd=%d: read=%zu, expect=%zu", i, csock, nRead, OPENAVB_ENDPOINT_MSG_LEN); if (nRead < OPENAVB_ENDPOINT_MSG_LEN) { // sock closed if (nRead == 0) { AVB_LOGF_DEBUG("Socket closed, h=%d", i); } else if (nRead < 0) { AVB_LOGF_ERROR("Socket read, h=%d: %s", i, strerror(errno)); } else { AVB_LOGF_ERROR("Short read, h=%d", i); } socketClose(i); } else { // got a message if (!openavbEptSrvrReceiveFromClient(i, &msgBuf)) { AVB_LOG_ERROR("Failed to handle message"); socketClose(i); } } } } } } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); }
// callback function - called for each name/value pair by ini parsing library static int openavbTLCfgCallback(void *user, const char *tlSection, const char *name, const char *value) { AVB_TRACE_ENTRY(AVB_TRACE_TL); parse_ini_data_t *pParseIniData = (parse_ini_data_t *)user; openavb_tl_cfg_t *pCfg = pParseIniData->pCfg; openavb_tl_cfg_name_value_t *pNVCfg = pParseIniData->pNVCfg; tl_state_t *pTLState = pParseIniData->pTLState; AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); bool valOK = FALSE; char *pEnd; int i; if (MATCH(name, "role")) { if (MATCH(value, "talker")) { pCfg->role = AVB_ROLE_TALKER; valOK = TRUE; } else if (MATCH(value, "listener")) { pCfg->role = AVB_ROLE_LISTENER; valOK = TRUE; } } else if (MATCH(name, "dest_addr")) { valOK = parse_mac(value, &pCfg->dest_addr); } else if (MATCH(name, "stream_addr")) { valOK = parse_mac(value, &pCfg->stream_addr); } else if (MATCH(name, "stream_uid")) { errno = 0; pCfg->stream_uid = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->stream_uid <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "max_interval_frames")) { errno = 0; pCfg->max_interval_frames = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_interval_frames <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "max_frame_size")) { errno = 0; pCfg->max_frame_size = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_interval_frames <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "sr_class")) { if (strlen(value) == 1) { if (tolower(value[0]) == 'a') { pCfg->sr_class = SR_CLASS_A; valOK = TRUE; } else if (tolower(value[0]) == 'b') { pCfg->sr_class = SR_CLASS_B; valOK = TRUE; } } } else if (MATCH(name, "sr_rank")) { if (strlen(value) == 1) { if (value[0] == '1') { pCfg->sr_rank = SR_RANK_REGULAR; valOK = TRUE; } else if (value[0] == '0') { pCfg->sr_rank = SR_RANK_EMERGENCY; valOK = TRUE; } } } else if (MATCH(name, "max_transit_usec")) { errno = 0; pCfg->max_transit_usec = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_transit_usec <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "max_transmit_deficit_usec")) { errno = 0; pCfg->max_transmit_deficit_usec = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_transmit_deficit_usec <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "internal_latency")) { errno = 0; pCfg->internal_latency = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->internal_latency <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "batch_factor")) { errno = 0; pCfg->batch_factor = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->batch_factor > 0 && pCfg->batch_factor <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "max_stale")) { errno = 0; pCfg->max_stale = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_stale >= 0 && pCfg->max_stale <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "raw_tx_buffers")) { errno = 0; pCfg->raw_tx_buffers = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->raw_tx_buffers <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "raw_rx_buffers")) { errno = 0; pCfg->raw_rx_buffers = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->raw_rx_buffers <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "report_seconds")) { errno = 0; pCfg->report_seconds = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && (int)pCfg->report_seconds >= 0 && pCfg->report_seconds <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "start_paused")) { // ignore this item - tl_host doesn't use it because // it pauses before reading any of its streams. errno = 0; long tmp; tmp = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && tmp >= 0 && tmp <= 1) { pCfg->start_paused = (tmp == 1); valOK = TRUE; } } else if (MATCH(name, "ifname")) { if_info_t ifinfo; if (openavbCheckInterface(value, &ifinfo)) { strncpy(pCfg->ifname, value, IFNAMSIZ - 1); valOK = TRUE; } } else if (MATCH(name, "vlan_id")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); // vlanID is 12 bit field if (*pEnd == '\0' && errno == 0 && tmp >= 0x0 && tmp <= 0xFFF) { pCfg->vlan_id = tmp; valOK = TRUE; } } else if (MATCH(name, "map_lib")) { if (pTLState->mapLib.libName) free(pTLState->mapLib.libName); pTLState->mapLib.libName = strdup(value); valOK = TRUE; } else if (MATCH(name, "map_fn")) { if (pTLState->mapLib.funcName) free(pTLState->mapLib.funcName); pTLState->mapLib.funcName = strdup(value); valOK = TRUE; } else if (MATCH(name, "intf_lib")) { if (pTLState->intfLib.libName) free(pTLState->intfLib.libName); pTLState->intfLib.libName = strdup(value); valOK = TRUE; } else if (MATCH(name, "intf_fn")) { if (pTLState->intfLib.funcName) free(pTLState->intfLib.funcName); pTLState->intfLib.funcName = strdup(value); valOK = TRUE; } else if (MATCH_LEFT(name, "intf_nv_", 8) || MATCH_LEFT(name, "map_nv_", 7)) { // Need to save the interface and mapping module configuration // until later (after those libraries are loaded.) // check if this setting replaces an earlier one for (i = 0; i < pNVCfg->nLibCfgItems; i++) { if (MATCH(name, pNVCfg->libCfgNames[i])) { if (pNVCfg->libCfgValues[i]) free(pNVCfg->libCfgValues[i]); pNVCfg->libCfgValues[i] = strdup(value); valOK = TRUE; } } if (i >= pNVCfg->nLibCfgItems) { // is a new name/value if (i >= MAX_LIB_CFG_ITEMS) { AVB_LOG_ERROR("Too many INI settings for interface/mapping modules"); } else { pNVCfg->libCfgNames[i] = strdup(name); pNVCfg->libCfgValues[i] = strdup(value); pNVCfg->nLibCfgItems++; valOK = TRUE; } } } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: name=%s", name); return 0; } if (!valOK) { // bad value AVB_LOGF_ERROR("Invalid value: name=%s, value=%s", name, value); return 0; } AVB_TRACE_EXIT(AVB_TRACE_TL); return 1; // OK }
/* Talker callback comes from endpoint, to indicate when listeners * come and go. We may need to start or stop the talker thread. */ void openavbEptClntNotifyTlkrOfSrpCb(int endpointHandle, AVBStreamID_t *streamID, char *ifname, U8 destAddr[], openavbSrpLsnrDeclSubtype_t lsnrDecl, U8 srClass, U32 classRate, U16 vlanID, U8 priority, U16 fwmark) { AVB_TRACE_ENTRY(AVB_TRACE_TL); tl_state_t *pTLState = TLHandleListGet(endpointHandle); talker_data_t *pTalkerData = pTLState->pPvtTalkerData; if (!pTLState) { AVB_LOG_WARNING("Unable to get talker from endpoint handle."); return; } // If not a talker, ignore this callback. if (pTLState->cfg.role != AVB_ROLE_TALKER) { AVB_LOG_DEBUG("Ignoring Talker callback"); return; } AVB_LOGF_DEBUG("%s streaming=%d, lsnrDecl=%d", __FUNCTION__, pTLState->bStreaming, lsnrDecl); openavb_tl_cfg_t *pCfg = &pTLState->cfg; if (!pTLState->bStreaming) { if (lsnrDecl == openavbSrp_LDSt_Ready || lsnrDecl == openavbSrp_LDSt_Ready_Failed) { // Save the data provided by endpoint/SRP if (!pCfg->ifname[0]) { strncpy(pTalkerData->ifname, ifname, IFNAMSIZ); } else { strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ); } memcpy(&pTalkerData->streamID, streamID, sizeof(AVBStreamID_t)); memcpy(&pTalkerData->destAddr, destAddr, ETH_ALEN); pTalkerData->srClass = srClass; pTalkerData->classRate = classRate; pTalkerData->vlanID = vlanID; pTalkerData->vlanPCP = priority; pTalkerData->fwmark = fwmark; // We should start streaming AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); talkerStartStream(pTLState); } else if (lsnrDecl == openavbSrp_LDSt_Stream_Info) { // Stream information is available does NOT mean listener is ready. Stream not started yet. if (!pCfg->ifname[0]) { strncpy(pTalkerData->ifname, ifname, IFNAMSIZ); } else { strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ); } memcpy(&pTalkerData->streamID, streamID, sizeof(AVBStreamID_t)); memcpy(&pTalkerData->destAddr, destAddr, ETH_ALEN); pTalkerData->srClass = srClass; pTalkerData->classRate = classRate; pTalkerData->vlanID = vlanID; pTalkerData->vlanPCP = priority; pTalkerData->fwmark = fwmark; } } else { if (lsnrDecl != openavbSrp_LDSt_Ready && lsnrDecl != openavbSrp_LDSt_Ready_Failed) { // Nobody is listening any more AVB_LOGF_INFO("Stopping stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); talkerStopStream(pTLState); } } // Let the AVDECC Msg server know our current stream ID, in case it was updated by MAAP. if (pTLState->avdeccMsgHandle != AVB_AVDECC_MSG_HANDLE_INVALID) { if (!openavbAvdeccMsgClntTalkerStreamID(pTLState->avdeccMsgHandle, pTalkerData->srClass, pTalkerData->streamID.addr, pTalkerData->streamID.uniqueID, pTalkerData->destAddr, pTalkerData->vlanID)) { AVB_LOG_ERROR("openavbAvdeccMsgClntTalkerStreamID() failed"); } } AVB_TRACE_EXIT(AVB_TRACE_TL); }
static void openavbAdpMessageRxFrameParse(U8* payload, int payload_len, hdr_info_t *hdr) { AVB_TRACE_ENTRY(AVB_TRACE_ADP); openavb_adp_control_header_t adpHeader; openavb_adp_data_unit_t adpPdu; #if 0 AVB_LOGF_DEBUG("openavbAdpMessageRxFrameParse packet data (length %d):", payload_len); AVB_LOG_BUFFER(AVB_LOG_LEVEL_DEBUG, payload, payload_len, 16); #endif U8 *pSrc = payload; { // AVTP Control Header openavb_adp_control_header_t *pDst = &adpHeader; BIT_B2DNTOHB(pDst->cd, pSrc, 0x80, 7, 0); BIT_B2DNTOHB(pDst->subtype, pSrc, 0x7f, 0, 1); BIT_B2DNTOHB(pDst->sv, pSrc, 0x80, 7, 0); BIT_B2DNTOHB(pDst->version, pSrc, 0x70, 4, 0); BIT_B2DNTOHB(pDst->message_type, pSrc, 0x0f, 0, 1); BIT_B2DNTOHB(pDst->valid_time, pSrc, 0xf800, 11, 0); BIT_B2DNTOHS(pDst->control_data_length, pSrc, 0x07ff, 0, 2); OCT_B2DMEMCP(pDst->entity_id, pSrc); } if (adpHeader.subtype == OPENAVB_ADP_AVTP_SUBTYPE && (adpHeader.message_type == OPENAVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER || (gAvdeccCfg.bFastConnectSupported && adpHeader.message_type == OPENAVB_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE))) { // ADP PDU openavb_adp_data_unit_t *pDst = &adpPdu; OCT_B2DMEMCP(pDst->entity_model_id, pSrc); OCT_B2DNTOHL(pDst->entity_capabilities, pSrc); OCT_B2DNTOHS(pDst->talker_stream_sources, pSrc); OCT_B2DNTOHS(pDst->talker_capabilities, pSrc); OCT_B2DNTOHS(pDst->listener_stream_sinks, pSrc); OCT_B2DNTOHS(pDst->listener_capabilities, pSrc); OCT_B2DNTOHL(pDst->controller_capabilities, pSrc); OCT_B2DNTOHL(pDst->available_index, pSrc); OCT_B2DMEMCP(pDst->gptp_grandmaster_id, pSrc); OCT_B2DNTOHB(pDst->gptp_domain_number, pSrc); OCT_B2DMEMCP(pDst->reserved0, pSrc); OCT_B2DNTOHS(pDst->identify_control_index, pSrc); OCT_B2DNTOHS(pDst->interface_index, pSrc); OCT_B2DMEMCP(pDst->association_id, pSrc); OCT_B2DMEMCP(pDst->reserved1, pSrc); if (adpHeader.message_type == OPENAVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER) { // Update the interface state machine openavbAdpSMAdvertiseInterfaceSet_entityID(adpHeader.entity_id); openavbAdpSMAdvertiseInterfaceSet_rcvdDiscover(TRUE); } else { // See if Fast Connect is waiting for this device to be available. if (adpPdu.talker_stream_sources > 0) { openavbAcmpSMListenerSet_talkerTestFastConnect(adpHeader.entity_id); } } } AVB_TRACE_EXIT(AVB_TRACE_ADP); }
static gboolean bus_message(GstBus *bus, GstMessage *message, void *pv) { switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: { GError *err = NULL; gchar *dbg_info = NULL; gst_message_parse_error(message, &err, &dbg_info); AVB_LOGF_ERROR("GStreamer ERROR message from element %s: %s", GST_OBJECT_NAME(message->src), err->message); AVB_LOGF_ERROR("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); g_error_free(err); g_free(dbg_info); break; } case GST_MESSAGE_WARNING: { GError *err = NULL; gchar *dbg_info = NULL; gst_message_parse_warning(message, &err, &dbg_info); AVB_LOGF_WARNING("GStreamer WARNING message from element %s: %s", GST_OBJECT_NAME(message->src), err->message); AVB_LOGF_WARNING("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); g_error_free(err); g_free(dbg_info); break; } case GST_MESSAGE_INFO: { GError *err = NULL; gchar *dbg_info = NULL; gst_message_parse_info(message, &err, &dbg_info); AVB_LOGF_ERROR("GStreamer INFO message from element %s: %s", GST_OBJECT_NAME(message->src), err->message); AVB_LOGF_ERROR("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); g_error_free(err); g_free(dbg_info); break; } case GST_MESSAGE_STATE_CHANGED: { GstState old_state, new_state; gst_message_parse_state_changed(message, &old_state, &new_state, NULL); AVB_LOGF_DEBUG("Element %s changed state from %s to %s", GST_OBJECT_NAME(message->src), gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); break; } case GST_MESSAGE_STREAM_STATUS: { // not so valuable break; } case GST_MESSAGE_EOS: AVB_LOG_INFO("EOS received"); break; default: AVB_LOGF_INFO("GStreamer '%s' message from element %s", gst_message_type_get_name(GST_MESSAGE_TYPE(message)), GST_OBJECT_NAME(message->src)); break; } return TRUE; }
static int cfgCallback(void *user, const char *section, const char *name, const char *value) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); if (!user || !section || !name || !value) { AVB_LOG_ERROR("Config: invalid arguments passed to callback"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } openavb_endpoint_cfg_t *pCfg = (openavb_endpoint_cfg_t*)user; AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); bool valOK = FALSE; char *pEnd; if (MATCH(section, "network")) { if (MATCH(name, "ifname")) { if_info_t ifinfo; if (openavbCheckInterface(value, &ifinfo)) { strncpy(pCfg->ifname, value, IFNAMSIZ - 1); memcpy(pCfg->ifmac, &ifinfo.mac, ETH_ALEN); pCfg->ifindex = ifinfo.index; pCfg->mtu = ifinfo.mtu; valOK = TRUE; } } else if (MATCH(name, "link_kbit")) { errno = 0; pCfg->link_kbit = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) valOK = TRUE; } else if (MATCH(name, "nsr_kbit")) { errno = 0; pCfg->nsr_kbit = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) valOK = TRUE; } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else if (MATCH(section, "ptp")) { if (MATCH(name, "start_options")) { pCfg->ptp_start_opts = strdup(value); valOK = TRUE; } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else if (MATCH(section, "fqtss")) { if (MATCH(name, "mode")) { errno = 0; pCfg->fqtss_mode = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) valOK = TRUE; } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else if (MATCH(section, "srp")) { if (MATCH(name, "preconfigured")) { errno = 0; unsigned temp = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) { valOK = TRUE; if (temp == 1) pCfg->noSrp = TRUE; else pCfg->noSrp = FALSE; } } else if (MATCH(name, "gptp_asCapable_not_required")) { errno = 0; unsigned temp = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) { valOK = TRUE; if (temp == 1) pCfg->bypassAsCapableCheck = TRUE; else pCfg->bypassAsCapableCheck = FALSE; } } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } if (!valOK) { cfgValErr(section, name, value); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 1; // OK }