GSM-BS: Add MPTY and ECT
authorAndreas Eversberg <jolly@eversberg.eu>
Thu, 19 Sep 2013 07:32:58 +0000 (09:32 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Tue, 15 Dec 2015 19:53:27 +0000 (20:53 +0100)
MPTY only works as 3PTY!

There is a hack, because most phones seem not to support ECT.
In order to execute ECT, the holdMPTY/retrieveMPTY message is used to
transfer two calls.

apppbx.cpp
gsm.cpp
gsm.h
gsm_bs.cpp
gsm_bs.h

index a2f62dd..98e6e64 100644 (file)
@@ -2397,7 +2397,8 @@ void EndpointAppPBX::port_transfer(struct port_list *portlist, int message_type,
                return;
        if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS)
                join_join_fxs();
                return;
        if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS)
                join_join_fxs();
-       else if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1) {
+       else if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1
+             || (port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_BS) {
                rc = join_join_dss1(param->transfer.invoke_id);
 
                if (rc < 0) {
                rc = join_join_dss1(param->transfer.invoke_id);
 
                if (rc < 0) {
@@ -3702,11 +3703,10 @@ reject:
 }
 
 
 }
 
 
-/* join calls (look for a join that is on hold (same isdn interface/terminal))
+/* join calls (look for a join that is on hold (same isdn/gsm interface/terminal))
  */
 int EndpointAppPBX::join_join_dss1(int invoke_id)
 {
  */
 int EndpointAppPBX::join_join_dss1(int invoke_id)
 {
-#ifdef WITH_MISDN
        struct lcr_msg *message;
        struct join_relation *add_relation, *remove_relation;
        struct join_relation **add_relation_pointer, **remove_relation_pointer;
        struct lcr_msg *message;
        struct join_relation *add_relation, *remove_relation;
        struct join_relation **add_relation_pointer, **remove_relation_pointer;
@@ -3714,7 +3714,12 @@ int EndpointAppPBX::join_join_dss1(int invoke_id)
        class JoinPBX *our_joinpbx, *other_joinpbx, *add_joinpbx, *remove_joinpbx;
        class EndpointAppPBX *other_eapp, *remove_eapp_hold, *remove_eapp_active;
        class Port *our_port, *other_port;
        class JoinPBX *our_joinpbx, *other_joinpbx, *add_joinpbx, *remove_joinpbx;
        class EndpointAppPBX *other_eapp, *remove_eapp_hold, *remove_eapp_active;
        class Port *our_port, *other_port;
-       class Pdss1 *our_pdss1, *other_pdss1;
+#ifdef WITH_MISDN
+       class Pdss1 *our_pdss1 = NULL, *other_pdss1;
+#endif
+#ifdef WITH_GSM_BS
+       class Pgsm_bs *our_gsm_bs = NULL, *other_gsm_bs;
+#endif
        class Endpoint *temp_epoint;
 
        /* are we a candidate to join a join? */
        class Endpoint *temp_epoint;
 
        /* are we a candidate to join a join? */
@@ -3732,20 +3737,19 @@ int EndpointAppPBX::join_join_dss1(int invoke_id)
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial);
                return -1;
        }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial);
                return -1;
        }
-       if (!e_ext.number[0]) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we are not internal extension.\n", ea_endpoint->ep_serial);
-               return -1;
-       }
        our_port = find_port_id(ea_endpoint->ep_portlist->port_id);
        if (!our_port) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial);
                return -1;
        }
        our_port = find_port_id(ea_endpoint->ep_portlist->port_id);
        if (!our_port) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial);
                return -1;
        }
-       if ((our_port->p_type & PORT_CLASS_mISDN_MASK) != PORT_CLASS_DSS1) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not isdn.\n", ea_endpoint->ep_serial);
-               return -1;
-       }
-       our_pdss1 = (class Pdss1 *)our_port;
+#ifdef WITH_MISDN
+       if ((our_port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1)
+               our_pdss1 = (class Pdss1 *)our_port;
+#endif
+#ifdef WITH_GSM_BS
+       if ((our_port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_BS)
+               our_gsm_bs = (class Pgsm_bs *)our_port;
+#endif
 
        /* find an endpoint that has the same mISDNport/ces that we are on */
        other_eapp = apppbx_first;
 
        /* find an endpoint that has the same mISDNport/ces that we are on */
        other_eapp = apppbx_first;
@@ -3755,20 +3759,33 @@ int EndpointAppPBX::join_join_dss1(int invoke_id)
                        continue;
                }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id);
                        continue;
                }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id);
