X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=gsm.cpp;h=e6806c34001290b8ecf4a6b6020a6fd1c5d9e7a7;hp=e4d5792a0fb98820525b0f6f0f67e95194a247c8;hb=41a0668da1dbd5644a50036eaa26c8ba85201ffd;hpb=a12d7eee22a72f4c999535892763dde15212e89e diff --git a/gsm.cpp b/gsm.cpp index e4d5792..e6806c3 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,11 +21,83 @@ extern "C" { } #include +#include -struct lcr_gsm *gsm = NULL; +#define SOCKET_RETRY_TIMER 5 + +//struct lcr_gsm *gsm = NULL; int new_callref = 1; +/* names of MNCC-SAP */ +static const struct _value_string { + int msg_type; + const char *name; +} mncc_names[] = { + { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, + { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, + { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, + { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, + { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, + { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, + { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, + { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, + { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, + { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, + { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, + { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, + { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, + { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, + { MNCC_DISC_IND, "MNCC_DISC_IND" }, + { MNCC_REL_REQ, "MNCC_REL_REQ" }, + { MNCC_REL_IND, "MNCC_REL_IND" }, + { MNCC_REL_CNF, "MNCC_REL_CNF" }, + { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, + { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, + { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, + { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, + { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, + { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, + { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, + { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, + { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, + { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, + { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, + { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, + { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, + { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, + { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, + { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, + { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, + { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, + { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, + { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, + { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, + { MNCC_REJ_IND, "MNCC_REJ_IND" }, + { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" }, + { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" }, + { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" }, + { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" }, + { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" }, + { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " }, + { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" }, + { 0, NULL } +}; + +const char *mncc_name(int value) +{ + int i = 0; + + while (mncc_names[i].name) { + if (mncc_names[i].msg_type == value) + return mncc_names[i].name; + i++; + } + return "unknown"; +} + +static int mncc_send(struct lcr_gsm *lcr_gsm, int msg_type, void *data); + /* * create and send mncc message */ @@ -36,16 +110,13 @@ 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; + int ret = 0; -#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); return ret; @@ -61,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; @@ -119,7 +190,7 @@ void Pgsm::bchannel_close(void) static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index); -/* open bsc side bchannel */ +/* open external side bchannel */ int Pgsm::bchannel_open(int index) { int ret; @@ -145,7 +216,7 @@ int Pgsm::bchannel_open(int index) /* bind socket to bchannel */ addr.family = AF_ISDN; - addr.dev = gsm->gsm_port; + addr.dev = mISDNloop.port; addr.channel = index+1+(index>15); ret = bind(p_m_g_gsm_b_sock, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { @@ -222,18 +293,16 @@ 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); + } } -void Pgsm::frame_receive(void *_frame) +void Pgsm::frame_receive(void *arg) { - struct gsm_data_frame *frame = (struct gsm_data_frame *)_frame; + struct gsm_data_frame *frame = (struct gsm_data_frame *)arg; signed short samples[160]; unsigned char data[160]; int i; @@ -257,29 +326,28 @@ void Pgsm::frame_receive(void *_frame) /* * create trace - **/ + */ void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned int msg_type, int direction) { char msgtext[64]; /* select message and primitive text */ - SCPY(msgtext, get_mncc_name(msg_type)); + SCPY(msgtext, mncc_name(msg_type)); /* add direction */ - switch(port->p_type) { - case PORT_TYPE_GSM_BS_OUT: - SCAT(msgtext, " LCR->BSC"); - break; - case PORT_TYPE_GSM_BS_IN: - SCAT(msgtext, " LCR<-BSC"); - break; - case PORT_TYPE_GSM_MS_OUT: - SCAT(msgtext, " LCR->MS"); - break; - case PORT_TYPE_GSM_MS_IN: - SCAT(msgtext, " LCR<-MS"); - break; - } + if (port) { + switch(port->p_type) { + case PORT_TYPE_GSM_BS_OUT: + case PORT_TYPE_GSM_BS_IN: + SCAT(msgtext, " LCR<->BSC"); + break; + case PORT_TYPE_GSM_MS_OUT: + case PORT_TYPE_GSM_MS_IN: + SCAT(msgtext, " LCR<->MS"); + break; + } + } else + SCAT(msgtext, " ----"); /* init trace with given values */ start_trace(mISDNport?mISDNport->portnum:-1, @@ -292,70 +360,10 @@ void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned msgtext); } -/* select bchannel */ +/* select free bchannel from loopback interface */ int Pgsm::hunt_bchannel(void) { - int channel; - int i; - char map[p_m_mISDNport->b_num]; - struct interface *interface; - struct interface_port *ifport; - - chan_trace_header(p_m_mISDNport, this, "CHANNEL SELECTION (setup)", DIRECTION_NONE); - add_trace("channel", "reserved", "%d", p_m_mISDNport->b_reserved); - if (p_m_mISDNport->b_reserved >= p_m_mISDNport->b_num) { // of out chan.. - add_trace("conclusion", NULL, "all channels are reserved"); - end_trace(); - return(-34); // no channel - } - - /* map all used ports of shared loopback interface */ - memset(map, 0, sizeof(map)); - interface = interface_first; - while(interface) { - ifport = interface->ifport; - while(ifport) { -#if defined WITH_GSM_BS && defined WITH_GSM_MS - if ((ifport->gsm_bs || ifport->gsm_ms) && ifport->mISDNport) { -#else -#ifdef WITH_GSM_BS - if (ifport->gsm_bs && ifport->mISDNport) { -#endif -#ifdef WITH_GSM_MS - if (ifport->gsm_ms && ifport->mISDNport) { -#endif -#endif - i = 0; - while(i < p_m_mISDNport->b_num) { - if (p_m_mISDNport->b_port[i]) - map[i] = 1; - i++; - } - } - ifport = ifport->next; - } - interface = interface->next; - } - - /* find channel */ - i = 0; - channel = 0; - while(i < p_m_mISDNport->b_num) { - if (!map[i]) { - channel = i+1+(i>=15); - break; - } - i++; - } - if (!channel) { - add_trace("conclusion", NULL, "no channel available"); - end_trace(); - return(-6); // channel unacceptable - } - add_trace("conclusion", NULL, "channel available"); - add_trace("connect", "channel", "%d", channel); - end_trace(); - return(channel); + return loop_hunt_bchannel(this, p_m_mISDNport); } /* PROCEEDING INDICATION */ @@ -375,16 +383,41 @@ void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm gsm_trace_header(p_m_mISDNport, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT); mode = create_mncc(MNCC_LCHAN_MODIFY, p_m_g_callref); mode->lchan_mode = 0x01; /* GSM V1 */ + mode->lchan_type = 0x02; 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); + +} + +/* CALL PROCEEDING INDICATION */ +void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) +{ + struct lcr_msg *message; + struct gsm_mncc *frame; + + gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); + end_trace(); + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING); + message_put(message); + + new_state(PORT_STATE_OUT_PROCEEDING); + + if (p_m_mISDNport->earlyb && !p_m_g_tch_connected) { /* only if ... */ + 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_lcr_gsm, frame->msg_type, frame); + p_m_g_tch_connected = 1; + } } /* ALERTING INDICATION */ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc) { struct lcr_msg *message; + struct gsm_mncc *frame; gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN); end_trace(); @@ -394,6 +427,13 @@ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc new_state(PORT_STATE_OUT_ALERTING); + if (p_m_mISDNport->earlyb && !p_m_g_tch_connected) { /* only if ... */ + 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_lcr_gsm, frame->msg_type, frame); + p_m_g_tch_connected = 1; + } } /* CONNECT INDICATION */ @@ -423,7 +463,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)); @@ -435,7 +475,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; } } @@ -454,7 +494,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; } } @@ -489,7 +529,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) { @@ -569,7 +609,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); } } } @@ -592,7 +632,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); @@ -600,7 +640,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; } } @@ -666,7 +706,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); } @@ -696,7 +736,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); @@ -704,7 +744,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; } } @@ -726,7 +766,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); @@ -850,127 +890,224 @@ static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int i return 0; } -static void gsm_sock_close(void) +int gsm_exit(int rc) { - if (gsm->gsm_sock > -1) - close(gsm->gsm_sock); - gsm->gsm_sock = -1; + return(rc); } -static int gsm_sock_open(char *portname) +int gsm_init(void) { - int ret; - int cnt; - unsigned long on = 1; - struct sockaddr_mISDN addr; - struct mISDN_devinfo devinfo; - int pri, bri; + /* seed the PRNG */ + srand(time(NULL)); - /* check port counts */ - ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt); - if (ret < 0) { - fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret); - return(ret); - } + return 0; +} - if (cnt <= 0) { - PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n"); - return -EIO; - } - gsm->gsm_port = mISDN_getportbyname(mISDNsocket, cnt, portname); - if (gsm->gsm_port < 0) { - PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface for GSM?.\n", portname); - return gsm->gsm_port; - } - /* get protocol */ - bri = pri = 0; - devinfo.id = gsm->gsm_port; - ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo); - if (ret < 0) { - PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", gsm->gsm_port, ret); - return ret; - } - if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) { - bri = 1; - } - if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) { - pri = 1; - } - if (!pri && !pri) { - PERROR_RUNTIME("GSM port %d does not support TE PRI or TE BRI.\n", gsm->gsm_port); - } - /* open socket */ - if ((gsm->gsm_sock = socket(PF_ISDN, SOCK_DGRAM, (pri)?ISDN_P_TE_E1:ISDN_P_TE_S0)) < 0) { - PERROR_RUNTIME("GSM port %d failed to open socket.\n", gsm->gsm_port); - gsm_sock_close(); - return gsm->gsm_sock; - } - /* set nonblocking io */ - if ((ret = ioctl(gsm->gsm_sock, FIONBIO, &on)) < 0) { - PERROR_RUNTIME("GSM port %d failed to set socket into nonblocking io.\n", gsm->gsm_port); - gsm_sock_close(); - return ret; - } - /* bind socket to dchannel */ - memset(&addr, 0, sizeof(addr)); - addr.family = AF_ISDN; - addr.dev = gsm->gsm_port; - addr.channel = 0; - if ((ret = bind(gsm->gsm_sock, (struct sockaddr *)&addr, sizeof(addr))) < 0) { - PERROR_RUNTIME("GSM port %d failed to bind socket. (name = %s errno=%d)\n", gsm->gsm_port, portname, errno); - gsm_sock_close(); - return (ret); +/* + * 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 gsm_exit(int rc) +static struct mncc_q_entry *mncc_q_dequeue(struct lcr_gsm *lcr_gsm) { - /* free gsm instance */ - if (gsm) { - if (gsm->gsm_sock > -1) - gsm_sock_close(); - free(gsm); - gsm = NULL; - } + 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; +} - return(rc); +/* 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); } -int gsm_init(void) +/* close MNCC socket */ +static int mncc_fd_close(struct lcr_gsm *lcr_gsm, struct lcr_fd *lfd) { - char conf_error[256] = ""; + class Port *port; + class Pgsm *pgsm = NULL; + struct lcr_msg *message; - /* seed the PRNG */ - srand(time(NULL)); + 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; +} - /* create gsm instance */ - gsm = (struct lcr_gsm *)MALLOC(sizeof(struct lcr_gsm)); - gsm->gsm_sock = -1; +/* 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; +} - /* parse options */ - if (!gsm_conf(&gsm->conf, conf_error)) { - PERROR("%s", conf_error); +/* 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 - gsm_bs_exit(-EINVAL); + case LCR_GSM_TYPE_NETWORK: + return message_bsc(lcr_gsm, mncc_prim->msg_type, mncc_prim); #endif #ifdef WITH_GSM_MS - gsm_ms_exit(-EINVAL); + case LCR_GSM_TYPE_MS: + return message_ms(lcr_gsm, mncc_prim->msg_type, mncc_prim); #endif - return gsm_exit(-EINVAL); + default: + return 0; } +} - /* open gsm loop interface */ - if (gsm_sock_open(gsm->conf.interface_bsc)) { - return gsm_exit(-1); - } +/* 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; - return 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 handle_gsm(void) +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; }