changed error message a bit...
[lcr.git] / chan_lcr.c
index ff8da54..53a7fb6 100644 (file)
@@ -21,8 +21,9 @@ Now the channel driver is linked to LCR and can receive and make calls.
 Call is initiated by LCR:
 
 If a call is received from LCR, a MESSAGE_NEWREF is received first.
+The ref_was_assigned ist set to 1.
 A new chan_call instance is created. The call reference (ref) is given by
-MESSAGE_NEWREF. The state is CHAN_LCR_STATE_IN_PREPARE.
+the received MESSAGE_NEWREF. The state is CHAN_LCR_STATE_IN_PREPARE.
 After receiving MESSAGE_SETUP from LCR, the ast_channel instance is created
 using ast_channel_alloc(1).  The setup information is given to asterisk.
 The new Asterisk instance pointer (ast) is stored to chan_call structure.
@@ -34,8 +35,9 @@ Call is initiated by Asterisk:
 If a call is requested from Asterisk, a new chan_call instance is created.
 The new Asterisk instance pointer (ast) is stored to chan_call structure.
 The current call ref is set to 0, the state is CHAN_LCR_STATE_OUT_PREPARE.
-If the call is received (lcr_call) A MESSASGE_NEWREF is sent to LCR requesting
+If the call is received (lcr_call) A MESSAGE_NEWREF is sent to LCR requesting
 a new call reference (ref).
+The ref_was_assigned ist set to 1.
 Further dialing information is queued.
 After the new callref is received by special MESSAGE_NEWREF reply, new ref
 is stored in the chan_call structure. 
@@ -57,6 +59,7 @@ Call is released by LCR:
 
 A MESSAGE_RELEASE is received with the call reference (ref) to be released.
 The current ref is set to 0, to indicate released reference.
+The ref_was_assigned==1 shows that there is no other ref to be assigned.
 The state changes to CHAN_LCR_STATE_RELEASE.
 ast_queue_hangup() is called, if asterisk instance (ast) exists, if not,
 the chan_call instance is destroyed.
@@ -73,6 +76,7 @@ lcr_hangup() is called-back by Asterisk. If the call reference (ref) is set,
 a MESSAGE_RELEASE is sent to LCR and the chan_call instance is destroyed.
 If the ref is 0 and the state is not CHAN_LCR_STATE_RELEASE, the new state is
 set to CHAN_LCR_STATE_RELEASE.
+The ref_was_assigned==0 shows that a ref is still requested.
 Later, if the MESSAGE_NEWREF reply is received, a MESSAGE_RELEASE is sent to
 LCR and the chan_call instance is destroyed.
 If the ref is 0 and the state is CHAN_LCR_STATE_RELEASE, see the proceedure
@@ -117,6 +121,12 @@ it is called from ast_channel process which has already locked ast_channel.
 
 #include <semaphore.h>
 
+#define HAVE_ATTRIBUTE_always_inline 1
+#define HAVE_ARPA_INET_H 1
+#define HAVE_TIMERSUB 1
+
+#include <asterisk/compiler.h>
+#include <asterisk/buildopts.h>
 #include <asterisk/module.h>
 #include <asterisk/channel.h>
 #include <asterisk/config.h>