-               if (other_eapp->e_ext.number[0] /* has terminal */
-                && other_eapp->ea_endpoint->ep_portlist /* has port */
+               if (other_eapp->ea_endpoint->ep_portlist /* has port */
                 && other_eapp->ea_endpoint->ep_join_id) { /* has join */
                        other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id);
                        if (other_port) { /* port still exists */
                 && other_eapp->ea_endpoint->ep_join_id) { /* has join */
                        other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id);
                        if (other_port) { /* port still exists */
-                               if (other_port->p_type==PORT_TYPE_DSS1_NT_OUT
-                                || other_port->p_type==PORT_TYPE_DSS1_NT_IN) { /* port is isdn nt-mode */
+#ifdef WITH_MISDN
+                               if (our_pdss1
+                                && (other_port->p_type==PORT_TYPE_DSS1_NT_OUT
+                                 || other_port->p_type==PORT_TYPE_DSS1_NT_IN)) { /* port is isdn nt-mode */
                                        other_pdss1 = (class Pdss1 *)other_port;
                                        other_pdss1 = (class Pdss1 *)other_port;
-                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type isdn! comparing our portnum=%d with other's portnum=%d hold=%s ces=%d\n", ea_endpoint->ep_serial, our_pdss1->p_m_mISDNport->portnum, other_pdss1->p_m_mISDNport->portnum, (other_pdss1->p_m_hold)?"YES":"NO", other_pdss1->p_m_d_ces);
-                                       if (1 //other_pdss1->p_m_hold /* port is on hold */
+                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type isdn! comparing our portnum=%d with other's portnum=%d hold=%s ces=%d\n", ea_endpoint->ep_serial, our_pdss1->p_m_mISDNport->portnum, other_pdss1->p_m_mISDNport->portnum, (other_pdss1->p_hold)?"YES":"NO", other_pdss1->p_m_d_ces);
+                                       if (1 //other_pdss1->p_hold /* port is on hold */
                                         && other_pdss1->p_m_mISDNport == our_pdss1->p_m_mISDNport /* same isdn interface */
                                         && other_pdss1->p_m_d_ces == our_pdss1->p_m_d_ces) /* same tei+sapi */
                                                break;
                                         && other_pdss1->p_m_mISDNport == our_pdss1->p_m_mISDNport /* same isdn interface */
                                         && other_pdss1->p_m_d_ces == our_pdss1->p_m_d_ces) /* same tei+sapi */
                                                break;
-                               } else {
+                               } else
+#endif
+#ifdef WITH_GSM_BS
+                               if (our_gsm_bs
+                                && (other_port->p_type==PORT_TYPE_GSM_BS_OUT
+                                 || other_port->p_type==PORT_TYPE_GSM_BS_IN)) { /* port is GSM bs-mode */
+                                       other_gsm_bs = (class Pgsm_bs *)other_port;
+                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type GSM! comparing our imsi with other imsi=%s\n", ea_endpoint->ep_serial, our_gsm_bs->p_g_imsi, other_gsm_bs->p_g_imsi);
+                                       if (!strcmp(other_gsm_bs->p_g_imsi, our_gsm_bs->p_g_imsi)) /* same tei+sapi */
+                                               break;
+                               } else
+#endif
+                               {
                                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial);
                                }
                        } else {
                                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial);
                                }
                        } else {
@@ -3931,9 +3948,6 @@ int EndpointAppPBX::join_join_dss1(int invoke_id)
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
-#else
-       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
-#endif
 
        return 0;
 }
 
        return 0;
 }
@@ -4110,21 +4124,28 @@ int EndpointAppPBX::join_join_fxs(void)
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
+
+       return 0;
 #else
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
 #else
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
-#endif
 
 
-       return 0;
+       return -1;
+#endif
 }
 
 }
 
