This was missing at merge:
[lcr.git] / gsm_bs.cpp
index 2f34fa3..e25732a 100644 (file)
 #define _GNU_SOURCE
 #endif
 extern "C" {
+#include <assert.h>
 #include <getopt.h>
 
-#include <openbsc/db.h>
-#include <osmocore/select.h>
-#include <openbsc/debug.h>
-#include <openbsc/e1_input.h>
-#include <osmocore/talloc.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
 #include <openbsc/mncc.h>
 #include <openbsc/trau_frame.h>
-#include <openbsc/osmo_msc.h>
-//#include <osmocom/vty/command.h>
-struct gsm_network *bsc_gsmnet = 0;
-extern int ipacc_rtp_direct;
-extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *),
-                                const char *cfg_file);
-extern int bsc_shutdown_net(struct gsm_network *net);
-void talloc_ctx_init(void);
-void on_dso_load_token(void);
-void on_dso_load_rrlp(void);
-void on_dso_load_ho_dec(void);
-int bts_model_unknown_init(void);
-int bts_model_bs11_init(void);
-int bts_model_nanobts_init(void);
-static struct log_target *stderr_target;
-extern const char *openbsc_copyright;
-
-/* timer to store statistics */
-#define DB_SYNC_INTERVAL       60, 0
-static struct timer_list db_sync_timer;
-
-/* FIXME: copied from the include file, because it will con compile with C++ */
-struct vty_app_info {
-       const char *name;
-       const char *version;
-       const char *copyright;
-       void *tall_ctx;
-       int (*go_parent_cb)(struct vty *vty);
-       int (*is_config_node)(struct vty *vty, int node);
-};
-
-extern int bsc_vty_go_parent(struct vty *vty);
-extern int bsc_vty_is_config_node(struct vty *vty, int node);
-static struct vty_app_info vty_info = {
-       "OpenBSC",
-       PACKAGE_VERSION,
-       NULL,
-       NULL,
-       bsc_vty_go_parent,
-       bsc_vty_is_config_node,
-};
-
-void vty_init(struct vty_app_info *app_info);
-int bsc_vty_init(void);
-
 }
 
-/* timer handling */
-static int _db_store_counter(struct counter *counter, void *data)
-{
-       return db_store_counter(counter);
-}
+#define SOCKET_RETRY_TIMER     5
+
+/*
+ * DTMF stuff
+ */
+unsigned char dtmf_samples[16][8000];
+static int dtmf_x[4] = { 1209, 1336, 1477, 1633 };
+static int dtmf_y[4] = { 697, 770, 852, 941 };
 
-static void db_sync_timer_cb(void *data)
+void generate_dtmf(void)
 {
-       /* store counters to database and re-schedule */
-       counters_for_each(_db_store_counter, NULL);
-       bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
+       double fx, fy, sample;
+       int i, x, y;
+       unsigned char *law;
+
+       for (y = 0; y < 4; y++) {
+               fy = 2 * 3.1415927 * ((double)dtmf_y[y]) / 8000.0;
+               for (x = 0; x < 4; x++) {
+                       fx = 2 * 3.1415927 * ((double)dtmf_x[x]) / 8000.0;
+                       law = dtmf_samples[y << 2 | x];
+                       for (i = 0; i < 8000; i++) {
+                               sample = sin(fy * ((double)i)) * 0.251 * 32767.0; /* -6 dB */
+                               sample += sin(fx * ((double)i)) * 0.158 * 32767.0; /* -8 dB */
+                               *law++ = audio_s16_to_law[(int)sample & 0xffff];
+                       }
+               }
+       }
 }
 
