Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h (revision 89569)
+++ include/asterisk/channel.h (working copy)
@@ -573,16 +573,35 @@
/*! \brief Free a channel datastore structure */
int ast_channel_datastore_free(struct ast_datastore *datastore);
-/*! \brief Add a datastore to a channel */
+/*!
+ * \brief Add a datastore to a channel
+ *
+ * \note The channel must be locked before calling this function.
+ */
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore);
-/*! \brief Remove a datastore from a channel */
+/*!
+ * \brief Remove a datastore from a channel
+ *
+ * \note The channel must be locked before calling this function.
+ */
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore);
-/*! \brief Find a datastore on a channel */
+/*!
+ * \brief Find a datastore on a channel
+ *
+ * \note The channel must be locked before calling this function.
+ */
struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid);
-/*! \brief Change the state of a channel */
+/*!
+ * \brief Change the state of a channel
+ *
+ * \note The channel must be locked before calling this function.
+ *
+ * \todo There are a large number of places throughout the code where this
+ * function is used without the channel locked.
+ */
int ast_setstate(struct ast_channel *chan, enum ast_channel_state);
/*! \brief Create a channel structure
@@ -593,43 +612,61 @@
*/
struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...);
-/*! \brief Queue an outgoing frame */
+/*!
+ * \brief Queue an outgoing frame
+ *
+ * \note This function will lock the channel as needed.
+ */
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f);
-/*! \brief Queue a hangup frame */
+/*!
+ * \brief Queue a hangup frame
+ *
+ * \note This function will lock the channel as needed.
+ */
int ast_queue_hangup(struct ast_channel *chan);
/*!
- \brief Queue a control frame with payload
- \param chan channel to queue frame onto
- \param control type of control frame
- \return zero on success, non-zero on failure
-*/
+ * \brief Queue a control frame with payload
+ *
+ * \param chan channel to queue frame onto
+ * \param control type of control frame
+ *
+ * \return zero on success, non-zero on failure
+ *
+ * \note This function will lock the channel as needed.
+ */
int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control);
/*!
- \brief Queue a control frame with payload
- \param chan channel to queue frame onto
- \param control type of control frame
- \param data pointer to payload data to be included in frame
- \param datalen number of bytes of payload data
- \return zero on success, non-zero on failure
-
- The supplied payload data is copied into the frame, so the caller's copy
- is not modified nor freed, and the resulting frame will retain a copy of
- the data even if the caller frees their local copy.
-
- \note This method should be treated as a 'network transport'; in other
- words, your frames may be transferred across an IAX2 channel to another
- system, which may be a different endianness than yours. Because of this,
- you should ensure that either your frames will never be expected to work
- across systems, or that you always put your payload data into 'network byte
- order' before calling this function.
-*/
+ * \brief Queue a control frame with payload
+ * \param chan channel to queue frame onto
+ * \param control type of control frame
+ * \param data pointer to payload data to be included in frame
+ * \param datalen number of bytes of payload data
+ * \return zero on success, non-zero on failure
+ *
+ * The supplied payload data is copied into the frame, so the caller's copy
+ * is not modified nor freed, and the resulting frame will retain a copy of
+ * the data even if the caller frees their local copy.
+ *
+ * \note This method should be treated as a 'network transport'; in other
+ * words, your frames may be transferred across an IAX2 channel to another
+ * system, which may be a different endianness than yours. Because of this,
+ * you should ensure that either your frames will never be expected to work
+ * across systems, or that you always put your payload data into 'network byte
+ * order' before calling this function.
+ *
+ * \note This function will lock the channel as needed.
+ */
int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control,
const void *data, size_t datalen);
-/*! \brief Change channel name */
+/*!
+ * \brief Change channel name
+ *
+ * \note The channel must be locked before calling this function.
+ */
void ast_change_name(struct ast_channel *chan, char *newname);
/*! \brief Free a channel structure */
@@ -682,21 +719,33 @@
*/
const struct ast_channel_tech *ast_get_channel_tech(const char *name);
-/*! \brief Hang up a channel
+/*!
+ * \brief Hang up a channel
+ *
+ * \param chan channel to hang up
+ *
+ * \return Returns 0 on success, -1 on failure.
+ *
* \note This function performs a hard hangup on a channel. Unlike the soft-hangup, this function
* performs all stream stopping, etc, on the channel that needs to end.
* chan is no longer valid after this call.
- * \param chan channel to hang up
- * \return Returns 0 on success, -1 on failure.
+ *
+ * \note This function will lock the channel as needed.
*/
int ast_hangup(struct ast_channel *chan);
-/*! \brief Softly hangup up a channel
+/*!
+ * \brief Softly hangup up a channel
+ *
* \param chan channel to be soft-hung-up
+ * \param cause Ast hangupcause for hangup
+ *
+ * \return Returns 0 regardless
+ *
* Call the protocol layer, but don't destroy the channel structure (use this if you are trying to
* safely hangup a channel managed by another thread.
- * \param cause Ast hangupcause for hangup
- * \return Returns 0 regardless
+ *
+ * \note This function will lock the channel as needed.
*/
int ast_softhangup(struct ast_channel *chan, int cause);
@@ -730,40 +779,63 @@
*/
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset);
-/*! \brief Answer a ringing call
+/*!
+ * \brief Answer a ringing call
+ *
* \param chan channel to answer
+ *
+ * \return Returns 0 on success, -1 on failure
+ *
* This function answers a channel and handles all necessary call
* setup functions.
- * \return Returns 0 on success, -1 on failure
+ *
+ * \note This function will lock the channel as needed.
*/
int ast_answer(struct ast_channel *chan);
-/*! \brief Make a call
+/*!
+ * \brief Make a call
+ *
* \param chan which channel to make the call on
* \param addr destination of the call
* \param timeout time to wait on for connect
+ *
+ * \return Returns -1 on failure, 0 on not enough time
+ *
* Place a call, take no longer than timeout ms.
- \return Returns -1 on failure, 0 on not enough time
- (does not automatically stop ringing), and
- the number of seconds the connect took otherwise.
- */
+ * (does not automatically stop ringing), and
+ * the number of seconds the connect took otherwise.
+ *
+ * \note This function will lock the channel as needed.
+ */
int ast_call(struct ast_channel *chan, char *addr, int timeout);
-/*! \brief Indicates condition of channel
- * \note Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel
+/*!
+ * \brief Indicates condition of channel
+ *
* \param chan channel to change the indication
* \param condition which condition to indicate on the channel
* \return Returns 0 on success, -1 on failure
+ *
+ * Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel
+ *
+ * \note This function will lock the channel as needed.
*/
int ast_indicate(struct ast_channel *chan, int condition);
-/*! \brief Indicates condition of channel, with payload
- * \note Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel
+/*!
+ * \brief Indicates condition of channel, with payload
+ *
* \param chan channel to change the indication
* \param condition which condition to indicate on the channel
* \param data pointer to payload data
* \param datalen size of payload data
+ *
* \return Returns 0 on success, -1 on failure
+ *
+ * Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel
+ *
+ * \note This function will lock the channel as needed.
*/
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen);
@@ -1130,8 +1202,15 @@
* \retval 0 success
* \retval -1 failure, or the channel is already being autoserviced
*/
-int ast_autoservice_start(struct ast_channel *chan);
+ #define ast_autoservice_start(c) ast_autoservice_start_full(c, 0)
+int ast_autoservice_start_full(struct ast_channel *chan, unsigned int flags);
+
+/* autoservice flags */
+enum {
+ AST_AUTOSERVICE_QUEUE_DTMF = (1 << 0),
+};
+
/*!
* \brief Stop servicing a channel for us...
*
Index: main/autoservice.c
===================================================================
--- main/autoservice.c (revision 89569)
+++ main/autoservice.c (working copy)
@@ -54,6 +54,8 @@
struct asent {
struct ast_channel *chan;
+ unsigned int queue_dtmf:1;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
AST_LIST_ENTRY(asent) list;
};
@@ -85,6 +87,23 @@
if (chan) {
/* Read and ignore anything that occurs */
struct ast_frame *f = ast_read(chan);
+
+ if (f->frametype == AST_FRAME_DTMF_BEGIN ||
+ f->frametype == AST_FRAME_DTMF_END) {
+ struct ast_frame *dup_f;
+
+ /* XXX would be nice to not have to traverse this here, especially
+ * since we don't know if they actually want the dtmf frames */
+ AST_LIST_LOCK(&aslist);
+ AST_LIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan != chan)
+ continue;
+ if (as->queue_dtmf && (dup_f = ast_frdup(f)))
+ AST_LIST_INSERT_TAIL(&as->dtmf_frames, dup_f, frame_list);
+ }
+ AST_LIST_UNLOCK(&aslist);
+ }
+
if (f)
ast_frfree(f);
}
@@ -93,7 +112,7 @@
return NULL;
}
-int ast_autoservice_start(struct ast_channel *chan)
+int ast_autoservice_start_full(struct ast_channel *chan, unsigned int flags)
{
int res = -1;
struct asent *as;
@@ -108,6 +127,8 @@
/* If not, start autoservice on channel */
if (!as && (as = ast_calloc(1, sizeof(*as)))) {
as->chan = chan;
+ if (flags & AST_AUTOSERVICE_QUEUE_DTMF)
+ as->queue_dtmf = 1;
AST_LIST_INSERT_HEAD(&aslist, as, list);
res = 0;
if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
@@ -130,11 +151,16 @@
{
int res = -1;
struct asent *as;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ struct ast_frame *f;
+ AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames);
+
AST_LIST_LOCK(&aslist);
AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
if (as->chan == chan) {
AST_LIST_REMOVE_CURRENT(&aslist, list);
+ AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list);
free(as);
if (!chan->_softhangup)
res = 0;
@@ -148,7 +174,13 @@
AST_LIST_UNLOCK(&aslist);
/* Wait for it to un-block */
- while(ast_test_flag(chan, AST_FLAG_BLOCKING))
+ while (ast_test_flag(chan, AST_FLAG_BLOCKING))
usleep(1000);
+
+ while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) {
+ ast_queue_frame(chan, f);
+ ast_frfree(f);
+ }
+
return res;
}
Index: main/pbx.c
===================================================================
--- main/pbx.c (revision 89569)
+++ main/pbx.c (working copy)
@@ -5608,6 +5608,7 @@
ms = 10000;
res = ast_waitfordigit(chan, ms);
if (!res) {
+ ast_autoservice_start_full(chan, AST_AUTOSERVICE_QUEUE_DTMF);
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Timeout on %s, continuing...\n", chan->name);
@@ -5619,6 +5620,7 @@
ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
res = -1;
}
+ ast_autoservice_stop(chan);
}
if (ast_test_flag(&flags, WAITEXTEN_MOH))