+/* do audio bridge of endpoints on same isdn/gsm terminal */
 int EndpointAppPBX::join_3pty_dss1(void)
 {
 int EndpointAppPBX::join_3pty_dss1(void)
 {
-#ifdef WITH_MISDN
        class Join *our_join, *other_join;
        class JoinPBX *our_joinpbx, *other_joinpbx;
        class EndpointAppPBX *other_eapp;
        class Port *our_port, *other_port;
        class Join *our_join, *other_join;
        class JoinPBX *our_joinpbx, *other_joinpbx;
        class EndpointAppPBX *other_eapp;
        class Port *our_port, *other_port;
-       class Pdss1 *our_pdss1, *other_pdss1;
+#ifdef WITH_MISDN
+       class Pdss1 *our_pdss1 = NULL, *other_pdss1;
+#endif
+#ifdef WITH_GSM_BS
+       class Pgsm_bs *our_gsm_bs = NULL, *other_gsm_bs;
+#endif
 
        /* are we a candidate to join a join? */
        our_join = find_join_id(ea_endpoint->ep_join_id);
 
        /* are we a candidate to join a join? */
        our_join = find_join_id(ea_endpoint->ep_join_id);
@@ -4141,20 +4162,19 @@ int EndpointAppPBX::join_3pty_dss1(void)
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial);
                return -1;
        }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial);
                return -1;
        }
-       if (!e_ext.number[0]) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we are not internal extension.\n", ea_endpoint->ep_serial);
-               return -1;
-       }
        our_port = find_port_id(ea_endpoint->ep_portlist->port_id);
        if (!our_port) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial);
                return -1;
        }
        our_port = find_port_id(ea_endpoint->ep_portlist->port_id);
        if (!our_port) {
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial);
                return -1;
        }
-       if ((our_port->p_type & PORT_CLASS_mISDN_MASK) != PORT_CLASS_DSS1) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not isdn.\n", ea_endpoint->ep_serial);
-               return -1;
-       }
-       our_pdss1 = (class Pdss1 *)our_port;
+#ifdef WITH_MISDN
+       if ((our_port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1)
+               our_pdss1 = (class Pdss1 *)our_port;
+#endif
+#ifdef WITH_GSM_BS
+       if ((our_port->p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_BS)
+               our_gsm_bs = (class Pgsm_bs *)our_port;
+#endif
 
        /* find an endpoint that has the same mISDNport/ces that we are on */
        other_eapp = apppbx_first;
 
        /* find an endpoint that has the same mISDNport/ces that we are on */
        other_eapp = apppbx_first;
@@ -4164,20 +4184,33 @@ int EndpointAppPBX::join_3pty_dss1(void)
                        continue;
                }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id);
                        continue;
                }
                PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id);