@@ -205,46 +215,24 @@ void chan_lcr_log(int type, const char *file, int line, const char *function, st
  */
 struct chan_call *call_first;
 
-struct chan_call *find_call_ref(unsigned int ref)
-{
-       struct chan_call *call = call_first;
-
-       while(call)
-       {
-               if (call->ref == ref)
-                       break;
-               call = call->next;
-       }
-       return(call);
-}
-
-#if 0
-struct chan_call *find_call_ast(struct ast_channel *ast)
-{
-       struct chan_call *call = call_first;
-
-       while(call)
-       {
-               if (call->ast == ast)
-                       break;
-               call = call->next;
-       }
-       return(call);
-}
+/*
+ * find call by ref
+ * special case: 0: find new ref, that has not been assigned a ref yet
+ */
 
-struct chan_call *find_call_handle(unsigned int handle)
+struct chan_call *find_call_ref(unsigned int ref)
 {
        struct chan_call *call = call_first;
-
+       int assigned = (ref > 0);
+       
        while(call)
        {
-               if (call->bchannel_handle == handle)
+               if (call->ref == ref && call->ref_was_assigned == assigned)
                        break;
                call = call->next;
        }
        return(call);
 }
-#endif
 
 void free_call(struct chan_call *call)
 {
@@ -295,11 +283,11 @@ struct chan_call *alloc_call(void)
                free_call(*callp);
                return(NULL);
        }
+       fcntl((*callp)->pipe[0], F_SETFL, O_NONBLOCK);
        CDEBUG(*callp, NULL, "Call instance allocated.\n");
        return(*callp);
 }
 
