Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2009, Digium, Inc.
*
* Mark Spencer <markster@digium.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 Analog signaling module
*
* \author Matthew Fredrickson <creslin@digium.com>
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
#include <errno.h>
#include <ctype.h>
#include "asterisk/utils.h"
#include "asterisk/options.h"
#include "asterisk/pbx.h"
#include "asterisk/file.h"
#include "asterisk/callerid.h"
#include "asterisk/say.h"
#include "asterisk/manager.h"
#include "asterisk/astdb.h"
#include "asterisk/features.h"
#include "asterisk/causes.h"
#include "asterisk/features_config.h"
#include "asterisk/parking.h"
#include "sig_analog.h"
Matthew Jordan
committed
/*** DOCUMENTATION
***/
/*! \note
* Define if you want to check the hook state for an FXO (FXS signalled) interface
* before dialing on it. Certain FXO interfaces always think they're out of
* service with this method however.
*/
/* #define DAHDI_CHECK_HOOKSTATE */
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#define POLARITY_IDLE 0
#define POLARITY_REV 1
#define MIN_MS_SINCE_FLASH ( (2000) ) /*!< 2000 ms */
static int analog_matchdigittimeout = 3000;
static int analog_gendigittimeout = 8000;
static int analog_firstdigittimeout = 16000;
static char analog_defaultcic[64] = "";
static char analog_defaultozz[64] = "";
static const struct {
enum analog_sigtype sigtype;
const char const *name;
} sigtypes[] = {
{ ANALOG_SIG_FXOLS, "fxo_ls" },
{ ANALOG_SIG_FXOKS, "fxo_ks" },
{ ANALOG_SIG_FXOGS, "fxo_gs" },
{ ANALOG_SIG_FXSLS, "fxs_ls" },
{ ANALOG_SIG_FXSKS, "fxs_ks" },
{ ANALOG_SIG_FXSGS, "fxs_gs" },
{ ANALOG_SIG_EMWINK, "em_w" },
{ ANALOG_SIG_EM, "em" },
{ ANALOG_SIG_EM_E1, "em_e1" },
{ ANALOG_SIG_FEATD, "featd" },
{ ANALOG_SIG_FEATDMF, "featdmf" },
{ ANALOG_SIG_FEATDMF_TA, "featdmf_ta" },
{ ANALOG_SIG_FEATB, "featb" },
{ ANALOG_SIG_FGC_CAMA, "fgccama" },
{ ANALOG_SIG_FGC_CAMAMF, "fgccamamf" },
{ ANALOG_SIG_SF, "sf" },
{ ANALOG_SIG_SFWINK, "sf_w" },
{ ANALOG_SIG_SF_FEATD, "sf_featd" },
{ ANALOG_SIG_SF_FEATDMF, "sf_featdmf" },
{ ANALOG_SIG_SF_FEATB, "sf_featb" },
{ ANALOG_SIG_E911, "e911" },
};
static const struct {
unsigned int cid_type;
const char const *name;
} cidtypes[] = {
{ CID_SIG_BELL, "bell" },
{ CID_SIG_V23, "v23" },
{ CID_SIG_V23_JP, "v23_jp" },
{ CID_SIG_DTMF, "dtmf" },
/* "smdi" is intentionally not supported here, as there is a much better
* way to do this in the dialplan now. */
};
#define ISTRUNK(p) ((p->sig == ANALOG_SIG_FXSLS) || (p->sig == ANALOG_SIG_FXSKS) || \
(p->sig == ANALOG_SIG_FXSGS))
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
enum analog_sigtype analog_str_to_sigtype(const char *name)
{
int i;
for (i = 0; i < ARRAY_LEN(sigtypes); i++) {
if (!strcasecmp(sigtypes[i].name, name)) {
return sigtypes[i].sigtype;
}
}
return 0;
}
const char *analog_sigtype_to_str(enum analog_sigtype sigtype)
{
int i;
for (i = 0; i < ARRAY_LEN(sigtypes); i++) {
if (sigtype == sigtypes[i].sigtype) {
return sigtypes[i].name;
}
}
return "Unknown";
}
unsigned int analog_str_to_cidtype(const char *name)
{
int i;
for (i = 0; i < ARRAY_LEN(cidtypes); i++) {
if (!strcasecmp(cidtypes[i].name, name)) {
return cidtypes[i].cid_type;
}
}
return 0;
}
const char *analog_cidtype_to_str(unsigned int cid_type)
{
int i;
for (i = 0; i < ARRAY_LEN(cidtypes); i++) {
if (cid_type == cidtypes[i].cid_type) {
return cidtypes[i].name;
}
}
return "Unknown";
}
static int analog_start_cid_detect(struct analog_pvt *p, int cid_signalling)
{
if (analog_callbacks.start_cid_detect) {
return analog_callbacks.start_cid_detect(p->chan_pvt, cid_signalling);
}
return -1;
}
static int analog_stop_cid_detect(struct analog_pvt *p)
{
if (analog_callbacks.stop_cid_detect) {
return analog_callbacks.stop_cid_detect(p->chan_pvt);
}
return -1;
}
static int analog_get_callerid(struct analog_pvt *p, char *name, char *number, enum analog_event *ev, size_t timeout)
{
if (analog_callbacks.get_callerid) {
return analog_callbacks.get_callerid(p->chan_pvt, name, number, ev, timeout);
}
return -1;
static const char *analog_get_orig_dialstring(struct analog_pvt *p)
{
if (analog_callbacks.get_orig_dialstring) {
return analog_callbacks.get_orig_dialstring(p->chan_pvt);
static int analog_get_event(struct analog_pvt *p)
{
if (analog_callbacks.get_event) {
return analog_callbacks.get_event(p->chan_pvt);
}
return -1;
}
static int analog_wait_event(struct analog_pvt *p)
{
if (analog_callbacks.wait_event) {
return analog_callbacks.wait_event(p->chan_pvt);
}
return -1;
static int analog_have_progressdetect(struct analog_pvt *p)
{
if (analog_callbacks.have_progressdetect) {
return analog_callbacks.have_progressdetect(p->chan_pvt);
}
/* Don't have progress detection. */
return 0;
}
enum analog_cid_start analog_str_to_cidstart(const char *value)
{
if (!strcasecmp(value, "ring")) {
return ANALOG_CID_START_RING;
} else if (!strcasecmp(value, "polarity")) {
return ANALOG_CID_START_POLARITY;
} else if (!strcasecmp(value, "polarity_in")) {
return ANALOG_CID_START_POLARITY_IN;
} else if (!strcasecmp(value, "dtmf")) {
return ANALOG_CID_START_DTMF_NOALERT;
}
return 0;
}
const char *analog_cidstart_to_str(enum analog_cid_start cid_start)
{
switch (cid_start) {
case ANALOG_CID_START_RING:
return "Ring";
case ANALOG_CID_START_POLARITY:
return "Polarity";
case ANALOG_CID_START_POLARITY_IN:
return "Polarity_In";
case ANALOG_CID_START_DTMF_NOALERT:
return "DTMF";
}
return "Unknown";
}
static char *analog_event2str(enum analog_event event)
{
char *res;
switch (event) {
case ANALOG_EVENT_ONHOOK:
res = "ANALOG_EVENT_ONHOOK";
break;
case ANALOG_EVENT_RINGOFFHOOK:
res = "ANALOG_EVENT_RINGOFFHOOK";
break;
case ANALOG_EVENT_WINKFLASH:
res = "ANALOG_EVENT_WINKFLASH";
break;
case ANALOG_EVENT_ALARM:
res = "ANALOG_EVENT_ALARM";
break;
case ANALOG_EVENT_NOALARM:
res = "ANALOG_EVENT_NOALARM";
break;
case ANALOG_EVENT_DIALCOMPLETE:
res = "ANALOG_EVENT_DIALCOMPLETE";
break;
case ANALOG_EVENT_HOOKCOMPLETE:
res = "ANALOG_EVENT_HOOKCOMPLETE";
break;
case ANALOG_EVENT_PULSE_START:
res = "ANALOG_EVENT_PULSE_START";
break;
case ANALOG_EVENT_POLARITY:
res = "ANALOG_EVENT_POLARITY";
break;
case ANALOG_EVENT_RINGBEGIN:
res = "ANALOG_EVENT_RINGBEGIN";
break;
case ANALOG_EVENT_EC_DISABLED:
res = "ANALOG_EVENT_EC_DISABLED";
break;
case ANALOG_EVENT_RINGERON:
res = "ANALOG_EVENT_RINGERON";
break;
case ANALOG_EVENT_RINGEROFF:
res = "ANALOG_EVENT_RINGEROFF";
break;
case ANALOG_EVENT_REMOVED:
res = "ANALOG_EVENT_REMOVED";
break;
case ANALOG_EVENT_NEONMWI_ACTIVE:
res = "ANALOG_EVENT_NEONMWI_ACTIVE";
break;
case ANALOG_EVENT_NEONMWI_INACTIVE:
res = "ANALOG_EVENT_NEONMWI_INACTIVE";
break;
#ifdef HAVE_DAHDI_ECHOCANCEL_FAX_MODE
case ANALOG_EVENT_TX_CED_DETECTED:
res = "ANALOG_EVENT_TX_CED_DETECTED";
break;
case ANALOG_EVENT_RX_CED_DETECTED:
res = "ANALOG_EVENT_RX_CED_DETECTED";
break;
case ANALOG_EVENT_EC_NLP_DISABLED:
res = "ANALOG_EVENT_EC_NLP_DISABLED";
break;
case ANALOG_EVENT_EC_NLP_ENABLED:
res = "ANALOG_EVENT_EC_NLP_ENABLED";
break;
#endif
case ANALOG_EVENT_PULSEDIGIT:
res = "ANALOG_EVENT_PULSEDIGIT";
break;
case ANALOG_EVENT_DTMFDOWN:
res = "ANALOG_EVENT_DTMFDOWN";
break;
case ANALOG_EVENT_DTMFUP:
res = "ANALOG_EVENT_DTMFUP";
break;
default:
res = "UNKNOWN/OTHER";
break;
}
return res;
}
static void analog_swap_subs(struct analog_pvt *p, enum analog_sub a, enum analog_sub b)
{
int tinthreeway;
struct ast_channel *towner;
ast_debug(1, "Swapping %u and %u\n", a, b);
towner = p->subs[a].owner;
p->subs[a].owner = p->subs[b].owner;
p->subs[b].owner = towner;
tinthreeway = p->subs[a].inthreeway;
p->subs[a].inthreeway = p->subs[b].inthreeway;
p->subs[b].inthreeway = tinthreeway;
if (analog_callbacks.swap_subs) {
analog_callbacks.swap_subs(p->chan_pvt, a, p->subs[a].owner, b, p->subs[b].owner);
}
static int analog_alloc_sub(struct analog_pvt *p, enum analog_sub x)
{
if (analog_callbacks.allocate_sub) {
Jeff Peeler
committed
int res;
res = analog_callbacks.allocate_sub(p->chan_pvt, x);
if (!res) {
Jeff Peeler
committed
p->subs[x].allocd = 1;
Jeff Peeler
committed
return res;
}
return 0;
}
static int analog_unalloc_sub(struct analog_pvt *p, enum analog_sub x)
{
p->subs[x].allocd = 0;
p->subs[x].owner = NULL;
if (analog_callbacks.unallocate_sub) {
return analog_callbacks.unallocate_sub(p->chan_pvt, x);
return 0;
}
static int analog_send_callerid(struct analog_pvt *p, int cwcid, struct ast_party_caller *caller)
ast_debug(1, "Sending callerid. CID_NAME: '%s' CID_NUM: '%s'\n",
caller->id.name.str,
caller->id.number.str);
if (cwcid) {
p->callwaitcas = 0;
}
if (analog_callbacks.send_callerid) {
return analog_callbacks.send_callerid(p->chan_pvt, cwcid, caller);
}
return 0;
#define analog_get_index(ast, p, nullok) _analog_get_index(ast, p, nullok, __PRETTY_FUNCTION__, __LINE__)
static int _analog_get_index(struct ast_channel *ast, struct analog_pvt *p, int nullok, const char *fname, unsigned long line)
{
int res;
if (p->subs[ANALOG_SUB_REAL].owner == ast) {
res = ANALOG_SUB_REAL;
} else if (p->subs[ANALOG_SUB_CALLWAIT].owner == ast) {
res = ANALOG_SUB_CALLWAIT;
} else if (p->subs[ANALOG_SUB_THREEWAY].owner == ast) {
res = ANALOG_SUB_THREEWAY;
} else {
if (!nullok) {
ast_log(LOG_WARNING,
"Unable to get index for '%s' on channel %d (%s(), line %lu)\n",
ast ? ast_channel_name(ast) : "", p->channel, fname, line);
}
return res;
}
static int analog_dsp_reset_and_flush_digits(struct analog_pvt *p)
{
if (analog_callbacks.dsp_reset_and_flush_digits) {
return analog_callbacks.dsp_reset_and_flush_digits(p->chan_pvt);
}
/* Return 0 since I think this is unnecessary to do in most cases it is used. Mostly only for ast_dsp */
return 0;
}
static int analog_play_tone(struct analog_pvt *p, enum analog_sub sub, enum analog_tone tone)
{
if (analog_callbacks.play_tone) {
return analog_callbacks.play_tone(p->chan_pvt, sub, tone);
}
return -1;
static void analog_set_new_owner(struct analog_pvt *p, struct ast_channel *new_owner)
{
p->owner = new_owner;
if (analog_callbacks.set_new_owner) {
analog_callbacks.set_new_owner(p->chan_pvt, new_owner);
static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub, const struct ast_channel *requestor)
{
struct ast_channel *c;
if (!analog_callbacks.new_ast_channel) {
return NULL;
c = analog_callbacks.new_ast_channel(p->chan_pvt, state, startpbx, sub, requestor);
ast_channel_call_forward_set(c, p->call_forward);
p->subs[sub].owner = c;
if (!p->owner) {
return c;
}
static int analog_set_echocanceller(struct analog_pvt *p, int enable)
{
if (analog_callbacks.set_echocanceller) {
return analog_callbacks.set_echocanceller(p->chan_pvt, enable);
}
return -1;
}
static int analog_train_echocanceller(struct analog_pvt *p)
{
if (analog_callbacks.train_echocanceller) {
return analog_callbacks.train_echocanceller(p->chan_pvt);
}
return -1;
}
static int analog_is_off_hook(struct analog_pvt *p)
{
if (analog_callbacks.is_off_hook) {
return analog_callbacks.is_off_hook(p->chan_pvt);
}
return -1;
}
static int analog_ring(struct analog_pvt *p)
{
if (analog_callbacks.ring) {
return analog_callbacks.ring(p->chan_pvt);
}
return -1;
static int analog_flash(struct analog_pvt *p)
{
if (analog_callbacks.flash) {
return analog_callbacks.flash(p->chan_pvt);
}
return -1;
static int analog_start(struct analog_pvt *p)
{
if (analog_callbacks.start) {
return analog_callbacks.start(p->chan_pvt);
}
return -1;
}
static int analog_dial_digits(struct analog_pvt *p, enum analog_sub sub, struct analog_dialoperation *dop)
{
if (analog_callbacks.dial_digits) {
return analog_callbacks.dial_digits(p->chan_pvt, sub, dop);
}
return -1;
}
static int analog_on_hook(struct analog_pvt *p)
{
if (analog_callbacks.on_hook) {
return analog_callbacks.on_hook(p->chan_pvt);
}
return -1;
static void analog_set_outgoing(struct analog_pvt *p, int is_outgoing)
{
p->outgoing = is_outgoing;
if (analog_callbacks.set_outgoing) {
analog_callbacks.set_outgoing(p->chan_pvt, is_outgoing);
static int analog_check_for_conference(struct analog_pvt *p)
{
if (analog_callbacks.check_for_conference) {
return analog_callbacks.check_for_conference(p->chan_pvt);
}
return -1;
}
static void analog_all_subchannels_hungup(struct analog_pvt *p)
{
if (analog_callbacks.all_subchannels_hungup) {
analog_callbacks.all_subchannels_hungup(p->chan_pvt);
}
static void analog_unlock_private(struct analog_pvt *p)
{
if (analog_callbacks.unlock_private) {
analog_callbacks.unlock_private(p->chan_pvt);
}
static void analog_lock_private(struct analog_pvt *p)
{
if (analog_callbacks.lock_private) {
analog_callbacks.lock_private(p->chan_pvt);
static void analog_deadlock_avoidance_private(struct analog_pvt *p)
{
if (analog_callbacks.deadlock_avoidance_private) {
analog_callbacks.deadlock_avoidance_private(p->chan_pvt);
} else {
/* Fallback to manual avoidance if callback not present. */
analog_unlock_private(p);
usleep(1);
analog_lock_private(p);
}
}
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
/*!
* \internal
* \brief Obtain the specified subchannel owner lock if the owner exists.
*
* \param pvt Analog private struct.
* \param sub_idx Subchannel owner to lock.
*
* \note Assumes the analog_lock_private(pvt->chan_pvt) is already obtained.
*
* \note
* Because deadlock avoidance may have been necessary, you need to confirm
* the state of things before continuing.
*
* \return Nothing
*/
static void analog_lock_sub_owner(struct analog_pvt *pvt, enum analog_sub sub_idx)
{
for (;;) {
if (!pvt->subs[sub_idx].owner) {
/* No subchannel owner pointer */
break;
}
if (!ast_channel_trylock(pvt->subs[sub_idx].owner)) {
/* Got subchannel owner lock */
break;
}
/* We must unlock the private to avoid the possibility of a deadlock */
analog_deadlock_avoidance_private(pvt);
static int analog_off_hook(struct analog_pvt *p)
{
if (analog_callbacks.off_hook) {
return analog_callbacks.off_hook(p->chan_pvt);
}
return -1;
static void analog_set_needringing(struct analog_pvt *p, int value)
{
if (analog_callbacks.set_needringing) {
analog_callbacks.set_needringing(p->chan_pvt, value);
#if 0
static void analog_set_polarity(struct analog_pvt *p, int value)
{
if (analog_callbacks.set_polarity) {
analog_callbacks.set_polarity(p->chan_pvt, value);
}
}
#endif
static void analog_start_polarityswitch(struct analog_pvt *p)
{
if (analog_callbacks.start_polarityswitch) {
analog_callbacks.start_polarityswitch(p->chan_pvt);
}
}
static void analog_answer_polarityswitch(struct analog_pvt *p)
{
if (analog_callbacks.answer_polarityswitch) {
analog_callbacks.answer_polarityswitch(p->chan_pvt);
}
}
static void analog_hangup_polarityswitch(struct analog_pvt *p)
{
if (analog_callbacks.hangup_polarityswitch) {
analog_callbacks.hangup_polarityswitch(p->chan_pvt);
}
}
static int analog_dsp_set_digitmode(struct analog_pvt *p, enum analog_dsp_digitmode mode)
{
if (analog_callbacks.dsp_set_digitmode) {
return analog_callbacks.dsp_set_digitmode(p->chan_pvt, mode);
}
return -1;
static void analog_cb_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest)
if (analog_callbacks.handle_dtmf) {
analog_callbacks.handle_dtmf(p->chan_pvt, ast, analog_index, dest);
}
static int analog_wink(struct analog_pvt *p, enum analog_sub index)
{
if (analog_callbacks.wink) {
return analog_callbacks.wink(p->chan_pvt, index);
}
return -1;
}
static int analog_has_voicemail(struct analog_pvt *p)
{
if (analog_callbacks.has_voicemail) {
return analog_callbacks.has_voicemail(p->chan_pvt);
}
return -1;
}
static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index)
{
if (analog_callbacks.is_dialing) {
return analog_callbacks.is_dialing(p->chan_pvt, index);
}
return -1;
/*!
* \internal
* \brief Attempt to transfer 3-way call.
*
* \param p Analog private structure.
*
Richard Mudgett
committed
* \note On entry these locks are held: real-call, private, 3-way call.
* \note On exit these locks are held: real-call, private.
Richard Mudgett
committed
* \retval 0 on success.
* \retval -1 on error.
Richard Mudgett
committed
static int analog_attempt_transfer(struct analog_pvt *p)
{
struct ast_channel *owner_real;
struct ast_channel *owner_3way;
Richard Mudgett
committed
enum ast_transfer_result xfer_res;
int res = 0;
Richard Mudgett
committed
owner_real = ast_channel_ref(p->subs[ANALOG_SUB_REAL].owner);
owner_3way = ast_channel_ref(p->subs[ANALOG_SUB_THREEWAY].owner);
Richard Mudgett
committed
ast_verb(3, "TRANSFERRING %s to %s\n",
ast_channel_name(owner_3way), ast_channel_name(owner_real));
ast_channel_unlock(owner_real);
ast_channel_unlock(owner_3way);
analog_unlock_private(p);
xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
res = -1;
Richard Mudgett
committed
/* Must leave with these locked. */
ast_channel_lock(owner_real);
analog_lock_private(p);
ast_channel_unref(owner_real);
ast_channel_unref(owner_3way);
return res;
}
static int analog_update_conf(struct analog_pvt *p)
{
int x;
int needconf = 0;
/* Start with the obvious, general stuff */
for (x = 0; x < 3; x++) {
/* Look for three way calls */
if ((p->subs[x].allocd) && p->subs[x].inthreeway) {
if (analog_callbacks.conf_add) {
analog_callbacks.conf_add(p->chan_pvt, x);
needconf++;
} else {
if (analog_callbacks.conf_del) {
analog_callbacks.conf_del(p->chan_pvt, x);
}
}
ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
if (analog_callbacks.complete_conference_update) {
analog_callbacks.complete_conference_update(p->chan_pvt, needconf);
return 0;
}
struct ast_channel * analog_request(struct analog_pvt *p, int *callwait, const struct ast_channel *requestor)
struct ast_channel *ast;
ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
*callwait = (p->owner != NULL);
if (p->owner) {
if (analog_alloc_sub(p, ANALOG_SUB_CALLWAIT)) {
ast_log(LOG_ERROR, "Unable to alloc subchannel\n");
return NULL;
}
}
analog_set_outgoing(p, 1);
ast = analog_new_ast_channel(p, AST_STATE_RESERVED, 0,
p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL, requestor);
if (!ast) {
analog_set_outgoing(p, 0);
int analog_available(struct analog_pvt *p)
{
int offhook;
ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
/* If do not disturb, definitely not */
if (p->dnd) {
/* If guard time, definitely not */
if (p->guardtime && (time(NULL) < p->guardtime)) {
/* If no owner definitely available */
if (!p->owner) {
offhook = analog_is_off_hook(p);
/* TDM FXO card, "onhook" means out of service (no battery on the line) */
if ((p->sig == ANALOG_SIG_FXSLS) || (p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) {
#ifdef DAHDI_CHECK_HOOKSTATE
if (offhook) {
}
return 0;
#endif
/* TDM FXS card, "offhook" means someone took the hook off so it's unavailable! */
} else if (offhook) {
ast_debug(1, "Channel %d off hook, can't use\n", p->channel);
/* Not available when the other end is off hook */
return 0;
}
return 1;
}
/* If it's not an FXO, forget about call wait */
if ((p->sig != ANALOG_SIG_FXOKS) && (p->sig != ANALOG_SIG_FXOLS) && (p->sig != ANALOG_SIG_FXOGS)) {
if (!p->callwaiting) {
/* If they don't have call waiting enabled, then for sure they're unavailable at this point */
return 0;
}
if (p->subs[ANALOG_SUB_CALLWAIT].allocd) {
/* If there is already a call waiting call, then we can't take a second one */
return 0;
}
if ((ast_channel_state(p->owner) != AST_STATE_UP) &&
((ast_channel_state(p->owner) != AST_STATE_RINGING) || p->outgoing)) {
/* If the current call is not up, then don't allow the call */
return 0;
}
if ((p->subs[ANALOG_SUB_THREEWAY].owner) && (!p->subs[ANALOG_SUB_THREEWAY].inthreeway)) {
/* Can't take a call wait when the three way calling hasn't been merged yet. */
return 0;
}
/* We're cool */
return 1;
}
static int analog_stop_callwait(struct analog_pvt *p)
{
if (analog_callbacks.stop_callwait) {
return analog_callbacks.stop_callwait(p->chan_pvt);
}
return 0;
}
static int analog_callwait(struct analog_pvt *p)
{
p->callwaitcas = p->callwaitingcallerid;
if (analog_callbacks.callwait) {
return analog_callbacks.callwait(p->chan_pvt);
}
return 0;
static void analog_set_callwaiting(struct analog_pvt *p, int callwaiting_enable)
{
p->callwaiting = callwaiting_enable;
if (analog_callbacks.set_callwaiting) {
analog_callbacks.set_callwaiting(p->chan_pvt, callwaiting_enable);
static void analog_set_cadence(struct analog_pvt *p, struct ast_channel *chan)
{
if (analog_callbacks.set_cadence) {
analog_callbacks.set_cadence(p->chan_pvt, &p->cidrings, chan);
Richard Mudgett
committed
static void analog_set_dialing(struct analog_pvt *p, int is_dialing)
Richard Mudgett
committed
p->dialing = is_dialing;
if (analog_callbacks.set_dialing) {
analog_callbacks.set_dialing(p->chan_pvt, is_dialing);
Richard Mudgett
committed
}
}
static void analog_set_alarm(struct analog_pvt *p, int in_alarm)
{
p->inalarm = in_alarm;
if (analog_callbacks.set_alarm) {
analog_callbacks.set_alarm(p->chan_pvt, in_alarm);
static void analog_set_ringtimeout(struct analog_pvt *p, int ringt)
{
p->ringt = ringt;
if (analog_callbacks.set_ringtimeout) {
analog_callbacks.set_ringtimeout(p->chan_pvt, ringt);
static void analog_set_waitingfordt(struct analog_pvt *p, struct ast_channel *ast)
{
if (analog_callbacks.set_waitingfordt) {
analog_callbacks.set_waitingfordt(p->chan_pvt, ast);
}
}
static int analog_check_waitingfordt(struct analog_pvt *p)
{
if (analog_callbacks.check_waitingfordt) {
return analog_callbacks.check_waitingfordt(p->chan_pvt);
}
return 0;
}
static void analog_set_confirmanswer(struct analog_pvt *p, int flag)
{
if (analog_callbacks.set_confirmanswer) {
analog_callbacks.set_confirmanswer(p->chan_pvt, flag);
}
}
static int analog_check_confirmanswer(struct analog_pvt *p)
{
if (analog_callbacks.check_confirmanswer) {
return analog_callbacks.check_confirmanswer(p->chan_pvt);
}
return 0;
}
static void analog_cancel_cidspill(struct analog_pvt *p)
{
if (analog_callbacks.cancel_cidspill) {
analog_callbacks.cancel_cidspill(p->chan_pvt);
static int analog_confmute(struct analog_pvt *p, int mute)
{
if (analog_callbacks.confmute) {
return analog_callbacks.confmute(p->chan_pvt, mute);
}
return 0;
}
static void analog_set_pulsedial(struct analog_pvt *p, int flag)
{
if (analog_callbacks.set_pulsedial) {
analog_callbacks.set_pulsedial(p->chan_pvt, flag);
static int analog_set_linear_mode(struct analog_pvt *p, enum analog_sub sub, int linear_mode)
if (analog_callbacks.set_linear_mode) {
/* Return provides old linear_mode setting or error indication */
return analog_callbacks.set_linear_mode(p->chan_pvt, sub, linear_mode);
}
return -1;
static void analog_set_inthreeway(struct analog_pvt *p, enum analog_sub sub, int inthreeway)
{
p->subs[sub].inthreeway = inthreeway;
if (analog_callbacks.set_inthreeway) {
analog_callbacks.set_inthreeway(p->chan_pvt, sub, inthreeway);
int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest, int timeout)
char *c, *n, *l;
char dest[256]; /* must be same length as p->dialdest */
ast_debug(1, "CALLING CID_NAME: %s CID_NUM:: %s\n",
S_COR(ast_channel_connected(ast)->id.name.valid, ast_channel_connected(ast)->id.name.str, ""),
S_COR(ast_channel_connected(ast)->id.number.valid, ast_channel_connected(ast)->id.number.str, ""));
ast_copy_string(dest, rdest, sizeof(dest));
ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest));
if ((ast_channel_state(ast) == AST_STATE_BUSY)) {
ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_BUSY);
return 0;
}
if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "analog_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
return -1;
}
p->dialednone = 0;
analog_set_outgoing(p, 1);