-               if (other_eapp->e_ext.number[0] /* has terminal */
-                && other_eapp->ea_endpoint->ep_portlist /* has port */
+               if (other_eapp->ea_endpoint->ep_portlist /* has port */
                 && other_eapp->ea_endpoint->ep_join_id) { /* has join */
                        other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id);
                        if (other_port) { /* port still exists */
                 && other_eapp->ea_endpoint->ep_join_id) { /* has join */
                        other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id);
                        if (other_port) { /* port still exists */
-                               if (other_port->p_type==PORT_TYPE_DSS1_NT_OUT
-                                || other_port->p_type==PORT_TYPE_DSS1_NT_IN) { /* port is isdn nt-mode */
+#ifdef WITH_MISDN
+                               if (our_pdss1
+                                && (other_port->p_type==PORT_TYPE_DSS1_NT_OUT
+                                 || other_port->p_type==PORT_TYPE_DSS1_NT_IN)) { /* port is isdn nt-mode */
                                        other_pdss1 = (class Pdss1 *)other_port;
                                        other_pdss1 = (class Pdss1 *)other_port;
-                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type isdn! comparing our portnum=%d with other's portnum=%d hold=%s ces=%d\n", ea_endpoint->ep_serial, our_pdss1->p_m_mISDNport->portnum, other_pdss1->p_m_mISDNport->portnum, (other_pdss1->p_m_hold)?"YES":"NO", other_pdss1->p_m_d_ces);
-                                       if (1 //other_pdss1->p_m_hold /* port is on hold */
+                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type isdn! comparing our portnum=%d with other's portnum=%d hold=%s ces=%d\n", ea_endpoint->ep_serial, our_pdss1->p_m_mISDNport->portnum, other_pdss1->p_m_mISDNport->portnum, (other_pdss1->p_hold)?"YES":"NO", other_pdss1->p_m_d_ces);
+                                       if (1 //other_pdss1->p_hold /* port is on hold */
                                         && other_pdss1->p_m_mISDNport == our_pdss1->p_m_mISDNport /* same isdn interface */
                                         && other_pdss1->p_m_d_ces == our_pdss1->p_m_d_ces) /* same tei+sapi */
                                                break;
                                         && other_pdss1->p_m_mISDNport == our_pdss1->p_m_mISDNport /* same isdn interface */
                                         && other_pdss1->p_m_d_ces == our_pdss1->p_m_d_ces) /* same tei+sapi */
                                                break;
-                               } else {
+                               } else
+#endif
+#ifdef WITH_GSM_BS
+                               if (our_gsm_bs
+                                && (other_port->p_type==PORT_TYPE_GSM_BS_OUT
+                                 || other_port->p_type==PORT_TYPE_GSM_BS_IN)) { /* port is GSM bs-mode */
+                                       other_gsm_bs = (class Pgsm_bs *)other_port;
+                                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type GSM! comparing our imsi with other imsi=%s\n", ea_endpoint->ep_serial, our_gsm_bs->p_g_imsi, other_gsm_bs->p_g_imsi);
+                                       if (!strcmp(other_gsm_bs->p_g_imsi, our_gsm_bs->p_g_imsi)) /* same tei+sapi */
+                                               break;
+                               } else
+#endif
+                               {
                                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial);
                                }
                        } else {
                                        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial);
                                }
                        } else {
@@ -4187,7 +4220,7 @@ int EndpointAppPBX::join_3pty_dss1(void)
                other_eapp = other_eapp->next;
        }
        if (!other_eapp) {
                other_eapp = other_eapp->next;
        }
        if (!other_eapp) {
-               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same isdn terminal.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same terminal.\n", ea_endpoint->ep_serial);
                return -1;
        }
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port with same terminal found.\n", ea_endpoint->ep_serial);
                return -1;
        }
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port with same terminal found.\n", ea_endpoint->ep_serial);
@@ -4231,13 +4264,11 @@ int EndpointAppPBX::join_3pty_dss1(void)
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
-#else
-       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
-#endif
 
        return 0;
 }
 
 
        return 0;
 }
 
+/* do audio bridge of endpoints on same fxs terminal */
 int EndpointAppPBX::join_3pty_fxs(void)
 {
 #ifdef WITH_MISDN
 int EndpointAppPBX::join_3pty_fxs(void)
 {
 #ifdef WITH_MISDN
@@ -4351,16 +4382,18 @@ int EndpointAppPBX::join_3pty_fxs(void)
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
+
+       return 0;
 #else
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
 #else
        PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
-#endif
 
 
-       return 0;
+       return -1;
+#endif
 }
 
 }
 
+/* split audio bridge */
 int EndpointAppPBX::split_3pty(void)
 {
 int EndpointAppPBX::split_3pty(void)
 {
-#ifdef WITH_MISDN
        class Join *our_join, *other_join;
        class JoinPBX *our_joinpbx, *other_joinpbx;
 
        class Join *our_join, *other_join;
        class JoinPBX *our_joinpbx, *other_joinpbx;
 
@@ -4401,9 +4434,6 @@ int EndpointAppPBX::split_3pty(void)
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
 
        /* we send a retrieve to that endpoint */
        // mixer will update the hold-state of the join and send it to the endpoints is changes
-#else
-       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot split: no mISDN support anyway.\n", ea_endpoint->ep_serial);
-#endif
 
        return 0;
 }
 
        return 0;
 }