-
 unsigned short new_bridge_id(void)
 {
        struct chan_call *call;
@@ -456,12 +444,12 @@ void apply_opt(struct chan_call *call, char *data)
                        break;
                case 't':
                        if (opt[1] != '\0') {
-                               CERROR(call, call->ast, "Option 't' (transparent) expects no parameter.\n", opt);
+                               CERROR(call, call->ast, "Option 't' (no_dsp) expects no parameter.\n", opt);
                                break;
                        }
-                       CDEBUG(call, call->ast, "Option 't' (transparent).\n");
-                       if (!call->transparent) {
-                               call->transparent = 1;
+                       CDEBUG(call, call->ast, "Option 't' (no dsp).\n");
+                       if (!call->nodsp) {
+                               call->nodsp = 1;
                                newmode = 1;
                        }
                        break;
@@ -475,7 +463,14 @@ void apply_opt(struct chan_call *call, char *data)
                        if (call->bchannel)
                                bchannel_pipeline(call->bchannel, call->pipeline);
                        break;
-#if 0
+               case 'r':
+                       if (opt[1] != '\0') {
+                               CERROR(call, call->ast, "Option 'r' (re-buffer 160 bytes) expects no parameter.\n", opt);
+                               break;
+                       }
+                       CDEBUG(call, call->ast, "Option 'r' (re-buffer 160 bytes)");
+                       call->rebuffer = 1;
+                       break;
                case 's':
                        if (opt[1] != '\0') {
                                CERROR(call, call->ast, "Option 's' (inband DTMF) expects no parameter.\n", opt);
@@ -483,9 +478,7 @@ void apply_opt(struct chan_call *call, char *data)
                        }
                        CDEBUG(call, call->ast, "Option 's' (inband DTMF).\n");
                        call->inband_dtmf = 1;
-todo
                        break;
-#endif
                case 'v':
                        if (opt[1] != 'r' && opt[1] != 't') {
                                CERROR(call, call->ast, "Option 'v' (volume) expects parameter.\n", opt);
@@ -515,7 +508,7 @@ todo
        /* re-open, if bchannel is created */
        if (call->bchannel && call->bchannel->b_sock > -1) {
                bchannel_destroy(call->bchannel);
-               if (bchannel_create(call->bchannel, ((call->transparent)?1:0) + ((call->hdlc)?2:0)))
+               if (bchannel_create(call->bchannel, ((call->nodsp)?1:0) + ((call->hdlc)?2:0)))
                        bchannel_activate(call->bchannel, 1);
        }
 }
@@ -556,17 +549,15 @@ static void send_setup_to_lcr(struct chan_call *call)
        }
        switch(ast->cid.cid_pres & AST_PRES_RESTRICTION)
        {
-               case AST_PRES_ALLOWED:
-               newparam.setup.callerinfo.present = INFO_PRESENT_ALLOWED;
-               break;
                case AST_PRES_RESTRICTED:
                newparam.setup.callerinfo.present = INFO_PRESENT_RESTRICTED;
                break;
                case AST_PRES_UNAVAILABLE:
                newparam.setup.callerinfo.present = INFO_PRESENT_NOTAVAIL;
                break;
+               case AST_PRES_ALLOWED:
                default:
-               newparam.setup.callerinfo.present = INFO_PRESENT_NULL;
+               newparam.setup.callerinfo.present = INFO_PRESENT_ALLOWED;
        }
        switch(ast->cid.cid_ton)
        {
@@ -583,8 +574,13 @@ static void send_setup_to_lcr(struct chan_call *call)
                newparam.setup.callerinfo.ntype = INFO_NTYPE_UNKNOWN;
        }
        newparam.setup.capainfo.bearer_capa = ast->transfercapability;
-       newparam.setup.capainfo.bearer_info1 = (options.law=='a')?3:2;
        newparam.setup.capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
+       if (call->hdlc)
+               newparam.setup.capainfo.source_mode = B_MODE_HDLC;
+       else {
+               newparam.setup.capainfo.source_mode = B_MODE_TRANSPARENT;
+               newparam.setup.capainfo.bearer_info1 = (options.law=='a')?3:2;
+       }
        newparam.setup.capainfo.hlc = INFO_HLC_NONE;
        newparam.setup.capainfo.exthlc = INFO_HLC_NONE;
        send_message(MESSAGE_SETUP, call->ref, &newparam);
@@ -623,7 +619,7 @@ static void bridge_message_if_bridged(struct chan_call *call, int message_type,
        /* check bridge */
        if (!call) return;
        if (!call->bridge_call) return;
-       CDEBUG(call, NULL, "Sending message due briding.\n");
+       CDEBUG(call, NULL, "Sending message due bridging.\n");
        send_message(message_type, call->bridge_call->ref, param);
 }
 
@@ -657,21 +653,24 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c
 {
        int cause, ret;
        union parameter newparam;
+       char *exten = ast->exten;
+       if (!*exten)
+               exten = "s";
 
-       CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", ast->exten, ast->context, complete?"yes":"no");
+       CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast->context, complete?"yes":"no");
        
        if (complete)
        {
                /* if not match */
-               if (!ast_canmatch_extension(ast, ast->context, ast->exten, 1, call->oad))
+               if (!ast_canmatch_extension(ast, ast->context, exten, 1, call->oad))
                {
-                       CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", ast->exten, ast->context);
+                       CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", exten, ast->context);
                        cause = 1;
                        goto release;
                }
-               if (!ast_exists_extension(ast, ast->context, ast->exten, 1, call->oad))
+               if (!ast_exists_extension(ast, ast->context, exten, 1, call->oad))
                {
-                       CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", ast->exten, ast->context);
+                       CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", exten, ast->context);
                        cause = 28;
                        goto release;
                }
@@ -686,7 +685,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c
                goto start;
        }
 
-       if (ast_canmatch_extension(ast, ast->context, ast->exten, 1, call->oad))
+       if (ast_canmatch_extension(ast, ast->context, exten, 1, call->oad))
        {
                /* send setup acknowledge to lcr */
                if (call->state != CHAN_LCR_STATE_IN_DIALING) {
@@ -698,7 +697,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c
                call->state = CHAN_LCR_STATE_IN_DIALING;
 
                /* if match, start pbx */
-               if (ast_exists_extension(ast, ast->context, ast->exten, 1, call->oad)) {
+               if (ast_exists_extension(ast, ast->context, exten, 1, call->oad)) {
                        CDEBUG(call, ast, "Extensions matches.\n");
                        goto start;
                }
@@ -708,6 +707,12 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c
                return;
        }
 
+       if (!*ast->exten) {
+               /* if can match */
+               CDEBUG(call, ast, "There is no 's' extension (and we tried to match it implicitly). Extensions may match, if more digits are dialed.\n");
+               return;
+       }
+
        /* if not match */
        cause = 1;
        release:
@@ -732,7 +737,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c
                goto release;
        }
        call->pbx_started = 1;
-       return;
+               ast_setstate(ast, AST_STATE_RING);
 }
 
 /*
@@ -801,9 +806,7 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet
        }
        ast->transfercapability = param->setup.capainfo.bearer_capa;
        /* enable hdlc if transcap is data */
-       if (ast->transfercapability == INFO_BC_DATAUNRESTRICTED
-        || ast->transfercapability == INFO_BC_DATARESTRICTED
-        || ast->transfercapability == INFO_BC_VIDEO)
+       if (param->setup.capainfo.source_mode == B_MODE_HDLC)
                call->hdlc = 1;
        strncpy(call->oad, numberrize_callerinfo(param->setup.callerinfo.id, param->setup.callerinfo.ntype, options.national, options.international), sizeof(call->oad)-1);
 
@@ -932,7 +935,7 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa
 }
 
 /*
- * incoming setup acknowledge from LCR
+ * incoming release from LCR
  */
 static void lcr_in_release(struct chan_call *call, int message_type, union parameter *param)
 {
@@ -986,13 +989,20 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p
                return;
        }
        
