diff --git a/src/call.c b/src/call.c index 5e0e726..ce339c0 100644 --- a/src/call.c +++ b/src/call.c @@ -23,8 +23,10 @@ #include +extern void *tall_mncc_ctx; LLIST_HEAD(g_call_list); +static uint32_t last_call_id = 5000; void calls_init(void) {} @@ -48,3 +50,26 @@ void call_leg_release(struct call *call, struct call_leg *leg) talloc_free(call); } } + +struct call *sip_call_mncc_create(void) +{ + struct call *call; + + call = talloc_zero(tall_mncc_ctx, struct call); + if (!call) { + LOGP(DCALL, LOGL_ERROR, "Failed to allocate memory for call\n"); + return NULL; + } + call->id = ++last_call_id; + + call->initial = (struct call_leg *) talloc_zero(call, struct mncc_call_leg); + if (!call->initial) { + LOGP(DCALL, LOGL_ERROR, "Failed to allocate MNCC leg\n"); + talloc_free(call); + return NULL; + } + + call->initial->type = CALL_TYPE_MNCC; + llist_add(&call->entry, &g_call_list); + return call; +} diff --git a/src/call.h b/src/call.h index ea2c847..c8e9f0b 100644 --- a/src/call.h +++ b/src/call.h @@ -2,6 +2,8 @@ #include +#include "mncc_protocol.h" + struct sip_agent; struct mncc_connection; @@ -42,9 +44,18 @@ struct sip_call_leg { struct sip_agent *agent; }; +enum mncc_cc_state { + MNCC_CC_INITIAL, +}; + struct mncc_call_leg { struct call_leg base; + enum mncc_cc_state state; + uint32_t callref; + struct gsm_mncc_number called; + struct gsm_mncc_number calling; + struct mncc_connection *conn; }; @@ -53,3 +64,6 @@ void calls_init(void); void call_leg_release(struct call *call, struct call_leg *leg); + + +struct call *sip_call_mncc_create(void); diff --git a/src/mncc.c b/src/mncc.c index 500d1e9..9b48f5c 100644 --- a/src/mncc.c +++ b/src/mncc.c @@ -22,8 +22,10 @@ #include "mncc_protocol.h" #include "app.h" #include "logging.h" +#include "call.h" #include +#include #include #include @@ -31,6 +33,88 @@ #include #include +static void close_connection(struct mncc_connection *conn); + + +static struct mncc_call_leg *mncc_find_leg(uint32_t callref, struct call **out_call) +{ + struct call *call; + + llist_for_each_entry(call, &g_call_list, entry) { + if (call->initial && call->initial->type == CALL_TYPE_MNCC) { + struct mncc_call_leg *leg = (struct mncc_call_leg *) call->initial; + if (leg->callref == callref) { + *out_call = call; + return leg; + } + } + if (call->remote && call->remote->type == CALL_TYPE_MNCC) { + struct mncc_call_leg *leg = (struct mncc_call_leg *) call->remote; + if (leg->callref == callref) { + *out_call = call; + return leg; + } + } + } + + *out_call = NULL; + return NULL; +} + +static void mncc_send(struct mncc_connection *conn, uint32_t msg_type, uint32_t callref) +{ + int rc; + struct gsm_mncc mncc = { 0, }; + + mncc.msg_type = msg_type; + mncc.callref = callref; + + /* + * TODO: we need to put cause in here for release or such? shall we return a + * static struct? + */ + rc = write(conn->fd.fd, &mncc, sizeof(mncc)); + if (rc != sizeof(mncc)) { + LOGP(DMNCC, LOGL_ERROR, "Failed to send message call(%u)\n", callref); + close_connection(conn); + } +} + +static void mncc_rtp_send(struct mncc_connection *conn, uint32_t msg_type, uint32_t callref) +{ + int rc; + struct gsm_mncc_rtp mncc = { 0, }; + + mncc.msg_type = msg_type; + mncc.callref = callref; + + rc = write(conn->fd.fd, &mncc, sizeof(mncc)); + if (rc != sizeof(mncc)) { + LOGP(DMNCC, LOGL_ERROR, "Failed to send message call(%u)\n", callref); + close_connection(conn); + } +} + + +static void mncc_call_leg_release(struct call *call, struct call_leg *_leg) +{ + struct mncc_call_leg *leg; + + OSMO_ASSERT(_leg->type == CALL_TYPE_MNCC); + leg = (struct mncc_call_leg *) _leg; + + /* drop it directly, if not connected */ + if (leg->conn->state != MNCC_READY) + return call_leg_release(call, _leg); + + switch (leg->state) { + case MNCC_CC_INITIAL: + mncc_send(leg->conn, MNCC_REJ_REQ, leg->callref); + call_leg_release(call, _leg); + break; + } +} + static void close_connection(struct mncc_connection *conn) { osmo_fd_unregister(&conn->fd); @@ -41,6 +125,79 @@ static void close_connection(struct mncc_connection *conn) conn->on_disconnect(conn); } +static void check_rtp_create(struct mncc_connection *conn, char *buf, int rc) +{ + struct gsm_mncc_rtp *rtp; + struct call *call; + struct mncc_call_leg *leg; + + if (rc < sizeof(*rtp)) { + LOGP(DMNCC, LOGL_ERROR, "gsm_mncc_rtp of wrong size %d < %d\n", + rc, sizeof(*rtp)); + return close_connection(conn); + } + + rtp = (struct gsm_mncc_rtp *) buf; + leg = mncc_find_leg(rtp->callref, &call); + if (!leg) { + LOGP(DMNCC, LOGL_ERROR, "call(%u) can not be found\n", rtp->callref); + return mncc_send(conn, MNCC_REJ_REQ, rtp->callref); + } + + /* TODO.. now we can continue with the call */ + mncc_send(leg->conn, MNCC_REJ_REQ, leg->callref); + call_leg_release(call, &leg->base); +} + +static void check_setup(struct mncc_connection *conn, char *buf, int rc) +{ + struct gsm_mncc *data; + struct call *call; + struct mncc_call_leg *leg; + + if (rc != sizeof(*data)) { + LOGP(DMNCC, LOGL_ERROR, "gsm_mncc of wrong size %d vs. %d\n", + rc, sizeof(*data)); + return close_connection(conn); + } + + data = (struct gsm_mncc *) buf; + + /* screen arguments */ + if ((data->fields & MNCC_F_CALLED) == 0) { + LOGP(DMNCC, LOGL_ERROR, + "MNCC call(%u) without called addr fields(%u)\n", + data->callref, data->fields); + return mncc_send(conn, MNCC_REJ_REQ, data->callref); + } + if ((data->fields & MNCC_F_CALLING) == 0) { + LOGP(DMNCC, LOGL_ERROR, + "MNCC call(%u) without calling addr fields(%u)\n", + data->callref, data->fields); + return mncc_send(conn, MNCC_REJ_REQ, data->callref); + } + + /* TODO.. bearer caps and better audio handling */ + + /* Create an RTP port and then allocate a call */ + call = sip_call_mncc_create(); + if (!call) { + LOGP(DMNCC, LOGL_ERROR, + "MNCC call(%u) failed to allocate call\n", data->callref); + return mncc_send(conn, MNCC_REJ_REQ, data->callref); + } + + leg = (struct mncc_call_leg *) call->initial; + leg->base.release_call = mncc_call_leg_release; + leg->callref = data->callref; + leg->conn = conn; + leg->state = MNCC_CC_INITIAL; + memcpy(&leg->called, &data->called, sizeof(leg->called)); + memcpy(&leg->calling, &data->calling, sizeof(leg->calling)); + + mncc_rtp_send(conn, MNCC_RTP_CREATE, data->callref); +} + static void check_hello(struct mncc_connection *conn, char *buf, int rc) { struct gsm_mncc_hello *hello; @@ -105,6 +262,12 @@ static int mncc_data(struct osmo_fd *fd, unsigned int what) case MNCC_SOCKET_HELLO: check_hello(conn, buf, rc); break; + case MNCC_SETUP_IND: + check_setup(conn, buf, rc); + break; + case MNCC_RTP_CREATE: + check_rtp_create(conn, buf, rc); + break; default: LOGP(DMNCC, LOGL_ERROR, "Unhandled message type %d/0x%x\n", msg_type, msg_type); diff --git a/src/mncc.h b/src/mncc.h index 5cbee4d..5944a1b 100644 --- a/src/mncc.h +++ b/src/mncc.h @@ -3,6 +3,8 @@ #include #include +#include + struct app_config; enum { @@ -18,6 +20,7 @@ struct mncc_connection { struct osmo_timer_list reconnect; + uint32_t last_callref; /* callback for application logic */ void (*on_disconnect)(struct mncc_connection *);