diff --git a/gsm.cpp b/gsm.cpp
index 36fb98b..6d98bd0 100644 (file)
--- a/gsm.cpp
+++ b/gsm.cpp
@@ -1060,6 +1060,13 @@ void Pgsm::message_disconnect(unsigned int epoint_id, int message_id, union para
        add_trace("cause", "coding", "%d", mncc->cause.coding);
        add_trace("cause", "location", "%d", mncc->cause.location);
        add_trace("cause", "value", "%d", mncc->cause.value);
        add_trace("cause", "coding", "%d", mncc->cause.coding);
        add_trace("cause", "location", "%d", mncc->cause.location);
        add_trace("cause", "value", "%d", mncc->cause.value);
+#ifdef WITH_GSM_MS
+       /* special case for BS mode */
+       if (param->disconnectinfo.transfer.result && (p_type & PORT_CLASS_GSM_MASK) == PORT_CLASS_GSM_BS) {
+               ((class Pgsm_bs *)this)->enc_ie_facility_ect(mncc, &param->disconnectinfo.transfer);
+               gsm_trace_facility((unsigned char *)mncc->facility.info, mncc->facility.len);
+       }
+#endif
        end_trace();
        send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
 
        end_trace();
        send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
 
diff --git a/gsm.h b/gsm.h
index b0b9b89..b6f058d 100644 (file)
--- a/gsm.h
+++ b/gsm.h
@@ -34,6 +34,7 @@ class Pgsm : public Port
        Pgsm(int type, char *portname, struct port_settings *settings, struct interface *interface);
        ~Pgsm();
 
        Pgsm(int type, char *portname, struct port_settings *settings, struct interface *interface);
        ~Pgsm();
 
+       char p_g_imsi[16]; /* imsi of current phone (used for ECT/MPTY with gsm_bs) */
        signed short p_g_samples[160]; /* last received audi packet */
        int p_g_tones; /* set, if tones are to be generated */
        int p_g_earlyb; /* set, if patterns are available */
        signed short p_g_samples[160]; /* last received audi packet */
        int p_g_tones; /* set, if tones are to be generated */
        int p_g_earlyb; /* set, if patterns are available */
index 6596411..e8c9b58 100644 (file)
@@ -14,6 +14,9 @@
 
 struct lcr_gsm *gsm_bs = NULL;
 
 
 struct lcr_gsm *gsm_bs = NULL;
 
+// use holdMPTY to transfer call
+#define TRANSFER_HACK
+
 #define PAYLOAD_TYPE_GSM 3
 
 /*
 #define PAYLOAD_TYPE_GSM 3
 
 /*
@@ -98,6 +101,8 @@ void Pgsm_bs::call_conf_ind(unsigned int msg_type, unsigned int callref, struct
        }
        end_trace();
 
        }
        end_trace();
 
+       SCPY(p_g_imsi, mncc->imsi);
+
        new_state(PORT_STATE_OUT_PROCEEDING);
 
        /* get list of offered payload types */
        new_state(PORT_STATE_OUT_PROCEEDING);
 
        /* get list of offered payload types */
@@ -271,6 +276,8 @@ void Pgsm_bs::hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_m
        message->param.notifyinfo.local = 1; /* call is held by supplementary service */
        message_put(message);
 
        message->param.notifyinfo.local = 1; /* call is held by supplementary service */
        message_put(message);
 
+       p_hold = 1;
+
        /* acknowledge hold */
        gsm_trace_header(p_interface_name, this, MNCC_HOLD_CNF, DIRECTION_OUT);
        end_trace();
        /* acknowledge hold */
        gsm_trace_header(p_interface_name, this, MNCC_HOLD_CNF, DIRECTION_OUT);
        end_trace();
@@ -303,6 +310,8 @@ void Pgsm_bs::retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_m
        message->param.notifyinfo.local = 1; /* call is retrieved by supplementary service */
        message_put(message);
 
        message->param.notifyinfo.local = 1; /* call is retrieved by supplementary service */
        message_put(message);
 
+       p_hold = 0;
+
        /* acknowledge retr */
        gsm_trace_header(p_interface_name, this, MNCC_RETRIEVE_CNF, DIRECTION_OUT);
        end_trace();
        /* acknowledge retr */
        gsm_trace_header(p_interface_name, this, MNCC_RETRIEVE_CNF, DIRECTION_OUT);
        end_trace();
@@ -411,6 +420,248 @@ void Pgsm_bs::select_payload_type(struct gsm_mncc *mncc, unsigned char *payload_
        end_trace();
 }
 
        end_trace();
 }
 