+       /* change dailing state after setup */
+       if (call->state == CHAN_LCR_STATE_IN_SETUP) {
+               CDEBUG(call, call->ast, "Changing from SETUP to DIALING state.\n");
+               call->state = CHAN_LCR_STATE_IN_DIALING;
+//             ast_setstate(ast, AST_STATE_DIALING);
+       }
+       
        /* queue digits */
        if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0])
                strncat(call->queue_string, param->information.id, sizeof(call->queue_string)-1);
 
        /* use bridge to forware message not supported by asterisk */
        if (call->state == CHAN_LCR_STATE_CONNECT) {
-               CDEBUG(call, call->ast, "Call is connected, briding.\n");
+               CDEBUG(call, call->ast, "Call is connected, bridging.\n");
                bridge_message_if_bridged(call, message_type, param);
        }
 }
@@ -1119,7 +1129,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param)
                                        bchannel_join(bchannel, call->bridge_id);
                                }
                                /* create only, if call exists, othewhise it bchannel is freed below... */
-                               if (bchannel_create(bchannel, ((call->transparent)?1:0) + ((call->hdlc)?2:0)))
+                               if (bchannel_create(bchannel, ((call->nodsp)?1:0) + ((call->hdlc)?2:0)))
                                        bchannel_activate(bchannel, 1);
                        }
                        /* acknowledge */
@@ -1176,6 +1186,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param)
                        call->state = CHAN_LCR_STATE_IN_PREPARE;
                        /* set ref */
                        call->ref = ref;
+                       call->ref_was_assigned = 1;
                        /* wait for setup (or release from asterisk) */
                } else
                {
@@ -1191,6 +1202,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param)
                        }
                        /* store new ref */
                        call->ref = ref;
+                       call->ref_was_assigned = 1;
                        /* send pending setup info */
                        if (call->state == CHAN_LCR_STATE_OUT_PREPARE)
                                send_setup_to_lcr(call);
