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))