+
 /*
  * constructor
  */
 Pgsm_bs::Pgsm_bs(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : Pgsm(type, mISDNport, portname, settings, channel, exclusive, mode)
 {
        p_m_g_instance = gsm->network;
+       p_m_g_dtmf = NULL;
+       p_m_g_dtmf_index = 0;
+
        PDEBUG(DEBUG_GSM, "Created new GSMBSPort(%s).\n", portname);
 }
 
@@ -105,7 +79,7 @@ Pgsm_bs::~Pgsm_bs()
 /* DTMF INDICATION */
 void Pgsm_bs::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
 {
-       struct lcr_msg *message;
+//     struct lcr_msg *message;
        struct gsm_mncc *resp;
 
        gsm_trace_header(p_m_mISDNport, this, msg_type, DIRECTION_IN);
@@ -119,13 +93,41 @@ void Pgsm_bs::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct
        add_trace("keypad", NULL, "%c", mncc->keypad);
        end_trace();
        resp = create_mncc(MNCC_START_DTMF_RSP, p_m_g_callref);
+       resp->fields |= MNCC_F_KEYPAD;
        resp->keypad = mncc->keypad;
        send_and_free_mncc(p_m_g_instance, resp->msg_type, resp);
 
+#if 0
        /* send dialing information */
        message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
        memcpy(&message->param.information, &p_dialinginfo, sizeof(struct dialing_info));
        message_put(message);
+#endif
+
+       /* generate DTMF tones */
+       switch (mncc->keypad) {
+               case '1': p_m_g_dtmf = dtmf_samples[0]; break;
+               case '2': p_m_g_dtmf = dtmf_samples[1]; break;
+               case '3': p_m_g_dtmf = dtmf_samples[2]; break;
+               case 'a':
+               case 'A': p_m_g_dtmf = dtmf_samples[3]; break;
+               case '4': p_m_g_dtmf = dtmf_samples[4]; break;
+               case '5': p_m_g_dtmf = dtmf_samples[5]; break;
+               case '6': p_m_g_dtmf = dtmf_samples[6]; break;
+               case 'b':
+               case 'B': p_m_g_dtmf = dtmf_samples[7]; break;
+               case '7': p_m_g_dtmf = dtmf_samples[8]; break;
+               case '8': p_m_g_dtmf = dtmf_samples[9]; break;
+               case '9': p_m_g_dtmf = dtmf_samples[10]; break;
+               case 'c':
+               case 'C': p_m_g_dtmf = dtmf_samples[11]; break;
+               case '*': p_m_g_dtmf = dtmf_samples[12]; break;
+               case '0': p_m_g_dtmf = dtmf_samples[13]; break;
+               case '#': p_m_g_dtmf = dtmf_samples[14]; break;
+               case 'd':
+               case 'D': p_m_g_dtmf = dtmf_samples[15]; break;
+       }
+       p_m_g_dtmf_index = 0;
 }
 void Pgsm_bs::stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
 {
@@ -142,6 +144,9 @@ void Pgsm_bs::stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct
        resp = create_mncc(MNCC_STOP_DTMF_RSP, p_m_g_callref);
        resp->keypad = mncc->keypad;
        send_and_free_mncc(p_m_g_instance, resp->msg_type, resp);
+       
+       /* stop DTMF */
+       p_m_g_dtmf = NULL;
 }
 
 /* HOLD INDICATION */
@@ -435,8 +440,22 @@ static int message_bsc(struct gsm_network *net, int msg_type, void *arg)
        }
 
        if (msg_type == GSM_TCHF_FRAME) {
-               if (port)
-                       pgsm_bs->frame_receive((struct gsm_trau_frame *)arg);
+               if (port) {
+                       /* inject DTMF, if enabled */
+                       if (pgsm_bs->p_m_g_dtmf) {
+                               unsigned char data[160];
+                               int i;
+
+                               for (i = 0; i < 160; i++) {
+                                       data[i] = pgsm_bs->p_m_g_dtmf[pgsm_bs->p_m_g_dtmf_index++];
+                                       if (pgsm_bs->p_m_g_dtmf_index == 8000)
+                                               pgsm_bs->p_m_g_dtmf_index = 0;
+                               }
+                               /* send */
+                               pgsm_bs->bchannel_send(PH_DATA_REQ, 0, data, 160);
+                       } else
+                               pgsm_bs->frame_receive(arg);
+               }
                return 0;
        }
 