@@ -1405,7 +1417,6 @@ int handle_socket(void)
 int open_socket(void)
 {
        int ret;
-       char *socket_name = SOCKET_NAME;
        int conn;
        struct sockaddr_un sock_address;
        unsigned int on = 1;
@@ -1421,7 +1432,7 @@ int open_socket(void)
        /* set socket address and name */
        memset(&sock_address, 0, sizeof(sock_address));
        sock_address.sun_family = PF_UNIX;
-       strcpy(sock_address.sun_path, socket_name);
+       sprintf(sock_address.sun_path, SOCKET_NAME, options.lock);
 
        /* connect socket */
        if ((conn = connect(lcr_sock, (struct sockaddr *)&sock_address, SUN_LEN(&sock_address))) < 0)
@@ -1494,6 +1505,7 @@ static int queue_send(void)
                                        case 'R':
                                                CDEBUG(call, ast, "Sending queued RINGING to Asterisk.\n");
                                                ast_queue_control(ast, AST_CONTROL_RINGING);
+                                               ast_setstate(ast, AST_STATE_RINGING);
                                                break;
                                        case 'A':
                                                CDEBUG(call, ast, "Sending queued ANSWER to Asterisk.\n");
@@ -1510,10 +1522,11 @@ static int queue_send(void)
                                                CDEBUG(call, ast, "Sending queued digit '%c' to Asterisk.\n", *p);
                                                /* send digit to asterisk */
                                                memset(&fr, 0, sizeof(fr));
-                                               fr.frametype = AST_FRAME_DTMF;
+                                               fr.frametype = AST_FRAME_DTMF_BEGIN;
                                                fr.subclass = *p;
                                                fr.delivery = ast_tv(0, 0);
-                                               fr.len = 100;
+                                               ast_queue_frame(ast, &fr);
+                                               fr.frametype = AST_FRAME_DTMF_END;
                                                ast_queue_frame(ast, &fr);
                                                break;
                                        default:
@@ -1753,6 +1766,42 @@ static int lcr_call(struct ast_channel *ast, char *dest, int timeout)
        return 0; 
 }
 
+static void send_digit_to_chan(struct ast_channel * ast, char digit )
+{
+        static const char* dtmf_tones[] = {
+                "!941+1336/100,!0/100", /* 0 */
+                "!697+1209/100,!0/100", /* 1 */
+                "!697+1336/100,!0/100", /* 2 */
+                "!697+1477/100,!0/100", /* 3 */
+                "!770+1209/100,!0/100", /* 4 */
+                "!770+1336/100,!0/100", /* 5 */
+                "!770+1477/100,!0/100", /* 6 */
+                "!852+1209/100,!0/100", /* 7 */
+                "!852+1336/100,!0/100", /* 8 */
+                "!852+1477/100,!0/100", /* 9 */
+                "!697+1633/100,!0/100", /* A */
+                "!770+1633/100,!0/100", /* B */
+                "!852+1633/100,!0/100", /* C */
+                "!941+1633/100,!0/100", /* D */
+                "!941+1209/100,!0/100", /* * */
+                "!941+1477/100,!0/100" };       /* # */
+
+        if (digit >= '0' && digit <='9')
+                ast_playtones_start(ast,0,dtmf_tones[digit-'0'], 0);
+        else if (digit >= 'A' && digit <= 'D')
+                ast_playtones_start(ast,0,dtmf_tones[digit-'A'+10], 0);
+        else if (digit == '*')
+                ast_playtones_start(ast,0,dtmf_tones[14], 0);
+        else if (digit == '#')
+                ast_playtones_start(ast,0,dtmf_tones[15], 0);
+        else {
+                /* not handled */
+                ast_log(LOG_DEBUG, "Unable to handle DTMF tone "
+                       "'%c' for '%s'\n", digit, ast->name);
+        }
+}
+
+
 static int lcr_digit_begin(struct ast_channel *ast, char digit)
 {
         struct chan_call *call;
@@ -1796,7 +1845,34 @@ static int lcr_digit_begin(struct ast_channel *ast, char digit)
 
 static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
 {
-       printf("DIGIT END %c\n", digit);
+       int inband_dtmf = 0;
+        struct chan_call *call;
+
+       ast_mutex_lock(&chan_lock);
+
+        call = ast->tech_pvt;
+
+       if (!call) {
+               CERROR(NULL, ast, 
+                      "Received digit from Asterisk, "
+                      "but no call instance exists.\n");
+               ast_mutex_unlock(&chan_lock);
+               return -1;
+       }
+
+       CDEBUG(call, ast, "DIGIT END '%c' from Asterisk.\n", digit);
+
+       if (call->state == CHAN_LCR_STATE_CONNECT && call->inband_dtmf) {
+               inband_dtmf = 1;
+       }
+
+       ast_mutex_unlock(&chan_lock);
+
+       if (inband_dtmf) {
+               CDEBUG(call, ast, "-> sending '%c' inband.\n", digit);
+               send_digit_to_chan(ast, digit);
+       }
+
        return (0);
 }
 
@@ -1813,17 +1889,19 @@ static int lcr_answer(struct ast_channel *ast)
                return -1;
        }
        
