Newer
Older
Kevin P. Fleming
committed
* Copyright (C) 2004 - 2006, Christian Richter
*
* Christian Richter <crich@beronet.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*
*/
/*!
* \file
*
* \brief the chan_misdn channel driver for Asterisk
/*! \li \ref chan_misdn.c uses the configuration file \ref misdn.conf
* \addtogroup configuration_file
*/
/*! \page misdn.conf misdn.conf
* \verbinclude misdn.conf.sample
*/
/*!
* \note
* To use the CCBS/CCNR supplementary service feature and other
* supplementary services using FACILITY messages requires a
* modified version of mISDN.
*
* \note
* The latest modified mISDN v1.1.x based version is available at:
* http://svn.digium.com/svn/thirdparty/mISDN/trunk
* http://svn.digium.com/svn/thirdparty/mISDNuser/trunk
*
* \note
* Taged versions of the modified mISDN code are available under:
* http://svn.digium.com/svn/thirdparty/mISDN/tags
* http://svn.digium.com/svn/thirdparty/mISDNuser/tags
*/
/* Define to enable cli commands to generate canned CCBS messages. */
// #define CCBS_TEST_MESSAGES 1
Richard Mudgett
committed
/*
* XXX The mISDN channel driver needs its native bridge code
* converted to the new bridge technology scheme. The
* chan_dahdi native bridge code can be used as an example. It
* is unlikely that this will ever get done. Support for this
* channel driver is dwindling because the supported version of
* mISDN does not support newer kernels.
*
* Without native bridge support, the following config file
* parameters have no effect: bridging.
*
* The existing native bridge code is marked with the
* mISDN_NATIVE_BRIDGING conditional.
*/
/*** MODULEINFO
<depend>isdnnet</depend>
<depend>misdn</depend>
<depend>suppserv</depend>
<support_level>extended</support_level>
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
Kevin P. Fleming
committed
#include <pthread.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
Christian Richter
committed
#include <signal.h>
#include <sys/file.h>
#include <semaphore.h>
Mark Michelson
committed
#include <ctype.h>
#include <time.h>
Kevin P. Fleming
committed
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/io.h"
#include "asterisk/frame.h"
#include "asterisk/translate.h"
#include "asterisk/cli.h"
#include "asterisk/musiconhold.h"
#include "asterisk/dsp.h"
#include "asterisk/file.h"
#include "asterisk/callerid.h"
#include "asterisk/indications.h"
#include "asterisk/app.h"
#include "asterisk/features.h"
Christian Richter
committed
#include "asterisk/term.h"
Christian Richter
committed
#include "asterisk/sched.h"
Kevin P. Fleming
committed
#include "asterisk/stringfields.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/format.h"
#include "asterisk/format_cap.h"
#include "asterisk/features_config.h"
Mark Michelson
committed
#include "asterisk/pickup.h"
#include "asterisk/format_cache.h"
Kevin P. Fleming
committed
#include "chan_misdn_config.h"
#include "isdn_lib.h"
static char global_tracefile[BUFFERSIZE + 1];
Christian Richter
committed
Tilghman Lesher
committed
static int g_config_initialized = 0;
Christian Richter
committed
struct misdn_jb{
int size;
int upper_threshold;
char *samples, *ok;
int wp,rp;
int state_empty;
int state_full;
int state_buffer;
int bytes_wrote;
ast_mutex_t mutexjb;
};
/*! \brief allocates the jb-structure and initialize the elements */
Christian Richter
committed
struct misdn_jb *misdn_jb_init(int size, int upper_threshold);
/*! \brief frees the data and destroys the given jitterbuffer struct */
Christian Richter
committed
void misdn_jb_destroy(struct misdn_jb *jb);
/*! \brief fills the jitterbuffer with len data returns < 0 if there was an
Christian Richter
committed
int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len);
/*! \brief gets len bytes out of the jitterbuffer if available, else only the
Christian Richter
committed
available data is returned and the return value indicates the number
of data. */
int misdn_jb_empty(struct misdn_jb *jb, char *data, int len);
static char *complete_ch(struct ast_cli_args *a);
static char *complete_debug_port(struct ast_cli_args *a);
static char *complete_show_config(struct ast_cli_args *a);
Christian Richter
committed
/* BEGIN: chan_misdn.h */
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#if defined(AST_MISDN_ENHANCEMENTS)
/*
* This timeout duration is to clean up any call completion records that
* are forgotten about by the switch.
*/
#define MISDN_CC_RECORD_AGE_MAX (6UL * 60 * 60) /* seconds */
#define MISDN_CC_REQUEST_WAIT_MAX 5 /* seconds */
/*!
* \brief Caller that initialized call completion services
*
* \details
* This data is the payload for a datastore that is put on the channel that
* initializes call completion services. This datastore is set to be inherited
* by the outbound mISDN channel. When one of these channels hangs up, the
* channel pointer will be set to NULL. That way, we can ensure that we do not
* touch this channel after it gets destroyed.
*/
struct misdn_cc_caller {
/*! \brief The channel that initialized call completion services */
struct ast_channel *chan;
};
struct misdn_cc_notify {
/*! \brief Dialplan: Notify extension priority */
int priority;
/*! \brief Dialplan: Notify extension context */
char context[AST_MAX_CONTEXT];
/*! \brief Dialplan: Notify extension number (User-A) */
char exten[AST_MAX_EXTENSION];
};
/*! \brief mISDN call completion record */
struct misdn_cc_record {
/*! \brief Call completion record linked list */
AST_LIST_ENTRY(misdn_cc_record) list;
/*! \brief Time the record was created. */
time_t time_created;
/*! \brief MISDN_CC_RECORD_ID value */
long record_id;
/*!
* \brief Logical Layer 1 port associated with this
* call completion record
*/
int port;
/*! \brief TRUE if point-to-point mode (CCBS-T/CCNR-T mode) */
int ptp;
/*! \brief Mode specific parameters */
union {
/*! \brief point-to-point specific parameters. */
struct {
/*!
* \brief Call-completion signaling link.
* NULL if signaling link not established.
*/
struct misdn_bchannel *bc;
/*!
* \brief TRUE if we requested the request retention option
* to be enabled.
*/
int requested_retention;
/*!
* \brief TRUE if the request retention option is enabled.
*/
int retention_enabled;
} ptp;
/*! \brief point-to-multi-point specific parameters. */
struct {
/*! \brief CallLinkageID (valid when port determined) */
int linkage_id;
/*! \breif CCBSReference (valid when activated is TRUE) */
int reference_id;
/*! \brief globalRecall(0), specificRecall(1) */
int recall_mode;
} ptmp;
} mode;
/*! \brief TRUE if call completion activated */
int activated;
/*! \brief Outstanding message ID (valid when outstanding_message) */
int invoke_id;
/*! \brief TRUE if waiting for a response from a message (invoke_id is valid) */
int outstanding_message;
/*! \brief TRUE if activation has been requested */
int activation_requested;
/*!
* \brief TRUE if User-A is free
* \note PTMP - Used to answer CCBSStatusRequest.
* PTP - Determines how to respond to CCBS_T_RemoteUserFree.
*/
int party_a_free;
/*! \brief Error code received from last outstanding message. */
enum FacErrorCode error_code;
/*! \brief Reject code received from last outstanding message. */
enum FacRejectCode reject_code;
/*!
* \brief Saved struct misdn_bchannel call information when
* attempted to call User-B
*/
struct {
/*! \brief User-A caller id information */
struct misdn_party_id caller;
/*! \brief User-B number information */
struct misdn_party_dialing dialed;
/*! \brief The BC, HLC (optional) and LLC (optional) contents from the SETUP message. */
struct Q931_Bc_Hlc_Llc setup_bc_hlc_llc;
/*! \brief SETUP message bearer capability field code value */
int capability;
/*! \brief TRUE if call made in digital HDLC mode */
int hdlc;
} redial;
/*! \brief Dialplan location to indicate User-B free and User-A is free */
struct misdn_cc_notify remote_user_free;
/*! \brief Dialplan location to indicate User-B free and User-A is busy */
struct misdn_cc_notify b_free;
};
/*! \brief mISDN call completion record database */
static AST_LIST_HEAD_STATIC(misdn_cc_records_db, misdn_cc_record);
/*! \brief Next call completion record ID to use */
static __u16 misdn_cc_record_id;
/*! \brief Next invoke ID to use */
static __s16 misdn_invoke_id;
static const char misdn_no_response_from_network[] = "No response from network";
static const char misdn_cc_record_not_found[] = "Call completion record not found";
/* mISDN channel variable names */
#define MISDN_CC_RECORD_ID "MISDN_CC_RECORD_ID"
#define MISDN_CC_STATUS "MISDN_CC_STATUS"
#define MISDN_ERROR_MSG "MISDN_ERROR_MSG"
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
static ast_mutex_t release_lock;
enum misdn_chan_state {
MISDN_NOTHING = 0, /*!< at beginning */
MISDN_WAITING4DIGS, /*!< when waiting for info */
MISDN_EXTCANTMATCH, /*!< when asterisk couldn't match our ext */
MISDN_INCOMING_SETUP, /*!< for incoming setup */
MISDN_DIALING, /*!< when pbx_start */
MISDN_PROGRESS, /*!< we have progress */
MISDN_PROCEEDING, /*!< we have progress */
MISDN_CALLING, /*!< when misdn_call is called */
MISDN_CALLING_ACKNOWLEDGE, /*!< when we get SETUP_ACK */
MISDN_ALERTING, /*!< when Alerting */
MISDN_BUSY, /*!< when BUSY */
MISDN_CONNECTED, /*!< when connected */
MISDN_DISCONNECTED, /*!< when connected */
MISDN_CLEANING, /*!< when hangup from * but we were connected before */
Richard Mudgett
committed
/*! Asterisk created the channel (outgoing call) */
#define ORG_AST 1
Richard Mudgett
committed
/*! mISDN created the channel (incoming call) */
#define ORG_MISDN 2
enum misdn_hold_state {
MISDN_HOLD_IDLE, /*!< HOLD not active */
MISDN_HOLD_ACTIVE, /*!< Call is held */
MISDN_HOLD_TRANSFER, /*!< Held call is being transferred */
MISDN_HOLD_DISCONNECT, /*!< Held call is being disconnected */
};
* \brief Call HOLD state.
*/
enum misdn_hold_state state;
/*!
* \brief Logical port the channel call record is HELD on
* because the B channel is no longer associated.
* \brief Original B channel number the HELD call was using.
* \note Used only for debug display messages.
*/
#define chan_list_ref(obj, debug) (ao2_t_ref((obj), +1, (debug)), (obj))
#define chan_list_unref(obj, debug) (ao2_t_ref((obj), -1, (debug)), NULL)
/*!
* \brief Channel call record structure
*/
struct chan_list {
* \brief The "allowed_bearers" string read in from /etc/asterisk/misdn.conf
*/
Tilghman Lesher
committed
char allowed_bearers[BUFFERSIZE + 1];
* \brief State of the channel
*/
enum misdn_chan_state state;
/*!
* \brief TRUE if a hangup needs to be queued
* \note This is a debug flag only used to catch calls to hangup_chan() that are already hungup.
*/
Christian Richter
committed
int need_queue_hangup;
/*!
* \brief TRUE if a channel can be hung up by calling asterisk directly when done.
*/
Christian Richter
committed
int need_hangup;
/*!
* \brief TRUE if we could send an AST_CONTROL_BUSY if needed.
*/
/*!
* \brief Who originally created this channel. ORG_AST or ORG_MISDN
*/
int originator;
* \brief TRUE of we are not to respond immediately to a SETUP message. Check the dialplan first.
* \note The "noautorespond_on_setup" boolean read in from /etc/asterisk/misdn.conf
*/
int noautorespond_on_setup;
int norxtone; /*!< Boolean assigned values but the value is not used. */
/*!
* \brief TRUE if we are not to generate tones (Playtones)
*/
/*!
* \brief TRUE if echo canceller is enabled. Value is toggled.
*/
int toggle_ec;
/*!
* \brief TRUE if you want to send Tone Indications to an incoming
* ISDN channel on a TE Port.
* \note The "incoming_early_audio" boolean read in from /etc/asterisk/misdn.conf
*/
Christian Richter
committed
int incoming_early_audio;
/*!
* \brief TRUE if DTMF digits are to be passed inband only.
* \note It is settable by the misdn_set_opt() application.
*/
int ignore_dtmf;
* \brief Pipe file descriptor handles array.
* Read from pipe[0], write to pipe[1]
/*!
* \brief Read buffer for inbound audio from pipe[0]
*/
char ast_rd_buf[4096];
/*!
* \brief Inbound audio frame returned by misdn_read().
*/
struct ast_frame frame;
/*!
* \brief Fax detection option. (0:no 1:yes 2:yes+nojump)
* \note The "faxdetect" option string read in from /etc/asterisk/misdn.conf
* \note It is settable by the misdn_set_opt() application.
*/
int faxdetect;
/*!
* \brief Number of seconds to detect a Fax machine when detection enabled.
* \note 0 disables the timeout.
* \note The "faxdetect_timeout" value read in from /etc/asterisk/misdn.conf
*/
int faxdetect_timeout;
/*!
* \brief Starting time of fax detection with timeout when nonzero.
*/
struct timeval faxdetect_tv;
/*!
* \brief TRUE if a fax has been detected.
*/
int faxhandled;
/*!
* \brief TRUE if we will use the Asterisk DSP to detect DTMF/Fax
* \note The "astdtmf" boolean read in from /etc/asterisk/misdn.conf
*/
Christian Richter
committed
/*!
* \brief Jitterbuffer length
* \note The "jitterbuffer" value read in from /etc/asterisk/misdn.conf
*/
/*!
* \brief Jitterbuffer upper threshold
* \note The "jitterbuffer_upper_threshold" value read in from /etc/asterisk/misdn.conf
*/
/*!
* \brief Allocated jitterbuffer controller
* \note misdn_jb_init() creates the jitterbuffer.
* \note Must use misdn_jb_destroy() to clean up.
Christian Richter
committed
struct misdn_jb *jb;
/*!
* \brief Allocated DSP controller
* \note ast_dsp_new() creates the DSP controller.
* \note Must use ast_dsp_free() to clean up.
struct ast_dsp *dsp;
/*!
* \brief Associated Asterisk channel structure.
*/
struct ast_channel * ast;
Christian Richter
committed
/*!
* \brief Associated B channel structure.
*/
struct misdn_bchannel *bc;
#if defined(AST_MISDN_ENHANCEMENTS)
/*!
* \brief Peer channel for which call completion was initialized.
*/
struct misdn_cc_caller *peer;
/*! \brief Associated call completion record ID (-1 if not associated) */
long record_id;
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
* \brief HELD channel call information
/*!
* \brief From associated B channel: Layer 3 process ID
* \note Used to find the HELD channel call record when retrieving a call.
unsigned int l3id;
* \brief From associated B channel: B Channel mISDN driver layer ID from mISDN_get_layerid()
* \note Used only for debug display messages.
*/
Christian Richter
committed
/*!
* \brief Incoming call dialplan context identifier.
* \note The "context" string read in from /etc/asterisk/misdn.conf
*/
char context[AST_MAX_CONTEXT];
/*!
* \brief The configured music-on-hold class to use for this call.
* \note The "musicclass" string read in from /etc/asterisk/misdn.conf
*/
char mohinterpret[MAX_MUSICCLASS];
/*!
* \brief Number of outgoing audio frames dropped since last debug gripe message.
*/
Christian Richter
committed
int dropped_frame_cnt;
/*!
* \brief TRUE if we must do the ringback tones.
* \note The "far_alerting" boolean read in from /etc/asterisk/misdn.conf
*/
Christian Richter
committed
int far_alerting;
/*!
* \brief TRUE if NT should disconnect an overlap dialing call when a timeout occurs.
* \note The "nttimeout" boolean read in from /etc/asterisk/misdn.conf
*/
/*!
* \brief Tone zone sound used for dialtone generation.
* \note Used as a boolean. Non-NULL to prod generation if enabled.
struct ast_tone_zone_sound *ts;
/*!
* \brief Enables overlap dialing for the set amount of seconds. (0 = Disabled)
* \note The "overlapdial" value read in from /etc/asterisk/misdn.conf
*/
Christian Richter
committed
int overlap_dial;
/*!
* \brief Overlap dialing timeout Task ID. -1 if not running.
*/
Christian Richter
committed
int overlap_dial_task;
/*!
* \brief overlap_tv access lock.
*/
Christian Richter
committed
ast_mutex_t overlap_tv_lock;
/*!
* \brief Overlap timer start time. Timer restarted for every digit received.
*/
Christian Richter
committed
struct timeval overlap_tv;
/*!
* \brief Next channel call record in the list.
*/
struct chan_list *next;
};
Christian Richter
committed
int MAXTICS = 8;
Christian Richter
committed
void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch);
void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch);
static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame);
Christian Richter
committed
Mark Michelson
committed
struct robin_list {
char *group;
int port;
int channel;
struct robin_list *next;
struct robin_list *prev;
Mark Michelson
committed
};
static struct robin_list *robin = NULL;
Christian Richter
committed
static void free_robin_list(void)
struct robin_list *r;
struct robin_list *next;
for (r = robin, robin = NULL; r; r = next) {
next = r->next;
ast_free(r->group);
Tilghman Lesher
committed
ast_free(r);
}
static struct robin_list *get_robin_position(char *group)
Tilghman Lesher
committed
struct robin_list *new;
struct robin_list *iter = robin;
for (; iter; iter = iter->next) {
if (!strcasecmp(iter->group, group)) {
Tilghman Lesher
committed
new = ast_calloc(1, sizeof(*new));
if (!new) {
return NULL;
}
new->group = ast_strdup(group);
if (!new->group) {
ast_free(new);
return NULL;
}
new->channel = 1;
if (robin) {
new->next = robin;
robin->prev = new;
}
robin = new;
return robin;
}
Christian Richter
committed
/*! \brief the main schedule context for stuff like l1 watcher, overlap dial, ... */
static struct ast_sched_context *misdn_tasks = NULL;
Christian Richter
committed
static pthread_t misdn_tasks_thread;
Kevin P. Fleming
committed
static void chan_misdn_log(int level, int port, char *tmpl, ...)
__attribute__((format(printf, 3, 4)));
Christian Richter
committed
static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, int port, int c);
static void send_digit_to_chan(struct chan_list *cl, char digit);
Christian Richter
committed
static int pbx_start_chan(struct chan_list *ch);
#define MISDN_ASTERISK_TECH_PVT(ast) ast_channel_tech_pvt(ast)
#define MISDN_ASTERISK_TECH_PVT_SET(ast, value) ast_channel_tech_pvt_set(ast, value)
#include "asterisk/strings.h"
/* #define MISDN_DEBUG 1 */
static const char misdn_type[] = "mISDN";
static int tracing = 0;
Christian Richter
committed
static int *misdn_debug;
static int *misdn_debug_only;
static int max_ports;
static int *misdn_in_calls;
static int *misdn_out_calls;
/*!
* \brief Global channel call record list head.
*/
static struct chan_list *cl_te=NULL;
static ast_mutex_t cl_te_lock;
Christian Richter
committed
static enum event_response_e
cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data);
static int send_cause2ast(struct ast_channel *ast, struct misdn_bchannel *bc, struct chan_list *ch);
Christian Richter
committed
static void cl_queue_chan(struct chan_list *chan);
Christian Richter
committed
Christian Richter
committed
static int dialtone_indicate(struct chan_list *cl);
static void hanguptone_indicate(struct chan_list *cl);
Christian Richter
committed
static int stop_indicate(struct chan_list *cl);
static int start_bc_tones(struct chan_list *cl);
static int stop_bc_tones(struct chan_list *cl);
static void release_chan_early(struct chan_list *ch);
static void release_chan(struct chan_list *ch, struct misdn_bchannel *bc);
#if defined(AST_MISDN_ENHANCEMENTS)
static const char misdn_command_name[] = "misdn_command";
static int misdn_command_exec(struct ast_channel *chan, const char *data);
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
static int misdn_check_l2l1(struct ast_channel *chan, const char *data);
static int misdn_set_opt_exec(struct ast_channel *chan, const char *data);
static int misdn_facility_exec(struct ast_channel *chan, const char *data);
Christian Richter
committed
int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len);
Mark Michelson
committed
void debug_numtype(int port, int numtype, char *type);
Christian Richter
committed
int add_out_calls(int port);
int add_in_calls(int port);
#ifdef MISDN_1_2
static int update_pipeline_config(struct misdn_bchannel *bc);
#else
static int update_ec_config(struct misdn_bchannel *bc);
/*************** Helpers *****************/
static int misdn_chan_is_valid(struct chan_list *ch)
{
struct chan_list *list;
ast_mutex_lock(&cl_te_lock);
for (list = cl_te; list; list = list->next) {
if (list == ch) {
ast_mutex_unlock(&cl_te_lock);
return 1;
}
}
ast_mutex_unlock(&cl_te_lock);
return 0;
}
Richard Mudgett
committed
#if defined(mISDN_NATIVE_BRIDGING)
/*! Returns a reference to the found chan_list. */
static struct chan_list *get_chan_by_ast(struct ast_channel *ast)
{
struct chan_list *tmp;
ast_mutex_lock(&cl_te_lock);
for (tmp = cl_te; tmp; tmp = tmp->next) {
if (tmp->ast == ast) {
chan_list_ref(tmp, "Found chan_list by ast");
ast_mutex_unlock(&cl_te_lock);
return tmp;
}
ast_mutex_unlock(&cl_te_lock);
return NULL;
}
Richard Mudgett
committed
#endif /* defined(mISDN_NATIVE_BRIDGING) */
/*! Returns a reference to the found chan_list. */
static struct chan_list *get_chan_by_ast_name(const char *name)
Mark Michelson
committed
{
struct chan_list *tmp;
ast_mutex_lock(&cl_te_lock);
Mark Michelson
committed
for (tmp = cl_te; tmp; tmp = tmp->next) {
if (tmp->ast && strcmp(ast_channel_name(tmp->ast), name) == 0) {
chan_list_ref(tmp, "Found chan_list by ast name");
ast_mutex_unlock(&cl_te_lock);
Mark Michelson
committed
return tmp;
}
}
ast_mutex_unlock(&cl_te_lock);
Mark Michelson
committed
return NULL;
}
#if defined(AST_MISDN_ENHANCEMENTS)
Mark Michelson
committed
/*!
* \internal
* \brief Destroy the misdn_cc_ds_info datastore payload
Mark Michelson
committed
*
* \param[in] data the datastore payload, a reference to an misdn_cc_caller
Mark Michelson
committed
*
* \details
* Since the payload is a reference to an astobj2 object, we just decrement its
* reference count. Before doing so, we NULL out the channel pointer inside of
* the misdn_cc_caller instance. This function will be called in one of two
* cases. In both cases, we no longer need the channel pointer:
*
* - The original channel that initialized call completion services, the same
* channel that is stored here, has been destroyed early. This could happen
* if it transferred the mISDN channel, for example.
*
* - The mISDN channel that had this datastore inherited on to it is now being
* destroyed. If this is the case, then the call completion events have
* already occurred and the appropriate channel variables have already been
* set on the original channel that requested call completion services.
*
* \return Nothing
Mark Michelson
committed
*/
static void misdn_cc_ds_destroy(void *data)
Mark Michelson
committed
{
struct misdn_cc_caller *cc_caller = data;
Mark Michelson
committed
ao2_lock(cc_caller);
cc_caller->chan = NULL;
ao2_unlock(cc_caller);
Mark Michelson
committed
ao2_ref(cc_caller, -1);
Mark Michelson
committed
}
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
Mark Michelson
committed
/*!
* \internal
* \brief Duplicate the misdn_cc_ds_info datastore payload
Mark Michelson
committed
*
* \param[in] data the datastore payload, a reference to an misdn_cc_caller
Mark Michelson
committed
*
* \details
* All we need to do is bump the reference count and return the same instance.
*
* \return A reference to an instance of a misdn_cc_caller
Mark Michelson
committed
*/
static void *misdn_cc_ds_duplicate(void *data)
Mark Michelson
committed
{
struct misdn_cc_caller *cc_caller = data;
Mark Michelson
committed
ao2_ref(cc_caller, +1);
Mark Michelson
committed
return cc_caller;
}
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
static const struct ast_datastore_info misdn_cc_ds_info = {
.type = "misdn_cc",
.destroy = misdn_cc_ds_destroy,
.duplicate = misdn_cc_ds_duplicate,
};
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
/*!
* \internal
* \brief Set a channel var on the peer channel for call completion services
*
* \param[in] peer The peer that initialized call completion services
* \param[in] var The variable name to set
* \param[in] value The variable value to set
*
* This function may be called from outside of the channel thread. It handles
* the fact that the peer channel may be hung up and destroyed at any time.
*
* \return nothing
*/
static void misdn_cc_set_peer_var(struct misdn_cc_caller *peer, const char *var,
const char *value)
{
ao2_lock(peer);
Mark Michelson
committed
/*! \todo XXX This nastiness can go away once ast_channel is ref counted! */
while (peer->chan && ast_channel_trylock(peer->chan)) {
ao2_unlock(peer);
sched_yield();
ao2_lock(peer);
}
Mark Michelson
committed
if (peer->chan) {
pbx_builtin_setvar_helper(peer->chan, var, value);
ast_channel_unlock(peer->chan);
Mark Michelson
committed
}
ao2_unlock(peer);
Mark Michelson
committed
}
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
Mark Michelson
committed
/*!
* \internal
* \brief Get a reference to the CC caller if it exists
Mark Michelson
committed
*/
static struct misdn_cc_caller *misdn_cc_caller_get(struct ast_channel *chan)
Mark Michelson
committed
{
struct ast_datastore *datastore;
struct misdn_cc_caller *cc_caller;
Mark Michelson
committed
ast_channel_lock(chan);
Mark Michelson
committed
if (!(datastore = ast_channel_datastore_find(chan, &misdn_cc_ds_info, NULL))) {
ast_channel_unlock(chan);
return NULL;
}
Mark Michelson
committed
ao2_ref(datastore->data, +1);
cc_caller = datastore->data;
Mark Michelson
committed
ast_channel_unlock(chan);
Mark Michelson
committed
return cc_caller;
Mark Michelson
committed
}
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
Mark Michelson
committed
/*!
* \internal
* \brief Find the call completion record given the record id.
Mark Michelson
committed
*
* \param record_id
Mark Michelson
committed
*
* \retval pointer to found call completion record
* \retval NULL if not found
*
* \note Assumes the misdn_cc_records_db lock is already obtained.
Mark Michelson
committed
*/
static struct misdn_cc_record *misdn_cc_find_by_id(long record_id)
Mark Michelson
committed
{
struct misdn_cc_record *current;
Mark Michelson
committed
AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
if (current->record_id == record_id) {
/* Found the record */
break;
}
Mark Michelson
committed
}
return current;
Mark Michelson
committed
}
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
Mark Michelson
committed
/*!
* \internal
* \brief Find the call completion record given the port and call linkage id.
Mark Michelson
committed
*
* \param port Logical port number
* \param linkage_id Call linkage ID number from switch.
Mark Michelson
committed
*
* \retval pointer to found call completion record
* \retval NULL if not found
*
* \note Assumes the misdn_cc_records_db lock is already obtained.
Mark Michelson
committed
*/
static struct misdn_cc_record *misdn_cc_find_by_linkage(int port, int linkage_id)
Mark Michelson
committed
{
struct misdn_cc_record *current;
Mark Michelson
committed
AST_LIST_TRAVERSE(&misdn_cc_records_db, current, list) {
if (current->port == port
&& !current->ptp
&& current->mode.ptmp.linkage_id == linkage_id) {
/* Found the record */
break;
}
}
Mark Michelson
committed
return current;
}
#endif /* defined(AST_MISDN_ENHANCEMENTS) */
Mark Michelson
committed
#if defined(AST_MISDN_ENHANCEMENTS)
/*!
* \internal
* \brief Find the call completion record given the port and outstanding invocation id.
*
* \param port Logical port number
* \param invoke_id Outstanding message invocation ID number.
*
* \retval pointer to found call completion record
* \retval NULL if not found
*
* \note Assumes the misdn_cc_records_db lock is already obtained.
*/