+void gsm_trace_facility(unsigned char *fac_ie, unsigned char fac_len)
+{
+       char debug[GSM_MAX_FACILITY * 3 + 1];
+       int i;
+
+       i = 0;
+       while(i < fac_len) {
+               UPRINT(debug+(i*3), " %02x", fac_ie[i]);
+               i++;
+       }
+       debug[i*3] = '\0';
+       add_trace("facility", NULL, "%s", debug[0]?debug+1:"<none>");
+}
+
+/* encode facility IE */
+void Pgsm_bs::enc_ie_facility(struct gsm_mncc *mncc, int operation_code, int error_code, unsigned char invoke_id)
+{
+       unsigned char *fac_ie, fac_len;
+
+       fac_ie = (unsigned char *)mncc->facility.info;
+
+       mncc->fields |= MNCC_F_FACILITY;
+       if (operation_code >= 0) {
+               fac_len = 8;
+               fac_ie[0] = 0xa2;
+               fac_ie[1] = 6;
+               fac_ie[2] = 0x02; /* invoke ID */
+               fac_ie[3] = 1;
+               fac_ie[4] = invoke_id;
+               fac_ie[5] = 0x02; /* Operation Code */
+               fac_ie[6] = 1;
+               fac_ie[7] = 124; /* buildMPTY */
+               fac_ie[7] = operation_code;
+       }
+       if (error_code >= 0) {
+               fac_len = 8;
+               fac_ie[0] = 0xa3;
+               fac_ie[1] = 6;
+               fac_ie[2] = 0x02; /* invoke ID */
+               fac_ie[3] = 1;
+               fac_ie[4] = invoke_id;
+               fac_ie[5] = 0x02; /* Error Code */
+               fac_ie[6] = 1;
+               fac_ie[7] = error_code;
+       }
+       mncc->facility.len = fac_len;
+}
+
+/* send facility request */
+void Pgsm_bs::facility_req(int operation_code, int error_code, unsigned char invoke_id)
+{
+       struct gsm_mncc *mncc;
+
+       gsm_trace_header(p_interface_name, this, MNCC_FACILITY_REQ, DIRECTION_OUT);
+       mncc = create_mncc(MNCC_FACILITY_REQ, p_g_callref);
+
+       enc_ie_facility(mncc, operation_code, error_code, invoke_id);
+
+       gsm_trace_facility((unsigned char *)mncc->facility.info, mncc->facility.len);
+       end_trace();
+       send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
+}
+
+/* FACILITY INDICATION */
+void Pgsm_bs::facility_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
+{
+       unsigned char *fac_ie, fac_len;
+       unsigned char comp_type, comp_len, *comp_val;
+       unsigned char invoke = 0, invoke_id = 0;
+       unsigned char operation = 0, operation_code = 0;
+       struct lcr_msg *message;
+       int i;
+
+       if (mncc->fields & MNCC_F_FACILITY) {
+               fac_ie = (unsigned char *)mncc->facility.info;
+               fac_len = mncc->facility.len;
+
+               gsm_trace_header(p_interface_name, this, msg_type, DIRECTION_IN);
+               gsm_trace_facility(fac_ie, fac_len);
+               end_trace();
+       } else
+               return;
+
+       /* facility */
+       if (fac_len<=2)
+               return;
+       /* component tag */
+       if (fac_ie[1] > fac_len - 2) {
+               PDEBUG(DEBUG_GSM, "Component Tag in facility message greater than message length\n");
+               return;
+       }
+       comp_type = fac_ie[0];
+       comp_len = fac_ie[1];
+       comp_val = fac_ie + 2;
+       PDEBUG(DEBUG_GSM, "Component Tag type 0x%02x\n", comp_type);
+       /* tags inside component */
+       for (i = 0; i != comp_len;) {
+               if (comp_val[1 + i] > comp_len - i - 2) {
+                       PDEBUG(DEBUG_GSM, "Tag inside Component TAG greater than Component length\n");
+                       break;
+               }
+               PDEBUG(DEBUG_GSM, "Tag inside Component Tag (type 0x%02x)\n", comp_val[0 + i]);
+               if (comp_val[0 + i] == 0x02 && ! invoke) { /* Invoke ID Tag */
+                       if (comp_val[1 + i] != 1) {
+                               PDEBUG(DEBUG_GSM, "Invoke ID Tag has invalid length\n");
+                               break;
+                       }
+                       invoke = 1;
+                       invoke_id = comp_val[2 + i];
+                       PDEBUG(DEBUG_GSM, "Invoke ID Tag inside Component TAG with ID=%d\n", invoke_id);
+               } else if (comp_val[0 + i] == 0x02 && !operation) { /* Operation Code Tag */
+                       if (comp_val[1 + i] != 1) {
+                               PDEBUG(DEBUG_GSM, "Operation Code Tag has invalid length\n");
+                               break;
+                       }
+                       operation = 1;
+                       operation_code = comp_val[2 + i];
+                       PDEBUG(DEBUG_GSM, "Operation Code Tag inside Component TAG with Code=%d\n", operation_code);
+               } else
+                       PDEBUG(DEBUG_GSM, "Unknown Tag (0x%02x) inside Component TAG, ignoring\n", comp_val[0 + i]);
+
+               i += comp_val[1 + i] + 2;
+       }
+
+       /* check component type */
+       switch (comp_type) {
+       case 0xa1: /* Invoke */
+               if (!invoke) {
+                       PDEBUG(DEBUG_GSM, "error: Invoke without Invoke ID\n");
+                       break;
+               }
+               if (!operation) {
+                       PDEBUG(DEBUG_GSM, "error: Invoke without Operation Tag\n");
+                       break;
+               }
+               switch(operation_code) {
+               case 124: /* buildMTPY */
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_3PTY);
+                       message->param.threepty.begin = 1;
+                       message->param.threepty.invoke = 1;
+                       message->param.threepty.invoke_id = invoke_id;
+                       message_put(message);
+                       return;
+
+               case 121: /* splitMTPY */
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_3PTY);
+                       message->param.threepty.end = 1;
+                       message->param.threepty.invoke = 1;
+                       message->param.threepty.invoke_id = invoke_id;
+                       message_put(message);
+                       return;
+
+               case 122: /* holdMTPY */
+#ifdef TRANSFER_HACK
+                       facility_req(-1, 122, invoke_id); /* rejected by network */
+
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
+                       message->param.transfer.invoke = 1;
+                       message->param.transfer.invoke_id = invoke_id;
+                       message_put(message);
+#else
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_3PTY);
+                       message->param.threepty.hold = 1;
+                       message->param.threepty.invoke = 1;
+                       message->param.threepty.invoke_id = invoke_id;
+                       message_put(message);
+#endif
+                       return;
+
+               case 123: /* retrieveMTPY */
+#ifdef TRANSFER_HACK
+                       facility_req(-1, 122, invoke_id); /* rejected by network */
+
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
+                       message->param.transfer.invoke = 1;
+                       message->param.transfer.invoke_id = invoke_id;
+                       message_put(message);
+#else
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_3PTY);
+                       message->param.threepty.retrieve = 1;
+                       message->param.threepty.invoke = 1;
+                       message->param.threepty.invoke_id = invoke_id;
+                       message_put(message);
+#endif
+                       return;
+
+               case 126: /* explicitCT */
+                       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
+                       message->param.transfer.invoke = 1;
+                       message->param.transfer.invoke_id = invoke_id;
+                       message_put(message);
+                       return;
+               default:
+                       PDEBUG(DEBUG_GSM, "error: Unsupported Operation\n");
+                       facility_req(-1, 122, invoke_id); /* rejected by network */
+                       return;
+               }
+               break;
+       }
+}
+
+
+/* MESSAGE_3PTY */
+void Pgsm_bs::message_3pty(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       if (param->threepty.result) {
+               if (param->threepty.begin)
+                       facility_req(124, -1, param->threepty.invoke_id); /* buildMPTY */
+               if (param->threepty.end)
+                       facility_req(121, -1, param->threepty.invoke_id); /* splitMPTY */
+       }
+       if (param->threepty.error) {
+               facility_req(-1, 122, param->threepty.invoke_id); /* rejected by network */
+       }
+}
+
+void Pgsm_bs::enc_ie_facility_ect(struct gsm_mncc *mncc, struct param_transfer *transfer)
+{
+       if (transfer->result) {
+               enc_ie_facility(mncc, 126, -1, transfer->invoke_id); /* explicitCT */
+       }
+       if (transfer->error) {
+               enc_ie_facility(mncc, -1, 122, transfer->invoke_id); /* rejected by network */
+       }
+}
+
+/* MESSAGE_TRANSFER */
+void Pgsm_bs::message_transfer(unsigned int epoint_id, int message_id, union parameter *param)
+{
+#ifdef TRANSFER_HACK
+       struct gsm_mncc *mncc;
+
+       /* sending facility */
+       gsm_trace_header(p_interface_name, this, MNCC_FACILITY_REQ, DIRECTION_OUT);
+       mncc = create_mncc(MNCC_FACILITY_REQ, p_g_callref);
+       enc_ie_facility_ect(mncc, &param->transfer);
+       gsm_trace_facility((unsigned char *)mncc->facility.info, mncc->facility.len);
+       end_trace();
+       send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
+#endif
+}
+
 /*
  * handles all indications
  */
 /*
  * handles all indications
  */