-       CDEBUG(call, ast, "Received answer from Asterisk.\n");
+       CDEBUG(call, ast, "Received answer from Asterisk (maybe during lcr_bridge).\n");
                
        /* copy connectinfo, if bridged */
        if (call->bridge_call)
                memcpy(&call->connectinfo, &call->bridge_call->connectinfo, sizeof(struct connect_info));
        /* send connect message to lcr */
-       memset(&newparam, 0, sizeof(union parameter));
-       memcpy(&newparam.connectinfo, &call->connectinfo, sizeof(struct connect_info));
-       send_message(MESSAGE_CONNECT, call->ref, &newparam);
+       if (call->state != CHAN_LCR_STATE_CONNECT) {
+               memset(&newparam, 0, sizeof(union parameter));
+               memcpy(&newparam.connectinfo, &call->connectinfo, sizeof(struct connect_info));
+               send_message(MESSAGE_CONNECT, call->ref, &newparam);
+               call->state = CHAN_LCR_STATE_CONNECT;
+       }
        /* change state */
-       call->state = CHAN_LCR_STATE_CONNECT;
        /* request bchannel */
        if (!call->bchannel) {
                CDEBUG(call, ast, "Requesting B-channel.\n");
@@ -1874,7 +1952,7 @@ static int lcr_hangup(struct ast_channel *ast)
                if (ast->hangupcause > 0)
                        send_release_and_import(call, ast->hangupcause, LOCATION_PRIVATE_LOCAL);
                else
-                       send_release_and_import(call, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL);
+                       send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL);
                /* remove call */
                free_call(call);
                if (!pthread_equal(tid, chan_tid))
@@ -1893,6 +1971,7 @@ static int lcr_hangup(struct ast_channel *ast)
                        /* during prepare, we change to release state */
                        CDEBUG(call, ast, "We must wait until we received our ref, until we can free call instance.\n");
                        call->state = CHAN_LCR_STATE_RELEASE;
+                       call->ast = NULL;
                }
        } 
        if (!pthread_equal(tid, chan_tid))
@@ -1925,8 +2004,7 @@ static int lcr_write(struct ast_channel *ast, struct ast_frame *f)
 static struct ast_frame *lcr_read(struct ast_channel *ast)
 {
         struct chan_call *call;
-       int i, len;
-       unsigned char *p;
+       int len;
 
        ast_mutex_lock(&chan_lock);
         call = ast->tech_pvt;
@@ -1935,20 +2013,23 @@ static struct ast_frame *lcr_read(struct ast_channel *ast)
                return NULL;
        }
        if (call->pipe[0] > -1) {
-               len = read(call->pipe[0], call->read_buff, sizeof(call->read_buff));
+               if (call->rebuffer && !call->hdlc) {
+                       len = read(call->pipe[0], call->read_buff, 160);
+               } else {
+                       len = read(call->pipe[0], call->read_buff, sizeof(call->read_buff));
+               }
+               if (len < 0 && errno == EAGAIN) {
+                       ast_mutex_unlock(&chan_lock);
+                       return &ast_null_frame;
+               }
                if (len <= 0) {
                        close(call->pipe[0]);
                        call->pipe[0] = -1;
+                       ast_mutex_unlock(&chan_lock);
                        return NULL;
                }
        }
 
-       p = call->read_buff;
-       for (i = 0; i < len; i++) {
-               *p = flip_bits[*p];
-               p++;
-       }
-
        call->read_fr.frametype = AST_FRAME_VOICE;
        call->read_fr.subclass = ast->nativeformats;
        call->read_fr.datalen = len;
