diff --git a/chan_openbsc.c b/chan_openbsc.c index bdc439e..bbff907 100644 --- a/chan_openbsc.c +++ b/chan_openbsc.c @@ -29,9 +29,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") #include #include "asterisk/channel.h" +#include "asterisk/io.h" #include "asterisk/linkedlists.h" #include "asterisk/logger.h" #include "asterisk/module.h" +#include "asterisk/rtp.h" +#include "asterisk/sched.h" +#include "asterisk/utils.h" enum call_direction { @@ -46,12 +50,20 @@ struct openbsc_chan_priv { u_int32_t callref; enum call_direction dir; + struct ast_rtp *rtp; + AST_LIST_ENTRY(openbsc_chan_priv) _list; }; static AST_RWLIST_HEAD_STATIC(g_privs, openbsc_chan_priv); static u_int32_t g_nextcallref = 0x00000001; /* uses g_privs lock */ +static struct sched_context *g_sched_ctx = NULL; +static struct io_context *g_io_ctx = NULL; + +static struct sockaddr_in g_bindaddr = { 0, }; +static struct in_addr g_ourip; + AST_MUTEX_DEFINE_STATIC(g_openbsc_lock); @@ -65,6 +77,7 @@ struct openbsc_config { char db_file[PATH_MAX]; char pcap_file[PATH_MAX]; char debug_filter[128]; + char bindaddr[128]; int debug_color; int debug_timestamp; int reject_cause; @@ -224,6 +237,9 @@ _openbsc_chan_new(struct openbsc_chan_priv *p, int state) chan->tech_pvt = p; p->owner = chan; + ast_channel_set_fd(chan, 0, ast_rtp_fd(p->rtp)); + ast_channel_set_fd(chan, 1, ast_rtcp_fd(p->rtp)); + ast_module_ref(ast_module_info->self); return chan; @@ -234,6 +250,9 @@ _openbsc_chan_detach(struct ast_channel *chan) { struct openbsc_chan_priv *p = chan->tech_pvt; + ast_channel_set_fd(chan, 0, -1); + ast_channel_set_fd(chan, 1, -1); + chan->tech_pvt = NULL; p->owner = NULL; @@ -263,6 +282,15 @@ _openbsc_chan_priv_new(u_int32_t callref, enum call_direction dir) p->callref = callref; p->dir = dir; + /* RTP init */ + p->rtp = ast_rtp_new_with_bindaddr(g_sched_ctx, g_io_ctx, 1, 0, g_bindaddr.sin_addr); + if (!p->rtp) { + ast_log(LOG_ERROR, "Failed to allocate channel RTP stream\n"); + goto error; + } + + ast_rtp_pt_clear(p->rtp); + /* Finally add to the list */ AST_RWLIST_INSERT_HEAD(&g_privs, p, _list); @@ -285,6 +313,9 @@ _openbsc_chan_priv_destroy(struct openbsc_chan_priv *p) AST_RWLIST_REMOVE(&g_privs, p, _list); AST_RWLIST_UNLOCK(&g_privs); + /* Destroy rtp */ + ast_rtp_destroy(p->rtp); + /* Free memory */ ast_free(p); } @@ -372,12 +403,26 @@ openbsc_chan_answer(struct ast_channel *chan) static struct ast_frame * openbsc_chan_read(struct ast_channel *chan) { - return 0; + struct openbsc_chan_priv *p = chan->tech_pvt; + return p->rtp ? ast_rtp_read(p->rtp) : &ast_null_frame; } static int openbsc_chan_write(struct ast_channel *chan, struct ast_frame *frame) { + struct openbsc_chan_priv *p = chan->tech_pvt; + + switch (frame->frametype) { + case AST_FRAME_VOICE: + return p->rtp ? ast_rtp_write(p->rtp, frame) : 0; + + default: + ast_log(LOG_WARNING, + "Can't send %d type frames with write\n", + frame->frametype + ); + } + return 0; } @@ -539,6 +584,87 @@ static const struct ast_channel_tech openbsc_tech = { /* }}} */ +/* ------------------------------------------------------------------------ */ +/* RTP interface */ +/* ---------------------------------------------------------------------{{{ */ + +/* Main thread */ +static pthread_t g_rtp_tid; + +static void * +openbsc_rtp_main(void *data) +{ + int rv; + + while(1) { + pthread_testcancel(); + /* Wait for sched or io */ + rv = ast_sched_wait(g_sched_ctx); + if ((rv < 0) || (rv > 1000)) { + rv = 1000; + } + rv = ast_io_wait(g_io_ctx, rv); + if (rv >= 0) { + ast_sched_runq(g_sched_ctx); + } + } + + return NULL; /* Never reached */ +} + +static int +openbsc_rtp_start(void) +{ + return ast_pthread_create(&g_rtp_tid, NULL, openbsc_rtp_main, NULL); +} + +static void +openbsc_rtp_stop(void) +{ + pthread_cancel(g_rtp_tid); + pthread_join(g_rtp_tid, NULL); +} + + +/* Interface implementation */ +static enum ast_rtp_get_result +openbsc_rtp_get_peer(struct ast_channel *chan, struct ast_rtp **rtp) +{ + struct openbsc_chan_priv *p = chan->tech_pvt; + if (p && p->rtp) { + *rtp = p->rtp; + return AST_RTP_TRY_PARTIAL; + } + return AST_RTP_GET_FAILED; +} + +static int +openbsc_rtp_set_peer(struct ast_channel *chan, + struct ast_rtp *peer, struct ast_rtp *vpeer, struct ast_rtp *tpeer, + int codecs, int nat_active) +{ + /* Direct media path not supported */ + ast_log(LOG_WARNING, "set_rtp_peer is not supported for now"); + return 0; +} + +static int +openbsc_rtp_get_codec(struct ast_channel *chan) +{ + return chan->nativeformats; +} + + +static struct ast_rtp_protocol openbsc_rtp = { + .type = "OpenBSC", + .get_rtp_info = openbsc_rtp_get_peer, + .set_rtp_peer = openbsc_rtp_set_peer, + .get_codec = openbsc_rtp_get_codec, +}; + +/* }}} */ + + /* ------------------------------------------------------------------------ */ /* Asterisk Module */ /* ---------------------------------------------------------------------{{{ */ @@ -595,6 +721,8 @@ config_module(void) cfg->reject_cause = atoi(v->value); } else if (!strcasecmp(v->name, "pcap_file")) { ast_copy_string(cfg->pcap_file, v->value, sizeof(cfg->pcap_file)); + } else if (!strcasecmp(v->name, "bindaddr")) { + ast_copy_string(cfg->bindaddr, v->value, sizeof(cfg->bindaddr)); } else { ast_log(LOG_WARNING, "Unknown config key %s ignored\n", v->name); } @@ -610,35 +738,91 @@ static int load_module(void) { struct openbsc_config *cfg; + struct ast_hostent ahp; + struct hostent *hp; int rv; cfg = config_module(); if (!cfg) return AST_MODULE_LOAD_DECLINE; + if (cfg->bindaddr[0]) { + if (!(hp = ast_gethostbyname(cfg->bindaddr, &ahp))) { + ast_log(LOG_WARNING, "Invalid address: %s\n", cfg->bindaddr); + } else { + memcpy(&g_bindaddr.sin_addr, hp->h_addr, sizeof(g_bindaddr.sin_addr)); + } + } + + if (ast_find_ourip(&g_ourip, g_bindaddr)) { + ast_log(LOG_ERROR, "Unable to get own IP address, OpenBSC disabled\n"); + goto error_free_cfg; + } + rv = openbsc_init(cfg); - - free(cfg); - if (rv) { ast_log(LOG_ERROR, "Failed to initialize OpenBSC\n"); - return AST_MODULE_LOAD_FAILURE; + goto error_free_cfg; + } + + g_io_ctx = io_context_create(); + if (!g_io_ctx) { + ast_log(LOG_ERROR, "Unable to create IO context\n"); + goto error_free; + } + + g_sched_ctx = sched_context_create(); + if (!g_sched_ctx) { + ast_log(LOG_ERROR, "Unable to create Scheduling context\n"); + goto error_free; + } + + if (ast_rtp_proto_register(&openbsc_rtp)) { + ast_log(LOG_ERROR, "Unable to RTP protocol class 'OpenBSC'\n"); + goto error_free; } if (ast_channel_register(&openbsc_tech)) { ast_log(LOG_ERROR, "Unable to register channel class 'OpenBSC'\n"); - return AST_MODULE_LOAD_FAILURE; + goto error_unreg_rtp; + } + + if (openbsc_rtp_start()) { + ast_log(LOG_ERROR, "Unable to start OpenBSC RTP thread\n"); + goto error_unreg_chan; } if (openbsc_start()) { - ast_channel_unregister(&openbsc_tech); ast_log(LOG_ERROR, "Unable to start OpenBSC main thread\n"); - return AST_MODULE_LOAD_FAILURE; + goto error_stoprtp; } ast_log(LOG_NOTICE, "OpenBSC channel driver loaded\n"); return AST_MODULE_LOAD_SUCCESS; + +error_stoprtp: + openbsc_rtp_stop(); + +error_unreg_chan: + ast_channel_unregister(&openbsc_tech); + +error_unreg_rtp: + ast_rtp_proto_unregister(&openbsc_rtp); + +error_free: + if (g_sched_ctx) + sched_context_destroy(g_sched_ctx); + + if (g_io_ctx) + io_context_destroy(g_io_ctx); + + openbsc_destroy(); + +error_free_cfg: + free(cfg); + + return AST_MODULE_LOAD_FAILURE; } @@ -649,7 +833,12 @@ unload_module(void) openbsc_stop(); + openbsc_rtp_stop(); + ast_channel_unregister(&openbsc_tech); + ast_rtp_proto_unregister(&openbsc_rtp); + sched_context_destroy(g_sched_ctx); + io_context_destroy(g_io_ctx); openbsc_destroy(); diff --git a/openbsc.conf.sample b/openbsc.conf.sample index 304788d..e429d6d 100644 --- a/openbsc.conf.sample +++ b/openbsc.conf.sample @@ -25,3 +25,5 @@ db_file = /path/to/hlr.sqlite3 ; PCAP file ; pcap_file = /path/to/log.pcap +; Bind Address +; bindaddr = 192.168.0.1