X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm.cpp;h=9236ed274fb33830a9585c774b018ba6c92ae395;hp=14f534ccd93da976375b3df6e689220d4bee6584;hb=97aa0881beacf536e3a2296381c055887583db8f;hpb=08bdc61deb2e8f8a5dc9fc129913cf1e55248d8f diff --git a/gsm.cpp b/gsm.cpp index 14f534c..9236ed2 100644 --- a/gsm.cpp +++ b/gsm.cpp @@ -4,12 +4,14 @@ ** ** **---------------------------------------------------------------------------** ** Copyright: Andreas Eversberg ** +** MNCC-Interface: Harald Welte ** ** ** ** mISDN gsm ** ** ** \*****************************************************************************/ #include "main.h" +#include "mncc.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE @@ -19,8 +21,11 @@ extern "C" { } #include +#include -struct lcr_gsm *gsm = NULL; +#define SOCKET_RETRY_TIMER 5 + +//struct lcr_gsm *gsm = NULL; int new_callref = 1; @@ -29,147 +34,53 @@ static const struct _value_string { int msg_type; const char *name; } mncc_names[] = { -#if defined(MNCC_SETUP_REQ) { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, -#endif -#if defined(MNCC_SETUP_IND) { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, -#endif -#if defined(MNCC_SETUP_RSP) { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, -#endif -#if defined(MNCC_SETUP_CNF) { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, -#endif -#if defined(MNCC_SETUP_COMPL_REQ) { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, -#endif -#if defined(MNCC_SETUP_COMPL_IND) { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, -#endif -#if defined(MNCC_CALL_CONF_IND) { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, -#endif -#if defined(MNCC_CALL_PROC_REQ) { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, -#endif -#if defined(MNCC_PROGRESS_REQ) { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, -#endif -#if defined(MNCC_ALERT_REQ) { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, -#endif -#if defined(MNCC_ALERT_IND) { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, -#endif -#if defined(MNCC_NOTIFY_REQ) { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, -#endif -#if defined(MNCC_NOTIFY_IND) { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, -#endif -#if defined(MNCC_DISC_REQ) { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, -#endif -#if defined(MNCC_DISC_IND) { MNCC_DISC_IND, "MNCC_DISC_IND" }, -#endif -#if defined(MNCC_REL_REQ) { MNCC_REL_REQ, "MNCC_REL_REQ" }, -#endif -#if defined(MNCC_REL_IND) { MNCC_REL_IND, "MNCC_REL_IND" }, -#endif -#if defined(MNCC_REL_CNF) { MNCC_REL_CNF, "MNCC_REL_CNF" }, -#endif -#if defined(MNCC_FACILITY_REQ) { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, -#endif -#if defined(MNCC_FACILITY_IND) { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, -#endif -#if defined(MNCC_START_DTMF_IND) { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, -#endif -#if defined(MNCC_START_DTMF_RSP) { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, -#endif -#if defined(MNCC_START_DTMF_REJ) { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, -#endif -#if defined(MNCC_STOP_DTMF_IND) { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, -#endif -#if defined(MNCC_STOP_DTMF_RSP) { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, -#endif -#if defined(MNCC_MODIFY_REQ) { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, -#endif -#if defined(MNCC_MODIFY_IND) { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, -#endif -#if defined(MNCC_MODIFY_RSP) { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, -#endif -#if defined(MNCC_MODIFY_CNF) { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, -#endif -#if defined(MNCC_MODIFY_REJ) { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, -#endif -#if defined(MNCC_HOLD_IND) { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, -#endif -#if defined(MNCC_HOLD_CNF) { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, -#endif -#if defined(MNCC_HOLD_REJ) { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, -#endif -#if defined(MNCC_RETRIEVE_IND) { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, -#endif -#if defined(MNCC_RETRIEVE_CNF) { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, -#endif -#if defined(MNCC_RETRIEVE_REJ) { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, -#endif -#if defined(MNCC_USERINFO_REQ) { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, -#endif -#if defined(MNCC_USERINFO_IND) { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, -#endif -#if defined(MNCC_REJ_REQ) { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, -#endif -#if defined(MNCC_REJ_IND) { MNCC_REJ_IND, "MNCC_REJ_IND" }, -#endif -#if defined(MNCC_PROGRESS_IND) { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" }, -#endif -#if defined(MNCC_CALL_PROC_IND) { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" }, -#endif -#if defined(MNCC_CALL_CONF_REQ) { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" }, -#endif -#if defined(MNCC_START_DTMF_REQ) { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" }, -#endif -#if defined(MNCC_STOP_DTMF_REQ) { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" }, -#endif -#if defined(MNCC_HOLD_REQ) { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " }, -#endif -#if defined(MNCC_RETRIEVE_REQ) { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" }, -#endif { 0, NULL } }; @@ -185,6 +96,8 @@ const char *mncc_name(int value) return "unknown"; } +static int mncc_send(struct lcr_gsm *lcr_gsm, int msg_type, void *data); + /* * create and send mncc message */ @@ -197,17 +110,12 @@ struct gsm_mncc *create_mncc(int msg_type, unsigned int callref) mncc->callref = callref; return (mncc); } -int send_and_free_mncc(void *instance, unsigned int msg_type, void *data) +int send_and_free_mncc(struct lcr_gsm *lcr_gsm, unsigned int msg_type, void *data) { int ret = 0; - if (instance) { -#ifdef WITH_GSM_BS - ret = mncc_send((struct gsm_network *)instance, msg_type, data); -#endif -#ifdef WITH_GSM_MS - ret = mncc_send((struct osmocom_ms *)instance, msg_type, data); -#endif + if (lcr_gsm) { + ret = mncc_send(lcr_gsm, msg_type, data); } free(data); @@ -224,7 +132,7 @@ Pgsm::Pgsm(int type, struct mISDNport *mISDNport, char *portname, struct port_se p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN; memset(&p_m_g_delete, 0, sizeof(p_m_g_delete)); add_work(&p_m_g_delete, delete_event, this, 0); - p_m_g_instance = NULL; + p_m_g_lcr_gsm = NULL; p_m_g_callref = 0; p_m_g_mode = 0; p_m_g_gsm_b_sock = -1; @@ -385,12 +293,10 @@ void Pgsm::frame_send(void *_frame) frame->msg_type = GSM_TCHF_FRAME; frame->callref = p_m_g_callref; memcpy(frame->data, _frame, 33); -#ifdef WITH_GSM_BS - mncc_send((struct gsm_network *)p_m_g_instance, frame->msg_type, frame); -#endif -#ifdef WITH_GSM_MS - mncc_send((struct osmocom_ms *)p_m_g_instance, frame->msg_type, frame); -#endif + + if (p_m_g_lcr_gsm) { + mncc_send(p_m_g_lcr_gsm, frame->msg_type, frame); + } } @@ -479,7 +385,7 @@ void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm mode->lchan_mode = 0x01; /* GSM V1 */ add_trace("mode", NULL, "0x%02x", mode->lchan_mode); end_trace(); - send_and_free_mncc(p_m_g_instance, mode->msg_type, mode); + send_and_free_mncc(p_m_g_lcr_gsm, mode->msg_type, mode); } @@ -501,7 +407,7 @@ void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, frame->msg_type, frame); + send_and_free_mncc(p_m_g_lcr_gsm, frame->msg_type, frame); p_m_g_tch_connected = 1; } } @@ -524,7 +430,7 @@ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, frame->msg_type, frame); + send_and_free_mncc(p_m_g_lcr_gsm, frame->msg_type, frame); p_m_g_tch_connected = 1; } } @@ -556,7 +462,7 @@ void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mnc gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_COMPL_REQ, DIRECTION_OUT); resp = create_mncc(MNCC_SETUP_COMPL_REQ, p_m_g_callref); end_trace(); - send_and_free_mncc(p_m_g_instance, resp->msg_type, resp); + send_and_free_mncc(p_m_g_lcr_gsm, resp->msg_type, resp); message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT); memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info)); @@ -568,7 +474,7 @@ void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mnc gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, frame->msg_type, frame); + send_and_free_mncc(p_m_g_lcr_gsm, frame->msg_type, frame); p_m_g_tch_connected = 1; } } @@ -587,7 +493,7 @@ void Pgsm::setup_compl_ind(unsigned int msg_type, unsigned int callref, struct g gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); frame = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, frame->msg_type, frame); + send_and_free_mncc(p_m_g_lcr_gsm, frame->msg_type, frame); p_m_g_tch_connected = 1; } } @@ -622,7 +528,7 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc add_trace("cause", "value", "%d", resp->cause.value); #endif end_trace(); - send_and_free_mncc(p_m_g_instance, resp->msg_type, resp); + send_and_free_mncc(p_m_g_lcr_gsm, resp->msg_type, resp); /* sending release to endpoint */ while(p_epointlist) { @@ -702,7 +608,7 @@ void Pgsm::message_notify(unsigned int epoint_id, int message_id, union paramete end_trace(); mncc = create_mncc(MNCC_NOTIFY_REQ, p_m_g_callref); mncc->notify = notify; - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); } } } @@ -725,7 +631,7 @@ void Pgsm::message_alerting(unsigned int epoint_id, int message_id, union parame add_trace("progress", "descr", "%d", mncc->progress.descr); } end_trace(); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_IN_ALERTING); @@ -733,7 +639,7 @@ void Pgsm::message_alerting(unsigned int epoint_id, int message_id, union parame gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); p_m_g_tch_connected = 1; } } @@ -799,7 +705,7 @@ void Pgsm::message_connect(unsigned int epoint_id, int message_id, union paramet add_trace("connected", "number", "%s", mncc->connected.number); } end_trace(); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_CONNECT_WAITING); } @@ -829,7 +735,7 @@ void Pgsm::message_disconnect(unsigned int epoint_id, int message_id, union para add_trace("cause", "location", "%d", mncc->cause.location); add_trace("cause", "value", "%d", mncc->cause.value); end_trace(); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_OUT_DISCONNECT); @@ -837,7 +743,7 @@ void Pgsm::message_disconnect(unsigned int epoint_id, int message_id, union para gsm_trace_header(p_m_mISDNport, this, MNCC_FRAME_RECV, DIRECTION_OUT); end_trace(); mncc = create_mncc(MNCC_FRAME_RECV, p_m_g_callref); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); p_m_g_tch_connected = 1; } } @@ -859,7 +765,7 @@ void Pgsm::message_release(unsigned int epoint_id, int message_id, union paramet add_trace("cause", "location", "%d", mncc->cause.location); add_trace("cause", "value", "%d", mncc->cause.value); end_trace(); - send_and_free_mncc(p_m_g_instance, mncc->msg_type, mncc); + send_and_free_mncc(p_m_g_lcr_gsm, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); trigger_work(&p_m_g_delete); @@ -985,12 +891,6 @@ static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int i int gsm_exit(int rc) { - /* free gsm instance */ - if (gsm) { - free(gsm); - gsm = NULL; - } - return(rc); } @@ -999,14 +899,214 @@ int gsm_init(void) /* seed the PRNG */ srand(time(NULL)); - /* create gsm instance */ - gsm = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm)); + return 0; +} + +/* + * MNCC interface + */ + +static int mncc_q_enqueue(struct lcr_gsm *lcr_gsm, struct gsm_mncc *mncc, unsigned int len) +{ + struct mncc_q_entry *qe; + + qe = (struct mncc_q_entry *) MALLOC(sizeof(*qe)+sizeof(*mncc)+len); + if (!qe) + return -ENOMEM; + + qe->next = NULL; + qe->len = len; + memcpy(qe->data, mncc, len); + + /* in case of empty list ... */ + if (!lcr_gsm->mncc_q_hd && !lcr_gsm->mncc_q_tail) { + /* the list head and tail both point to the new qe */ + lcr_gsm->mncc_q_hd = lcr_gsm->mncc_q_tail = qe; + } else { + /* append to tail of list */ + lcr_gsm->mncc_q_tail->next = qe; + lcr_gsm->mncc_q_tail = qe; + } + + lcr_gsm->mncc_lfd.when |= LCR_FD_WRITE; return 0; } -int handle_gsm(void) +static struct mncc_q_entry *mncc_q_dequeue(struct lcr_gsm *lcr_gsm) +{ + struct mncc_q_entry *qe = lcr_gsm->mncc_q_hd; + if (!qe) + return NULL; + + /* dequeue the successfully sent message */ + lcr_gsm->mncc_q_hd = qe->next; + if (!qe) + return NULL; + if (qe == lcr_gsm->mncc_q_tail) + lcr_gsm->mncc_q_tail = NULL; + + return qe; +} + +/* routine called by LCR code if it wants to send a message to OpenBSC */ +static int mncc_send(struct lcr_gsm *lcr_gsm, int msg_type, void *data) +{ + int len = 0; + + /* FIXME: the caller should provide this */ + switch (msg_type) { + case GSM_TCHF_FRAME: + len = sizeof(struct gsm_data_frame) + 33; + break; + default: + len = sizeof(struct gsm_mncc); + break; + } + + return mncc_q_enqueue(lcr_gsm, (struct gsm_mncc *)data, len); +} + +/* close MNCC socket */ +static int mncc_fd_close(struct lcr_gsm *lcr_gsm, struct lcr_fd *lfd) { + class Port *port; + class Pgsm *pgsm = NULL; + struct lcr_msg *message; + + PERROR("Lost MNCC socket, retrying in %u seconds\n", SOCKET_RETRY_TIMER); + close(lfd->fd); + unregister_fd(lfd); + lfd->fd = -1; + + /* free all the calls that were running through the MNCC interface */ + port = port_first; + while(port) { + if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_GSM) { + pgsm = (class Pgsm *)port; + if (pgsm->p_m_g_lcr_gsm == lcr_gsm) { + message = message_create(pgsm->p_serial, ACTIVE_EPOINT(pgsm->p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 27; // temp. unavail. + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + pgsm->new_state(PORT_STATE_RELEASE); + trigger_work(&pgsm->p_m_g_delete); + } + } + port = port->next; + } + + /* flush the queue */ + while (mncc_q_dequeue(lcr_gsm)) + ; + + /* start the re-connect timer */ + schedule_timer(&lcr_gsm->socket_retry, SOCKET_RETRY_TIMER, 0); + + return 0; +} + +/* write to OpenBSC via MNCC socket */ +static int mncc_fd_write(struct lcr_fd *lfd, void *inst, int idx) +{ + struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst; + struct mncc_q_entry *qe, *qe2; + int rc; + + while (1) { + qe = lcr_gsm->mncc_q_hd; + if (!qe) { + lfd->when &= ~LCR_FD_WRITE; + break; + } + rc = write(lfd->fd, qe->data, qe->len); + if (rc == 0) + return mncc_fd_close(lcr_gsm, lfd); + if (rc < 0) + return rc; + if (rc < (int)qe->len) + return -1; + /* dequeue the successfully sent message */ + qe2 = mncc_q_dequeue(lcr_gsm); + assert(qe == qe2); + free(qe); + } + return 0; +} + +/* read from OpenBSC via MNCC socket */ +static int mncc_fd_read(struct lcr_fd *lfd, void *inst, int idx) +{ + struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst; + int rc; + static char buf[sizeof(struct gsm_mncc)+1024]; + struct gsm_mncc *mncc_prim = (struct gsm_mncc *) buf; + + memset(buf, 0, sizeof(buf)); + rc = recv(lfd->fd, buf, sizeof(buf), 0); + if (rc == 0) + return mncc_fd_close(lcr_gsm, lfd); + if (rc < 0) + return rc; + + /* Hand the MNCC message into LCR */ + switch (lcr_gsm->type) { +#ifdef WITH_GSM_BS + case LCR_GSM_TYPE_NETWORK: + return message_bsc(lcr_gsm, mncc_prim->msg_type, mncc_prim); +#endif +#ifdef WITH_GSM_MS + case LCR_GSM_TYPE_MS: + return message_ms(lcr_gsm, mncc_prim->msg_type, mncc_prim); +#endif + default: + return 0; + } +} + +/* file descriptor callback if we can read or write form MNCC socket */ +static int mncc_fd_cb(struct lcr_fd *lfd, unsigned int what, void *inst, int idx) +{ + int rc = 0; + + if (what & LCR_FD_READ) + rc = mncc_fd_read(lfd, inst, idx); + if (rc < 0) + return rc; + + if (what & LCR_FD_WRITE) + rc = mncc_fd_write(lfd, inst, idx); + + return rc; +} + +int mncc_socket_retry_cb(struct lcr_timer *timer, void *inst, int index) +{ + struct lcr_gsm *lcr_gsm = (struct lcr_gsm *) inst; + int fd, rc; + + lcr_gsm->mncc_lfd.fd = -1; + + fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); + if (fd < 0) { + PERROR("Cannot create SEQPACKET socket, giving up!\n"); + return fd; + } + + rc = connect(fd, (struct sockaddr *) &lcr_gsm->sun, + sizeof(lcr_gsm->sun)); + if (rc < 0) { + PERROR("Could not connect to MNCC socket %s, " + "retrying in %u seconds\n", lcr_gsm->sun.sun_path, + SOCKET_RETRY_TIMER); + close(fd); + schedule_timer(&lcr_gsm->socket_retry, SOCKET_RETRY_TIMER, 0); + } else { + PDEBUG(DEBUG_GSM, "Connected to MNCC socket %s!\n", lcr_gsm->sun.sun_path); + lcr_gsm->mncc_lfd.fd = fd; + register_fd(&lcr_gsm->mncc_lfd, LCR_FD_READ, &mncc_fd_cb, lcr_gsm, 0); + } + return 0; }