@@ -2042,6 +2123,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz
                        
                        /*start music onhold*/
                        ast_moh_start(ast,data,ast->musicclass);
+                       call->on_hold = 1;
                         break;
                 case AST_CONTROL_UNHOLD:
                        CDEBUG(call, ast, "Received indicate AST_CONTROL_UNHOLD from Asterisk.\n");
@@ -2052,8 +2134,13 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz
 
                        /*stop moh*/
                        ast_moh_stop(ast);
+                       call->on_hold = 0;
                        break;
-
+#ifdef AST_CONTROL_SRCUPDATE
+               case AST_CONTROL_SRCUPDATE:
+                       CDEBUG(call, ast, "Received AST_CONTROL_SRCUPDATE from Asterisk.\n");
+                        break;
+#endif
                 default:
                        CERROR(call, ast, "Received indicate from Asterisk with unknown condition %d.\n", cond);
                         res = -1;
@@ -2068,21 +2155,25 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz
 /*
  * fixup asterisk
  */
-static int lcr_fixup(struct ast_channel *oldast, struct ast_channel *newast)
+static int lcr_fixup(struct ast_channel *oldast, struct ast_channel *ast)
 {
         struct chan_call *call;
 
+       if (!ast) {
+               return -1;
+       }
+
        ast_mutex_lock(&chan_lock);
-       call = oldast->tech_pvt;
+       call = ast->tech_pvt;
        if (!call) {
-               CERROR(NULL, oldast, "Received fixup from Asterisk, but no call instance exists.\n");
+               CERROR(NULL, ast, "Received fixup from Asterisk, but no call instance exists.\n");
                ast_mutex_unlock(&chan_lock);
                return -1;
        }
 
-       CDEBUG(call, oldast, "Received fixup from Asterisk.\n");
-       call->ast = newast;
-       ast_mutex_lock(&chan_lock);
+       CDEBUG(call, ast, "Received fixup from Asterisk.\n");
+       call->ast = ast;
+       ast_mutex_unlock(&chan_lock);
        return 0;
 }
 
@@ -2125,30 +2216,82 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1,
        struct ast_frame        *f;
        int                     bridge_id;
 
-       CDEBUG(NULL, NULL, "Received briding request from Asterisk.\n");
+       CDEBUG(NULL, NULL, "Received bridging request from Asterisk.\n");
 
        carr[0] = ast1;
        carr[1] = ast2;
-       
+
        /* join via dsp (if the channels are currently open) */
        ast_mutex_lock(&chan_lock);
-       bridge_id = new_bridge_id();
        call1 = ast1->tech_pvt;
        call2 = ast2->tech_pvt;
