diff --git a/apps/app_rpt.c b/apps/app_rpt.c index 26b2036b8..3a9919c7e 100644 --- a/apps/app_rpt.c +++ b/apps/app_rpt.c @@ -260,7 +260,6 @@ tonezone res_curl curl - dahdi extended ***/ @@ -289,6 +288,8 @@ #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/logger.h" +#include "asterisk/bridge.h" +#include "asterisk/bridge_channel.h" #include "asterisk/channel.h" #include "asterisk/callerid.h" #include "asterisk/pbx.h" @@ -305,6 +306,7 @@ #include "asterisk/format.h" #include "asterisk/dsp.h" #include "asterisk/audiohook.h" +#include "asterisk/core_unreal.h" #include "app_rpt/app_rpt.h" @@ -1060,6 +1062,7 @@ static void *perform_statpost(void *data) struct ast_str *response_msg; struct statpost *sp = (struct statpost *) data; struct rpt *myrpt = sp->myrpt; + char *url = ast_str_buffer(sp->stats_url); if (!curl) { ast_free(sp->stats_url); @@ -1067,7 +1070,7 @@ static void *perform_statpost(void *data) return NULL; } - response_msg = ast_str_create(50); + response_msg = ast_str_create(RPT_AST_STR_INIT_SIZE); if (!response_msg) { ast_free(sp->stats_url); ast_free(sp); @@ -1075,9 +1078,9 @@ static void *perform_statpost(void *data) return NULL; } curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunction); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response_msg); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_msg); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(curl, CURLOPT_URL, sp->stats_url); + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_USERAGENT, AST_CURL_USER_AGENT); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer); @@ -1086,12 +1089,12 @@ static void *perform_statpost(void *data) if (*error_buffer) { /* Anything in the error buffer? */ failed = 1; if (!myrpt->last_statpost_failed) { - ast_log(LOG_WARNING, "statpost to URL '%s' failed with error: %s\n", sp->stats_url, error_buffer); + ast_log(LOG_WARNING, "statpost to URL '%s' failed with error: %s\n", url, error_buffer); } } else { failed = 1; if (!myrpt->last_statpost_failed) { - ast_log(LOG_WARNING, "statpost to URL '%s' failed with error: %s\n", sp->stats_url, curl_easy_strerror(res)); + ast_log(LOG_WARNING, "statpost to URL '%s' failed with error: %s\n", url, curl_easy_strerror(res)); } } } else { @@ -1099,13 +1102,13 @@ static void *perform_statpost(void *data) if (!is_http_success(rescode)) { failed = 1; if (!myrpt->last_statpost_failed) { - ast_log(LOG_WARNING, "statpost to URL '%s' failed with code %ld: %s\n", sp->stats_url, rescode, http_status_text(rescode)); + ast_log(LOG_WARNING, "statpost to URL '%s' failed with code %ld: %s\n", url, rescode, http_status_text(rescode)); } } } myrpt->last_statpost_failed = ((failed) ? 1 : 0); - ast_debug(3, "Response: %s\n", ast_str_buffer(response_msg)); + ast_debug(5, "Response: %s\n", ast_str_buffer(response_msg)); ast_free(sp->stats_url); /* Free here since parent is not responsible for it. */ ast_free(sp); ast_free(response_msg); @@ -1113,11 +1116,11 @@ static void *perform_statpost(void *data) return NULL; } -static void statpost(struct rpt *myrpt, char *pairs) +static void statpost(struct rpt *myrpt, struct ast_str *pairs) { time_t now; unsigned int seq; - int res, len; + int res; pthread_t statpost_thread; struct statpost *sp; @@ -1129,20 +1132,22 @@ static void statpost(struct rpt *myrpt, char *pairs) return; } - len = strlen(pairs) + strlen(myrpt->p.statpost_url) + 200; - sp->stats_url = ast_malloc(len); - + sp->stats_url = ast_str_create(RPT_AST_STR_INIT_SIZE); + if (!sp->stats_url) { + ast_free(sp); + return; + } ast_mutex_lock(&myrpt->statpost_lock); seq = ++myrpt->statpost_seqno; ast_mutex_unlock(&myrpt->statpost_lock); time(&now); sp->myrpt = myrpt; - snprintf(sp->stats_url, len, "%s?node=%s&time=%u&seqno=%u%s%s", myrpt->p.statpost_url, myrpt->name, (unsigned int) now, seq, - pairs ? "&" : "", S_OR(pairs, "")); + ast_str_set(&sp->stats_url, 0, "%s?node=%s&time=%u&seqno=%u%s", myrpt->p.statpost_url, myrpt->name, (unsigned int) now, seq, + ast_str_buffer(pairs)); /* Make the actual cURL call in a separate thread, so we can continue without blocking. */ - ast_debug(4, "Making statpost to %s\n", sp->stats_url); + ast_debug(4, "Making statpost to %s\n", ast_str_buffer(sp->stats_url)); res = ast_pthread_create_detached(&statpost_thread, NULL, perform_statpost, sp); if (res) { ast_log(LOG_ERROR, "Error creating statpost thread: %s\n", strerror(res)); @@ -1262,7 +1267,7 @@ struct rpt_autopatch { }; /*! - * \brief Create an autopatch specific pbx run thread with no_hangup_chan = 1 arg. + * \brief Create an autopatch specific pbx run thread. * \param data Structure of rpt_autopatch */ static void *rpt_pbx_autopatch_run(void *data) @@ -1270,9 +1275,8 @@ static void *rpt_pbx_autopatch_run(void *data) struct rpt_autopatch *autopatch = data; struct rpt *myrpt = autopatch->myrpt; enum ast_pbx_result res; - struct ast_pbx_args pbx_args = { .no_hangup_chan = 1 }; - res = ast_pbx_run_args(autopatch->mychannel, &pbx_args); + res = ast_pbx_run(autopatch->mychannel); if (res) { /* could not start PBX */ rpt_mutex_lock(&myrpt->lock); myrpt->callmode = CALLMODE_FAILED; @@ -1295,6 +1299,19 @@ static void *rpt_pbx_autopatch_run(void *data) return NULL; } +static int rpt_handle_talker_cb(struct ast_bridge_channel *bridge_cannel, void *hook_pvt, int talking) +{ + struct rpt *myrpt = hook_pvt; + + if (!myrpt) { + return 1; + } + ast_debug(1, "Autopatch callback triggered: talking=%d\n", talking); + rpt_mutex_lock(&myrpt->lock); + myrpt->patch_talking = talking; + rpt_mutex_unlock(&myrpt->lock); + return 0; +} /* * * WARNING: YOU ARE NOW HEADED INTO ONE GIANT MAZE OF SWITCH STATEMENTS THAT DO MOST OF THE WORK FOR @@ -1317,69 +1334,55 @@ void *rpt_call(void *this) struct ast_channel *mychannel, *genchannel; struct ast_format_cap *cap; struct rpt_autopatch *patch_thread_data; + struct ast_bridge_channel *bridge_chan; + struct ast_unreal_pvt *p; + pthread_t threadid; cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!cap) { ast_log(LOG_ERROR, "Failed to alloc cap\n"); myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + return NULL; } ast_format_cap_append(cap, ast_format_slin, 0); - myrpt->mydtmf = 0; - mychannel = rpt_request_pseudo_chan(cap); + mychannel = rpt_request_local_chan(cap, "Autopatch"); if (!mychannel) { - ast_log(LOG_WARNING, "Unable to obtain pseudo channel\n"); + ast_log(LOG_WARNING, "Unable to obtain AutoPatch local channel\n"); ao2_ref(cap, -1); myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + return NULL; } ast_debug(1, "Requested channel %s\n", ast_channel_name(mychannel)); - if (rpt_conf_add_speaker(mychannel, myrpt)) { - ast_hangup(mychannel); - myrpt->callmode = CALLMODE_DOWN; - ao2_ref(cap, -1); - pthread_exit(NULL); - } - genchannel = rpt_request_pseudo_chan(cap); + genchannel = rpt_request_local_chan(cap, "GenChannel"); ao2_ref(cap, -1); if (!genchannel) { - ast_log(LOG_WARNING, "Unable to obtain pseudo channel\n"); + ast_log(LOG_WARNING, "Unable to obtain Gen local channel\n"); ast_hangup(mychannel); myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + return NULL; } - ast_debug(1, "Requested channel %s\n", ast_channel_name(genchannel)); - - /* first put the channel on the conference */ - if (rpt_conf_add_speaker(genchannel, myrpt)) { - ast_hangup(mychannel); - ast_hangup(genchannel); - myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + if (rpt_conf_add(genchannel, myrpt, RPT_CONF)) { + ast_log(LOG_WARNING, "Unable to place Gen local channel on conference\n"); + goto cleanup; } + + ast_autoservice_start(genchannel); + ast_debug(1, "Created Gen channel '%s' and added to conference bridge '%s'\n", ast_channel_name(genchannel), RPT_CONF_NAME); + if (myrpt->p.tonezone && rpt_set_tone_zone(mychannel, myrpt->p.tonezone)) { - ast_hangup(mychannel); - ast_hangup(genchannel); - myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + goto cleanup; } if (myrpt->p.tonezone && rpt_set_tone_zone(genchannel, myrpt->p.tonezone)) { - ast_hangup(mychannel); - ast_hangup(genchannel); - myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + goto cleanup; } /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */ if (!myrpt->patchquiet && !myrpt->patchexten[0] && rpt_play_dialtone(genchannel) < 0) { - ast_hangup(mychannel); - ast_hangup(genchannel); - myrpt->callmode = CALLMODE_DOWN; - pthread_exit(NULL); + goto cleanup; } stopped = 0; congstarted = 0; @@ -1433,16 +1436,11 @@ void *rpt_call(void *this) rpt_play_congestion(genchannel); } } - res = ast_safe_sleep(mychannel, MSWAIT); - if (res < 0) { - ast_debug(1, "ast_safe_sleep=%i\n", res); - ast_hangup(mychannel); - ast_hangup(genchannel); - rpt_mutex_lock(&myrpt->lock); - myrpt->callmode = CALLMODE_DOWN; - rpt_mutex_unlock(&myrpt->lock); - pthread_exit(NULL); - } + + /* At this point, genchannel is in autoservice, and mychannel is not connected to any frame generation. + * safesleep is not necessary. + */ + usleep(MSWAIT * 1000); dialtimer += MSWAIT; } @@ -1453,16 +1451,42 @@ void *rpt_call(void *this) if (myrpt->callmode == CALLMODE_DOWN) { ast_debug(1, "callmode==0\n"); ast_hangup(mychannel); + ast_autoservice_stop(genchannel); ast_hangup(genchannel); rpt_mutex_lock(&myrpt->lock); myrpt->macropatch = 0; - channel_revert(myrpt); rpt_mutex_unlock(&myrpt->lock); if ((!myrpt->patchquiet) && aborted) rpt_telemetry(myrpt, TERM, NULL); - pthread_exit(NULL); + return NULL; } + /* First we add mychannel to the conference */ + if (rpt_conf_add(mychannel, myrpt, RPT_CONF)) { + ast_log(LOG_WARNING, "Unable to place AutoPatch local channel on conference\n"); + goto cleanup; + } + ast_debug(1, "Autopatch channel %s placed on CONF", ast_channel_name(mychannel)); + + /* Next, add talker callback */ + myrpt->patch_talking = 0; /* Initialize patch_talking flag */ + p = ast_channel_tech_pvt(mychannel); + ast_debug(1, "Adding talker callback to channel %s, private data %p\n", ast_channel_name(mychannel), p); + if (p) { + bridge_chan = ast_channel_get_bridge_channel(p->chan); + if (bridge_chan) { + bridge_chan->tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD; + bridge_chan->tech_args.silence_threshold = VOX_OFF_DEBOUNCE_COUNT * 20; /* VOX is in 20ms count */ + ast_bridge_talk_detector_hook(bridge_chan->features, rpt_handle_talker_cb, myrpt, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL); + ast_debug(1, "Got Bridge channel %p\n", bridge_chan); + ao2_ref(bridge_chan, -1); + } else { + ast_log(LOG_WARNING, "Failed to get Bridge channel"); + } + } else { + ast_log(LOG_WARNING, "Failed to get channel tech private"); + } + /* Now we can set the caller id and extension for the PBX to use */ if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid) { char *name, *loc, *instr; instr = ast_strdup(myrpt->p.ourcallerid); @@ -1479,34 +1503,18 @@ void *rpt_call(void *this) ast_channel_accountcode_set(mychannel, myrpt->p.acctcode); ast_channel_priority_set(mychannel, 1); ast_channel_undefer_dtmf(mychannel); - if (rpt_call_bridge_setup(myrpt, mychannel)) { - rpt_mutex_lock(&myrpt->lock); - myrpt->callmode = CALLMODE_DOWN; - rpt_mutex_unlock(&myrpt->lock); - ast_hangup(mychannel); - ast_hangup(genchannel); - pthread_exit(NULL); - } patch_thread_data = ast_calloc(1, sizeof(struct rpt_autopatch)); if (!patch_thread_data) { - rpt_mutex_lock(&myrpt->lock); - myrpt->callmode = CALLMODE_DOWN; - rpt_mutex_unlock(&myrpt->lock); - ast_hangup(mychannel); - ast_hangup(genchannel); - pthread_exit(NULL); + goto cleanup; } + /* Finally, we can start the call */ patch_thread_data->myrpt = myrpt; patch_thread_data->mychannel = mychannel; res = ast_pthread_create(&threadid, NULL, rpt_pbx_autopatch_run, patch_thread_data); if (res < 0) { ast_log(LOG_ERROR, "Unable to start PBX!\n"); - rpt_mutex_lock(&myrpt->lock); - myrpt->callmode = CALLMODE_DOWN; - rpt_mutex_unlock(&myrpt->lock); - ast_hangup(mychannel); - ast_hangup(genchannel); - pthread_exit(NULL); + ast_free(patch_thread_data); + goto cleanup; } rpt_mutex_lock(&myrpt->lock); @@ -1563,24 +1571,26 @@ void *rpt_call(void *this) if (!patch_thread_data->pbx_exited) { ast_softhangup(mychannel, AST_SOFTHANGUP_DEV); - } else { - ast_hangup(mychannel); } + ast_autoservice_stop(genchannel); pthread_join(threadid, NULL); ast_hangup(genchannel); rpt_mutex_lock(&myrpt->lock); myrpt->callmode = CALLMODE_DOWN; myrpt->macropatch = 0; - channel_revert(myrpt); rpt_mutex_unlock(&myrpt->lock); - - /* first put the channel on the conference in announce mode */ - if (myrpt->p.duplex == 2 || myrpt->p.duplex == 4) { - rpt_conf_add_announcer_monitor(myrpt->pchannel, myrpt); - } ast_free(patch_thread_data); - pthread_exit(NULL); + return NULL; + +cleanup: + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = CALLMODE_DOWN; + rpt_mutex_unlock(&myrpt->lock); + ast_autoservice_stop(genchannel); + ast_hangup(mychannel); + ast_hangup(genchannel); + return NULL; } /* @@ -1785,6 +1795,8 @@ static inline void handle_callmode_dialing(struct rpt *myrpt, char c) if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) { /* call has failed, inform user */ myrpt->callmode = CALLMODE_FAILED; + } else { /* otherwise, reset timer */ + myrpt->calldigittimer = 1; } } @@ -2444,6 +2456,7 @@ static void *attempt_reconnect(void *data) rpt_mutex_lock(&myrpt->lock); ao2_ref(l, +1); /* We don't want the link to free after removing from the list */ rpt_link_remove(myrpt->links, l); /* remove from queue */ + ast_autoservice_start(l->pchan); /* We need to dump audio on l->chan while redialing or we receive long voice queue warnings */ rpt_mutex_unlock(&myrpt->lock); parse_node_format(tmp, &s1, sx, sizeof(sx)); snprintf(deststr, sizeof(deststr), "IAX2/%s", s1); @@ -2467,13 +2480,14 @@ static void *attempt_reconnect(void *data) while ((f1 = AST_LIST_REMOVE_HEAD(&l->textq, frame_list))) ast_frfree(f1); if (l->chan) { - rpt_make_call(l->chan, tele, 999, deststr, "(Remote Rx)", "attempt_reconnect", myrpt->name); + rpt_make_call(l->chan, tele, 999, deststr, "Remote Rx", "attempt_reconnect", myrpt->name); } else { ast_verb(3, "Unable to place call to %s/%s\n", deststr, tele); rpt_mutex_lock(&myrpt->lock); l->retrytimer = RETRY_TIMER_MS; rpt_mutex_unlock(&myrpt->lock); } + ast_autoservice_stop(l->pchan); rpt_mutex_lock(&myrpt->lock); rpt_link_add(myrpt->links, l); /* put back in queue */ ao2_ref(l, -1); /* and drop the extra ref we're holding */ @@ -2817,6 +2831,7 @@ static void _load_rpt_vars_by_rpt(struct rpt *myrpt, int force) } #define rpt_hangup_rx_tx(myrpt) \ + ast_autoservice_stop(myrpt->rxchannel); \ rpt_hangup(myrpt, RPT_RXCHAN); \ if (myrpt->txchannel) { \ rpt_hangup(myrpt, RPT_TXCHAN); \ @@ -2827,104 +2842,133 @@ static void _load_rpt_vars_by_rpt(struct rpt *myrpt, int force) static int rpt_setup_channels(struct rpt *myrpt, struct ast_format_cap *cap) { - int res; + int res = 0; - if (rpt_request(myrpt, cap, RPT_RXCHAN)) { + /* make a conference for the tx */ + if (rpt_conf_create(myrpt, RPT_TXCONF)) { + return -1; + } + + res = rpt_conf_create(myrpt, RPT_CONF); + if (res) { return -1; } + if (IS_PSEUDO_NAME(myrpt->rxchanname)) { + ast_log(LOG_ERROR, "Using DAHDI/Pseudo channel %s is deprecated. Update your rpt.conf to use Local/Pseudo.\n", myrpt->rxchanname); + ast_free(myrpt->rxchanname); + myrpt->rxchanname = ast_strdup("Local/pseudo"); + if (!myrpt->rxchanname) { + return -1; + } + } + if (rpt_request(myrpt, cap, RPT_RXCHAN)) { + return -1; + } + ast_autoservice_start(myrpt->rxchannel); if (myrpt->txchanname) { if (rpt_request(myrpt, cap, RPT_TXCHAN)) { + ast_autoservice_stop(myrpt->rxchannel); rpt_hangup(myrpt, RPT_RXCHAN); return -1; } } else { myrpt->txchannel = myrpt->rxchannel; - myrpt->dahditxchannel = IS_DAHDI_CHAN_NAME(myrpt->rxchanname) && !IS_PSEUDO_NAME(myrpt->rxchanname) ? myrpt->txchannel : NULL; + /* If it is a DAHDI hardware channel (Not PSEUDO), use the configured txchannel. */ + myrpt->localtxchannel = IS_DAHDI_CHAN_NAME(myrpt->rxchanname) && !IS_PSEUDO_NAME(myrpt->rxchanname) ? myrpt->txchannel : NULL; } - if (rpt_request_pseudo(myrpt, cap, RPT_PCHAN)) { + if (rpt_request_local(myrpt, cap, RPT_PCHAN, "PChan")) { rpt_hangup_rx_tx(myrpt); return -1; } - if (!myrpt->dahditxchannel) { - if (rpt_request_pseudo(myrpt, cap, RPT_DAHDITXCHAN)) { + if (IS_LOCAL_NAME(ast_channel_name(myrpt->txchannel))) { + /* IF we have a local channel setup in txchannel this is a hub + * there is no "real" hardware and there is no listener. + * Autoservice is required to "dump" audio frames. + */ + struct ast_unreal_pvt *p = ast_channel_tech_pvt(myrpt->txchannel); + if (!p || !p->chan) { + ast_log(LOG_WARNING, "Local channel %s missing endpoints\n", ast_channel_name(myrpt->txchannel)); + rpt_hangup_rx_tx(myrpt); + rpt_hangup(myrpt, RPT_PCHAN); + return -1; + } + ast_raw_answer(p->chan); + ast_autoservice_start(p->chan); + } + + if (!myrpt->localtxchannel) { + if (rpt_request_local(myrpt, cap, RPT_LOCALTXCHAN, "LocalTX")) { /* Listen only link */ rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); return -1; } } - if (rpt_request_pseudo(myrpt, cap, RPT_MONCHAN)) { + if (rpt_conf_add(myrpt->localtxchannel, myrpt, RPT_TXCONF)) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); - rpt_hangup(myrpt, RPT_DAHDITXCHAN); + rpt_hangup(myrpt, RPT_LOCALTXCHAN); return -1; } - /* make a conference for the tx */ - if (rpt_conf_create(myrpt->dahditxchannel, myrpt, RPT_TXCONF, RPT_CONF_CONF | RPT_CONF_LISTENER)) { + if (rpt_request_local(myrpt, cap, RPT_MONCHAN, "MonChan")) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); - rpt_hangup(myrpt, RPT_MONCHAN); + rpt_hangup(myrpt, RPT_LOCALTXCHAN); return -1; } - if (myrpt->p.duplex == 2 || myrpt->p.duplex == 4) { - res = rpt_conf_create(myrpt->pchannel, myrpt, RPT_CONF, RPT_CONF_CONFANNMON); - } else { - res = rpt_conf_create(myrpt->pchannel, myrpt, RPT_CONF, RPT_CONF_CONF | RPT_CONF_LISTENER | RPT_CONF_TALKER); - } - if (res) { + if (rpt_request_local(myrpt, cap, RPT_RXPCHAN, "RXPChan")) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); rpt_hangup(myrpt, RPT_MONCHAN); return -1; } - if (rpt_mon_setup(myrpt)) { + if (rpt_conf_add(myrpt->pchannel, myrpt, RPT_CONF)) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); + rpt_hangup(myrpt, RPT_RXPCHAN); rpt_hangup(myrpt, RPT_MONCHAN); return -1; } - if (rpt_request_pseudo(myrpt, cap, RPT_PARROTCHAN)) { + if (rpt_conf_add(myrpt->rxpchannel, myrpt, RPT_CONF)) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); + rpt_hangup(myrpt, RPT_RXPCHAN); rpt_hangup(myrpt, RPT_MONCHAN); return -1; } - if (rpt_request_pseudo(myrpt, cap, RPT_VOXCHAN)) { + /*! \todo Need to verify always setting MONCHAN to TXCONF is "ok" or how to deal at dialtime*/ + + if (rpt_conf_add(myrpt->monchannel, myrpt, RPT_TXCONF)) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); + rpt_hangup(myrpt, RPT_RXPCHAN); rpt_hangup(myrpt, RPT_MONCHAN); - rpt_hangup(myrpt, RPT_PARROTCHAN); return -1; } - if (rpt_request_pseudo(myrpt, cap, RPT_TXPCHAN)) { + if (rpt_request_local(myrpt, cap, RPT_TXPCHAN, "TXPChan")) { rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); + rpt_hangup(myrpt, RPT_RXPCHAN); rpt_hangup(myrpt, RPT_MONCHAN); - rpt_hangup(myrpt, RPT_PARROTCHAN); - rpt_hangup(myrpt, RPT_VOXCHAN); return -1; } - - /* make a conference for the tx */ - if (rpt_conf_add(myrpt->txpchannel, myrpt, RPT_TXCONF, RPT_CONF_CONF | RPT_CONF_TALKER)) { + if (rpt_conf_add(myrpt->txpchannel, myrpt, RPT_TXCONF)) { rpt_hangup_rx_tx(myrpt); + rpt_hangup(myrpt, RPT_TXPCHAN); rpt_hangup(myrpt, RPT_PCHAN); + rpt_hangup(myrpt, RPT_RXPCHAN); rpt_hangup(myrpt, RPT_MONCHAN); - rpt_hangup(myrpt, RPT_PARROTCHAN); - rpt_hangup(myrpt, RPT_VOXCHAN); - rpt_hangup(myrpt, RPT_TXPCHAN); return -1; } - return 0; } @@ -3030,16 +3074,13 @@ static inline int rpt_any_hangups(struct rpt *myrpt) if (ast_check_hangup(myrpt->monchannel)) { return -1; } - if (myrpt->parrotchannel && ast_check_hangup(myrpt->parrotchannel)) { - return -1; - } - if (myrpt->voxchannel && ast_check_hangup(myrpt->voxchannel)) { + if (ast_check_hangup(myrpt->txpchannel)) { return -1; } - if (ast_check_hangup(myrpt->txpchannel)) { + if (ast_check_hangup(myrpt->rxpchannel)) { return -1; } - if (myrpt->dahditxchannel && ast_check_hangup(myrpt->dahditxchannel)) { + if (myrpt->localtxchannel && ast_check_hangup(myrpt->localtxchannel)) { return -1; } return 0; @@ -3084,7 +3125,7 @@ static inline void log_keyed(struct rpt *myrpt) myrpt->dailykeyups++; myrpt->totalkeyups++; rpt_mutex_unlock(&myrpt->lock); - if (!IS_PSEUDO(myrpt->txchannel)) { + if (!IS_LOCAL(myrpt->txchannel)) { ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_KEY); } rpt_mutex_lock(&myrpt->lock); @@ -3100,7 +3141,7 @@ static inline void log_unkeyed(struct rpt *myrpt) myrpt->txkeyed = 0; time(&myrpt->lasttxkeyedtime); rpt_mutex_unlock(&myrpt->lock); - if (!IS_PSEUDO(myrpt->txchannel)) { + if (!IS_LOCAL(myrpt->txchannel)) { ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY); } rpt_mutex_lock(&myrpt->lock); @@ -3410,13 +3451,17 @@ static inline void periodic_process_links(struct rpt *myrpt, const int elap) */ static inline void do_key_post(struct rpt *myrpt) { - char str[100]; + struct ast_str *str = ast_str_create(RPT_AST_STR_INIT_SIZE); time_t now; + if (!str) { + return; + } time(&now); - snprintf(str, sizeof(str), "keyed=%d&keytime=%d", myrpt->keyed, myrpt->lastkeyedtime ? ((int) (now - myrpt->lastkeyedtime)) : 0); + ast_str_set(&str, 0, "&keyed=%d&keytime=%d", myrpt->keyed, myrpt->lastkeyedtime ? ((int) (now - myrpt->lastkeyedtime)) : 0); rpt_mutex_unlock(&myrpt->lock); statpost(myrpt, str); + ast_free(str); rpt_mutex_lock(&myrpt->lock); } @@ -3441,7 +3486,7 @@ static inline int do_link_post(struct rpt *myrpt) return -1; } nstr = 0; - ast_str_set(&str, 0, "%s", "nodes="); + ast_str_set(&str, 0, "%s", "&nodes="); RPT_LIST_TRAVERSE(myrpt->links, l, l_it) { /* if is not a real link, ignore it */ if (l->name[0] == '0') { @@ -3469,7 +3514,7 @@ static inline int do_link_post(struct rpt *myrpt) (int) myrpt->totaltxtime / 1000, myrpt->timeouts, myrpt->totalexecdcommands, myrpt->keyed, myrpt->lastkeyedtime ? ((int) (now - myrpt->lastkeyedtime)) : 0); rpt_mutex_unlock(&myrpt->lock); - statpost(myrpt, ast_str_buffer(str)); + statpost(myrpt, str); rpt_mutex_lock(&myrpt->lock); ast_free(str); return 0; @@ -3552,7 +3597,9 @@ static inline int update_timers(struct rpt *myrpt, const int elap, const int tot return 0; } - +/*! + * \brief Update parrot channel -> parrot record timer is complete OR parrot mode changed to off + */ static inline int update_parrot(struct rpt *myrpt) { union { @@ -3561,10 +3608,6 @@ static inline int update_parrot(struct rpt *myrpt) char _filler[8]; } pu; - if (rpt_parrot_add(myrpt)) { - return -1; - } - if (myrpt->parrotstream) { ast_closestream(myrpt->parrotstream); } @@ -3688,6 +3731,22 @@ static int rxchannel_qwrite_cb(void *obj, void *arg, int flags) return 0; } +/*! \brief Check and close parrot files if needed */ +static inline void check_parrot(struct rpt *myrpt) +{ + if (!(myrpt->p.parrotmode != PARROT_MODE_OFF || myrpt->parrotonce)) { + char myfname[300]; + + if (myrpt->parrotstream) { + ast_closestream(myrpt->parrotstream); + myrpt->parrotstream = NULL; + } + + snprintf(myfname, sizeof(myfname), PARROTFILE ".wav", myrpt->name, myrpt->parrotcnt); + unlink(myfname); + } +} + static inline int rxchannel_read(struct rpt *myrpt, const int lasttx) { int ismuted; @@ -3699,7 +3758,7 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx) ast_debug(1, "@@@@ rpt:Hung Up\n"); return -1; } - + check_parrot(myrpt); if (f->frametype == AST_FRAME_TEXT && myrpt->rxchankeyed) { char myrxrssi[RSSI_SZ + 1]; if (sscanf((char *) f->data.ptr, "R " S_FMT(RSSI_SZ), myrxrssi) == 1) { @@ -3856,7 +3915,7 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx) RPT_MUTE_FRAME(f); } - ismuted = dtmfed || rpt_conf_get_muted(myrpt->dahdirxchannel, myrpt); + ismuted = dtmfed || rpt_conf_get_muted(myrpt->localrxchannel, myrpt); dtmfed = 0; if (myrpt->p.votertype == 1) { @@ -3887,9 +3946,14 @@ static inline int rxchannel_read(struct rpt *myrpt, const int lasttx) } f1 = rpt_frame_queue_helper(&myrpt->frame_queue, f, ismuted); if (f1) { - ast_write(myrpt->localoverride ? myrpt->txpchannel : myrpt->pchannel, f1); - if (((myrpt->p.duplex < 2 && !myrpt->txkeyed) || myrpt->p.duplex == 3) && myrpt->monstream && myrpt->keyed) { - ast_writestream(myrpt->monstream, f1); + ast_write(myrpt->localoverride ? myrpt->txpchannel : myrpt->rxpchannel, f1); + if (((myrpt->p.duplex < 2 && !myrpt->txkeyed) || myrpt->p.duplex == 3) && myrpt->keyed) { + if (myrpt->monstream) { + ast_writestream(myrpt->monstream, f1); + } + if (myrpt->parrotstream) { + ast_writestream(myrpt->parrotstream, f1); + } } if ((myrpt->p.duplex < 2) && myrpt->keyed && myrpt->p.outstreamcmd && (myrpt->outstreampipe[1] != -1)) { outstream_write(myrpt, f1); @@ -4106,15 +4170,17 @@ static inline int pchannel_read(struct rpt *myrpt) return -1; } if (f->frametype == AST_FRAME_VOICE) { - if (!myrpt->localoverride) { + if (!myrpt->localoverride && ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4))) { + /* We are in full duplex mode, send pchannel audio to txpchannel + * this audio includes the rxaudio frames to be retransmitted + * In DAHDI this was conference join in ANNOUNCER and MONITOR + */ ast_write(myrpt->txpchannel, f); } } if (f->frametype == AST_FRAME_CONTROL) { if (f->subclass.integer == AST_CONTROL_HANGUP) { ast_debug(1, "@@@@ rpt:Hung Up\n"); - ast_frfree(f); - return 0; } } ast_frfree(f); @@ -4149,9 +4215,9 @@ static inline int txchannel_read(struct rpt *myrpt) return wait_for_hangup_helper(myrpt->txchannel, "txchannel"); } -static inline int dahditxchannel_read(struct rpt *myrpt, char *restrict myfirst) +static inline int localtxchannel_read(struct rpt *myrpt, char *restrict myfirst) { - struct ast_frame *f = ast_read(myrpt->dahditxchannel); + struct ast_frame *f = ast_read(myrpt->localtxchannel); if (!f) { ast_debug(1, "@@@@ rpt:Hung Up\n"); return -1; @@ -4199,7 +4265,7 @@ static inline int dahditxchannel_read(struct rpt *myrpt, char *restrict myfirst) } ast_write(myrpt->txchannel, f); } - return hangup_frame_helper(myrpt->dahditxchannel, "dahditxchannel", f); + return hangup_frame_helper(myrpt->localtxchannel, "localtxchannel", f); } /*! \brief Safely hang up any channel, even if a PBX could be running on it */ @@ -4330,7 +4396,21 @@ static inline int process_link_channels(struct rpt *myrpt, struct ast_channel *w struct timeval now; if (l->disctime) { - continue; + /* We are disconnected but still need to read and discard frames */ + if (who == l->pchan) { + struct ast_frame *f; + + rpt_mutex_unlock(&myrpt->lock); + f = ast_read(l->pchan); + if (!f) { + ast_debug(1, "@@@@ rpt:Hung Up\n"); + return -1; + } + ast_frfree(f); + return 0; + } else { + continue; + } } remrx = 0; @@ -4397,8 +4477,6 @@ static inline int process_link_channels(struct rpt *myrpt, struct ast_channel *w int ismuted, n1; float fac; - dahdi_bump_buffers(l->pchan, f->samples); /* Make room if needed */ - fac = 1.0; if (l->chan) { if (CHAN_TECH(l->chan, "echolink")) { @@ -4644,11 +4722,18 @@ static inline int monchannel_read(struct rpt *myrpt) ast_debug(1, "@@@@ rpt:Hung Up\n"); return -1; } + check_parrot(myrpt); + if (f->frametype == AST_FRAME_VOICE) { struct rpt_link *l; - if (((myrpt->p.duplex > 1 && myrpt->p.duplex != 3) || (myrpt->txkeyed && !myrpt->keyed)) && myrpt->monstream) { - ast_writestream(myrpt->monstream, f); + if ((myrpt->p.duplex > 1 && myrpt->p.duplex != 3) || (myrpt->txkeyed && !myrpt->keyed)) { + if (myrpt->monstream) { + ast_writestream(myrpt->monstream, f); + } + if (myrpt->parrotstream) { + ast_writestream(myrpt->parrotstream, f); + } } if (((myrpt->p.duplex >= 2) || (!myrpt->keyed)) && myrpt->p.outstreamcmd && (myrpt->outstreampipe[1] != -1)) { outstream_write(myrpt, f); @@ -4675,56 +4760,33 @@ static inline int monchannel_read(struct rpt *myrpt) } return hangup_frame_helper(myrpt->monchannel, "monchannel", f); } - -static inline int parrotchannel_read(struct rpt *myrpt) +static inline int rxpchannel_read(struct rpt *myrpt) { - struct ast_frame *f = ast_read(myrpt->parrotchannel); + struct ast_frame *f = ast_read(myrpt->rxpchannel); if (!f) { ast_debug(1, "@@@@ rpt:Hung Up\n"); return -1; } - if (!((myrpt->p.parrotmode != PARROT_MODE_OFF) || myrpt->parrotonce)) { - char myfname[300]; - - if (myrpt->parrotstream) { - ast_closestream(myrpt->parrotstream); - myrpt->parrotstream = 0; + if (f->frametype == AST_FRAME_VOICE) { + if (!myrpt->localoverride && (myrpt->p.duplex != 2) && (myrpt->p.duplex != 4)) { + /* We are in 1/2 duplex mode or mode 3, send rxpchannel audio to txpchannel + * this audio excludes the rxaudio frames + * In DAHDI this was conference join in TALKER and LISTENER + */ + ast_write(myrpt->txpchannel, f); } - snprintf(myfname, sizeof(myfname), PARROTFILE, myrpt->name, myrpt->parrotcnt); - strcat(myfname, ".wav"); - unlink(myfname); - } else if (f->frametype == AST_FRAME_VOICE) { - if (myrpt->parrotstream) - ast_writestream(myrpt->parrotstream, f); } - return hangup_frame_helper(myrpt->parrotchannel, "parrotchannel", f); + return hangup_frame_helper(myrpt->rxpchannel, "rxpchannel", f); } -static inline int voxchannel_read(struct rpt *myrpt) +static inline int txpchannel_read(struct rpt *myrpt) { - struct ast_frame *f = ast_read(myrpt->voxchannel); + struct ast_frame *f = ast_read(myrpt->txpchannel); if (!f) { ast_debug(1, "@@@@ rpt:Hung Up\n"); return -1; } - if (f->frametype == AST_FRAME_VOICE) { - int n = dovox(&myrpt->vox, f->data.ptr, f->datalen / 2); - if (n != myrpt->wasvox) { - ast_debug(1, "Node %s, vox %d\n", myrpt->name, n); - myrpt->wasvox = n; - myrpt->voxtostate = 0; - if (n) - myrpt->voxtotimer = myrpt->p.voxtimeout_ms; - else - myrpt->voxtotimer = 0; - } - } - return hangup_frame_helper(myrpt->voxchannel, "voxchannel", f); -} - -static inline int txpchannel_read(struct rpt *myrpt) -{ - return wait_for_hangup_helper(myrpt->txpchannel, "txpchannel"); + return hangup_frame_helper(myrpt->txpchannel, "txpchannel", f); } static inline void voxtostate_to_voxtotimer(struct rpt *myrpt) @@ -4774,7 +4836,7 @@ static void *rpt(void *this) myrpt->macrobuf = ast_str_create(MAXMACRO); if (!myrpt->macrobuf) { myrpt->rpt_thread = AST_PTHREADT_STOP; - pthread_exit(NULL); + return NULL; } } rpt_mutex_lock(&myrpt->lock); @@ -4807,7 +4869,7 @@ static void *rpt(void *this) ast_log(LOG_ERROR, "ioperm(%x) not supported on this architecture\n", myrpt->p.iobase); #endif myrpt->rpt_thread = AST_PTHREADT_STOP; - pthread_exit(NULL); + return NULL; } cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); @@ -4815,17 +4877,17 @@ static void *rpt(void *this) ast_log(LOG_ERROR, "Failed to alloc cap\n"); rpt_mutex_unlock(&myrpt->lock); myrpt->rpt_thread = AST_PTHREADT_STOP; - pthread_exit(NULL); + return NULL; } ast_format_cap_append(cap, ast_format_slin, 0); - + ast_debug(1, "Setting up channels"); if (rpt_setup_channels(myrpt, cap)) { rpt_mutex_unlock(&myrpt->lock); myrpt->rpt_thread = AST_PTHREADT_STOP; disable_rpt(myrpt); /* Disable repeater */ ao2_ref(cap, -1); - pthread_exit(NULL); + return NULL; } ao2_ref(cap, -1); @@ -4837,7 +4899,7 @@ static void *rpt(void *this) rpt_mutex_unlock(&myrpt->lock); rpt_hangup(myrpt, RPT_PCHAN); rpt_hangup_rx_tx(myrpt); - pthread_exit(NULL); + return NULL; } myrpt->links = ao2_container_alloc_list(0, /* AO2 object flags. 0 means to use the default behavior */ AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN, /* AO2 container flags. New items should be added to the front of the list */ @@ -4910,7 +4972,7 @@ static void *rpt(void *this) if (!(myrpt->dsp = ast_dsp_new())) { rpt_hangup(myrpt, RPT_RXCHAN); myrpt->rpt_thread = AST_PTHREADT_STOP; - pthread_exit(NULL); + return NULL; } /*! \todo At this point, we have a memory leak, because dsp needs to be freed. */ /*! \todo Find out what the right place is to free dsp, i.e. when myrpt itself goes away. */ @@ -4952,7 +5014,7 @@ static void *rpt(void *this) rpt_update_boolean(myrpt, "RPT_ALINKS", -1); myrpt->ready = 1; looptimestart = rpt_tvnow(); - + ast_autoservice_stop(myrpt->rxchannel); while (ms >= 0) { struct ast_channel *who; struct ast_channel *cs[300], *cs1[300]; @@ -5366,16 +5428,10 @@ static void *rpt(void *this) myrpt->rem_dtmfbuf[0] = 0; } - if (myrpt->exttx && myrpt->parrotchannel && ((myrpt->p.parrotmode != PARROT_MODE_OFF) || myrpt->parrotonce) && - (myrpt->parrotstate == PARROT_STATE_IDLE)) { + if (myrpt->exttx && ((myrpt->p.parrotmode != PARROT_MODE_OFF) || myrpt->parrotonce) && (myrpt->parrotstate == PARROT_STATE_IDLE)) { char myfname[300]; - - /* first put the channel on the conference in announce mode */ - if (rpt_conf_add_announcer_monitor(myrpt->parrotchannel, myrpt)) { - rpt_mutex_unlock(&myrpt->lock); - break; - } - + /* setup audiohook to spy on the pchannel. */ + ast_verb(4, "Parrot attached to %s\n", ast_channel_name(myrpt->pchannel)); snprintf(myfname, sizeof(myfname), PARROTFILE ".wav", myrpt->name, myrpt->parrotcnt); unlink(myfname); myrpt->parrotstate = PARROT_STATE_RECORDING; @@ -5424,7 +5480,7 @@ static void *rpt(void *this) myrpt->lastitx = x; if (myrpt->p.itxctcss) { if (IS_DAHDI_CHAN(myrpt->rxchannel)) { - dahdi_radio_set_ctcss_encode(myrpt->dahdirxchannel, !x); + dahdi_radio_set_ctcss_encode(myrpt->localrxchannel, !x); } else if (CHAN_TECH(myrpt->rxchannel, "radio") || CHAN_TECH(myrpt->rxchannel, "simpleusb")) { snprintf(str, sizeof(str), "TXCTCSS %d", !(!x)); ast_sendtext(myrpt->rxchannel, str); @@ -5455,19 +5511,19 @@ static void *rpt(void *this) n = 0; cs[n++] = myrpt->rxchannel; cs[n++] = myrpt->pchannel; - cs[n++] = myrpt->monchannel; - if (myrpt->parrotchannel) - cs[n++] = myrpt->parrotchannel; - if (myrpt->voxchannel) - cs[n++] = myrpt->voxchannel; cs[n++] = myrpt->txpchannel; + cs[n++] = myrpt->rxpchannel; + if (myrpt->monchannel) + cs[n++] = myrpt->monchannel; if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel; - if (myrpt->dahditxchannel != myrpt->txchannel) - cs[n++] = myrpt->dahditxchannel; + if (myrpt->localtxchannel != myrpt->txchannel) + cs[n++] = myrpt->localtxchannel; RPT_LIST_TRAVERSE(myrpt->links, l, l_it) { - if ((!l->killme) && (!l->disctime) && l->chan) { - cs[n++] = l->chan; + if (!l->killme) { + if (l->chan) { + cs[n++] = l->chan; + } cs[n++] = l->pchan; } } @@ -5520,13 +5576,25 @@ static void *rpt(void *this) len = ast_str_strlen(myrpt->macrobuf); c = str[0]; time(&t); + if (myrpt->patch_talking != myrpt->wasvox) { + /* Autopatch vox has changed states. */ + ast_debug(1, "Node %s, vox %d\n", myrpt->name, myrpt->patch_talking); + myrpt->wasvox = myrpt->patch_talking; + myrpt->voxtostate = 0; + if (myrpt->patch_talking) { + myrpt->voxtotimer = myrpt->p.voxtimeout_ms; + } else { + myrpt->voxtotimer = 0; + } + } if (c && !myrpt->macrotimer && starttime && t > starttime) { char cin = c & 0x7f; myrpt->macrotimer = MACROTIME; ast_copy_string(str, str + 1, len); ast_str_truncate(myrpt->macrobuf, len - 1); - if ((cin == 'p') || (cin == 'P')) + if ((cin == 'p') || (cin == 'P')) { myrpt->macrotimer = MACROPTIME; + } rpt_mutex_unlock(&myrpt->lock); donodelog_fmt(myrpt, "DTMF(M),MAIN,%c", cin); local_dtmf_helper(myrpt, c); @@ -5545,38 +5613,30 @@ static void *rpt(void *this) break; } continue; - } else if (who == myrpt->txchannel) { /* if it was a read from tx */ - if (txchannel_read(myrpt)) { + } else if (who == myrpt->rxpchannel) { + if (rxpchannel_read(myrpt)) { break; } - continue; - } else if (who == myrpt->dahditxchannel) { /* if it was a read from pseudo-tx */ - if (dahditxchannel_read(myrpt, &myfirst)) { + } else if (who == myrpt->txchannel) { /* if it was a read from tx - Note if rxchannel = txchannel, we won't get here */ + if (txchannel_read(myrpt)) { break; } continue; - } - - if (process_link_channels(myrpt, who, &myfirst)) { - break; - } - - if (who == myrpt->monchannel) { - if (monchannel_read(myrpt)) { - break; - } - } else if (myrpt->parrotchannel && who == myrpt->parrotchannel) { - if (parrotchannel_read(myrpt)) { - break; - } - } else if (myrpt->voxchannel && who == myrpt->voxchannel) { - if (voxchannel_read(myrpt)) { + } else if (who == myrpt->localtxchannel) { /* if it was a read from local-tx */ + if (localtxchannel_read(myrpt, &myfirst)) { break; } + continue; } else if (who == myrpt->txpchannel) { /* if it was a read from remote tx */ if (txpchannel_read(myrpt)) { break; } + } else if (who == myrpt->monchannel) { + if (monchannel_read(myrpt)) { + break; + } + } else if (process_link_channels(myrpt, who, &myfirst)) { + break; } } @@ -5590,17 +5650,14 @@ static void *rpt(void *this) usleep(50000); } rpt_hangup(myrpt, RPT_PCHAN); - rpt_hangup(myrpt, RPT_MONCHAN); - if (myrpt->parrotchannel) { - rpt_hangup(myrpt, RPT_PARROTCHAN); + if (myrpt->monchannel) { + rpt_hangup(myrpt, RPT_MONCHAN); } myrpt->parrotstate = PARROT_STATE_IDLE; - if (myrpt->voxchannel) { - rpt_hangup(myrpt, RPT_VOXCHAN); - } rpt_hangup(myrpt, RPT_TXPCHAN); - if (myrpt->dahditxchannel != myrpt->txchannel) { - rpt_hangup(myrpt, RPT_DAHDITXCHAN); + rpt_hangup(myrpt, RPT_RXPCHAN); + if (myrpt->localtxchannel != myrpt->txchannel) { + rpt_hangup(myrpt, RPT_LOCALTXCHAN); } rpt_hangup_rx_tx(myrpt); rpt_frame_queue_free(&myrpt->frame_queue); @@ -6029,7 +6086,7 @@ static void *rpt_master(void *ignore) done: ast_mutex_unlock(&rpt_master_lock); ast_debug(1, "app_rpt master thread exiting\n"); - pthread_exit(NULL); + return NULL; } static inline int exec_chan_read(struct rpt *myrpt, struct ast_channel *chan, char *restrict keyed, @@ -6298,15 +6355,15 @@ static int get_his_ip(struct ast_channel *chan, char *buf, size_t len) static inline int kenwood_uio_helper(struct rpt *myrpt) { - if (rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_UIOMODE, 3)) { - ast_log(LOG_ERROR, "Cannot set UIOMODE on %s: %s\n", ast_channel_name(myrpt->dahditxchannel), strerror(errno)); + if (rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_UIOMODE, 3)) { + ast_log(LOG_ERROR, "Cannot set UIOMODE on %s: %s\n", ast_channel_name(myrpt->localtxchannel), strerror(errno)); return -1; } - if (rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_UIODATA, 3)) { - ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->dahditxchannel), strerror(errno)); + if (rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_UIODATA, 3)) { + ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->localtxchannel), strerror(errno)); return -1; } - if (dahdi_set_offhook(myrpt->dahditxchannel)) { + if (dahdi_set_offhook(myrpt->localtxchannel)) { return -1; } return 0; @@ -6822,7 +6879,6 @@ static int rpt_exec(struct ast_channel *chan, const char *data) } l->mode = MODE_TRANSCEIVE; ast_copy_string(l->name, b1, MAXNODESTR); - l->isremote = 0; l->chan = chan; l->last_frame_sent = 0; l->connected = 1; @@ -6832,8 +6888,6 @@ static int rpt_exec(struct ast_channel *chan, const char *data) l->phonemode = phone_mode; l->phonevox = phone_vox; l->phonemonitor = phone_monitor; - l->dtmfed = 0; - l->gott = 0; l->rxlingertimer = RX_LINGER_TIME; l->newkeytimer = NEWKEYTIME; l->link_newkey = RADIO_KEY_ALLOWED; @@ -6872,7 +6926,7 @@ static int rpt_exec(struct ast_channel *chan, const char *data) ast_format_cap_append(cap, ast_format_slin, 0); /* allocate a pseudo-channel thru asterisk */ - if (__rpt_request_pseudo(l, cap, RPT_PCHAN, RPT_LINK_CHAN)) { + if (__rpt_request_local(l, cap, RPT_PCHAN, RPT_LINK_CHAN, "IAXLink")) { ao2_ref(cap, -1); ao2_ref(l, -1); return -1; @@ -6881,7 +6935,7 @@ static int rpt_exec(struct ast_channel *chan, const char *data) ao2_ref(cap, -1); /* make a conference for the tx */ - if (rpt_conf_add_speaker(l->pchan, myrpt)) { + if (rpt_conf_add(l->pchan, myrpt, RPT_CONF)) { ao2_ref(l, -1); return -1; } @@ -7053,7 +7107,7 @@ static int rpt_exec(struct ast_channel *chan, const char *data) return -1; } - myrpt->dahditxchannel = NULL; + myrpt->localtxchannel = NULL; if (myrpt->txchanname) { if (__rpt_request(myrpt, cap, RPT_TXCHAN, RPT_LINK_CHAN)) { rpt_mutex_unlock(&myrpt->lock); @@ -7063,15 +7117,15 @@ static int rpt_exec(struct ast_channel *chan, const char *data) } } else { myrpt->txchannel = myrpt->rxchannel; - if (IS_DAHDI_CHAN_NAME(myrpt->rxchanname)) { - myrpt->dahditxchannel = myrpt->rxchannel; + if (IS_LOCAL_NAME(myrpt->rxchanname)) { + myrpt->localtxchannel = myrpt->rxchannel; } } i = 3; ast_channel_setoption(myrpt->rxchannel, AST_OPTION_TONE_VERIFY, &i, sizeof(char), 0); - if (rpt_request_pseudo(myrpt, cap, RPT_PCHAN)) { + if (rpt_request_local(myrpt, cap, RPT_PCHAN, "PChan")) { rpt_mutex_unlock(&myrpt->lock); rpt_hangup_rx_tx(myrpt); ao2_ref(cap, -1); @@ -7080,21 +7134,25 @@ static int rpt_exec(struct ast_channel *chan, const char *data) ao2_ref(cap, -1); - if (!myrpt->dahdirxchannel) - myrpt->dahdirxchannel = myrpt->pchannel; - if (!myrpt->dahditxchannel) - myrpt->dahditxchannel = myrpt->pchannel; + if (!myrpt->localrxchannel) + myrpt->localrxchannel = myrpt->rxpchannel; + if (!myrpt->localtxchannel) + myrpt->localtxchannel = myrpt->pchannel; /* first put the channel on the conference in announce/monitor mode */ - if (rpt_conf_create(myrpt->pchannel, myrpt, RPT_TXCONF, RPT_CONF_CONFANNMON)) { + if (rpt_conf_create(myrpt, RPT_TXCONF)) { rpt_mutex_unlock(&myrpt->lock); rpt_hangup_rx_tx(myrpt); rpt_hangup(myrpt, RPT_PCHAN); return -1; } - rpt_equate_tx_conf(myrpt); - + if (rpt_conf_add(myrpt->pchannel, myrpt, RPT_TXCONF)) { + rpt_mutex_unlock(&myrpt->lock); + rpt_hangup_rx_tx(myrpt); + rpt_hangup(myrpt, RPT_PCHAN); + return -1; + } /* if serial io port, open it */ myrpt->iofd = -1; if (myrpt->p.ioport && ((myrpt->iofd = openserial(myrpt, myrpt->p.ioport)) == -1)) { @@ -7106,8 +7164,8 @@ static int rpt_exec(struct ast_channel *chan, const char *data) } iskenwood_pci4 = 0; - if ((myrpt->iofd < 1) && (myrpt->txchannel == myrpt->dahditxchannel)) { - res = rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_NONE); + if ((myrpt->iofd < 1) && (myrpt->txchannel == myrpt->localtxchannel)) { + res = rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_NONE); /* if PCIRADIO and kenwood selected */ if ((!res) && (!strcmp(myrpt->remoterig, REMOTE_RIG_KENWOOD))) { if (kenwood_uio_helper(myrpt)) { @@ -7117,20 +7175,20 @@ static int rpt_exec(struct ast_channel *chan, const char *data) iskenwood_pci4 = 1; } } - if (myrpt->txchannel == myrpt->dahditxchannel) { - dahdi_set_onhook(myrpt->dahditxchannel); + if (myrpt->txchannel == myrpt->localtxchannel) { + dahdi_set_onhook(myrpt->localtxchannel); /* if PCIRADIO and Yaesu ft897/ICOM IC-706 selected */ if ((myrpt->iofd < 1) && (!res) && ((!strcmp(myrpt->remoterig, REMOTE_RIG_FT897)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_FT950)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_FT100)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_XCAT)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_IC706)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_TM271)))) { - if (rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_UIOMODE, 1)) { - ast_log(LOG_ERROR, "Cannot set UIOMODE on %s: %s\n", ast_channel_name(myrpt->dahditxchannel), strerror(errno)); + if (rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_UIOMODE, 1)) { + ast_log(LOG_ERROR, "Cannot set UIOMODE on %s: %s\n", ast_channel_name(myrpt->localtxchannel), strerror(errno)); rpt_mutex_unlock(&myrpt->lock); return -1; } - if (rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_UIODATA, 3)) { - ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->dahditxchannel), strerror(errno)); + if (rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_UIODATA, 3)) { + ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->localtxchannel), strerror(errno)); rpt_mutex_unlock(&myrpt->lock); return -1; } @@ -7190,15 +7248,11 @@ static int rpt_exec(struct ast_channel *chan, const char *data) ast_set_read_format(chan, ast_format_slin); rem_rx = 0; remkeyed = 0; - /* if we are on 2w loop and are a remote, turn EC on */ - if (myrpt->remote && myrpt->rxchannel == myrpt->txchannel) { - dahdi_set_echocancel(myrpt->dahdirxchannel, 128); - } answer_newkey_helper(myrpt, chan, phone_mode); - if (myrpt->rxchannel == myrpt->dahdirxchannel) { - if (dahdi_rx_offhook(myrpt->dahdirxchannel) == 1) { + if (myrpt->rxchannel == myrpt->localrxchannel) { + if (dahdi_rx_offhook(myrpt->localrxchannel) == 1) { ast_indicate(chan, AST_CONTROL_RADIO_KEY); myrpt->remoterx = 1; remkeyed = 1; @@ -7457,9 +7511,9 @@ static int rpt_exec(struct ast_channel *chan, const char *data) } telem = telem->next; } - if (iskenwood_pci4 && myrpt->txchannel == myrpt->dahditxchannel) { - if (rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_UIODATA, 1)) { - ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->dahditxchannel), strerror(errno)); + if (iskenwood_pci4 && myrpt->txchannel == myrpt->localtxchannel) { + if (rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_UIODATA, 1)) { + ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->localtxchannel), strerror(errno)); return -1; } } else { @@ -7475,9 +7529,9 @@ static int rpt_exec(struct ast_channel *chan, const char *data) if (!myrpt->remtxfreqok) { rpt_telemetry(myrpt, UNAUTHTX, NULL); } - if (iskenwood_pci4 && myrpt->txchannel == myrpt->dahditxchannel) { - if (rpt_radio_set_param(myrpt->dahditxchannel, RPT_RADPAR_UIODATA, 3)) { - ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->dahditxchannel), strerror(errno)); + if (iskenwood_pci4 && myrpt->txchannel == myrpt->localtxchannel) { + if (rpt_radio_set_param(myrpt->localtxchannel, RPT_RADPAR_UIODATA, 3)) { + ast_log(LOG_ERROR, "Cannot set UIODATA on %s: %s\n", ast_channel_name(myrpt->localtxchannel), strerror(errno)); return -1; } } else { @@ -7556,7 +7610,7 @@ static int rpt_exec(struct ast_channel *chan, const char *data) myrpt->remoteon = 0; rpt_mutex_unlock(&myrpt->lock); rpt_frame_queue_free(&myrpt->frame_queue); - if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->dahditxchannel)) { + if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->localtxchannel)) { if (kenwood_uio_helper(myrpt)) { return -1; } @@ -7708,6 +7762,12 @@ static int reload(void) ast_mutex_unlock(&rpt_master_lock); return (0); } - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Radio Repeater/Remote Base Application", .support_level = AST_MODULE_SUPPORT_EXTENDED, - .load = load_module, .unload = unload_module, .reload = reload, .requires = "res_curl", ); +/* clang-format off */ +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Radio Repeater/Remote Base Application", + .support_level = AST_MODULE_SUPPORT_EXTENDED, + .load = load_module, + .unload = unload_module, + .reload = reload, + .requires = "res_curl, bridge_softmix, chan_bridge_media", +); +/* clang-format on */ diff --git a/apps/app_rpt/app_rpt.h b/apps/app_rpt/app_rpt.h index 0e101354b..53c664d3f 100644 --- a/apps/app_rpt/app_rpt.h +++ b/apps/app_rpt/app_rpt.h @@ -741,12 +741,13 @@ struct rpt_cmd_struct { enum rpt_command_source command_source; }; +struct ast_bridge; /* Forward declaration */ + +/*! \brief Structure used to manage conference bridges */ struct rpt_conf { - /* DAHDI conference numbers */ - struct { - int conf; - int txconf; - } dahdiconf; + /* Conference bridge channels */ + struct ast_bridge *conf; + struct ast_bridge *txconf; }; /*! \brief Populate rpt structure with data */ @@ -941,16 +942,21 @@ struct rpt { int parrottimer; unsigned int parrotcnt; int telemmode; - struct ast_channel *rxchannel,*txchannel, *monchannel, *parrotchannel; - struct ast_channel *pchannel,*txpchannel, *dahdirxchannel, *dahditxchannel; - struct ast_channel *voxchannel; + struct ast_channel *rxchannel; /*!< Channel connected to physical hardware, can be bi-directional */ + struct ast_channel *txchannel; /*!< Channel connect to physical hardware if separate otherwise equal to rxchannel */ + struct ast_channel *monchannel; /*!< Monitor channel used to record activity on the TXCONF */ + struct ast_channel *pchannel; /*!< Channel used to copy CONF bridge audio into txpchannel */ + struct ast_channel *rxpchannel; /*!< Channel used to copy RX audio into CONF bridge */ + struct ast_channel *txpchannel; /*!< Channel used to receive RX audio into the TXCONF bridge */ + struct ast_channel *localrxchannel; /*!< Channel used when in remote configuration for rx, may be set equal to pchannel */ + struct ast_channel *localtxchannel; /*!< Channel used to receive audio from the TXCONF bridge into the txchannel */ struct rpt_frame_queue frame_queue; struct rpt_tele tele; struct timeval lasttv,curtv; - pthread_t rpt_call_thread,rpt_thread; - time_t dtmf_time,rem_dtmf_time,dtmf_time_rem; - int calldigittimer; struct rpt_conf rptconf; + pthread_t rpt_call_thread, rpt_thread; + time_t dtmf_time, rem_dtmf_time, dtmf_time_rem; + int calldigittimer; int tailtimer, totimer, idtimer, cidx, scantimer, tmsgtimer, skedtimer, linkactivitytimer, elketimer; int remote_time_out_reset_unkey_interval_timer, time_out_reset_unkey_interval_timer; enum patch_call_mode callmode; @@ -1024,6 +1030,7 @@ struct rpt { int lastkeytimer; enum newkey rpt_newkey; int rxlingertimer; + rpt_bool patch_talking:1; rpt_bool inpadtest:1; rpt_bool localoverride:1; rpt_bool wasvox:1; @@ -1098,7 +1105,7 @@ struct nodelog { struct statpost { struct rpt *myrpt; - char *stats_url; + struct ast_str *stats_url; }; /*! \brief Whether a channel is using a specified technology */ @@ -1107,6 +1114,9 @@ struct statpost { #define IS_PSEUDO(c) (!strncasecmp(ast_channel_name(c), "DAHDI/pseudo", 12)) #define IS_PSEUDO_NAME(c) (!strncasecmp(c, "DAHDI/pseudo", 12)) +#define IS_LOCAL(c) (!strncasecmp(ast_channel_name(c), "Local", 5)) +#define IS_LOCAL_NAME(c) (!strncasecmp(c, "Local", 5)) + int rpt_debug_level(void); int rpt_set_debug_level(int newlevel); int rpt_num_rpts(void); diff --git a/apps/app_rpt/rpt_bridging.c b/apps/app_rpt/rpt_bridging.c index c4c73d68d..aa1073fd5 100644 --- a/apps/app_rpt/rpt_bridging.c +++ b/apps/app_rpt/rpt_bridging.c @@ -27,17 +27,22 @@ #include "asterisk.h" #include -#include /* use tone_zone_set_zone */ +#include "asterisk/bridge.h" +#include "asterisk/core_unreal.h" #include "asterisk/channel.h" #include "asterisk/indications.h" #include "asterisk/format_cache.h" /* use ast_format_slin */ +#include "asterisk/audiohook.h" #include "app_rpt.h" #include "rpt_bridging.h" #include "rpt_call.h" +/*! + * \brief used to display "words" in debug messages. + */ static const char *rpt_chan_type_str(enum rpt_chan_type chantype) { switch (chantype) { @@ -47,16 +52,14 @@ static const char *rpt_chan_type_str(enum rpt_chan_type chantype) return "txchan"; case RPT_PCHAN: return "pchan"; - case RPT_DAHDITXCHAN: - return "dahditxchan"; + case RPT_LOCALTXCHAN: + return "localtxchan"; case RPT_MONCHAN: return "monchan"; - case RPT_PARROTCHAN: - return "parrotchan"; - case RPT_VOXCHAN: - return "voxchan"; case RPT_TXPCHAN: return "txpchan"; + case RPT_RXPCHAN: + return "rxpchan"; } ast_assert(0); return NULL; @@ -70,11 +73,10 @@ static const char *rpt_chan_name(struct rpt *myrpt, enum rpt_chan_type chantype) case RPT_TXCHAN: return myrpt->txchanname; case RPT_PCHAN: - case RPT_DAHDITXCHAN: + case RPT_LOCALTXCHAN: case RPT_MONCHAN: - case RPT_PARROTCHAN: - case RPT_VOXCHAN: case RPT_TXPCHAN: + case RPT_RXPCHAN: return NULL; } ast_assert(0); @@ -91,16 +93,14 @@ static struct ast_channel **rpt_chan_channel(struct rpt *myrpt, struct rpt_link return &myrpt->txchannel; case RPT_PCHAN: return &myrpt->pchannel; - case RPT_DAHDITXCHAN: - return &myrpt->dahditxchannel; + case RPT_LOCALTXCHAN: + return &myrpt->localtxchannel; case RPT_MONCHAN: return &myrpt->monchannel; - case RPT_PARROTCHAN: - return &myrpt->parrotchannel; - case RPT_VOXCHAN: - return &myrpt->voxchannel; case RPT_TXPCHAN: return &myrpt->txpchannel; + case RPT_RXPCHAN: + return &myrpt->rxpchannel; } } else if (link) { switch (chantype) { @@ -114,7 +114,7 @@ static struct ast_channel **rpt_chan_channel(struct rpt *myrpt, struct rpt_link return NULL; } -#define RPT_DIAL_TIME 999 +#define RPT_DIAL_DURATION 999 /*! \brief Wait timeout for ast_call() executions (asterisk may not be using) */ void rpt_hangup(struct rpt *myrpt, enum rpt_chan_type chantype) { @@ -139,6 +139,10 @@ void rpt_hangup(struct rpt *myrpt, enum rpt_chan_type chantype) ast_debug(2, "Also resetting rxchannel\n"); myrpt->rxchannel = NULL; } + if (myrpt->localtxchannel && myrpt->localtxchannel == *chanptr) { + ast_debug(2, "Also resetting localtxchannel\n"); + myrpt->localtxchannel = NULL; + } break; default: break; @@ -153,15 +157,14 @@ static const char *rpt_chan_app(enum rpt_chan_type chantype, enum rpt_chan_flags { switch (chantype) { case RPT_RXCHAN: - return flags & RPT_LINK_CHAN ? "(Link Rx)" : "(Repeater Rx)"; + return flags & RPT_LINK_CHAN ? "Link Rx" : "Repeater Rx"; case RPT_TXCHAN: - return flags & RPT_LINK_CHAN ? "(Link Tx)" : "(Repeater Tx)"; + return flags & RPT_LINK_CHAN ? "Link Tx" : "Repeater Tx"; case RPT_PCHAN: - case RPT_DAHDITXCHAN: + case RPT_LOCALTXCHAN: case RPT_MONCHAN: - case RPT_PARROTCHAN: - case RPT_VOXCHAN: case RPT_TXPCHAN: + case RPT_RXPCHAN: return NULL; } ast_assert(0); @@ -176,11 +179,10 @@ static const char *rpt_chan_app_data(enum rpt_chan_type chantype) case RPT_TXCHAN: return "Tx"; case RPT_PCHAN: - case RPT_DAHDITXCHAN: + case RPT_LOCALTXCHAN: case RPT_MONCHAN: - case RPT_PARROTCHAN: - case RPT_VOXCHAN: case RPT_TXPCHAN: + case RPT_RXPCHAN: return NULL; } ast_assert(0); @@ -224,21 +226,13 @@ int __rpt_request(void *data, struct ast_format_cap *cap, enum rpt_chan_type cha return -1; } - /* XXX - * Note: Removed in refactoring: - * inside rpt_make_call, we should rpt_mutex_unlock(&myrpt->lock); - * before ast_call - * and - * rpt_mutex_lock(&myrpt->lock); - * afterwards, - * if flags & RPT_LINK_CHAN. - * This might not be necessary, but if it is, this should be re-added. */ - - rpt_make_call(chan, device, RPT_DIAL_TIME, tech, rpt_chan_app(chantype, flags), rpt_chan_app_data(chantype), myrpt->name); - if (ast_channel_state(chan) != AST_STATE_UP) { - ast_log(LOG_ERROR, "Requested channel %s not up?\n", ast_channel_name(chan)); - ast_hangup(chan); - return -1; + if (!IS_LOCAL_NAME(tech)) { + rpt_make_call(chan, device, RPT_DIAL_DURATION, tech, rpt_chan_app(chantype, flags), rpt_chan_app_data(chantype), myrpt->name); + if (ast_channel_state(chan) != AST_STATE_UP) { + ast_log(LOG_ERROR, "Requested channel %s not up?\n", ast_channel_name(chan)); + ast_hangup(chan); + return -1; + } } chanptr = rpt_chan_channel(myrpt, NULL, chantype); @@ -246,15 +240,10 @@ int __rpt_request(void *data, struct ast_format_cap *cap, enum rpt_chan_type cha switch (chantype) { case RPT_RXCHAN: - myrpt->dahdirxchannel = !strcasecmp(tech, "DAHDI") ? chan : NULL; + myrpt->localrxchannel = IS_LOCAL_NAME(tech) ? chan : NULL; break; case RPT_TXCHAN: - if (flags & RPT_LINK_CHAN) { - /* XXX Dunno if this difference is really necessary, but this is a literal refactor of existing logic... */ - myrpt->dahditxchannel = !strcasecmp(tech, "DAHDI") ? chan : NULL; - } else { - myrpt->dahditxchannel = !strcasecmp(tech, "DAHDI") && strcasecmp(device, "pseudo") ? chan : NULL; - } + myrpt->localtxchannel = IS_LOCAL_NAME(tech) ? chan : NULL; break; default: break; @@ -263,19 +252,62 @@ int __rpt_request(void *data, struct ast_format_cap *cap, enum rpt_chan_type cha return 0; } -struct ast_channel *rpt_request_pseudo_chan(struct ast_format_cap *cap) +static const char *rpt_bridge_chan_type_name(enum rpt_bridge_chan_type type) +{ + switch (type) { + case RPT_LOCAL: + return "Local"; + case RPT_TELEMETRY: + return "Announcer"; + case RPT_MONITOR: + return "Recorder"; + } + ast_assert(0); + return NULL; +} + +static const char *rpt_chan_type_name(enum rpt_chan_type type, enum rpt_chan_flags flags) { - struct ast_channel *chan = ast_request("DAHDI", cap, NULL, NULL, "pseudo", NULL); + if (flags & RPT_LINK_CHAN) { + return "Announcer"; + } + + switch (type) { + case RPT_MONCHAN: + case RPT_PCHAN: + return "Recorder"; + case RPT_RXPCHAN: + case RPT_TXPCHAN: + return "Announcer"; + default: + return "Local"; + } +} + +struct ast_channel *__rpt_request_local_chan(struct ast_format_cap *cap, const char *exten, enum rpt_bridge_chan_type type) +{ + struct ast_channel *chan; + + chan = ast_request(rpt_bridge_chan_type_name(type), cap, NULL, NULL, exten, NULL); if (!chan) { - ast_log(LOG_ERROR, "Failed to request pseudo channel\n"); + ast_log(LOG_ERROR, "Failed to request local channel\n"); return NULL; } - rpt_disable_cdr(chan); - ast_answer(chan); + + ast_debug(1, "Requesting channel %s setup\n", ast_channel_name(chan)); + ast_set_read_format(chan, ast_format_slin); + ast_set_write_format(chan, ast_format_slin); + if (type == RPT_LOCAL) { + /* Local channel needs to be answered. + * Announcer channels auto answer on creation. + */ + rpt_disable_cdr(chan); + ast_debug(1, "Requested channel %s cdr disabled\n", ast_channel_name(chan)); + } return chan; } -int __rpt_request_pseudo(void *data, struct ast_format_cap *cap, enum rpt_chan_type chantype, enum rpt_chan_flags flags) +int __rpt_request_local(void *data, struct ast_format_cap *cap, enum rpt_chan_type chantype, enum rpt_chan_flags flags, const char *exten) { struct rpt *myrpt = NULL; struct rpt_link *link = NULL; @@ -286,30 +318,25 @@ int __rpt_request_pseudo(void *data, struct ast_format_cap *cap, enum rpt_chan_t } else { myrpt = data; } - - chan = ast_request("DAHDI", cap, NULL, NULL, "pseudo", NULL); + chan = ast_request(rpt_chan_type_name(chantype, flags), cap, NULL, NULL, exten, NULL); if (!chan) { - ast_log(LOG_ERROR, "Failed to request pseudo channel\n"); + ast_log(LOG_ERROR, "Failed to request local channel\n"); return -1; } - - ast_debug(1, "Requested channel %s\n", ast_channel_name(chan)); - - /* A subset of what rpt_make_call does... */ ast_set_read_format(chan, ast_format_slin); ast_set_write_format(chan, ast_format_slin); rpt_disable_cdr(chan); - ast_answer(chan); - chanptr = rpt_chan_channel(myrpt, link, chantype); *chanptr = chan; switch (chantype) { + case RPT_MONCHAN: + break; /* WE don't need to answer MONCHAN */ case RPT_PCHAN: if (!(flags & RPT_LINK_CHAN)) { ast_assert(myrpt != NULL); - if (!myrpt->dahdirxchannel) { - myrpt->dahdirxchannel = chan; + if (!myrpt->localrxchannel) { + myrpt->localrxchannel = chan; } } break; @@ -320,322 +347,119 @@ int __rpt_request_pseudo(void *data, struct ast_format_cap *cap, enum rpt_chan_t return 0; } -#define join_dahdiconf(chan, ci) __join_dahdiconf(chan, ci, __FILE__, __LINE__, __PRETTY_FUNCTION__) - -static int __join_dahdiconf(struct ast_channel *chan, struct dahdi_confinfo *ci, const char *file, int line, const char *function) -{ - ci->chan = 0; - - /* First put the channel on the conference in proper mode */ - if (ioctl(ast_channel_fd(chan, 0), DAHDI_SETCONF, ci) == -1) { - ast_log(LOG_WARNING, "%s:%d (%s) Unable to set conference mode on %s\n", file, line, function, ast_channel_name(chan)); - return -1; - } - return 0; -} - -static int dahdi_conf_create(struct ast_channel *chan, int *confno, int mode) -{ - int res; - struct dahdi_confinfo ci; /* conference info */ - - ci.confno = -1; - ci.confmode = mode; - - res = join_dahdiconf(chan, &ci); - if (res) { - ast_log(LOG_WARNING, "Failed to join DAHDI conf (mode: %d)\n", mode); - } else { - *confno = ci.confno; - } - return res; -} - -static int dahdi_conf_add(struct ast_channel *chan, int confno, int mode) -{ - int res; - struct dahdi_confinfo ci; /* conference info */ - - ci.confno = confno; - ci.confmode = mode; - - ast_debug(2, "Channel %s joining conference %i", ast_channel_name(chan), confno); - - res = join_dahdiconf(chan, &ci); - if (res) { - ast_log(LOG_WARNING, "Failed to join DAHDI conf (mode: %d)\n", mode); - } - return res; -} - -#define RPT_DAHDI_FLAG(r, d) \ - if (rflags & r) { \ - dflags |= d; \ - } - -static int dahdi_conf_flags(enum rpt_conf_flags rflags) -{ - int dflags = 0; - - RPT_DAHDI_FLAG(RPT_CONF_NORMAL, DAHDI_CONF_NORMAL); - RPT_DAHDI_FLAG(RPT_CONF_MONITOR, DAHDI_CONF_MONITOR); - RPT_DAHDI_FLAG(RPT_CONF_MONITORTX, DAHDI_CONF_MONITORTX); - RPT_DAHDI_FLAG(RPT_CONF_CONF, DAHDI_CONF_CONF); - RPT_DAHDI_FLAG(RPT_CONF_CONFANN, DAHDI_CONF_CONFANN); - RPT_DAHDI_FLAG(RPT_CONF_CONFMON, DAHDI_CONF_CONFMON); - RPT_DAHDI_FLAG(RPT_CONF_CONFANNMON, DAHDI_CONF_CONFANNMON); - RPT_DAHDI_FLAG(RPT_CONF_LISTENER, DAHDI_CONF_LISTENER); - RPT_DAHDI_FLAG(RPT_CONF_TALKER, DAHDI_CONF_TALKER); - - return dflags; -} - -static int *dahdi_confno(struct rpt *myrpt, enum rpt_conf_type type) +int __rpt_conf_create(struct rpt *myrpt, enum rpt_conf_type type, const char *file, int line) { + struct ast_bridge *conf = NULL, **confptr; + const char *conference_name = ""; switch (type) { case RPT_CONF: - return &myrpt->rptconf.dahdiconf.conf; + conference_name = RPT_CONF_NAME; + confptr = &myrpt->rptconf.conf; + break; case RPT_TXCONF: - return &myrpt->rptconf.dahdiconf.txconf; - } - ast_assert(0); - return NULL; -} - -/*! - * \brief Get the channel number of a DAHDI channel - * \param chan DAHDI channel - * \retval -1 on failure, conference number on success - */ -static int dahdi_conf_get_channo(struct ast_channel *chan) -{ - struct dahdi_confinfo ci = {0}; - - if (ioctl(ast_channel_fd(chan, 0), DAHDI_CHANNO, &ci.chan)) { - ast_log(LOG_WARNING, "DAHDI_CHANNO failed: %s\n", strerror(errno)); + conference_name = RPT_TXCONF_NAME; + confptr = &myrpt->rptconf.txconf; + break; + default: + __builtin_unreachable(); return -1; } - - return ci.chan; -} - -int __rpt_conf_create(struct ast_channel *chan, struct rpt *myrpt, enum rpt_conf_type type, enum rpt_conf_flags flags, const char *file, int line) -{ - int *confno, dflags; - /* Convert RPT conf flags to DAHDI conf flags... for now. */ - dflags = dahdi_conf_flags(flags); - confno = dahdi_confno(myrpt, type); - if (dahdi_conf_create(chan, confno, dflags)) { - ast_log(LOG_ERROR, "%s:%d: Failed to create conference using chan type %d\n", file, line, type); + ast_debug(3, "Setting up conference '%s' mixing bridge \n", conference_name); + conf = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_MULTIMIX, + AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY, "app_rpt", conference_name, NULL); + if (!conf) { + ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name); return -1; } - return 0; -} -int rpt_equate_tx_conf(struct rpt *myrpt) -{ - /* save pseudo channel conference number */ - myrpt->rptconf.dahdiconf.conf = myrpt->rptconf.dahdiconf.txconf; - return 0; -} - -int __rpt_conf_add(struct ast_channel *chan, struct rpt *myrpt, enum rpt_conf_type type, enum rpt_conf_flags flags, const char *file, int line) -{ - /* Convert RPT conf flags to DAHDI conf flags... for now. */ - int *confno, dflags; - - dflags = dahdi_conf_flags(flags); - confno = dahdi_confno(myrpt, type); - - if (dahdi_conf_add(chan, *confno, dflags)) { - ast_log(LOG_ERROR, "%s:%d: Failed to add to conference using chan type %d\n", file, line, type); - return -1; - } + *confptr = conf; return 0; } -int rpt_call_bridge_setup(struct rpt *myrpt, struct ast_channel *mychannel) +int __rpt_conf_add(struct ast_channel *chan, struct rpt *myrpt, enum rpt_conf_type type, const char *file, int line) { + struct ast_bridge *conf = NULL; + const char *conference_name = ""; + struct ast_unreal_pvt *p; int res; - /* Put pchannel back on the conference in speaker mode */ - if (myrpt->p.duplex == 4 || myrpt->p.duplex == 3) { - if (rpt_conf_add_speaker(myrpt->pchannel, myrpt)) { - return -1; - } - } - - /* get its conference number */ - res = dahdi_conf_get_channo(mychannel); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to get autopatch channel number\n"); - /* Put pchannel back on the conference in announce mode */ - if (myrpt->p.duplex == 4 || myrpt->p.duplex == 3) { - rpt_conf_add_announcer_monitor(myrpt->pchannel, myrpt); - } + switch (type) { + case RPT_CONF: + conference_name = RPT_CONF_NAME; + conf = myrpt->rptconf.conf; + break; + case RPT_TXCONF: + conference_name = RPT_TXCONF_NAME; + conf = myrpt->rptconf.txconf; + break; + default: + __builtin_unreachable(); return -1; } - - /* put vox channel monitoring on the channel - * - * This uses the internal DAHDI channel number to create the - * monitor conference. This code will hang here when trying to - * join the conference when the underlying version of DAHDI in use - * is missing a patch that allows the DAHDI_CONF_MONITOR option - * to monitor a pseudo channel. This patch prevents the hardware - * pre-echo routines from acting on a pseudo channel. It also - * prevents the DAHDI check conference routine from acting - * on a channel number being used as a conference. - */ - if (dahdi_conf_add(myrpt->voxchannel, res, DAHDI_CONF_MONITOR)) { - /* Put pchannel back on the conference in announce mode */ - if (myrpt->p.duplex == 4 || myrpt->p.duplex == 3) { - rpt_conf_add_announcer_monitor(myrpt->pchannel, myrpt); - } + if (!conf) { + ast_log(LOG_ERROR, "Conference '%s' mixing bridge doesn't exist, can't add channel %s\n", conference_name, ast_channel_name(chan)); return -1; } - return 0; -} - -int rpt_mon_setup(struct rpt *myrpt) -{ - int res; - - if (!IS_PSEUDO(myrpt->txchannel) && myrpt->dahditxchannel == myrpt->txchannel) { - int confno = dahdi_conf_get_channo(myrpt->txchannel); /* get tx channel's port number */ - if (confno < 0) { - return -1; - } - res = dahdi_conf_add(myrpt->monchannel, confno, DAHDI_CONF_MONITORTX); - } else { - /* first put the channel on the conference in announce mode */ - res = rpt_conf_add(myrpt->monchannel, myrpt, RPT_TXCONF, RPT_CONF_CONFANNMON); + ast_debug(3, "Adding channel %s to conference '%s' mixing bridge \n", ast_channel_name(chan), conference_name); + res = ast_unreal_channel_push_to_bridge(chan, conf, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE); + if (res) { + ast_log(LOG_ERROR, "Failed to add channel %s to conference '%s'\n", ast_channel_name(chan), conference_name); + return res; + } + p = ast_channel_tech_pvt(chan); + if (p && p->chan) { + ast_raw_answer(p->chan); /* We can not wait 500ms for media to start flowing */ } return res; } -int rpt_parrot_add(struct rpt *myrpt) +int rpt_conf_get_muted(struct ast_channel *chan, struct rpt *myrpt) { - /* first put the channel on the conference in announce mode */ - if (dahdi_conf_add(myrpt->parrotchannel, 0, DAHDI_CONF_NORMAL)) { - return -1; - } + /*! \todo: Do we need to check mute? What should it do?*/ return 0; } -static int dahdi_conf_get_muted(struct ast_channel *chan) +int rpt_play_tone(struct ast_channel *chan, const char *tone) { - int muted; - - if (!CHAN_TECH(chan, "DAHDI")) { - return 0; - } - - if (ioctl(ast_channel_fd(chan, 0), DAHDI_GETCONFMUTE, &muted) == -1) { - ast_log(LOG_WARNING, "Couldn't get mute status on %s: %s\n", ast_channel_name(chan), strerror(errno)); - muted = 0; + int res = 0; + struct ast_tone_zone *zone = ast_channel_zone(chan); + struct ast_tone_zone_sound *ts = ast_get_indication_tone(zone, tone); + if (ts) { + res = ast_playtones_start(chan, 0, ts->data, 0); + ts = ast_tone_zone_sound_unref(ts); + } else { + ast_log(LOG_WARNING, "No tone '%s' found in zone '%s'\n", tone, (zone && zone->country[0]) ? zone->country : "default"); + return -1; } - return muted; -} - -int rpt_conf_get_muted(struct ast_channel *chan, struct rpt *myrpt) -{ - return dahdi_conf_get_muted(chan); -} -/*! - * \param chan - * \param tone DAHDI_TONE_DIALTONE, DAHDI_TONE_CONGESTION, or -1 to stop tone - * \retval 0 on success, -1 on failure - */ -static int rpt_play_tone(struct ast_channel *chan, int tone) -{ - if (tone_zone_play_tone(ast_channel_fd(chan, 0), tone)) { + if (res) { ast_log(LOG_WARNING, "Cannot start tone on %s\n", ast_channel_name(chan)); return -1; } return 0; } -int rpt_play_dialtone(struct ast_channel *chan) -{ - return rpt_play_tone(chan, DAHDI_TONE_DIALTONE); -} - -int rpt_play_congestion(struct ast_channel *chan) -{ - return rpt_play_tone(chan, DAHDI_TONE_CONGESTION); -} - int rpt_stop_tone(struct ast_channel *chan) { - return rpt_play_tone(chan, -1); -} - -int rpt_set_tone_zone(struct ast_channel *chan, const char *tz) -{ - if (tone_zone_set_zone(ast_channel_fd(chan, 0), (char*) tz) == -1) { - ast_log(LOG_WARNING, "Unable to set tone zone %s on %s\n", tz, ast_channel_name(chan)); - return -1; - } + ast_playtones_stop(chan); return 0; } -int dahdi_write_wait(struct ast_channel *chan) -{ - int res, i, flags; - - for (i = 0; i < 20; i++) { - flags = DAHDI_IOMUX_WRITEEMPTY | DAHDI_IOMUX_NOWAIT; - res = ioctl(ast_channel_fd(chan, 0), DAHDI_IOMUX, &flags); - if (res) { - ast_log(LOG_WARNING, "DAHDI_IOMUX failed: %s\n", strerror(errno)); - break; - } - if (flags & DAHDI_IOMUX_WRITEEMPTY) { - break; - } - if (ast_safe_sleep(chan, 50)) { - res = -1; - break; - } - } - return res; -} - -int dahdi_flush(struct ast_channel *chan) +int rpt_set_tone_zone(struct ast_channel *chan, const char *tz) { - int i = DAHDI_FLUSH_EVENT; - if (ioctl(ast_channel_fd(chan, 0), DAHDI_FLUSH, &i) == -1) { - ast_log(LOG_ERROR, "Can't flush events on %s: %s", ast_channel_name(chan), strerror(errno)); + struct ast_tone_zone *new_zone; + if (!(new_zone = ast_get_indication_zone(tz))) { + ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", tz); return -1; } - return 0; -} -int dahdi_bump_buffers(struct ast_channel *chan, int samples) -{ - struct dahdi_bufferinfo bi; - - /* This is a miserable kludge. For some unknown reason, which I dont have - time to properly research, buffer settings do not get applied to dahdi - pseudo-channels. So, if we have a need to fit more then 1 160 sample - buffer into the psuedo-channel at a time, and there currently is not - room, it increases the number of buffers to accommodate the larger number - of samples (version 0.257 9/3/10) */ - memset(&bi, 0, sizeof(bi)); - - if (ioctl(ast_channel_fd(chan, 0), DAHDI_GET_BUFINFO, &bi) == -1) { - ast_log(LOG_ERROR, "Failed to get buffer info on %s: %s\n", ast_channel_name(chan), strerror(errno)); - return -1; - } - if (samples > bi.bufsize && (bi.numbufs < ((samples / bi.bufsize) + 1))) { - bi.numbufs = (samples / bi.bufsize) + 1; - if (ioctl(ast_channel_fd(chan, 0), DAHDI_SET_BUFINFO, &bi)) { - ast_log(LOG_ERROR, "Failed to set buffer info on %s: %s\n", ast_channel_name(chan), strerror(errno)); - return -1; - } + ast_channel_lock(chan); + if (ast_channel_zone(chan)) { + ast_channel_zone_set(chan, ast_tone_zone_unref(ast_channel_zone(chan))); } + ast_channel_zone_set(chan, ast_tone_zone_ref(new_zone)); + ast_channel_unlock(chan); + new_zone = ast_tone_zone_unref(new_zone); return 0; } @@ -665,4 +489,4 @@ int dahdi_set_echocancel(struct ast_channel *chan, int ec) return -1; } return 0; -} +} \ No newline at end of file diff --git a/apps/app_rpt/rpt_bridging.h b/apps/app_rpt/rpt_bridging.h index 06ae13a44..a57a95398 100644 --- a/apps/app_rpt/rpt_bridging.h +++ b/apps/app_rpt/rpt_bridging.h @@ -2,12 +2,17 @@ enum rpt_chan_type { RPT_RXCHAN, /* Receive channel */ RPT_TXCHAN, /* Transmit channel */ - RPT_PCHAN, - RPT_DAHDITXCHAN, + RPT_PCHAN, /* CONF to TXCONF */ + RPT_LOCALTXCHAN, RPT_MONCHAN, /* Monitor channel */ - RPT_PARROTCHAN, - RPT_VOXCHAN, RPT_TXPCHAN, + RPT_RXPCHAN, /* RXChannel to CONF */ +}; + +enum rpt_bridge_chan_type { + RPT_LOCAL, + RPT_TELEMETRY, + RPT_MONITOR, }; /* Each of these corresponds to a member of the rpt_conf structure in app_rpt.h */ @@ -16,19 +21,8 @@ enum rpt_conf_type { RPT_TXCONF, /* Local Audio */ }; -/* Uses same flag name style as DAHDI_CONF flags, since that's what these are based on */ -enum rpt_conf_flags { - RPT_CONF_NORMAL = (1 << 0), - RPT_CONF_MONITOR = (1 << 1), - RPT_CONF_MONITORTX = (1 << 2), - RPT_CONF_CONF = (1 << 3), - RPT_CONF_CONFANN = (1 << 4), - RPT_CONF_CONFMON = (1 << 5), - RPT_CONF_CONFANNMON = (1 << 6), - RPT_CONF_LISTENER = (1 << 7), - RPT_CONF_TALKER = (1 << 8), -}; - +#define RPT_TXCONF_NAME "TXCONF" /* TX Conference Name */ +#define RPT_CONF_NAME "CONF" /* Repeater Conference Name */ enum rpt_chan_flags { RPT_LINK_CHAN = (1 << 0), }; @@ -54,12 +48,16 @@ int __rpt_request(void *data, struct ast_format_cap *cap, enum rpt_chan_type cha #define rpt_request(data, cap, chantype) __rpt_request(data, cap, chantype, 0) /*! - * \brief Request a pseudo channel + * \brief Request a Local channel * \param cap * \return channel on success * \return NULL on failure */ -struct ast_channel *rpt_request_pseudo_chan(struct ast_format_cap *cap); +struct ast_channel *__rpt_request_local_chan(struct ast_format_cap *cap, const char *exten, enum rpt_bridge_chan_type type); + +#define rpt_request_local_chan(cap, exten) __rpt_request_local_chan(cap, exten, RPT_LOCAL) +#define rpt_request_telem_chan(cap, exten) __rpt_request_local_chan(cap, exten, RPT_TELEMETRY) +#define rpt_request_mon_chan(cap, exten) __rpt_request_local_chan(cap, exten, RPT_MONITOR) /*! * \brief Request a repeater channel not associated with a real device @@ -69,59 +67,45 @@ struct ast_channel *rpt_request_pseudo_chan(struct ast_format_cap *cap); * \note myrpt->lock must be held when calling * \retval 0 on success, -1 on failure */ -int __rpt_request_pseudo(void *data, struct ast_format_cap *cap, enum rpt_chan_type chantype, enum rpt_chan_flags flags); - -#define rpt_request_pseudo(data, cap, chantype) __rpt_request_pseudo(data, cap, chantype, 0) - -int __rpt_conf_create(struct ast_channel *chan, struct rpt *myrpt, enum rpt_conf_type type, enum rpt_conf_flags flags, const char *file, int line); - -int __rpt_conf_add(struct ast_channel *chan, struct rpt *myrpt, enum rpt_conf_type type, enum rpt_conf_flags flags, const char *file, int line); - -int rpt_equate_tx_conf(struct rpt *myrpt); - -#define rpt_conf_create(chan, myrpt, type, flags) __rpt_conf_create(chan, myrpt, type, flags, __FILE__, __LINE__) -#define rpt_conf_add(chan, myrpt, type, flags) __rpt_conf_add(chan, myrpt, type, flags, __FILE__, __LINE__) - -#define rpt_conf_add_speaker(chan, myrpt) rpt_conf_add(chan, myrpt, RPT_CONF, RPT_CONF_CONF | RPT_CONF_LISTENER | RPT_CONF_TALKER) +int __rpt_request_local(void *data, struct ast_format_cap *cap, enum rpt_chan_type chantype, enum rpt_chan_flags flags, const char *exten); -#define rpt_tx_conf_add_speaker(chan, myrpt) rpt_conf_add(chan, myrpt, RPT_TXCONF, RPT_CONF_CONF | RPT_CONF_LISTENER | RPT_CONF_TALKER) +#define rpt_request_local(data, cap, chantype, exten) __rpt_request_local(data, cap, chantype, 0, exten) -#define rpt_conf_add_announcer(chan, myrpt) rpt_conf_add(chan, myrpt, RPT_CONF, RPT_CONF_CONFANN) +int __rpt_conf_create(struct rpt *myrpt, enum rpt_conf_type type, const char *file, int line); -#define rpt_conf_add_announcer_monitor(chan, myrpt) rpt_conf_add(chan, myrpt, RPT_CONF, RPT_CONF_CONFANNMON) +int __rpt_conf_add(struct ast_channel *chan, struct rpt *myrpt, enum rpt_conf_type type, const char *file, int line); -#define rpt_tx_conf_add_announcer(chan, myrpt) rpt_conf_add(chan, myrpt, RPT_TXCONF, RPT_CONF_CONFANN) +#define rpt_conf_create(myrpt, type) __rpt_conf_create(myrpt, type, __FILE__, __LINE__) +#define rpt_conf_add(chan, myrpt, type) __rpt_conf_add(chan, myrpt, type, __FILE__, __LINE__) -/*! \note Used in app_rpt.c */ -int rpt_call_bridge_setup(struct rpt *myrpt, struct ast_channel *mychannel); - -/*! \note Used in app_rpt.c */ -int rpt_mon_setup(struct rpt *myrpt); - -/*! \note Used in app_rpt.c */ -int rpt_parrot_add(struct rpt *myrpt); +/*! + * \param chan Channel to play tone on + * \param tone tone type (e.g., "dial", "congestion") + * \retval 0 on success, -1 on failure + */ +int rpt_play_tone(struct ast_channel *chan, const char *tone); /*! - * \brief Get if channel is muted in conference + * \brief Play congestion on a channel * \param chan - * \param myrpt - * \retval 0 if not muted, 1 if muted + * \retval 0 on success, -1 on failure */ -int rpt_conf_get_muted(struct ast_channel *chan, struct rpt *myrpt); +#define rpt_play_congestion(chan) rpt_play_tone(chan, "congestion") /*! * \brief Play dialtone on a channel * \param chan * \retval 0 on success, -1 on failure */ -int rpt_play_dialtone(struct ast_channel *chan); +#define rpt_play_dialtone(chan) rpt_play_tone(chan, "dial") /*! - * \brief Play congestion tone on a channel + * \brief Get if channel is muted in conference * \param chan - * \retval 0 on success, -1 on failure + * \param myrpt + * \retval 0 if not muted, 1 if muted */ -int rpt_play_congestion(struct ast_channel *chan); +int rpt_conf_get_muted(struct ast_channel *chan, struct rpt *myrpt); /*! * \brief Stop playing tones on a channel @@ -137,31 +121,7 @@ int rpt_stop_tone(struct ast_channel *chan); */ int rpt_set_tone_zone(struct ast_channel *chan, const char *tz); -/*! - * \brief Wait for the DAHDI driver to physically write all audio to the hardware - * \note Up to a max of 1 second - * \note Only use with DAHDI channels! - * \param chan - * \retval 0 on success, -1 on failure - */ -int dahdi_write_wait(struct ast_channel *chan); - -/*! - * \brief Flush events on a DAHDI channel - * \note Only use with DAHDI channels! - * \param chan - * \retval 0 on success, -1 on failure - */ -int dahdi_flush(struct ast_channel *chan); - -/*! - * \brief Increase buffer space on DAHDI channel, if needed to accommodate samples - * \note Only use with DAHDI channels! - * \param chan - * \param samples - * \retval 0 on success, -1 on failure - */ -int dahdi_bump_buffers(struct ast_channel *chan, int samples); +#define DEFAULT_TALKING_THRESHOLD 160 /* Bridge talking threshold - setting VOX level for when a user is indicated at "talking" */ /*! * \brief Get value of rxisoffhook @@ -191,4 +151,4 @@ int dahdi_set_hook(struct ast_channel *chan, int offhook); * \param ec 0 to disable, non-zero to enable * \retval 0 on success, -1 on failure */ -int dahdi_set_echocancel(struct ast_channel *chan, int ec); +int dahdi_set_echocancel(struct ast_channel *chan, int ec); \ No newline at end of file diff --git a/apps/app_rpt/rpt_call.c b/apps/app_rpt/rpt_call.c index 72e33c3c2..fc7c079fe 100644 --- a/apps/app_rpt/rpt_call.c +++ b/apps/app_rpt/rpt_call.c @@ -22,21 +22,58 @@ #include "asterisk/channel.h" #include "asterisk/format_cache.h" +#include "asterisk/core_unreal.h" #include "app_rpt.h" #include "rpt_call.h" int rpt_disable_cdr(struct ast_channel *chan) { - if (!ast_channel_cdr(chan)) { - ast_debug(4, "No CDR present on %s\n", ast_channel_name(chan)); + struct ast_unreal_pvt *p; + int res = 0; + + if (!IS_LOCAL(chan)) { + if (ast_channel_cdr(chan)) { + if (ast_cdr_set_property(ast_channel_name(chan), AST_CDR_FLAG_DISABLE_ALL)) { + ast_log(AST_LOG_WARNING, "Failed to disable CDR for channel %s\n", ast_channel_name(chan)); + return -1; + } + } else { + ast_debug(4, "No CDR present on %s\n", ast_channel_name(chan)); + } return 0; } - if (ast_cdr_set_property(ast_channel_name(chan), AST_CDR_FLAG_DISABLE_ALL)) { - ast_log(AST_LOG_WARNING, "Failed to disable CDR for channel %s\n", ast_channel_name(chan)); + + /* It's a local channel */ + p = ast_channel_tech_pvt(chan); + if (!p) { + ast_log(AST_LOG_WARNING, "Local channel %s missing private\n", ast_channel_name(chan)); return -1; } - return 0; + ao2_lock(p); + if (p->owner) { + if (ast_channel_cdr(p->owner)) { + if (ast_cdr_set_property(ast_channel_name(p->owner), AST_CDR_FLAG_DISABLE_ALL)) { + ast_log(AST_LOG_WARNING, "Failed to disable CDR for channel %s\n", ast_channel_name(p->owner)); + res = -1; + } + } else { + ast_debug(4, "No CDR present on %s\n", ast_channel_name(p->owner)); + } + } + + if (p->chan) { + if (ast_channel_cdr(p->chan)) { + if (ast_cdr_set_property(ast_channel_name(p->chan), AST_CDR_FLAG_DISABLE_ALL)) { + ast_log(AST_LOG_WARNING, "Failed to disable CDR for channel %s\n", ast_channel_name(p->chan)); + res = -1; + } + } else { + ast_debug(4, "No CDR present on %s\n", ast_channel_name(p->chan)); + } + } + ao2_unlock(p); + return res; } int rpt_setup_call(struct ast_channel *chan, const char *addr, int timeout, const char *driver, const char *data, const char *desc, const char *callerid) diff --git a/apps/app_rpt/rpt_channel.c b/apps/app_rpt/rpt_channel.c index 01ba3c069..1bf39bbda 100644 --- a/apps/app_rpt/rpt_channel.c +++ b/apps/app_rpt/rpt_channel.c @@ -54,7 +54,7 @@ int wait_interval(struct rpt *myrpt, enum rpt_delay type, struct ast_channel *ch } interval = get_wait_interval(myrpt, type); - ast_debug(1, "Delay interval = %d\n", interval); + ast_debug(1, "Delay interval = %d on %s\n", interval, ast_channel_name(chan)); if (interval && ast_safe_sleep(chan, interval) < 0) { return -1; } diff --git a/apps/app_rpt/rpt_cli.c b/apps/app_rpt/rpt_cli.c index 7bb8e8965..1ce728434 100644 --- a/apps/app_rpt/rpt_cli.c +++ b/apps/app_rpt/rpt_cli.c @@ -1079,12 +1079,11 @@ static int rpt_show_channels(int fd, int argc, const char *const *argv) DUMP_CHANNEL(rxchannel); DUMP_CHANNEL(txchannel); DUMP_CHANNEL(monchannel); - DUMP_CHANNEL(parrotchannel); DUMP_CHANNEL(pchannel); + DUMP_CHANNEL(rxpchannel); DUMP_CHANNEL(txpchannel); - DUMP_CHANNEL(dahdirxchannel); - DUMP_CHANNEL(dahditxchannel); - DUMP_CHANNEL(voxchannel); + DUMP_CHANNEL(localrxchannel); + DUMP_CHANNEL(localtxchannel); rpt_mutex_unlock(&rpt_vars[this_rpt].lock); #undef DUMP_CHANNEL diff --git a/apps/app_rpt/rpt_functions.c b/apps/app_rpt/rpt_functions.c index 28b30a353..b21b56b1f 100644 --- a/apps/app_rpt/rpt_functions.c +++ b/apps/app_rpt/rpt_functions.c @@ -835,7 +835,7 @@ enum rpt_function_response function_remote(struct rpt *myrpt, char *param, char (!strcmp(myrpt->remoterig, REMOTE_RIG_FT100)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_FT950)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_IC706)))) { myrpt->remotetx = 0; - if (!IS_PSEUDO(myrpt->txchannel)) { + if (!IS_LOCAL(myrpt->txchannel)) { ast_indicate(myrpt->txchannel, AST_CONTROL_RADIO_UNKEY); } myrpt->tunetx = 0; diff --git a/apps/app_rpt/rpt_link.c b/apps/app_rpt/rpt_link.c index a8ed768a6..2a1356fc6 100644 --- a/apps/app_rpt/rpt_link.c +++ b/apps/app_rpt/rpt_link.c @@ -764,9 +764,9 @@ void *rpt_link_connect(void *data) goto cleanup; } - rpt_make_call(l->chan, tele, 2000, deststr, "(Remote Rx)", "remote", myrpt->name); + rpt_make_call(l->chan, tele, 2000, deststr, "Remote Rx", "remote", myrpt->name); - if (__rpt_request_pseudo(l, cap, RPT_PCHAN, RPT_LINK_CHAN)) { + if (__rpt_request_local(l, cap, RPT_PCHAN, RPT_LINK_CHAN, "IAXLink")) { ao2_ref(cap, -1); ast_hangup(l->chan); l->connect_in_progress = 0; @@ -776,8 +776,7 @@ void *rpt_link_connect(void *data) ao2_ref(cap, -1); - /* make a conference for the tx */ - if (rpt_conf_add_speaker(l->pchan, myrpt)) { + if (rpt_conf_add(l->pchan, myrpt, RPT_CONF)) { ast_hangup(l->chan); ast_hangup(l->pchan); l->connect_in_progress = 0; diff --git a/apps/app_rpt/rpt_manager.c b/apps/app_rpt/rpt_manager.c index 43d964740..66f6f2312 100644 --- a/apps/app_rpt/rpt_manager.c +++ b/apps/app_rpt/rpt_manager.c @@ -279,15 +279,15 @@ static int rpt_manager_do_xstat(struct mansession *ses, const struct message *m) /* Get variables info */ j = 0; - if (!strcasecmp(rxchanname, "DAHDI/pseudo")) { - /* DAHDI/pseudo isn't a real channel name, calling ast_channel_get_by_name + if (!strcasecmp(rxchanname, "Local/pseudo")) { + /* Local/pseudo isn't a real channel name, calling ast_channel_get_by_name * will always fail, so avoid an unnecessary traversal of the channels container for nothing. */ pseudo = 1; } else { rxchan = ast_channel_get_by_name(rxchanname); } /* rxchan might've disappeared in the meantime. Verify it still exists before we try to lock it, - * at least unless it's a DAHDI pseudo channel. + * at least unless it's a Local channel. * XXX This was added to address assertions due to bad locking, but app_rpt should probably * be globally ref'ing the channel and holding it until it unloads. Should be investigated. */ if (rxchan || pseudo) { diff --git a/apps/app_rpt/rpt_radio.c b/apps/app_rpt/rpt_radio.c index ef7906708..1489d3707 100644 --- a/apps/app_rpt/rpt_radio.c +++ b/apps/app_rpt/rpt_radio.c @@ -38,7 +38,7 @@ static int dahdi_radio_set_ctcss_decode(struct ast_channel *chan, int enable) int rpt_radio_rx_set_ctcss_decode(struct rpt *myrpt, int enable) { if (CHAN_TECH(myrpt->rxchannel, "DAHDI")) { - return dahdi_radio_set_ctcss_decode(myrpt->dahdirxchannel, enable); + return dahdi_radio_set_ctcss_decode(myrpt->localrxchannel, enable); } return 1; } @@ -73,33 +73,30 @@ int rpt_pciradio_serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int t int i, index, oldmode, olddata; struct dahdi_radio_param prm; + if (!myrpt->localrxchannel) { + return -1; + } prm.radpar = DAHDI_RADPAR_UIOMODE; - if (ioctl(ast_channel_fd(myrpt->dahdirxchannel, 0), DAHDI_RADIO_GETPARAM, &prm) == -1) { + if (ioctl(ast_channel_fd(myrpt->localrxchannel, 0), DAHDI_RADIO_GETPARAM, &prm) == -1) { return -1; } oldmode = prm.data; prm.radpar = DAHDI_RADPAR_UIODATA; - if (ioctl(ast_channel_fd(myrpt->dahdirxchannel, 0), DAHDI_RADIO_GETPARAM, &prm) == -1) { + if (ioctl(ast_channel_fd(myrpt->localrxchannel, 0), DAHDI_RADIO_GETPARAM, &prm) == -1) { return -1; } olddata = prm.data; prm.radpar = DAHDI_RADPAR_REMMODE; if ((asciiflag & 1) && strcmp(myrpt->remoterig, REMOTE_RIG_TM271) && strcmp(myrpt->remoterig, REMOTE_RIG_KENWOOD)) { - if (rpt_radio_set_param(myrpt->dahdirxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_SERIAL_ASCII)) { + if (rpt_radio_set_param(myrpt->localrxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_SERIAL_ASCII)) { return -1; } } else { - if (rpt_radio_set_param(myrpt->dahdirxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_SERIAL)) { + if (rpt_radio_set_param(myrpt->localrxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_SERIAL)) { return -1; } } - if (asciiflag & 2) { - if (dahdi_set_onhook(myrpt->dahdirxchannel)) { - return -1; - } - usleep(100000); - } if ((!strcmp(myrpt->remoterig, REMOTE_RIG_TM271)) || (!strcmp(myrpt->remoterig, REMOTE_RIG_KENWOOD))) { for (i = 0; i < txbytes - 1; i++) { @@ -107,7 +104,7 @@ int rpt_pciradio_serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int t prm.data = 0; prm.buf[0] = txbuf[i]; prm.index = 1; - if (ioctl(ast_channel_fd(myrpt->dahdirxchannel, 0), DAHDI_RADIO_SETPARAM, &prm) == -1) + if (ioctl(ast_channel_fd(myrpt->localrxchannel, 0), DAHDI_RADIO_SETPARAM, &prm) == -1) return -1; usleep(6666); } @@ -116,7 +113,7 @@ int rpt_pciradio_serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int t prm.data = DAHDI_RADPAR_REM_SERIAL_ASCII; else prm.data = DAHDI_RADPAR_REM_SERIAL; - if (ioctl(ast_channel_fd(myrpt->dahdirxchannel, 0), DAHDI_RADIO_SETPARAM, &prm) == -1) + if (ioctl(ast_channel_fd(myrpt->localrxchannel, 0), DAHDI_RADIO_SETPARAM, &prm) == -1) return -1; prm.radpar = DAHDI_RADPAR_REMCOMMAND; prm.data = rxmaxbytes; @@ -128,25 +125,25 @@ int rpt_pciradio_serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int t memcpy(prm.buf, txbuf, txbytes); prm.index = txbytes; } - if (ioctl(ast_channel_fd(myrpt->dahdirxchannel, 0), DAHDI_RADIO_SETPARAM, &prm) == -1) + if (ioctl(ast_channel_fd(myrpt->localrxchannel, 0), DAHDI_RADIO_SETPARAM, &prm) == -1) return -1; if (rxbuf) { *rxbuf = 0; memcpy(rxbuf, prm.buf, prm.index); } index = prm.index; - if (rpt_radio_set_param(myrpt->dahdirxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_NONE)) { + if (rpt_radio_set_param(myrpt->localrxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_NONE)) { return -1; } if (asciiflag & 2) { - if (dahdi_set_offhook(myrpt->dahdirxchannel)) { + if (dahdi_set_offhook(myrpt->localrxchannel)) { return -1; } } - if (rpt_radio_set_param(myrpt->dahdirxchannel, RPT_RADPAR_UIOMODE, oldmode)) { + if (rpt_radio_set_param(myrpt->localrxchannel, RPT_RADPAR_UIOMODE, oldmode)) { return -1; } - if (rpt_radio_set_param(myrpt->dahdirxchannel, RPT_RADPAR_UIODATA, olddata)) { + if (rpt_radio_set_param(myrpt->localrxchannel, RPT_RADPAR_UIODATA, olddata)) { return -1; } return index; diff --git a/apps/app_rpt/rpt_serial.c b/apps/app_rpt/rpt_serial.c index 3f560f3f9..19d8d85a4 100644 --- a/apps/app_rpt/rpt_serial.c +++ b/apps/app_rpt/rpt_serial.c @@ -416,12 +416,15 @@ static void rbi_out_parallel(struct rpt *myrpt, unsigned char *data) static void rbi_out(struct rpt *myrpt, unsigned char *data) { - if (rpt_radio_set_param(myrpt->dahdirxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_RBI1)) { + if (!myrpt->localrxchannel) { + return; + } + if (rpt_radio_set_param(myrpt->localrxchannel, RPT_RADPAR_REMMODE, RPT_RADPAR_REM_RBI1)) { /* if setparam ioctl fails, its probably not a pciradio card */ rbi_out_parallel(myrpt, data); return; } - rpt_radio_set_remcommand_data(myrpt->dahdirxchannel, data, 5); + rpt_radio_set_remcommand_data(myrpt->localrxchannel, data, 5); } int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes, unsigned char *rxbuf, int rxmaxbytes, int asciiflag) @@ -489,7 +492,7 @@ int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes, unsig } /* if not a DAHDI channel, can't use pciradio stuff */ - if (myrpt->rxchannel != myrpt->dahdirxchannel) { + if (myrpt->rxchannel != myrpt->localrxchannel) { return -1; } diff --git a/apps/app_rpt/rpt_telemetry.c b/apps/app_rpt/rpt_telemetry.c index 92543292a..d8a02158c 100644 --- a/apps/app_rpt/rpt_telemetry.c +++ b/apps/app_rpt/rpt_telemetry.c @@ -686,8 +686,6 @@ static int send_tone_telemetry(struct ast_channel *chan, const char *tonestring) ast_stopstream(chan); - /* Wait for the DAHDI driver to physically write the tone blocks to the hardware */ - res = dahdi_write_wait(chan); return res; } @@ -871,9 +869,6 @@ static void handle_varcmd_tele(struct rpt *myrpt, struct ast_channel *mychannel, return; } if (!strcasecmp(strs[0], "PROC")) { - if (wait_interval(myrpt, DLY_TELEM, mychannel) == -1) { - return; - } res = telem_lookup(myrpt, mychannel, "patchup", "PROC"); if (res < 0) { /* Then default message */ sayfile(mychannel, "rpt/callproceeding"); @@ -1292,32 +1287,6 @@ void *rpt_tele_thread(void *this) ident = ""; id_malloc = 0; } - rpt_mutex_unlock(&myrpt->lock); - - cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); - if (!cap) { - ast_log(LOG_ERROR, "Failed to alloc cap\n"); - rpt_mutex_lock(&myrpt->lock); - goto abort2; /* Didn't set active_telem, so goto abort2, not abort. */ - } - - ast_format_cap_append(cap, ast_format_slin, 0); - - /* allocate a pseudo-channel thru asterisk */ - mychannel = rpt_request_pseudo_chan(cap); - ao2_ref(cap, -1); - - if (!mychannel) { - ast_log(LOG_WARNING, "Unable to obtain pseudo channel (mode: %d)\n", mytele->mode); - rpt_mutex_lock(&myrpt->lock); - goto abort2; /* Didn't set active_telem, so goto abort2, not abort. */ - } - ast_debug(1, "Requested channel %s\n", ast_channel_name(mychannel)); - - rpt_mutex_lock(&myrpt->lock); - ast_channel_ref(mychannel); /* Create a reference to prevent channel from being freed too soon */ - mytele->chan = mychannel; - /* Wait for previous telemetry to finish before we start so we're not speaking on top of each other. */ ast_debug(5, "Queued telemetry, active_telem = %p, mytele = %p\n", myrpt->active_telem, mytele); while (myrpt->active_telem && ((myrpt->active_telem->mode == PAGE) || (myrpt->active_telem->mode == MDC1200))) { @@ -1341,29 +1310,49 @@ void *rpt_tele_thread(void *this) ast_debug(5, "Beginning telemetry, active_telem = %p, mytele = %p\n", myrpt->active_telem, mytele); - /* make a conference for the tx */ - /* If the telemetry is only intended for a local audience, only connect the ID audio to the local tx conference so linked systems can't hear it */ - /* first put the channel on the conference in announce mode */ + cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!cap) { + ast_log(LOG_ERROR, "Failed to alloc cap\n"); + rpt_mutex_lock(&myrpt->lock); + goto abort; + } + + ast_format_cap_append(cap, ast_format_slin, 0); + /* allocate a local channel thru asterisk and call the correct conference */ + mychannel = rpt_request_telem_chan(cap, "Telemetry"); + ao2_ref(cap, -1); + + if (!mychannel) { + ast_log(LOG_WARNING, "Unable to obtain local channel (mode: %d)\n", mytele->mode); + rpt_mutex_lock(&myrpt->lock); + goto abort; + } + ast_debug(1, "Requested channel %s\n", ast_channel_name(mychannel)); + ast_channel_ref(mychannel); /* Create a reference to prevent channel from being freed too soon */ + mytele->chan = mychannel; + switch (mytele->mode) { - case ID1: - case PLAYBACK: - case TEST_TONE: - case STATS_GPS_LEGACY: - type = RPT_CONF; - break; - default: - type = RPT_TXCONF; - break; + case ID1: + case PLAYBACK: + case TEST_TONE: + case STATS_GPS_LEGACY: + type = RPT_CONF; + break; + default: + type = RPT_TXCONF; + break; } + if (ast_audiohook_volume_set_float(mychannel, AST_AUDIOHOOK_DIRECTION_WRITE, myrpt->p.telemnomgain)) { ast_log(LOG_WARNING, "Setting the volume on channel %s to %2.2f failed", ast_channel_name(mychannel), myrpt->p.telemnomgain); } - if (rpt_conf_add(mychannel, myrpt, type, RPT_CONF_CONFANN)) { + if (rpt_conf_add(mychannel, myrpt, type)) { + ast_log(LOG_WARNING, "Unable to join local channel to conference %s\n", type == RPT_CONF ? RPT_CONF_NAME : RPT_TXCONF_NAME); rpt_mutex_lock(&myrpt->lock); goto abort; } - ast_stopstream(mychannel); + res = 0; switch (mytele->mode) { case USEROUT: @@ -1671,11 +1660,6 @@ void *rpt_tele_thread(void *this) res = telem_send_ct(myrpt, mychannel, "unlinkedct", mytele->mode == UNKEY ? "UNKEY" : "LOCUNKEY", 0); } if (hasremote && ((!myrpt->cmdnode[0]) || (!strcmp(myrpt->cmdnode, "aprstt")))) { - /* set for all to hear */ - if (rpt_conf_add_announcer(mychannel, myrpt)) { - rpt_mutex_lock(&myrpt->lock); - goto abort; - } /* Remote Unkey Courtesy Tone */ res = telem_send_ct(myrpt, mychannel, "remotect", mytele->mode == UNKEY ? "UNKEY" : "LOCUNKEY", 200); } @@ -1684,11 +1668,6 @@ void *rpt_tele_thread(void *this) char mystr[10]; ast_safe_sleep(mychannel, 200); - /* set for all to hear */ - if (rpt_tx_conf_add_announcer(mychannel, myrpt)) { - rpt_mutex_lock(&myrpt->lock); - goto abort; - } snprintf(mystr, sizeof(mystr), "%04x", myrpt->lastunit); myrpt->lastunit = 0; ast_say_character_str(mychannel, mystr, NULL, ast_channel_language(mychannel)); @@ -2008,21 +1987,10 @@ void *rpt_tele_thread(void *this) res = -1; break; } - if (myrpt->iofd < 0) { - int rxisoffhook; - if (dahdi_flush(myrpt->dahditxchannel) || ((rxisoffhook = dahdi_rx_offhook(myrpt->dahdirxchannel)) < 0)) { - myrpt->remsetting = 0; - ast_mutex_unlock(&myrpt->remlock); - res = -1; - break; - } - myrpt->remoterx = rxisoffhook || myrpt->tele.next != &myrpt->tele; - } } else if (!strcmp(myrpt->remoterig, REMOTE_RIG_TMD700)) { res = set_tmd700(myrpt); setxpmr(myrpt, 0); } - myrpt->remsetting = 0; ast_mutex_unlock(&myrpt->remlock); if (!res) { diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 9b9dd5c3f..85898ad57 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -187,6 +187,7 @@ struct chan_simpleusb_pvt { char devicenum; char devstr[128]; + char serial[128]; int spkrmax; int micmax; int micplaymax; @@ -791,14 +792,17 @@ static int load_tune_config(struct chan_simpleusb_pvt *o, const struct ast_confi int opened = 0; int configured = 0; char devstr[sizeof(o->devstr)]; + char serial[sizeof(o->serial)]; o->rxmixerset = 500; o->txmixaset = 500; o->txmixbset = 500; devstr[0] = '\0'; + serial[0] = '\0'; if (!reload) { - o->devstr[0] = 0; + o->devstr[0] = '\0'; + o->serial[0] = '\0'; } if (!cfg) { @@ -819,11 +823,13 @@ static int load_tune_config(struct chan_simpleusb_pvt *o, const struct ast_confi CV_UINT("txmixaset", o->txmixaset); CV_UINT("txmixbset", o->txmixbset); CV_STR("devstr", devstr); + CV_STR("serial", serial); CV_END; } if (!reload) { /* Using the ternary operator in CV_STR won't work, due to butchering the sizeof, so copy after if needed */ strcpy(o->devstr, devstr); /* Safe */ + strcpy(o->serial, serial); /* Safe */ } if (opened) { ast_config_destroy(cfg2); @@ -893,6 +899,8 @@ static void *hidthread(void *arg) * with the usb hid device */ while (!o->stophid) { + char serial[sizeof(o->serial)] = { '\0' }; + ast_radio_time(&o->lasthidtime); ast_mutex_lock(&usb_dev_lock); o->hasusb = 0; @@ -913,7 +921,37 @@ static void *hidthread(void *arg) * found device. */ ast_radio_time(&o->lasthidtime); - + + /* If configuration has a serial number defined, find the device */ + if (!ast_strlen_zero(o->serial)) { + int index; + char *index_devstr = NULL; + + for (index = 0;; index++) { + index_devstr = ast_radio_usb_get_devstr(index); + if (ast_strlen_zero(index_devstr)) { + /* if no more devices */ + break; + } + + /* get the device serial number */ + if (ast_radio_usb_get_serial(index_devstr, serial, sizeof(serial)) == 0) { + /* if no serial number */ + continue; + } + + if (strcmp(o->serial, serial) == 0) { + /* + * We found a device with the matching serial number, set + * the devstr to the matching device. + */ + ast_log(LOG_NOTICE, "Matched device serial %s to %s\n", o->serial, o->name); + ast_copy_string(o->devstr, index_devstr, sizeof(o->devstr)); + break; + } + } + } + /* Automatically assign a devstr if one was not specified in the configuration. */ if (ast_strlen_zero(o->devstr)) { int index = 0; @@ -943,6 +981,9 @@ static void *hidthread(void *arg) /* We found an unused device assign it to our node */ ast_copy_string(o->devstr, index_devstr, sizeof(o->devstr)); ast_log(LOG_NOTICE, "Channel %s: Automatically assigned USB device %s to SimpleUSB channel\n", o->name, o->devstr); + if (ast_radio_usb_get_serial(index_devstr, serial, sizeof(serial)) > 0) { + ast_copy_string(o->serial, serial, sizeof(o->serial)); + } break; } if (ast_strlen_zero(o->devstr)) { @@ -3118,6 +3159,9 @@ static void _menu_print(int fd, struct chan_simpleusb_pvt *o) ast_cli(fd, "Active radio interface is [%s]\n", simpleusb_active); ast_mutex_lock(&usb_dev_lock); ast_cli(fd, "Device String is %s\n", o->devstr); + if (!ast_strlen_zero(o->serial)) { + ast_cli(fd, "Device Serial is %s\n", o->serial); + } ast_mutex_unlock(&usb_dev_lock); ast_cli(fd, "Card is %i\n", ast_radio_usb_get_usbdev(o->devstr)); ast_cli(fd, "Rx Level currently set to %d\n", o->rxmixerset); @@ -3320,7 +3364,36 @@ static void tune_write(struct chan_simpleusb_pvt *o) if (!category) { ast_log(LOG_ERROR, "No category '%s' exists?\n", o->name); } else { - CONFIG_UPDATE_STR(devstr); + /* + * To simplify channel driver setup we allow the "devstr=" value + * to be empty/blank indicating that we should match the first + * available interface. + * + * This works (and will continue to work) well as long as the + * "devstr=" value in the configuration file remains empty/blank. + * But, if the value is ever provided then we only match interfaces + * with the specified string. Moving the interface (accidentally + * or intentionally) to a different "port" will result in not + * finding/matching the interface. + * + * To minimize conflicts, we want to avoid writing out the specific + * "devstr=" value to the configuration file unless needed. Here, + * we check if the current "devstr=" value is empty/blank and + * that there is only a single audio interface connected to the + * system. If so, we leave the value empty/blank. + */ + const char *val; + char *dev; + + val = ast_variable_retrieve(cfg, o->name, "devstr"); + dev = ast_radio_usb_get_devstr(1); + if (!ast_strlen_zero(val) || !ast_strlen_zero(dev)) { + /* if the "devstr=" value exists or there is more than 1 sound device */ + CONFIG_UPDATE_STR(devstr); + if (!ast_strlen_zero(o->serial)) { + CONFIG_UPDATE_STR(serial); + } + } CONFIG_UPDATE_INT(rxmixerset); CONFIG_UPDATE_INT(txmixaset); CONFIG_UPDATE_INT(txmixbset); diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 9fc364684..0a8bfa4c0 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -203,6 +203,7 @@ struct chan_usbradio_pvt { char devicenum; char devstr[128]; + char serial[128]; int spkrmax; int micmax; int micplaymax; @@ -753,6 +754,7 @@ static int load_tune_config(struct chan_usbradio_pvt *o, const struct ast_config int opened = 0; int configured = 0; char devstr[sizeof(o->devstr)]; + char serial[sizeof(o->serial)]; /* No load defaults */ o->rxmixerset = 500; @@ -764,8 +766,10 @@ static int load_tune_config(struct chan_usbradio_pvt *o, const struct ast_config o->txslimsp = DEFAULT_TX_SOFT_LIMITER_SETPOINT; devstr[0] = '\0'; + serial[0] = '\0'; if (!reload) { o->devstr[0] = 0; + o->serial[0] = 0; } if (!cfg) { @@ -791,11 +795,13 @@ static int load_tune_config(struct chan_usbradio_pvt *o, const struct ast_config CV_UINT("txslimsp", o->txslimsp); CV_UINT("fever", o->fever); CV_STR("devstr", devstr); + CV_STR("serial", serial); CV_END; } if (!reload) { /* Using the ternary operator in CV_STR won't work, due to butchering the sizeof, so copy after if needed */ strcpy(o->devstr, devstr); /* Safe */ + strcpy(o->serial, serial); /* Safe */ } if (opened) { ast_config_destroy(cfg2); @@ -865,6 +871,8 @@ static void *hidthread(void *arg) * with the usb hid device */ while (!o->stophid) { + char serial[sizeof(o->serial)] = { '\0' }; + ast_radio_time(&o->lasthidtime); ast_mutex_lock(&usb_dev_lock); o->hasusb = 0; @@ -886,6 +894,36 @@ static void *hidthread(void *arg) */ ast_radio_time(&o->lasthidtime); + /* If configuration has a serial number defined, find the device */ + if (!ast_strlen_zero(o->serial)) { + int index; + char *index_devstr = NULL; + + for (index = 0;; index++) { + index_devstr = ast_radio_usb_get_devstr(index); + if (ast_strlen_zero(index_devstr)) { + /* if no more devices */ + break; + } + + /* get the device serial number */ + if (ast_radio_usb_get_serial(index_devstr, serial, sizeof(serial)) == 0) { + /* if no serial number */ + continue; + } + + if (strcmp(o->serial, serial) == 0) { + /* + * We found a device with the matching serial number, set + * the devstr to the matching device. + */ + ast_log(LOG_NOTICE, "Matched device serial %s to %s\n", o->serial, o->name); + ast_copy_string(o->devstr, index_devstr, sizeof(o->devstr)); + break; + } + } + } + /* Automatically assign a devstr if one was not specified in the configuration. */ if (ast_strlen_zero(o->devstr)) { int index = 0; @@ -915,6 +953,9 @@ static void *hidthread(void *arg) /* We found an unused device assign it to our node */ ast_copy_string(o->devstr, index_devstr, sizeof(o->devstr)); ast_log(LOG_NOTICE, "Channel %s: Automatically assigned USB device %s to USBRadio channel\n", o->name, o->devstr); + if (ast_radio_usb_get_serial(index_devstr, serial, sizeof(serial)) > 0) { + ast_copy_string(o->serial, serial, sizeof(o->serial)); + } break; } if (ast_strlen_zero(o->devstr)) { @@ -3648,6 +3689,9 @@ static void _menu_print(int fd, struct chan_usbradio_pvt *o) ast_cli(fd, "Active radio interface is [%s]\n", usbradio_active); ast_mutex_lock(&usb_dev_lock); ast_cli(fd, "Device String is %s\n", o->devstr); + if (!ast_strlen_zero(o->serial)) { + ast_cli(fd, "Device Serial is %s\n", o->serial); + } ast_mutex_unlock(&usb_dev_lock); ast_cli(fd, "Card is %i\n", ast_radio_usb_get_usbdev(o->devstr)); ast_cli(fd, "Output A is currently set to "); @@ -4512,7 +4556,36 @@ static void tune_write(struct chan_usbradio_pvt *o) if (!category) { ast_log(LOG_ERROR, "No category '%s' exists?\n", o->name); } else { - CONFIG_UPDATE_STR(devstr); + /* + * To simplify channel driver setup we allow the "devstr=" value + * to be empty/blank indicating that we should match the first + * available interface. + * + * This works (and will continue to work) well as long as the + * "devstr=" value in the configuration file remains empty/blank. + * But, if the value is ever provided then we only match interfaces + * with the specified string. Moving the interface (accidentally + * or intentionally) to a different "port" will result in not + * finding/matching the interface. + * + * To minimize conflicts, we want to avoid writing out the specific + * "devstr=" value to the configuration file unless needed. Here, + * we check if the current "devstr=" value is empty/blank and + * that there is only a single audio interface connected to the + * system. If so, we leave the value empty/blank. + */ + const char *val; + char *dev; + + val = ast_variable_retrieve(cfg, o->name, "devstr"); + dev = ast_radio_usb_get_devstr(1); + if (!ast_strlen_zero(val) || !ast_strlen_zero(dev)) { + /* if the "devstr=" value exists or there is more than 1 sound device */ + CONFIG_UPDATE_STR(devstr); + if (!ast_strlen_zero(o->serial)) { + CONFIG_UPDATE_STR(serial); + } + } CONFIG_UPDATE_INT(rxmixerset); CONFIG_UPDATE_INT(txmixaset); CONFIG_UPDATE_INT(txmixbset); diff --git a/configs/rpt/extensions.conf b/configs/rpt/extensions.conf index 7f8a724ba..fef6ba820 100644 --- a/configs/rpt/extensions.conf +++ b/configs/rpt/extensions.conf @@ -67,12 +67,14 @@ exten => ${NODE},1,Ringing() ; Comment-out the following clause if you want Allstar Autopatch service [pstn-out] -exten => _NXXNXXXXXX,1,playback(ss-noservice) - same => n,Congestion +exten => _NXXNXXXXXX,1,Wait(1) + same => n,Playback(ss-noservice) + same => n,Hangup ; Un-comment out the following clause if you want Allstar Autopatch service ;[pstn-out] -;exten => _NXXNXXXXXX,1,Dial(IAX2/allstar-autopatch/\${EXTEN}) +;exten => _NXXNXXXXXX,1,Wait(1) +; same => n, Dial(IAX2/allstar-autopatch/\${EXTEN}) ; same => n,Busy [invalidnum] diff --git a/configs/rpt/modules.conf b/configs/rpt/modules.conf index 87a224e68..d4e68dfa5 100644 --- a/configs/rpt/modules.conf +++ b/configs/rpt/modules.conf @@ -41,9 +41,14 @@ load = app_sendtext.so ; Send and Receive Text Applications load = app_system.so ; Generic System() application load = app_transfer.so ; Transfers a caller to another extension +; Bridging + +require = bridge_softmix.so ; Multi-party software based channel mixing + ; Channel Drivers -load = chan_dahdi.so ; DAHDI Telephony w/PRI & SS7 & MFC/R2 +require = chan_bridge_media.so ; Bridge Media Channel Driver +noload = chan_dahdi.so ; DAHDI Telephony w/PRI & SS7 & MFC/R2 noload = chan_echolink.so ; Echolink Channel Driver require = chan_iax2.so ; Inter Asterisk eXchange (Ver 2) noload = chan_mobile.so ; Bluetooth Mobile Device Channel Driver @@ -121,7 +126,7 @@ load = pbx_config.so ; Text Extension Configuration load = res_crypto.so ; Cryptographic Digital Signatures require = res_curl.so ; cURL Resource Module require = res_rpt_http_registrations.so ; RPT HTTP Periodic Registrations -load = res_timing_dahdi.so ; DAHDI Timing Interface +noload = res_timing_dahdi.so ; DAHDI Timing Interface load = res_timing_timerfd.so ; Timerfd Timing Interface is preferred for ASL3 require = res_usbradio.so ; USB Radio Resource @@ -135,8 +140,6 @@ require = res_usbradio.so ; USB Radio Resource ;load = bridge_holding.so ; Holding bridge module ;load = bridge_native_rtp.so ; Native RTP bridging module ;load = bridge_simple.so ; Simple two channel bridging module -;load = bridge_softmix.so ; Multi-party software based channel mixing -;load = chan_bridge_media.so ; Bridge Media Channel Driver ;load = chan_pjsip.so ; PJSIP Channel Driver ;load = func_pjsip_endpoint.so ; Get information about a PJSIP endpoint ;load = func_sorcery.so ; Get a field from a sorcery object diff --git a/configs/rpt/rpt.conf b/configs/rpt/rpt.conf index 64ec6c209..29a2b5866 100644 --- a/configs/rpt/rpt.conf +++ b/configs/rpt/rpt.conf @@ -65,13 +65,13 @@ node_lookup_method = dns ;method used to lookup nodes ; Must also be enabled in modules.conf ; Enable the selected channel driver in modules.conf !!! ; Rx/Tx audio/signaling channel. Choose ONLY 1 per node stanza. -; rxchannel = dahdi/pseudo ; No radio (hub) +; rxchannel = Local/pseudo ; No radio (hub) ; rxchannel = SimpleUSB/1999 ; SimpleUSB ; rxchannel = Radio/1999 ; USBRadio (DSP) ; rxchannel = Voter/1999 ; RTCM device ; rxchannel = USRP/127.0.0.1:34001:32001 ;GNU Radio interface USRP -rxchannel = dahdi/pseudo ; No radio (hub) +rxchannel = Local/pseudo ; No radio (hub) duplex = 2 ; 0 = Half duplex with no telemetry tones or hang time. ; Special Case: Full duplex if linktolink is set to yes. diff --git a/configs/rpt/simpleusb.conf b/configs/rpt/simpleusb.conf index 8847962f6..32dce21f4 100644 --- a/configs/rpt/simpleusb.conf +++ b/configs/rpt/simpleusb.conf @@ -133,7 +133,7 @@ legacyaudioscaling = no ; If yes, continue to do raw audio sample sc ; place settings that are different than the template. ; ; Note: the device string is automatically found when the -; USB setting "devstr=" is empty. +; USB setting "devstr=" and "serial=" are both empty. ; ; Note: the interface "tune" settings will be added to the ; per-node settings (below). @@ -142,6 +142,7 @@ legacyaudioscaling = no ; If yes, continue to do raw audio sample sc ;;;;; ASL3 Tune settings ;;;;; devstr= +serial= rxmixerset=500 txmixaset=500 txmixbset=500 diff --git a/configs/rpt/usbradio.conf b/configs/rpt/usbradio.conf index a9e1e658d..faba981d7 100644 --- a/configs/rpt/usbradio.conf +++ b/configs/rpt/usbradio.conf @@ -196,7 +196,7 @@ legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling an ; place settings that are different than the template. ; ; Note: the device string is automatically found when the -; USB setting "devstr=" is empty. +; USB setting "devstr=" and "serial=" are both empty. ; ; Note: the interface "tune" settings will be added to the ; per-node settings (below). @@ -205,6 +205,7 @@ legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling an ;;;;; ASL3 Tune settings ;;;;; devstr= +serial= rxmixerset=500 txmixaset=500 txmixbset=500 diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index ba4dbd7fe..5edea68e7 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -116,6 +116,7 @@ legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling an ;;;;; Tune settings ;;;;; ;devstr= +;serial= ;rxmixerset=500 ;txmixaset=500 ;txmixbset=500 diff --git a/configs/samples/usbradio.conf.sample b/configs/samples/usbradio.conf.sample index 03a092849..cff9c8260 100644 --- a/configs/samples/usbradio.conf.sample +++ b/configs/samples/usbradio.conf.sample @@ -184,6 +184,7 @@ legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling an ;;;;; Tune settings ;;;;; ;devstr= +;serial= ;rxmixerset=500 ;txmixaset=500 ;txmixbset=500 diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index 6cf4bffee..a3a3fff32 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -346,6 +346,17 @@ struct usb_device *ast_radio_hid_device_init(const char *desired_device); */ int ast_radio_usb_get_usbdev(const char *devstr); +/*! + * \brief Get serial number from device if available + * This function will attempt to get the serial number from a media device + * + * \param devstr The USB device string + * \param buf Pointer to buffer for serial number + * \param buflen Length of the serial number buffer + * \retval Length of returned serial number; 0 if no serial + */ +int ast_radio_usb_get_serial(const char *devstr, char *buf, size_t buflen); + /*! * \brief See if the internal usb_device_list contains the * specified device string. diff --git a/res/res_usbradio.c b/res/res_usbradio.c index 225e4d14f..0243f3fe2 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -598,6 +598,38 @@ int ast_radio_usb_get_usbdev(const char *devstr) return i; } +/*! + * \brief Get serial number from device if available + * This function will attempt to get the serial number from a media device + * + * \param devstr The USB device string + * \param buf Pointer to buffer for serial number + * \param buflen Length of the serial number buffer + * \retval Length of returned serial number; 0 if no serial + */ +int ast_radio_usb_get_serial(const char *devstr, char *buf, size_t buflen) +{ + struct usb_device *usb_dev; + struct usb_dev_handle *usb_handle; + int length = 0; + + usb_dev = ast_radio_hid_device_init(devstr); + if (!usb_dev) { + return 0; + } + + if (usb_dev->descriptor.iSerialNumber) { + usb_handle = usb_open(usb_dev); + if (!usb_handle) { + return 0; + } + length = usb_get_string_simple(usb_handle, usb_dev->descriptor.iSerialNumber, buf, buflen); + usb_close(usb_handle); + } + + return length; +} + int ast_radio_usb_list_check(char *devstr) { /* See usb_device_list definition for the format */