@@ -457,6 +708,8 @@ void Pgsm_bs::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_
        p_g_callref = callref;
        end_trace();
 
        p_g_callref = callref;
        end_trace();
 
+       SCPY(p_g_imsi, mncc->imsi);
+
        /* caller info */
        if (mncc->clir.inv)
                p_callerinfo.present = INFO_PRESENT_RESTRICTED;
        /* caller info */
        if (mncc->clir.inv)
                p_callerinfo.present = INFO_PRESENT_RESTRICTED;
@@ -755,6 +1008,10 @@ int message_bsc(struct lcr_gsm *lcr_gsm, int msg_type, void *arg)
                pgsm_bs->retr_ind(msg_type, callref, mncc);
                break;
 
                pgsm_bs->retr_ind(msg_type, callref, mncc);
                break;
 
+               case MNCC_FACILITY_IND:
+               pgsm_bs->facility_ind(msg_type, callref, mncc);
+               break;
+
                default:
                PDEBUG(DEBUG_GSM, "Pgsm_bs(%s) gsm port with (caller id %s) received unhandled nessage: 0x%x\n", pgsm_bs->p_name, pgsm_bs->p_callerinfo.id, msg_type);
        }
                default:
                PDEBUG(DEBUG_GSM, "Pgsm_bs(%s) gsm port with (caller id %s) received unhandled nessage: 0x%x\n", pgsm_bs->p_name, pgsm_bs->p_callerinfo.id, msg_type);
        }
