Merged revisions 164201 via svnmerge from
[asterisk/asterisk.git] / main / channel.c
index b11caf8..3753599 100644 (file)
@@ -2854,64 +2854,126 @@ int ast_indicate(struct ast_channel *chan, int condition)
        return ast_indicate_data(chan, condition, NULL, 0);
 }
 
-int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+static int attribute_const is_visible_indication(enum ast_control_frame_type condition)
+{
+       /* Don't include a default case here so that we get compiler warnings
+        * when a new type is added. */
+
+       switch (condition) {
+       case AST_CONTROL_PROGRESS:
+       case AST_CONTROL_PROCEEDING:
+       case AST_CONTROL_VIDUPDATE:
+       case AST_CONTROL_SRCUPDATE:
+       case AST_CONTROL_RADIO_KEY:
+       case AST_CONTROL_RADIO_UNKEY:
+       case AST_CONTROL_OPTION:
+       case AST_CONTROL_WINK:
+       case AST_CONTROL_FLASH:
+       case AST_CONTROL_OFFHOOK:
+       case AST_CONTROL_TAKEOFFHOOK:
+       case AST_CONTROL_ANSWER:
+       case AST_CONTROL_HANGUP:
+       case AST_CONTROL_T38:
+               return 0;
+
+       case AST_CONTROL_CONGESTION:
+       case AST_CONTROL_BUSY:
+       case AST_CONTROL_RINGING:
+       case AST_CONTROL_RING:
+       case AST_CONTROL_HOLD:
+       case AST_CONTROL_UNHOLD:
+               return 1;
+       }
+
+       return 0;
+}
+
+int ast_indicate_data(struct ast_channel *chan, int _condition,
+               const void *data, size_t datalen)
 {
+       /* By using an enum, we'll get compiler warnings for values not handled 
+        * in switch statements. */
+       enum ast_control_frame_type condition = _condition;
+       const struct ind_tone_zone_sound *ts = NULL;
        int res = -1;
 
        ast_channel_lock(chan);
-       /* Stop if we're a zombie or need a soft hangup */
+
+       /* Don't bother if the channel is about to go away, anyway. */
        if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
                ast_channel_unlock(chan);
                return -1;
        }
-       if (chan->tech->indicate)
+
+       if (chan->tech->indicate) {
+               /* See if the channel driver can handle this condition. */
                res = chan->tech->indicate(chan, condition, data, datalen);
+       }
+
        ast_channel_unlock(chan);
