X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm_ms.cpp;h=481a4ac0b0d387796341cbbe3b4337ba85cbc812;hp=f8541fafb172f3cf6a79b2c56269bc6ecc33672c;hb=16445870043275f7d5a0b7aa9a1abfa9e8a5c249;hpb=c215a8b0b74d08bb529e4ae3a195e4fee2e2f7b1 diff --git a/gsm_ms.cpp b/gsm_ms.cpp index f8541fa..481a4ac 100644 --- a/gsm_ms.cpp +++ b/gsm_ms.cpp @@ -16,24 +16,26 @@ #endif extern "C" { #include +#include #include #include +#include -#include -#include -#include -#include +#include +#include +#include +#include } -const char *openbsc_copyright = ""; +static const char *config_file = "/etc/osmocom/osmocom.cfg"; short vty_port = 4247; struct llist_head ms_list; struct log_target *stderr_target; void *l23_ctx = NULL; -int (*l23_app_work) (struct osmocom_ms *ms) = NULL; -int (*l23_app_exit) (struct osmocom_ms *ms) = NULL; + +static int dtmf_timeout(struct lcr_timer *timer, void *instance, int index); /* * constructor @@ -52,8 +54,11 @@ Pgsm_ms::Pgsm_ms(int type, struct mISDNport *mISDNport, char *portname, struct p } } - if (!p_m_g_instance) - FATAL("MS name %s does not exists. Please fix!"); + p_m_g_dtmf_state = DTMF_ST_IDLE; + p_m_g_dtmf_index = 0; + p_m_g_dtmf[0] = '\0'; + memset(&p_m_g_dtmf_timer, 0, sizeof(p_m_g_dtmf_timer)); + add_timer(&p_m_g_dtmf_timer, dtmf_timeout, this, 0); PDEBUG(DEBUG_GSM, "Created new GSMMSPort(%s %s).\n", portname, ms_name); } @@ -64,6 +69,7 @@ Pgsm_ms::Pgsm_ms(int type, struct mISDNport *mISDNport, char *portname, struct p Pgsm_ms::~Pgsm_ms() { PDEBUG(DEBUG_GSM, "Destroyed GSM MS process(%s).\n", p_name); + del_timer(&p_m_g_dtmf_timer); } /* @@ -123,7 +129,7 @@ void Pgsm_ms::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ return; } - l1l2l3_trace_header(p_m_mISDNport, this, MNCC_SETUP_IND, DIRECTION_IN); + gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_IND, DIRECTION_IN); /* caller information */ p_callerinfo.ntype = INFO_NTYPE_NOTPRESENT; if (mncc->fields & MNCC_F_CALLING) { @@ -199,6 +205,7 @@ void Pgsm_ms::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ add_trace("dialing", "plan", "%d", mncc->called.plan); add_trace("dialing", "number", "%s", mncc->called.number); } + p_dialinginfo.sending_complete = 1; /* redir info */ p_redirinfo.ntype = INFO_NTYPE_NOTPRESENT; if (mncc->fields & MNCC_F_REDIRECTING) { @@ -317,16 +324,6 @@ void Pgsm_ms::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ if (bchannel_open(p_m_b_index)) goto no_channel; - /* what infos did we got ... */ - gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); - if (p_callerinfo.id[0]) - add_trace("calling", "number", "%s", p_callerinfo.id); - else - SPRINT(p_callerinfo.id, "imsi-%s", p_callerinfo.imsi); - add_trace("calling", "imsi", "%s", p_callerinfo.imsi); - add_trace("dialing", "number", "%s", p_dialinginfo.id); - end_trace(); - /* create endpoint */ if (p_epointlist) FATAL("Incoming call but already got an endpoint.\n"); @@ -345,17 +342,12 @@ void Pgsm_ms::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_ send_and_free_mncc(p_m_g_instance, mode->msg_type, mode); /* send call proceeding */ - gsm_trace_header(p_m_mISDNport, this, MNCC_CALL_PROC_REQ, DIRECTION_OUT); - proceeding = create_mncc(MNCC_CALL_PROC_REQ, p_m_g_callref); - if (p_m_mISDNport->tones) { - proceeding->fields |= MNCC_F_PROGRESS; - proceeding->progress.coding = 3; /* GSM */ - proceeding->progress.location = 1; - proceeding->progress.descr = 8; - add_trace("progress", "coding", "%d", proceeding->progress.coding); - add_trace("progress", "location", "%d", proceeding->progress.location); - add_trace("progress", "descr", "%d", proceeding->progress.descr); - } + gsm_trace_header(p_m_mISDNport, this, MNCC_CALL_CONF_REQ, DIRECTION_OUT); + proceeding = create_mncc(MNCC_CALL_CONF_REQ, p_m_g_callref); + // FIXME: bearer + /* DTMF supported */ + proceeding->fields |= MNCC_F_CCCAP; + proceeding->cccap.dtmf = 1; end_trace(); send_and_free_mncc(p_m_g_instance, proceeding->msg_type, proceeding); @@ -396,7 +388,31 @@ static int message_ms(struct osmocom_ms *ms, int msg_type, void *arg) struct mISDNport *mISDNport; /* Special messages */ - if (msg_type) { + switch (msg_type) { + case MS_NEW: + PDEBUG(DEBUG_GSM, "MS %s comes available\n", ms->name); + return 0; + case MS_DELETE: + PDEBUG(DEBUG_GSM, "MS %s is removed\n", ms->name); + port = port_first; + while(port) { + if ((port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_MS) { + pgsm_ms = (class Pgsm_ms *)port; + if (pgsm_ms->p_m_g_instance == ms) { + struct lcr_msg *message; + + pgsm_ms->p_m_g_instance = 0; + message = message_create(pgsm_ms->p_serial, ACTIVE_EPOINT(pgsm_ms->p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 27; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + pgsm_ms->new_state(PORT_STATE_RELEASE); + trigger_work(&pgsm_ms->p_m_g_delete); + } + } + port = port->next; + } + return 0; } /* find callref */ @@ -414,7 +430,7 @@ static int message_ms(struct osmocom_ms *ms, int msg_type, void *arg) if (msg_type == GSM_TCHF_FRAME) { if (port) - pgsm_ms->frame_receive((struct gsm_trau_frame *)arg); + pgsm_ms->frame_receive(arg); return 0; } @@ -456,6 +472,10 @@ static int message_ms(struct osmocom_ms *ms, int msg_type, void *arg) pgsm_ms->setup_ind(msg_type, callref, mncc); break; + case MNCC_CALL_PROC_IND: + pgsm_ms->call_proc_ind(msg_type, callref, mncc); + break; + case MNCC_ALERT_IND: pgsm_ms->alert_ind(msg_type, callref, mncc); break; @@ -482,6 +502,12 @@ static int message_ms(struct osmocom_ms *ms, int msg_type, void *arg) pgsm_ms->notify_ind(msg_type, callref, mncc); break; + case MNCC_START_DTMF_RSP: + case MNCC_START_DTMF_REJ: + case MNCC_STOP_DTMF_RSP: + pgsm_ms->dtmf_statemachine(mncc); + break; + default: ; } @@ -503,6 +529,20 @@ void Pgsm_ms::message_setup(unsigned int epoint_id, int message_id, union parame memcpy(&p_capainfo, ¶m->setup.capainfo, sizeof(p_capainfo)); memcpy(&p_redirinfo, ¶m->setup.redirinfo, sizeof(p_redirinfo)); + /* no instance */ + if (!p_m_g_instance) { + gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT); + add_trace("failure", NULL, "MS %s instance is unavailable", p_m_mISDNport->ifport->gsm_ms_name); + end_trace(); + message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 27; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_m_g_delete); + return; + } + /* no number */ if (!p_dialinginfo.id[0]) { gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT); @@ -617,6 +657,13 @@ void Pgsm_ms::message_setup(unsigned int epoint_id, int message_id, union parame mncc->bearer_cap.mode = 0; break; } + /* DTMF supported */ + mncc->fields |= MNCC_F_CCCAP; + mncc->cccap.dtmf = 1; + + /* request keypad from remote */ + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ENABLEKEYPAD); + message_put(message); } end_trace(); @@ -630,11 +677,119 @@ void Pgsm_ms::message_setup(unsigned int epoint_id, int message_id, union parame new_state(PORT_STATE_OUT_PROCEEDING); } +void Pgsm_ms::dtmf_statemachine(struct gsm_mncc *mncc) +{ + struct gsm_mncc *dtmf; + + switch (p_m_g_dtmf_state) { + case DTMF_ST_SPACE: + case DTMF_ST_IDLE: + /* end of string */ + if (!p_m_g_dtmf[p_m_g_dtmf_index]) { + PDEBUG(DEBUG_GSM, "done with DTMF\n"); + p_m_g_dtmf_state = DTMF_ST_IDLE; + return; + } + gsm_trace_header(p_m_mISDNport, this, MNCC_START_DTMF_REQ, DIRECTION_OUT); + dtmf = create_mncc(MNCC_START_DTMF_REQ, p_m_g_callref); + dtmf->keypad = p_m_g_dtmf[p_m_g_dtmf_index++]; + p_m_g_dtmf_state = DTMF_ST_START; + PDEBUG(DEBUG_GSM, "start DTMF (keypad %c)\n", + dtmf->keypad); + end_trace(); + send_and_free_mncc(p_m_g_instance, dtmf->msg_type, dtmf); + return; + case DTMF_ST_START: + if (mncc->msg_type != MNCC_START_DTMF_RSP) { + PDEBUG(DEBUG_GSM, "DTMF was rejected\n"); + return; + } + schedule_timer(&p_m_g_dtmf_timer, 0, 70 * 1000); + p_m_g_dtmf_state = DTMF_ST_MARK; + PDEBUG(DEBUG_GSM, "DTMF is on\n"); + break; + case DTMF_ST_MARK: + gsm_trace_header(p_m_mISDNport, this, MNCC_STOP_DTMF_REQ, DIRECTION_OUT); + dtmf = create_mncc(MNCC_STOP_DTMF_REQ, p_m_g_callref); + p_m_g_dtmf_state = DTMF_ST_STOP; + end_trace(); + send_and_free_mncc(p_m_g_instance, dtmf->msg_type, dtmf); + return; + case DTMF_ST_STOP: + schedule_timer(&p_m_g_dtmf_timer, 0, 120 * 1000); + p_m_g_dtmf_state = DTMF_ST_SPACE; + PDEBUG(DEBUG_GSM, "DTMF is off\n"); + break; + } +} + +static int dtmf_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class Pgsm_ms *pgsm_ms = (class Pgsm_ms *)instance; + + PDEBUG(DEBUG_GSM, "DTMF timer has fired\n"); + pgsm_ms->dtmf_statemachine(NULL); + + return 0; +} + +/* MESSAGE_DTMF */ +void Pgsm_ms::message_dtmf(unsigned int epoint_id, int message_id, union parameter *param) +{ + char digit = param->dtmf; + + if (digit >= 'a' && digit <= 'c') + digit = digit - 'a' + 'A'; + if (!strchr("01234567890*#ABC", digit)) + return; + + /* schedule */ + if (p_m_g_dtmf_state == DTMF_ST_IDLE) { + p_m_g_dtmf_index = 0; + p_m_g_dtmf[0] = '\0'; + } + SCCAT(p_m_g_dtmf, digit); + if (p_m_g_dtmf_state == DTMF_ST_IDLE) + dtmf_statemachine(NULL); +} + +/* MESSAGE_INFORMATION */ +void Pgsm_ms::message_information(unsigned int epoint_id, int message_id, union parameter *param) +{ + char digit; + int i; + + for (i = 0; i < (int)strlen(param->information.id); i++) { + digit = param->information.id[i]; + if (digit >= 'a' && digit <= 'c') + digit = digit - 'a' + 'A'; + if (!strchr("01234567890*#ABC", digit)) + continue; + + /* schedule */ + if (p_m_g_dtmf_state == DTMF_ST_IDLE) { + p_m_g_dtmf_index = 0; + p_m_g_dtmf[0] = '\0'; + } + SCCAT(p_m_g_dtmf, digit); + if (p_m_g_dtmf_state == DTMF_ST_IDLE) + dtmf_statemachine(NULL); + } +} + /* * endpoint sends messages to the port */ int Pgsm_ms::message_epoint(unsigned int epoint_id, int message_id, union parameter *param) { + struct lcr_msg *message; + + if (message_id == MESSAGE_CONNECT) { + /* request keypad from remote */ + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ENABLEKEYPAD); + message_put(message); + } + if (Pgsm::message_epoint(epoint_id, message_id, param)) return(1); @@ -645,6 +800,14 @@ int Pgsm_ms::message_epoint(unsigned int epoint_id, int message_id, union parame message_setup(epoint_id, message_id, param); break; + case MESSAGE_DTMF: + message_dtmf(epoint_id, message_id, param); + break; + + case MESSAGE_INFORMATION: + message_information(epoint_id, message_id, param); + break; + default: PDEBUG(DEBUG_GSM, "Pgsm_ms(%s) gsm port with (caller id %s) received unhandled nessage: %d\n", p_name, p_callerinfo.id, message_id); } @@ -654,6 +817,7 @@ int Pgsm_ms::message_epoint(unsigned int epoint_id, int message_id, union parame int gsm_ms_exit(int rc) { + l23_app_exit(); return(rc); } @@ -668,50 +832,40 @@ int gsm_ms_init(void) l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); - return 0; -} - -/* add a new GSM mobile instance */ -int gsm_ms_new(const char *name, const char *socket_path) -{ - struct osmocom_ms *ms = NULL; - int rc; - - PDEBUG(DEBUG_GSM, "GSM: creating new instance '%s' on '%s'\n", name, socket_path); + log_parse_category_mask(stderr_target, "DCS:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DPAG:DSUM"); + log_set_log_level(stderr_target, LOGL_INFO); - ms = talloc_zero(l23_ctx, struct osmocom_ms); - if (!ms) { - FATAL("Failed to allocate MS\n"); +#if 0 + if (gsmtap_ip) { + rc = gsmtap_init(gsmtap_ip); + if (rc < 0) { + fprintf(stderr, "Failed during gsmtap_init()\n"); + exit(1); + } } - /* must add here, because other init processes may search in the list */ - llist_add_tail(&ms->entity, &ms_list); - - - SPRINT(ms->name, name); +#endif - rc = layer2_open(ms, socket_path); - if (rc < 0) { - FATAL("Failed during layer2_open()\n"); - } + l23_app_init(message_ms, config_file, vty_port); - lapdm_init(&ms->l2_entity.lapdm_dcch, ms); - lapdm_init(&ms->l2_entity.lapdm_acch, ms); + return 0; +} - rc = l23_app_init(ms); - if (rc < 0) { - FATAL("Failed to init layer23\n"); - } - ms->cclayer.mncc_recv = message_ms; +/* add a new GSM mobile instance */ +int gsm_ms_new(const char *name) +{ + PDEBUG(DEBUG_GSM, "GSM: interface for MS '%s' is up\n", name); return 0; } int gsm_ms_delete(const char *name) { - struct osmocom_ms *ms = NULL; + struct osmocom_ms *ms; int found = 0; + class Port *port; + class Pgsm_ms *pgsm_ms = NULL; - PDEBUG(DEBUG_GSM, "GSM: destroying instance '%s'\n", name); + PDEBUG(DEBUG_GSM, "GSM: interface for MS '%s' is down\n", name); llist_for_each_entry(ms, &ms_list, entity) { if (!strcmp(ms->name, name)) { @@ -720,15 +874,29 @@ int gsm_ms_delete(const char *name) } } - if (!found) { - FATAL("Failed delete layer23, instance '%s' not found\n", name); - } - - l23_app_exit(ms); - lapdm_exit(&ms->l2_entity.lapdm_dcch); - lapdm_exit(&ms->l2_entity.lapdm_acch); + if (!found) + return 0; - llist_del(&ms->entity); + port = port_first; + while(port) { + if ((port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_MS) { + pgsm_ms = (class Pgsm_ms *)port; + if (pgsm_ms->p_m_g_instance == ms && pgsm_ms->p_m_g_callref) { + struct gsm_mncc *rej; + + rej = create_mncc(MNCC_REL_REQ, pgsm_ms->p_m_g_callref); + rej->fields |= MNCC_F_CAUSE; + rej->cause.coding = 3; + rej->cause.location = 1; + rej->cause.value = 27; + gsm_trace_header(NULL, NULL, MNCC_REJ_REQ, DIRECTION_OUT); + add_trace("cause", "coding", "%d", rej->cause.coding); + add_trace("cause", "location", "%d", rej->cause.location); + add_trace("cause", "value", "%d", rej->cause.value); + end_trace(); + } + } + } return 0; } @@ -736,18 +904,17 @@ int gsm_ms_delete(const char *name) /* * handles bsc select function within LCR's main loop */ -int handle_gsm_ms(void) +int handle_gsm_ms(int *_quit) { - struct osmocom_ms *ms = NULL; - int work = 0; - - llist_for_each_entry(ms, &ms_list, entity) { - if (l23_app_work(ms)) - work = 1; -// debug_reset_context(); - if (bsc_select_main(1)) /* polling */ - work = 1; - } + int work = 0, quit = 0; + + if (l23_app_work(&quit)) + work = 1; + if (quit && llist_empty(&ms_list)) + *_quit = 1; +// debug_reset_context(); + if (bsc_select_main(1)) /* polling */ + work = 1; return work; }