@@ -1030,6 +1287,14 @@ int Pgsm_bs::message_epoint(unsigned int epoint_id, int message_id, union parame
                message_setup(epoint_id, message_id, param);
                break;
 
                message_setup(epoint_id, message_id, param);
                break;
 
+               case MESSAGE_3PTY:
+               message_3pty(epoint_id, message_id, param);
+               break;
+
+               case MESSAGE_TRANSFER:
+               message_transfer(epoint_id, message_id, param);
+               break;
+
                default:
                PDEBUG(DEBUG_GSM, "Pgsm_bs(%s) gsm port with (caller id %s) received unhandled nessage: %d\n", p_name, p_callerinfo.id, message_id);
        }
                default:
                PDEBUG(DEBUG_GSM, "Pgsm_bs(%s) gsm port with (caller id %s) received unhandled nessage: %d\n", p_name, p_callerinfo.id, message_id);
        }
index 5575daa..9f2ea89 100644 (file)
--- a/gsm_bs.h
+++ b/gsm_bs.h
@@ -16,6 +16,12 @@ class Pgsm_bs : public Pgsm
        void stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
        void hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
        void retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
        void stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
        void hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
        void retr_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
+       void enc_ie_facility(struct gsm_mncc *mncc, int operation_code, int error_code, unsigned char invoke_id);
+       void facility_req(int operation_code, int error_code, unsigned char invoke_id);
+       void facility_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
+       void message_3pty(unsigned int epoint_id, int message_id, union parameter *param);
+       void enc_ie_facility_ect(struct gsm_mncc *mncc, struct param_transfer *transfer);
+       void message_transfer(unsigned int epoint_id, int message_id, union parameter *param);
        void message_setup(unsigned int epoint_id, int message_id, union parameter *param);
        int message_epoint(unsigned int epoint_id, int message_id, union parameter *param);
 };
        void message_setup(unsigned int epoint_id, int message_id, union parameter *param);
        int message_epoint(unsigned int epoint_id, int message_id, union parameter *param);
 };
@@ -24,4 +30,6 @@ int gsm_bs_conf(struct gsm_conf *gsm_conf, char *conf_error);
 int gsm_bs_exit(int rc);
 int gsm_bs_init(struct interface *interface);
 
 int gsm_bs_exit(int rc);
 int gsm_bs_init(struct interface *interface);
 
+void gsm_trace_facility(unsigned char *fac_ie, unsigned char fac_len);
+
 int message_bsc(struct lcr_gsm *lcr_gsm, int msg_type, void *arg);
 int message_bsc(struct lcr_gsm *lcr_gsm, int msg_type, void *arg);