-       if (!chan->tech->indicate || res) {
-               /*
-                * Device does not support (that) indication, lets fake
-                * it by doing our own tone generation. (PM2002)
-                */
-               if (condition < 0)
-                       ast_playtones_stop(chan);
-               else {
-                       const struct ind_tone_zone_sound *ts = NULL;
-                       switch (condition) {
-                       case AST_CONTROL_RINGING:
-                               ts = ast_get_indication_tone(chan->zone, "ring");
-                               break;
-                       case AST_CONTROL_BUSY:
-                               ts = ast_get_indication_tone(chan->zone, "busy");
-                               break;
-                       case AST_CONTROL_CONGESTION:
-                               ts = ast_get_indication_tone(chan->zone, "congestion");
-                               break;
-                       }
-                       if (ts && ts->data[0]) {
-                               ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
-                               ast_playtones_start(chan,0,ts->data, 1);
-                               res = 0;
-                               chan->visible_indication = condition;
-                       } else if (condition == AST_CONTROL_PROGRESS) {
-                               /* ast_playtones_stop(chan); */
-                       } else if (condition == AST_CONTROL_PROCEEDING) {
-                               /* Do nothing, really */
-                       } else if (condition == AST_CONTROL_HOLD) {
-                               /* Do nothing.... */
-                       } else if (condition == AST_CONTROL_UNHOLD) {
-                               /* Do nothing.... */
-                       } else if (condition == AST_CONTROL_VIDUPDATE) {
-                               /* Do nothing.... */
-                       } else if (condition == AST_CONTROL_SRCUPDATE) {
-                               /* Do nothing... */
-                       } else {
-                               /* not handled */
-                               ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
-                               res = -1;
-                       }
+
+       if (chan->tech->indicate && !res) {
+               /* The channel driver successfully handled this indication */
+               if (is_visible_indication(condition)) {
+                       chan->visible_indication = condition;
                }
-       } else
+               return 0;
+       }
+
+       /* The channel driver does not support this indication, let's fake
+        * it by doing our own tone generation if applicable. */
+
+       if (condition < 0) {
+               /* Stop any tones that are playing */
+               ast_playtones_stop(chan);
+               return 0;
+       }
+
+       /* Handle conditions that we have tones for. */
+       switch (condition) {
+       case AST_CONTROL_RINGING:
+               ts = ast_get_indication_tone(chan->zone, "ring");
+               break;
+       case AST_CONTROL_BUSY:
+               ts = ast_get_indication_tone(chan->zone, "busy");
+               break;
+       case AST_CONTROL_CONGESTION:
+               ts = ast_get_indication_tone(chan->zone, "congestion");
+               break;
+       case AST_CONTROL_PROGRESS:
+       case AST_CONTROL_PROCEEDING:
+       case AST_CONTROL_VIDUPDATE:
+       case AST_CONTROL_SRCUPDATE:
+       case AST_CONTROL_RADIO_KEY:
+       case AST_CONTROL_RADIO_UNKEY:
+       case AST_CONTROL_OPTION:
+       case AST_CONTROL_WINK:
+       case AST_CONTROL_FLASH:
+       case AST_CONTROL_OFFHOOK:
+       case AST_CONTROL_TAKEOFFHOOK:
+       case AST_CONTROL_ANSWER:
+       case AST_CONTROL_HANGUP:
+       case AST_CONTROL_RING:
+       case AST_CONTROL_HOLD:
+       case AST_CONTROL_UNHOLD:
+       case AST_CONTROL_T38:
+               /* Nothing left to do for these. */
+               res = 0;
+               break;
+       }
+
+       if (ts && ts->data[0]) {
+               /* We have a tone to play, yay. */
+               ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
+               ast_playtones_start(chan, 0, ts->data, 1);
+               res = 0;
                chan->visible_indication = condition;
+       }
+
+       if (res) {
+               /* not handled */
+               ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
+       }
 
        return res;
 }
@@ -4039,7 +4101,7 @@ int ast_do_masquerade(struct ast_channel *original)
        /* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
        /* Application and data remain the same */
        /* Clone exception  becomes real one, as with fdno */
-       ast_copy_flags(original, clonechan, AST_FLAG_EXCEPTION);
+       ast_copy_flags(original, clonechan, AST_FLAG_EXCEPTION | AST_FLAG_OUTGOING);
        original->fdno = clonechan->fdno;
        /* Schedule context remains the same */
        /* Stream stuff stays the same */
@@ -4085,10 +4147,18 @@ int ast_do_masquerade(struct ast_channel *original)
                ast_log(LOG_WARNING, "Channel type '%s' does not have a fixup routine (for %s)!  Bad things may happen.\n",
                        original->tech->type, original->name);
 
-       /* If an indication is currently playing maintain it on the channel that is taking the place of original */
-       if (original->visible_indication)
+       /* 
+        * If an indication is currently playing, maintain it on the channel 
+        * that is taking the place of original 
+        *
+        * This is needed because the masquerade is swapping out in the internals
+        * of this channel, and the new channel private data needs to be made
+        * aware of the current visible indication (RINGING, CONGESTION, etc.)
+        */
+       if (original->visible_indication) {
                ast_indicate(original, original->visible_indication);
-
+       }
+       
        /* Now, at this point, the "clone" channel is totally F'd up.  We mark it as
           a zombie so nothing tries to touch it.  If it's already been marked as a
           zombie, then free it now (since it already is considered invalid). */