@@ -545,6 +564,20 @@ void Pgsm_bs::message_setup(unsigned int epoint_id, int message_id, union parame
        memcpy(&p_capainfo, &param->setup.capainfo, sizeof(p_capainfo));
        memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
 
+       /* no GSM MNCC connection */
+       if (gsm->mncc_lfd.fd < 0) {
+               gsm_trace_header(p_m_mISDNport, this, MNCC_SETUP_REQ, DIRECTION_OUT);
+               add_trace("failure", NULL, "No MNCC connection.");
+               end_trace();
+               message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+               message->param.disconnectinfo.cause = 27; // temp. unavail.
+               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);
@@ -763,6 +796,7 @@ int Pgsm_bs::message_epoint(unsigned int epoint_id, int message_id, union parame
 
 int gsm_bs_exit(int rc)
 {
+#if 0
        /* free gsm instance */
        if (gsm) {
                /* shutdown network */
@@ -773,108 +807,211 @@ int gsm_bs_exit(int rc)
 //                     free((struct gsm_network *)gsm->network); /* TBD */
 //             }
        }
-
+#endif
        return(rc);
 }
 
-int gsm_bs_init(void)
+extern "C" {
+
+static int mncc_q_enqueue(struct gsm_mncc *mncc, unsigned int len)
 {
-       char hlr[128], cfg[128], filename[128];
-        mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-       int pcapfd, rc;
-
-       vty_info.copyright = openbsc_copyright;
-
-       log_init(&log_info);
-       tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
-       talloc_ctx_init();
-       on_dso_load_token();
-       on_dso_load_rrlp();
-       on_dso_load_ho_dec();
-       stderr_target = log_target_create_stderr();
-       log_add_target(stderr_target);
-
-       bts_model_unknown_init();
-       bts_model_bs11_init();
-       bts_model_nanobts_init();
-
-       /* enable filters */
-       log_set_all_filter(stderr_target, 1);
-
-       /* Init VTY (need to preceed options) */
-       vty_init(&vty_info);
-       bsc_vty_init();
-
-       /* set debug */
-       if (gsm->conf.debug[0])
-               log_parse_category_mask(stderr_target, gsm->conf.debug);
-
-       /* open pcap file */
-       if (gsm->conf.pcapfile[0]) {
-               if (gsm->conf.pcapfile[0] == '/')
-                       SCPY(filename, gsm->conf.pcapfile);
-               else
-                       SPRINT(filename, "%s/%s", CONFIG_DATA, gsm->conf.pcapfile);
-               pcapfd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, mode);
-               if (pcapfd < 0) {
-                       PERROR("Failed to open file for pcap\n");
-                       return gsm_exit(-1);
-               }
-               e1_set_pcap_fd(pcapfd);
-       }
+       struct mncc_q_entry *qe;
 
-       /* use RTP proxy for audio streaming */
-       ipacc_rtp_direct = 0;
+       qe = (struct mncc_q_entry *) MALLOC(sizeof(*qe)+sizeof(*mncc)+len);
+       if (!qe)
+               return -ENOMEM;
 
-       /* bootstrap network */
-       if (gsm->conf.openbsc_cfg[0] == '/')
-               SCPY(cfg, gsm->conf.openbsc_cfg);
-       else
-               SPRINT(cfg, "%s/%s", CONFIG_DATA, gsm->conf.openbsc_cfg);
-       rc = bsc_bootstrap_network(&message_bsc, cfg);
-       if (rc < 0) {
-               PERROR("Failed to bootstrap GSM network.\n");
-               return gsm_exit(-1);
+       qe->next = NULL;
+       qe->len = len;
+       memcpy(qe->data, mncc, len);
+
+       /* in case of empty list ... */
+       if (!gsm->mncc_q_hd && !gsm->mncc_q_tail) {
+               /* the list head and tail both point to the new qe */
+               gsm->mncc_q_hd = gsm->mncc_q_tail = qe;
+       } else {
+               /* append to tail of list */
+               gsm->mncc_q_tail->next = qe;
+               gsm->mncc_q_tail = qe;
        }
-       bsc_api_init(bsc_gsmnet, msc_bsc_api());
-       gsm->network = bsc_gsmnet;
 
-       /* init database */
-       if (gsm->conf.hlr[0] == '/')
-               SCPY(hlr, gsm->conf.hlr);
-       else
-               SPRINT(hlr, "%s/%s", CONFIG_DATA, gsm->conf.hlr);
-       if (db_init(hlr)) {
-               PERROR("GSM DB: Failed to init database '%s'. Please check the option settings.\n", hlr);
-               return gsm_exit(-1);
+       gsm->mncc_lfd.when |= LCR_FD_WRITE;
+
+       return 0;
+}
+
+static struct mncc_q_entry *mncc_q_dequeue(void)
+{
+       struct mncc_q_entry *qe = gsm->mncc_q_hd;
+       if (!qe)
+               return NULL;
+
+       /* dequeue the successfully sent message */
+       gsm->mncc_q_hd = qe->next;
+       if (!qe)
+               return NULL;
+       if (qe == gsm->mncc_q_tail)
+               gsm->mncc_q_tail = NULL;
+
+       return qe;
+}
+
+/* routine called by LCR code if it wants to send a message to OpenBSC */
+int mncc_send(struct gsm_network *instance, 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;
        }
-       printf("DB: Database initialized.\n");
-       if (db_prepare()) {
-               PERROR("GSM DB: Failed to prepare database.\n");
-               return gsm_exit(-1);
+               
+       return mncc_q_enqueue((struct gsm_mncc *)data, len);
+}
+
+} // extern "C"
+
+/* close MNCC socket */
+static int mncc_fd_close(struct lcr_fd *lfd)
+{
+       class Port *port;
+       class Pgsm_bs *pgsm_bs = 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_GSM_MASK) == PORT_CLASS_GSM_BS) {
+                       pgsm_bs = (class Pgsm_bs *)port;
+                       message = message_create(pgsm_bs->p_serial, ACTIVE_EPOINT(pgsm_bs->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_bs->new_state(PORT_STATE_RELEASE);
+                       trigger_work(&pgsm_bs->p_m_g_delete);
+               }
+               port = port->next;
        }
-       printf("DB: Database prepared.\n");
 
-       /* setup the timer */
-       db_sync_timer.cb = db_sync_timer_cb;
-       db_sync_timer.data = NULL;
-       bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
+       /* flush the queue */
+       while (mncc_q_dequeue())
+               ;
+
+       /* start the re-connect timer */
+       schedule_timer(&gsm->socket_retry, SOCKET_RETRY_TIMER, 0);
+
+       generate_dtmf();
 
        return 0;
 }
 
-/*
- * handles bsc select function within LCR's main loop
- */
-int handle_gsm_bs(void)
+/* read from OpenBSC via MNCC socket */
+static int mncc_fd_write(struct lcr_fd *lfd, void *inst, int idx)
+{
+       struct mncc_q_entry *qe, *qe2;
+       int rc;
+
+       while (1) {
+               qe = 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(lfd);
+               if (rc < 0)
+                       return rc;
+               if (rc < (int)qe->len)
+                       return -1;
+               /* dequeue the successfully sent message */
+               qe2 = mncc_q_dequeue();
+               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)
 {
-       int ret1, ret2;
+       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(lfd);
+       if (rc < 0)
+               return rc;
+
+       /* Hand the MNCC message into LCR */
+       return message_bsc(NULL, mncc_prim->msg_type, mncc_prim);
+}
+
+/* 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 *instance, int idx)
+{
+       int rc = 0;
+
+       if (what & LCR_FD_READ)
+               rc = mncc_fd_read(lfd, instance, idx);
+       if (rc < 0)
+               return rc;
+
+       if (what & LCR_FD_WRITE)
+               rc = mncc_fd_write(lfd, instance, idx);
+
+       return rc;
+}
+
+static int socket_retry_cb(struct lcr_timer *timer, void *instance, int index)
+{
+       int fd, rc;
+
+       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 *) &gsm->sun,
+                    sizeof(gsm->sun));
+       if (rc < 0) {
+               PERROR("Could not connect to MNCC socket, "
+                       "retrying in %u seconds\n", SOCKET_RETRY_TIMER);
+               close(fd);
+               schedule_timer(&gsm->socket_retry, SOCKET_RETRY_TIMER, 0);
+       } else {
+               PDEBUG(DEBUG_GSM, "Connected to MNCC socket!\n");
+               gsm->mncc_lfd.fd = fd;
+               register_fd(&gsm->mncc_lfd, LCR_FD_READ, &mncc_fd_cb, NULL, 0);
+       }
 
-       ret1 = bsc_upqueue((struct gsm_network *)gsm->network);
-       log_reset_context();
-       ret2 = bsc_select_main(1); /* polling */
-       if (ret1 || ret2)
-               return 1;
        return 0;
 }
 
+int gsm_bs_init(void)
+{
+       gsm->sun.sun_family = AF_UNIX;
+       SCPY(gsm->sun.sun_path, "/tmp/bsc_mncc");
+
+       memset(&gsm->socket_retry, 0, sizeof(gsm->socket_retry));
+       add_timer(&gsm->socket_retry, socket_retry_cb, NULL, 0);
+
+       /* do the initial connect */
+       socket_retry_cb(&gsm->socket_retry, NULL, 0);
+
+       return 0;
+}