-       if (call1 && call2)
-       {
+       if (!call1 || !call2) {
+               CDEBUG(NULL, NULL, "Bridge, but we don't have two call instances, exitting.\n");
+               ast_mutex_unlock(&chan_lock);
+               return AST_BRIDGE_COMPLETE;
+       }
+
+       /* join, if both call instances uses dsp */
+       if (!call1->nodsp && !call2->nodsp) {
+               CDEBUG(NULL, NULL, "Both calls use DSP, bridging via DSP.\n");
+
+               /* get bridge id and join */
+               bridge_id = new_bridge_id();
+               
                call1->bridge_id = bridge_id;
                if (call1->bchannel)
                        bchannel_join(call1->bchannel, bridge_id);
-               call1->bridge_call = call2;
-       }
-       if (call2)
-       {
+
                call2->bridge_id = bridge_id;
                if (call2->bchannel)
                        bchannel_join(call2->bchannel, bridge_id);
-               call2->bridge_call = call1;
+       } else
+       if (call1->nodsp && call2->nodsp)
+               CDEBUG(NULL, NULL, "Both calls use no DSP, bridging in channel driver.\n");
+       else
+               CDEBUG(NULL, NULL, "One call uses no DSP, bridging in channel driver.\n");
+       call1->bridge_call = call2;
+       call2->bridge_call = call1;
+
+       if (call1->state == CHAN_LCR_STATE_IN_SETUP
+        || call1->state == CHAN_LCR_STATE_IN_DIALING
+        || call1->state == CHAN_LCR_STATE_IN_PROCEEDING
+        || call1->state == CHAN_LCR_STATE_IN_ALERTING) {
+               CDEBUG(call1, ast1, "Bridge established before lcr_answer, so we call it ourself: Calling lcr_answer...\n");
+               lcr_answer(ast1);
+       }
+       if (call2->state == CHAN_LCR_STATE_IN_SETUP
+        || call2->state == CHAN_LCR_STATE_IN_DIALING
+        || call2->state == CHAN_LCR_STATE_IN_PROCEEDING
+        || call2->state == CHAN_LCR_STATE_IN_ALERTING) {
+               CDEBUG(call2, ast2, "Bridge established before lcr_answer, so we call it ourself: Calling lcr_answer...\n");
+               lcr_answer(ast2);
+       }
+
+       /* sometimes SIP phones forget to send RETRIEVE before TRANSFER
+          so let's do it for them. Hmpf.
+       */
+
+       if (call1->on_hold) {
+               union parameter newparam;
+
+               memset(&newparam, 0, sizeof(union parameter));
+               newparam.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL;
+               send_message(MESSAGE_NOTIFY, call1->ref, &newparam);
+
+               call1->on_hold = 0;
+       }
+
+       if (call2->on_hold) {
+               union parameter newparam;
+
+               memset(&newparam, 0, sizeof(union parameter));
+               newparam.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL;
+               send_message(MESSAGE_NOTIFY, call2->ref, &newparam);
+
+               call2->on_hold = 0;
        }
+       
        ast_mutex_unlock(&chan_lock);
        
        while(1) {
@@ -2189,30 +2332,30 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1,
     
        }
        
-       CDEBUG(NULL, NULL, "Releasing bride.\n");
+       CDEBUG(NULL, NULL, "Releasing bridge.\n");
 
        /* split channels */
        ast_mutex_lock(&chan_lock);
        call1 = ast1->tech_pvt;
        call2 = ast2->tech_pvt;
-       if (call1)
+       if (call1 && call1->bridge_id)
        {
                call1->bridge_id = 0;
                if (call1->bchannel)
                        bchannel_join(call1->bchannel, 0);
                if (call1->bridge_call)
                        call1->bridge_call->bridge_call = NULL;
-               call1->bridge_call = NULL;
        }
-       if (call2)
+       if (call2 && call1->bridge_id)
        {
                call2->bridge_id = 0;
                if (call2->bchannel)
                        bchannel_join(call2->bchannel, 0);
                if (call2->bridge_call)
                        call2->bridge_call->bridge_call = NULL;
-               call2->bridge_call = NULL;
        }
+       call1->bridge_call = NULL;
+       call2->bridge_call = NULL;
 
        ast_mutex_unlock(&chan_lock);
        return AST_BRIDGE_COMPLETE;
@@ -2396,11 +2539,11 @@ int load_module(void)
                                 "    d - Send display text on called phone, text is the optarg.\n"
                                 "    n - Don't detect dtmf tones on called channel.\n"
                                 "    h - Force data call (HDLC).\n" 
-                                "    t - Disable all audio features (required for fax application).\n"
+                                "    t - Disable mISDN_dsp features (required for fax application).\n"
                                 "    c - Make crypted outgoing call, optarg is keyindex.\n"
                                 "    e - Perform echo cancelation on this channel.\n"
                                 "        Takes mISDN pipeline option as optarg.\n"
-//                              "    s - Send Non Inband DTMF as inband.\n"
+                                "    s - Send Non Inband DTMF as inband.\n"
                                 "   vr - rxgain control\n"
                                 "   vt - txgain control\n"
                                 "        Volume changes at factor 2 ^ optarg.\n"