提交 4f4aee4f authored 作者: Stefan Knoblich's avatar Stefan Knoblich

Merge branch 'ftmod_isdn'

......@@ -2923,102 +2923,49 @@ static switch_status_t load_config(void)
if ((spans = switch_xml_child(cfg, "pri_spans"))) {
for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) {
char *id = (char *) switch_xml_attr(myspan, "id");
char *name = (char *) switch_xml_attr(myspan, "name");
ftdm_conf_parameter_t spanparameters[10];
ftdm_status_t zstatus = FTDM_FAIL;
const char *context = "default";
const char *dialplan = "XML";
//Q921NetUser_t mode = Q931_TE;
//Q931Dialect_t dialect = Q931_Dialect_National;
char *mode = NULL;
char *dialect = NULL;
uint32_t span_id = 0;
unsigned paramindex = 0;
ftdm_span_t *span = NULL;
const char *tonegroup = NULL;
char *digit_timeout = NULL;
const char *opts = "none";
uint32_t to = 0;
int q921loglevel = -1;
int q931loglevel = -1;
// quick debug
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ID: '%s', Name:'%s'\n",id,name);
uint32_t span_id = 0;
for (param = switch_xml_child(myspan, "param"); param; param = param->next) {
if (!name) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n");
continue;
}
for (param = switch_xml_child(myspan, "param"); param && paramindex < 10; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcasecmp(var, "tonegroup")) {
tonegroup = val;
} else if (!strcasecmp(var, "mode")) {
mode = val;
} else if (!strcasecmp(var, "dialect")) {
dialect = val;
} else if (!strcasecmp(var, "q921loglevel")) {
if ((q921loglevel = switch_log_str2level(val)) == SWITCH_LOG_INVALID) {
q921loglevel = -1;
}
} else if (!strcasecmp(var, "q931loglevel")) {
if ((q931loglevel = switch_log_str2level(val)) == SWITCH_LOG_INVALID) {
q931loglevel = -1;
}
} else if (!strcasecmp(var, "context")) {
if (ftdm_array_len(spanparameters) == paramindex) {
ftdm_log(FTDM_LOG_ERROR, "Too many parameters for pri span '%s', ignoring everything after '%s'\n", name, var);
break;
}
if (!strcasecmp(var, "context")) {
context = val;
} else if (!strcasecmp(var, "opts")) {
opts = val;
} else if (!strcasecmp(var, "dialplan")) {
dialplan = val;
} else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) {
digit_timeout = val;
}
}
if (!id && !name) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n");
continue;
}
if (name) {
zstatus = ftdm_span_find_by_name(name, &span);
} else {
if (switch_is_number(id)) {
span_id = atoi(id);
zstatus = ftdm_span_find(span_id, &span);
}
if (zstatus != FTDM_SUCCESS) {
zstatus = ftdm_span_find_by_name(id, &span);
} else {
spanparameters[paramindex].var = var;
spanparameters[paramindex].val = val;
paramindex++;
}
}
if (digit_timeout) {
to = atoi(digit_timeout);
}
zstatus = ftdm_span_find_by_name(name, &span);
if (zstatus != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name);
continue;
}
if (!span_id) {
span_id = ftdm_span_get_id(span);
}
if (!tonegroup) {
tonegroup = "us";
}
if (ftdm_configure_span(span, "isdn", on_clear_channel_signal,
"mode", mode,
"dialect", dialect,
"digit_timeout", &to,
"opts", opts,
"tonemap", tonegroup,
"q921loglevel", q921loglevel,
"q931loglevel", q931loglevel,
FTDM_TAG_END) != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d mode: %s dialect: %s error: %s\n", span_id,
mode, dialect, ftdm_span_get_last_error(span));
span_id = ftdm_span_get_id(span);
if (ftdm_configure_span_signaling(span, "isdn", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name);
continue;
}
......
......@@ -54,6 +54,24 @@
#define DEFAULT_NATIONAL_PREFIX "0"
#define DEFAULT_INTERNATIONAL_PREFIX "00"
static const char *ftdm_span_get_trunk_type_str(const ftdm_span_t *span)
{
assert(span);
return ftdm_trunk_type2str(span->trunk_type);
}
static int ftdm_channel_get_state(const ftdm_channel_t *chan)
{
assert(chan);
return chan->state;
}
static int ftdm_channel_get_last_state(const ftdm_channel_t *chan)
{
assert(chan);
return chan->last_state;
}
/*****************************************************************************************
* PCAP
* Based on Helmut Kuper's (<helmut.kuper@ewetel.de>) implementation,
......@@ -90,13 +108,8 @@ static ftdm_status_t isdn_pcap_open(struct ftdm_isdn_data *isdn, char *filename)
{
struct pcap_context *pcap = NULL;
if (!isdn) {
return FTDM_FAIL;
}
if (ftdm_strlen_zero(filename)) {
if (!isdn || ftdm_strlen_zero(filename))
return FTDM_FAIL;
}
pcap = malloc(sizeof(struct pcap_context));
if (!pcap) {
......@@ -116,23 +129,20 @@ static ftdm_status_t isdn_pcap_open(struct ftdm_isdn_data *isdn, char *filename)
pcap->dump = pcap_dump_open(pcap->handle, pcap->filename);
if (!pcap->dump) {
ftdm_log(FTDM_LOG_ERROR, "Failed to open capture file: %s\n", pcap_geterr(pcap->handle));
ftdm_log(FTDM_LOG_ERROR, "Failed to open capture file: '%s'\n", pcap_geterr(pcap->handle));
goto error;
}
ftdm_log(FTDM_LOG_INFO, "Capture file \"%s\" opened\n", pcap->filename);
ftdm_log(FTDM_LOG_INFO, "Capture file '%s' opened\n", pcap->filename);
isdn->pcap = pcap;
return FTDM_SUCCESS;
error:
if (pcap->handle) {
if (pcap->handle)
pcap_close(pcap->handle);
}
if (pcap->filename) {
if (pcap->filename)
free(pcap->filename);
}
free(pcap);
......@@ -144,9 +154,9 @@ static ftdm_status_t isdn_pcap_close(struct ftdm_isdn_data *isdn)
struct pcap_context *pcap = NULL;
long size;
if (!isdn || !isdn->pcap) {
if (!isdn || !isdn->pcap)
return FTDM_FAIL;
}
pcap = isdn->pcap;
isdn->flags &= ~(FTDM_ISDN_CAPTURE | FTDM_ISDN_CAPTURE_L3ONLY);
......@@ -155,7 +165,7 @@ static ftdm_status_t isdn_pcap_close(struct ftdm_isdn_data *isdn)
pcap_dump_flush(pcap->dump);
size = pcap_dump_ftell(pcap->dump);
ftdm_log(FTDM_LOG_INFO, "File \"%s\" captured %ld bytes of data\n", pcap->filename, size);
ftdm_log(FTDM_LOG_INFO, "File '%s' captured %ld bytes of data\n", pcap->filename, size);
pcap_dump_close(pcap->dump);
pcap_close(pcap->handle);
......@@ -209,9 +219,8 @@ static ftdm_status_t isdn_pcap_write(struct ftdm_isdn_data *isdn, unsigned char
int offset = sizeof(struct isdn_sll_hdr);
int nbytes;
if (!isdn || !isdn->pcap || !buf || !len) {
if (!isdn || !isdn->pcap || !buf || !len)
return FTDM_FAIL;
}
pcap = isdn->pcap;
......@@ -431,7 +440,7 @@ static FIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request)
isdn_data->outbound_crv[gen->CRV] = caller_data;
//isdn_data->channels_local_crv[gen->CRV] = ftdmchan;
while(ftdm_running() && caller_data->call_state == FTDM_CALLER_STATE_DIALING) {
while (ftdm_running() && caller_data->call_state == FTDM_CALLER_STATE_DIALING) {
ftdm_sleep(1);
if (!--sanity) {
......@@ -446,8 +455,8 @@ static FIO_CHANNEL_REQUEST_FUNCTION(isdn_channel_request)
int fail = 1;
new_chan = NULL;
if (caller_data->chan_id < FTDM_MAX_CHANNELS_SPAN && caller_data->chan_id <= span->chan_count) {
new_chan = span->channels[caller_data->chan_id];
if (caller_data->chan_id > 0 && caller_data->chan_id <= ftdm_span_get_chan_count(span)) {
new_chan = ftdm_span_get_channel(span, caller_data->chan_id);
}
if (new_chan && (status = ftdm_channel_open_chan(new_chan) == FTDM_SUCCESS)) {
......@@ -565,10 +574,8 @@ static void ftdm_isdn_call_event(struct Q931_Call *call, struct Q931_CallEvent *
/*
* Global event
*/
ftdm_log(FTDM_LOG_DEBUG, "Received global event from Q.931\n");
}
else {
} else {
ftdm_channel_t *ftdmchan = NULL;
ftdm_sigmsg_t sig;
int call_crv = Q931CallGetCRV(call);
......@@ -711,9 +718,8 @@ static void __isdn_get_number(const char *digits, const int ton, char *buf, int
{
int offset = 0;
if (!digits || !buf || size <= 0) {
if (!digits || !buf || size <= 0)
return;
}
switch (ton) {
case Q931_TON_NATIONAL:
......@@ -742,24 +748,23 @@ static void __isdn_get_number(const char *digits, const int ton, char *buf, int
*/
static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic *msg, int mlen)
{
Q931mes_Generic *gen = (Q931mes_Generic *) msg;
ftdm_span_t *span = (ftdm_span_t *) pvt;
ftdm_isdn_data_t *isdn_data = span->signal_data;
Q931mes_Generic *gen = (Q931mes_Generic *) msg;
ftdm_channel_t *ftdmchan = NULL;
int chan_id = 0;
int chan_hunt = 0;
ftdm_channel_t *ftdmchan = NULL;
// ftdm_caller_data_t *caller_data = NULL;
if (Q931IsIEPresent(gen->ChanID)) {
Q931ie_ChanID *chanid = Q931GetIEPtr(gen->ChanID, gen->buf);
if(chanid->IntType)
if (chanid->IntType)
chan_id = chanid->ChanSlot;
else
chan_id = chanid->InfoChanSel;
/* "any" channel specified */
if(chanid->InfoChanSel == 3) {
if (chanid->InfoChanSel == 3) {
chan_hunt++;
}
} else if (FTDM_SPAN_IS_NT(span)) {
......@@ -805,9 +810,7 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
case Q931mes_ALERTING:
case Q931mes_PROGRESS:
case Q931mes_CONNECT:
{
caller_data->call_state = FTDM_CALLER_STATE_SUCCESS;
}
caller_data->call_state = FTDM_CALLER_STATE_SUCCESS;
break;
default:
caller_data->call_state = FTDM_CALLER_STATE_FAIL;
......@@ -824,8 +827,12 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
ftdmchan = isdn_data->channels_remote_crv[gen->CRV];
}
ftdm_log(FTDM_LOG_DEBUG, "ftdmchan %x (%d:%d) source isdn_data->channels_%s_crv[%#hx]\n", ftdmchan, ftdmchan ? ftdmchan->span_id : -1, ftdmchan ? ftdmchan->chan_id : -1, gen->CRVFlag ? "local" : "remote", gen->CRV);
ftdm_log(FTDM_LOG_DEBUG, "ftdmchan %x (%d:%d) source isdn_data->channels_%s_crv[%#hx]\n",
ftdmchan,
((ftdmchan) ? ftdm_channel_get_span_id(ftdmchan) : -1),
((ftdmchan) ? ftdm_channel_get_id(ftdmchan) : -1),
((gen->CRVFlag) ? "local" : "remote"),
gen->CRV);
if (gen->ProtDisc == 3) {
switch(gen->MesType) {
......@@ -837,7 +844,9 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
case 0: /* change status to "in service" */
{
ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_SUSPENDED);
ftdm_log(FTDM_LOG_DEBUG, "Channel %d:%d in service\n", ftdmchan->span_id, ftdmchan->chan_id);
ftdm_log(FTDM_LOG_DEBUG, "Channel %d:%d in service\n",
ftdm_channel_get_span_id(ftdmchan),
ftdm_channel_get_id(ftdmchan));
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART);
}
break;
......@@ -869,19 +878,20 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
case Q931mes_RESTART:
{
if (chan_id) {
ftdmchan = span->channels[chan_id];
ftdmchan = ftdm_span_get_channel(span, chan_id);
}
if (ftdmchan) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART);
} else {
uint32_t i;
for (i = 1; i < span->chan_count; i++) {
for (i = 1; i < ftdm_span_get_chan_count(span); i++) {
/* Skip channels that are down and D-Channels (#OpenZAP-39) */
if (span->channels[i]->state == FTDM_CHANNEL_STATE_DOWN || span->channels[i]->type == FTDM_CHAN_TYPE_DQ921)
if (ftdm_channel_get_state(span->channels[i]) == FTDM_CHANNEL_STATE_DOWN ||
ftdm_channel_get_type(span->channels[i]) == FTDM_CHAN_TYPE_DQ921)
continue;
ftdm_set_state_locked((span->channels[i]), FTDM_CHANNEL_STATE_RESTART);
ftdm_set_state_locked(span->channels[i], FTDM_CHANNEL_STATE_RESTART);
}
}
}
......@@ -891,15 +901,17 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
{
const char *what = gen->MesType == Q931mes_RELEASE ? "Release" : "Release Complete";
if (ftdmchan) {
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING || ftdmchan->state == FTDM_CHANNEL_STATE_HANGUP) {
if (ftdm_channel_get_state(ftdmchan) == FTDM_CHANNEL_STATE_TERMINATING ||
ftdm_channel_get_state(ftdmchan) == FTDM_CHANNEL_STATE_HANGUP)
{
if (gen->MesType == Q931mes_RELEASE) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
} else {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
}
else if((gen->MesType == Q931mes_RELEASE && ftdmchan->state <= FTDM_CHANNEL_STATE_UP) ||
(gen->MesType == Q931mes_RELEASE_COMPLETE && ftdmchan->state == FTDM_CHANNEL_STATE_DIALING)) {
else if ((gen->MesType == Q931mes_RELEASE && ftdm_channel_get_state(ftdmchan) <= FTDM_CHANNEL_STATE_UP) ||
(gen->MesType == Q931mes_RELEASE_COMPLETE && ftdm_channel_get_state(ftdmchan) == FTDM_CHANNEL_STATE_DIALING)) {
/*
* Don't keep inbound channels open if the remote side hangs up before we answered
......@@ -909,18 +921,20 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
ftdm_status_t status;
memset(&sig, 0, sizeof(sig));
sig.chan_id = ftdmchan->chan_id;
sig.span_id = ftdmchan->span_id;
sig.span_id = ftdm_channel_get_span_id(ftdmchan);
sig.chan_id = ftdm_channel_get_id(ftdmchan);
sig.channel = ftdmchan;
sig.channel->caller_data.hangup_cause = (cause) ? cause->Value : FTDM_CAUSE_NORMAL_UNSPECIFIED;
sig.event_id = FTDM_SIGEVENT_STOP;
status = isdn_data->sig_cb(&sig);
ftdm_log(FTDM_LOG_DEBUG, "Received %s in state %s, requested hangup for channel %d:%d\n", what, ftdm_channel_state2str(ftdmchan->state), ftdmchan->span_id, chan_id);
}
else {
ftdm_log(FTDM_LOG_DEBUG, "Ignoring %s on channel %d\n", what, chan_id);
ftdm_log(FTDM_LOG_DEBUG, "Received %s in state %s, requested hangup for channel %d:%d\n", what,
ftdm_channel_get_state_str(ftdmchan),
ftdm_channel_get_span_id(ftdmchan),
ftdm_channel_get_id(ftdmchan));
} else {
ftdm_log(FTDM_LOG_DEBUG, "Ignoring %s on channel %d\n", what, ftdm_channel_get_id(ftdmchan));
}
} else {
ftdm_log(FTDM_LOG_CRIT, "Received %s with no matching channel %d\n", what, chan_id);
......@@ -977,18 +991,18 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
{
Q931ie_CallingNum *callingnum = Q931GetIEPtr(gen->CallingNum, gen->buf);
Q931ie_CalledNum *callednum = Q931GetIEPtr(gen->CalledNum, gen->buf);
int fail = 1;
int fail_cause = 0;
int overlap_dial = 0;
uint32_t cplen = mlen;
int overlap_dial = 0;
int fail_cause = 0;
int fail = 1;
if(ftdmchan && ftdmchan == isdn_data->channels_remote_crv[gen->CRV]) {
if (ftdmchan && ftdmchan == isdn_data->channels_remote_crv[gen->CRV]) {
ftdm_log(FTDM_LOG_INFO, "Duplicate SETUP message(?) for Channel %d:%d ~ %d:%d in state %s [ignoring]\n",
ftdmchan->span_id,
ftdmchan->chan_id,
ftdmchan->physical_span_id,
ftdmchan->physical_chan_id,
ftdm_channel_state2str(ftdmchan->state));
ftdm_channel_get_span_id(ftdmchan),
ftdm_channel_get_id(ftdmchan),
ftdm_channel_get_ph_span_id(ftdmchan),
ftdm_channel_get_ph_id(ftdmchan),
ftdm_channel_get_state_str(ftdmchan));
break;
}
......@@ -1003,10 +1017,10 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
* In NT-mode with channel selection "any",
* try to find a free channel
*/
for (x = 1; x <= span->chan_count; x++) {
ftdm_channel_t *zc = span->channels[x];
for (x = 1; x <= ftdm_span_get_chan_count(span); x++) {
ftdm_channel_t *zc = ftdm_span_get_channel(span, x);
if (!ftdm_test_flag(zc, FTDM_CHANNEL_INUSE) && zc->state == FTDM_CHANNEL_STATE_DOWN) {
if (!ftdm_test_flag(zc, FTDM_CHANNEL_INUSE) && ftdm_channel_get_state(zc) == FTDM_CHANNEL_STATE_DOWN) {
ftdmchan = zc;
break;
}
......@@ -1027,8 +1041,8 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
* TODO: NT mode is abled to select a different channel if the one chosen
* by the TE side is already in use
*/
if (chan_id > 0 && chan_id < FTDM_MAX_CHANNELS_SPAN && chan_id <= span->chan_count) {
ftdmchan = span->channels[chan_id];
if (chan_id > 0 && chan_id < FTDM_MAX_CHANNELS_SPAN && chan_id <= ftdm_span_get_chan_count(span)) {
ftdmchan = ftdm_span_get_channel(span, chan_id);
}
else {
/* invalid channel id */
......@@ -1042,22 +1056,22 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
if (FTDM_SPAN_IS_NT(span)) {
ftdm_log(FTDM_LOG_NOTICE, "No destination number found, assuming overlap dial\n");
overlap_dial++;
}
else {
} else {
ftdm_log(FTDM_LOG_ERROR, "No destination number found\n");
ftdmchan = NULL;
}
}
if (ftdmchan) {
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE) || ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) {
if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN || ftdmchan->state >= FTDM_CHANNEL_STATE_TERMINATING) {
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE) || ftdm_channel_get_state(ftdmchan) != FTDM_CHANNEL_STATE_DOWN) {
if (ftdm_channel_get_state(ftdmchan) == FTDM_CHANNEL_STATE_DOWN || ftdm_channel_get_state(ftdmchan) >= FTDM_CHANNEL_STATE_TERMINATING)
{
int x = 0;
ftdm_log(FTDM_LOG_WARNING, "Channel %d:%d ~ %d:%d is already in use waiting for it to become available.\n",
ftdmchan->span_id,
ftdmchan->chan_id,
ftdmchan->physical_span_id,
ftdmchan->physical_chan_id);
ftdm_channel_get_span_id(ftdmchan),
ftdm_channel_get_id(ftdmchan),
ftdm_channel_get_ph_span_id(ftdmchan),
ftdm_channel_get_ph_id(ftdmchan));
for (x = 0; x < 200; x++) {
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) {
......@@ -1068,16 +1082,17 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
}
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) {
ftdm_log(FTDM_LOG_ERROR, "Channel %d:%d ~ %d:%d is already in use.\n",
ftdmchan->span_id,
ftdmchan->chan_id,
ftdmchan->physical_span_id,
ftdmchan->physical_chan_id
);
ftdm_channel_get_span_id(ftdmchan),
ftdm_channel_get_id(ftdmchan),
ftdm_channel_get_ph_span_id(ftdmchan),
ftdm_channel_get_ph_id(ftdmchan));
ftdmchan = NULL;
}
}
if (ftdmchan && ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) {
if (ftdmchan && ftdm_channel_get_state(ftdmchan) == FTDM_CHANNEL_STATE_DOWN) {
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(ftdmchan);
isdn_data->channels_remote_crv[gen->CRV] = ftdmchan;
memset(&ftdmchan->caller_data, 0, sizeof(ftdmchan->caller_data));
......@@ -1086,34 +1101,32 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
}
/* copy number readd prefix as needed */
isdn_get_number(callingnum, ftdmchan->caller_data.cid_num.digits);
isdn_get_number(callingnum, ftdmchan->caller_data.cid_name);
isdn_get_number(callingnum, ftdmchan->caller_data.ani.digits);
isdn_get_number(callingnum, caller_data->cid_num.digits);
isdn_get_number(callingnum, caller_data->cid_name);
isdn_get_number(callingnum, caller_data->ani.digits);
// ftdm_set_string(ftdmchan->caller_data.cid_num.digits, (char *)callingnum->Digit);
// ftdm_set_string(ftdmchan->caller_data.cid_name, (char *)callingnum->Digit);
// ftdm_set_string(ftdmchan->caller_data.ani.digits, (char *)callingnum->Digit);
if (!overlap_dial) {
// ftdm_set_string(ftdmchan->caller_data.dnis.digits, (char *)callednum->Digit);
isdn_get_number(callednum, ftdmchan->caller_data.dnis.digits);
isdn_get_number(callednum, caller_data->dnis.digits);
}
#ifdef __TODO_OR_REMOVE__
ftdmchan->caller_data.CRV = gen->CRV;
#endif
if (cplen > sizeof(ftdmchan->caller_data.raw_data)) {
cplen = sizeof(ftdmchan->caller_data.raw_data);
if (cplen > sizeof(caller_data->raw_data)) {
cplen = sizeof(caller_data->raw_data);
}
gen->CRVFlag = !(gen->CRVFlag);
memcpy(ftdmchan->caller_data.raw_data, msg, cplen);
ftdmchan->caller_data.raw_data_len = cplen;
memcpy(caller_data->raw_data, msg, cplen);
caller_data->raw_data_len = cplen;
fail = 0;
}
}
if (fail) {
Q931ie_Cause cause;
gen->MesType = Q931mes_DISCONNECT;
gen->CRVFlag = 1; /* inbound call */
cause.IEId = Q931ie_CAUSE;
cause.Size = sizeof(Q931ie_Cause);
cause.CodStand = Q931_CODING_ITU;
......@@ -1144,12 +1157,12 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
Q931InitIEChanID(&ChanID);
ChanID.IntType = FTDM_SPAN_IS_BRI(ftdmchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */
ChanID.PrefExcl = FTDM_SPAN_IS_NT(ftdmchan->span) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */
if(ChanID.IntType) {
if (ChanID.IntType) {
ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */
ChanID.ChanMapType = 3; /* B-Chan */
ChanID.ChanSlot = (unsigned char)ftdmchan->chan_id;
ChanID.ChanSlot = (unsigned char)ftdm_channel_get_id(ftdmchan);
} else {
ChanID.InfoChanSel = (unsigned char)ftdmchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */
ChanID.InfoChanSel = (unsigned char)ftdm_channel_get_id(ftdmchan) & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */
}
gen->ChanID = Q931AppendIE(gen, (L3UCHAR *) &ChanID);
......@@ -1187,7 +1200,7 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
case Q931mes_CALL_PROCEEDING:
{
if (ftdmchan) {
ftdm_log(FTDM_LOG_CRIT, "Received CALL PROCEEDING message for channel %d\n", chan_id);
ftdm_log(FTDM_LOG_CRIT, "Received CALL PROCEEDING message for channel %d\n", ftdm_channel_get_id(ftdmchan));
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS);
} else {
ftdm_log(FTDM_LOG_CRIT, "Received CALL PROCEEDING with no matching channel %d\n", chan_id);
......@@ -1197,7 +1210,7 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
case Q931mes_CONNECT_ACKNOWLEDGE:
{
if (ftdmchan) {
ftdm_log(FTDM_LOG_DEBUG, "Received CONNECT_ACK message for channel %d\n", chan_id);
ftdm_log(FTDM_LOG_DEBUG, "Received CONNECT_ACK message for channel %d\n", ftdm_channel_get_id(ftdmchan));
} else {
ftdm_log(FTDM_LOG_DEBUG, "Received CONNECT_ACK with no matching channel %d\n", chan_id);
}
......@@ -1207,9 +1220,9 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
case Q931mes_INFORMATION:
{
if (ftdmchan) {
ftdm_log(FTDM_LOG_CRIT, "Received INFORMATION message for channel %d\n", ftdmchan->chan_id);
ftdm_log(FTDM_LOG_CRIT, "Received INFORMATION message for channel %d\n", ftdm_channel_get_id(ftdmchan));
if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALTONE) {
if (ftdm_channel_get_state(ftdmchan) == FTDM_CHANNEL_STATE_DIALTONE) {
char digit = '\0';
/*
......@@ -1227,7 +1240,7 @@ static L3INT ftdm_isdn_931_34(void *pvt, struct Q931_Call *call, Q931mes_Generic
/* TODO: make this more safe with strncat() */
pos = strlen(ftdmchan->caller_data.dnis.digits);
strcat(&ftdmchan->caller_data.dnis.digits[pos], (char *)callednum->Digit);
strcat(&ftdmchan->caller_data.dnis.digits[pos], (char *)callednum->Digit);
/* update timer */
data->digit_timeout = ftdm_time_now() + isdn_data->digit_timeout;
......@@ -1307,14 +1320,16 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
ftdm_status_t status;
ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n",
ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
ftdm_channel_get_span_id(ftdmchan),
ftdm_channel_get_id(ftdmchan),
ftdm_channel_get_state_str(ftdmchan));
memset(&sig, 0, sizeof(sig));
sig.chan_id = ftdmchan->chan_id;
sig.span_id = ftdmchan->span_id;
sig.span_id = ftdm_channel_get_span_id(ftdmchan);
sig.chan_id = ftdm_channel_get_id(ftdmchan);
sig.channel = ftdmchan;
switch (ftdmchan->state) {
switch (ftdm_channel_get_state(ftdmchan)) {
case FTDM_CHANNEL_STATE_DOWN:
{
if (gen->CRV) {
......@@ -1343,22 +1358,22 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
gen->CRV = crv;
gen->CRVFlag = 1; /* inbound */
if (FTDM_SPAN_IS_NT(ftdmchan->span)) {
if (FTDM_SPAN_IS_NT(ftdm_channel_get_span(ftdmchan))) {
Q931ie_ChanID ChanID;
/*
* Set new Channel ID
*/
Q931InitIEChanID(&ChanID);
ChanID.IntType = FTDM_SPAN_IS_BRI(ftdmchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */
ChanID.IntType = FTDM_SPAN_IS_BRI(ftdm_channel_get_span(ftdmchan)) ? 0 : 1; /* PRI = 1, BRI = 0 */
ChanID.PrefExcl = 1; /* always exclusive in NT-mode */
if(ChanID.IntType) {
if (ChanID.IntType) {
ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */
ChanID.ChanMapType = 3; /* B-Chan */
ChanID.ChanSlot = (unsigned char)ftdmchan->chan_id;
ChanID.ChanSlot = (unsigned char)ftdm_channel_get_id(ftdmchan);
} else {
ChanID.InfoChanSel = (unsigned char)ftdmchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */
ChanID.InfoChanSel = (unsigned char)ftdm_channel_get_id(ftdmchan) & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */
}
gen->ChanID = Q931AppendIE(gen, (L3UCHAR *) &ChanID);
}
......@@ -1437,6 +1452,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
break;
case FTDM_CHANNEL_STATE_DIALING:
if (!(isdn_data->opts & FTDM_ISDN_OPT_SUGGEST_CHANNEL)) {
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(ftdmchan);
Q931ie_BearerCap BearerCap;
Q931ie_ChanID ChanID;
Q931ie_CallingNum CallingNum;
......@@ -1451,7 +1467,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
/*
* get codec type
*/
ftdm_channel_command(ftdmchan->span->channels[ftdmchan->chan_id], FTDM_COMMAND_GET_NATIVE_CODEC, &codec);
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_NATIVE_CODEC, &codec);
/*
* Q.931 Setup Message
......@@ -1476,14 +1492,14 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
* ChannelID IE
*/
Q931InitIEChanID(&ChanID);
ChanID.IntType = FTDM_SPAN_IS_BRI(ftdmchan->span) ? 0 : 1; /* PRI = 1, BRI = 0 */
ChanID.PrefExcl = FTDM_SPAN_IS_NT(ftdmchan->span) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */
if(ChanID.IntType) {
ChanID.IntType = FTDM_SPAN_IS_BRI(ftdm_channel_get_span(ftdmchan)) ? 0 : 1; /* PRI = 1, BRI = 0 */
ChanID.PrefExcl = FTDM_SPAN_IS_NT(ftdm_channel_get_span(ftdmchan)) ? 1 : 0; /* Exclusive in NT-mode = 1, Preferred otherwise = 0 */
if (ChanID.IntType) {
ChanID.InfoChanSel = 1; /* None = 0, See Slot = 1, Any = 3 */
ChanID.ChanMapType = 3; /* B-Chan */
ChanID.ChanSlot = (unsigned char)ftdmchan->chan_id;
ChanID.ChanSlot = (unsigned char)ftdm_channel_get_id(ftdmchan);
} else {
ChanID.InfoChanSel = (unsigned char)ftdmchan->chan_id & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */
ChanID.InfoChanSel = (unsigned char)ftdm_channel_get_id(ftdmchan) & 0x03; /* None = 0, B1 = 1, B2 = 2, Any = 3 */
}
gen->ChanID = Q931AppendIE(gen, (L3UCHAR *) &ChanID);
......@@ -1499,26 +1515,26 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
/*
* Display IE
*/
if (!(isdn_data->opts & FTDM_ISDN_OPT_OMIT_DISPLAY_IE) && FTDM_SPAN_IS_NT(ftdmchan->span)) {
if (!(isdn_data->opts & FTDM_ISDN_OPT_OMIT_DISPLAY_IE) && FTDM_SPAN_IS_NT(ftdm_channel_get_span(ftdmchan))) {
Q931InitIEDisplay(&Display);
Display.Size = Display.Size + (unsigned char)strlen(ftdmchan->caller_data.cid_name);
Display.Size = Display.Size + (unsigned char)strlen(caller_data->cid_name);
gen->Display = Q931AppendIE(gen, (L3UCHAR *) &Display);
ptrDisplay = Q931GetIEPtr(gen->Display, gen->buf);
ftdm_copy_string((char *)ptrDisplay->Display, ftdmchan->caller_data.cid_name, strlen(ftdmchan->caller_data.cid_name)+1);
ftdm_copy_string((char *)ptrDisplay->Display, caller_data->cid_name, strlen(caller_data->cid_name) + 1);
}
/*
* CallingNum IE
*/
Q931InitIECallingNum(&CallingNum);
CallingNum.TypNum = ftdmchan->caller_data.ani.type;
CallingNum.TypNum = caller_data->ani.type;
CallingNum.NumPlanID = Q931_NUMPLAN_E164;
CallingNum.PresInd = Q931_PRES_ALLOWED;
CallingNum.ScreenInd = Q931_SCREEN_USER_NOT_SCREENED;
CallingNum.Size = CallingNum.Size + (unsigned char)strlen(ftdmchan->caller_data.cid_num.digits);
CallingNum.Size = CallingNum.Size + (unsigned char)strlen(caller_data->cid_num.digits);
gen->CallingNum = Q931AppendIE(gen, (L3UCHAR *) &CallingNum);
ptrCallingNum = Q931GetIEPtr(gen->CallingNum, gen->buf);
ftdm_copy_string((char *)ptrCallingNum->Digit, ftdmchan->caller_data.cid_num.digits, strlen(ftdmchan->caller_data.cid_num.digits)+1);
ftdm_copy_string((char *)ptrCallingNum->Digit, caller_data->cid_num.digits, strlen(caller_data->cid_num.digits) + 1);
/*
* CalledNum IE
......@@ -1526,10 +1542,10 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
Q931InitIECalledNum(&CalledNum);
CalledNum.TypNum = Q931_TON_UNKNOWN;
CalledNum.NumPlanID = Q931_NUMPLAN_E164;
CalledNum.Size = CalledNum.Size + (unsigned char)strlen(ftdmchan->caller_data.ani.digits);
CalledNum.Size = CalledNum.Size + (unsigned char)strlen(caller_data->ani.digits);
gen->CalledNum = Q931AppendIE(gen, (L3UCHAR *) &CalledNum);
ptrCalledNum = Q931GetIEPtr(gen->CalledNum, gen->buf);
ftdm_copy_string((char *)ptrCalledNum->Digit, ftdmchan->caller_data.ani.digits, strlen(ftdmchan->caller_data.ani.digits)+1);
ftdm_copy_string((char *)ptrCalledNum->Digit, caller_data->ani.digits, strlen(caller_data->ani.digits) + 1);
/*
* High-Layer Compatibility IE (Note: Required for AVM FritzBox)
......@@ -1554,7 +1570,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
call = Q931GetCallByCRV(&isdn_data->q931, gen->CRV);
if (call) {
ftdm_log(FTDM_LOG_DEBUG, "Storing reference to current span in call %d [0x%x]\n", gen->CRV, gen->CRV);
Q931CallSetPrivate(call, ftdmchan->span);
Q931CallSetPrivate(call, ftdm_channel_get_span(ftdmchan));
}
}
}
......@@ -1562,7 +1578,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
{
/* reply RELEASE with RELEASE_COMPLETE message */
if(ftdmchan->last_state == FTDM_CHANNEL_STATE_HANGUP) {
if (ftdm_channel_get_last_state(ftdmchan) == FTDM_CHANNEL_STATE_HANGUP) {
gen->MesType = Q931mes_RELEASE_COMPLETE;
Q931Rx43(&isdn_data->q931, gen, gen->Size);
......@@ -1572,6 +1588,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
break;
case FTDM_CHANNEL_STATE_HANGUP:
{
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(ftdmchan);
Q931ie_Cause cause;
ftdm_log(FTDM_LOG_DEBUG, "Hangup: Direction %s\n", ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? "Outbound" : "Inbound");
......@@ -1588,7 +1605,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
* BRI PTMP needs special handling here...
* TODO: cleanup / refine (see above)
*/
if (ftdmchan->last_state == FTDM_CHANNEL_STATE_RING) {
if (ftdm_channel_get_last_state(ftdmchan) == FTDM_CHANNEL_STATE_RING) {
/*
* inbound call [was: number unknown (= not found in routing state)]
* (in Q.931 spec terms: Reject request)
......@@ -1596,7 +1613,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
gen->MesType = Q931mes_RELEASE_COMPLETE;
//cause.Value = (unsigned char) FTDM_CAUSE_UNALLOCATED;
cause.Value = (unsigned char) ftdmchan->caller_data.hangup_cause;
cause.Value = (unsigned char) caller_data->hangup_cause;
*cause.Diag = '\0';
gen->Cause = Q931AppendIE(gen, (L3UCHAR *) &cause);
Q931Rx43(&isdn_data->q931, gen, gen->Size);
......@@ -1605,13 +1622,13 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
//ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
else if (ftdmchan->last_state <= FTDM_CHANNEL_STATE_PROGRESS) {
else if (ftdm_channel_get_last_state(ftdmchan) <= FTDM_CHANNEL_STATE_PROGRESS) {
/*
* just release all unanswered calls [was: inbound call, remote side hung up before we answered]
*/
gen->MesType = Q931mes_RELEASE;
cause.Value = (unsigned char) ftdmchan->caller_data.hangup_cause;
cause.Value = (unsigned char) caller_data->hangup_cause;
*cause.Diag = '\0';
gen->Cause = Q931AppendIE(gen, (L3UCHAR *) &cause);
Q931Rx43(&isdn_data->q931, gen, gen->Size);
......@@ -1625,7 +1642,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
*/
gen->MesType = Q931mes_DISCONNECT;
cause.Value = (unsigned char) ftdmchan->caller_data.hangup_cause;
cause.Value = (unsigned char) caller_data->hangup_cause;
*cause.Diag = '\0';
gen->Cause = Q931AppendIE(gen, (L3UCHAR *) &cause);
Q931Rx43(&isdn_data->q931, gen, gen->Size);
......@@ -1638,6 +1655,7 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
sig.event_id = FTDM_SIGEVENT_STOP;
status = isdn_data->sig_cb(&sig);
gen->MesType = Q931mes_RELEASE;
gen->CRVFlag = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? 0 : 1;
Q931Rx43(&isdn_data->q931, gen, gen->Size);
......@@ -1649,19 +1667,25 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
static __inline__ void check_state(ftdm_span_t *span)
{
if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
uint32_t j;
ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
for(j = 1; j <= span->chan_count; j++) {
if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_mutex_lock(span->channels[j]->mutex);
ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE);
state_advance(span->channels[j]);
ftdm_channel_complete_state(span->channels[j]);
ftdm_mutex_unlock(span->channels[j]->mutex);
}
}
}
if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
uint32_t j;
ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
for (j = 1; j <= ftdm_span_get_chan_count(span); j++) {
ftdm_channel_t *chan = ftdm_span_get_channel(span, j);
if (ftdm_test_flag(chan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_channel_lock(chan);
ftdm_clear_flag(chan, FTDM_CHANNEL_STATE_CHANGE);
state_advance(chan);
ftdm_channel_complete_state(chan);
ftdm_channel_unlock(chan);
}
}
}
}
......@@ -1672,27 +1696,33 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
ftdm_sigmsg_t sig;
memset(&sig, 0, sizeof(sig));
sig.chan_id = event->channel->chan_id;
sig.span_id = event->channel->span_id;
sig.span_id = ftdm_channel_get_span_id(event->channel);
sig.chan_id = ftdm_channel_get_id(event->channel);
sig.channel = event->channel;
ftdm_log(FTDM_LOG_DEBUG, "EVENT [%s][%d:%d] STATE [%s]\n",
ftdm_oob_event2str(event->enum_id), event->channel->span_id, event->channel->chan_id, ftdm_channel_state2str(event->channel->state));
ftdm_oob_event2str(event->enum_id),
ftdm_channel_get_span_id(event->channel),
ftdm_channel_get_id(event->channel),
ftdm_channel_get_state_str(event->channel));
switch(event->enum_id) {
switch (event->enum_id) {
case FTDM_OOB_ALARM_TRAP:
{
sig.event_id = FTDM_OOB_ALARM_TRAP;
if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) {
if (ftdm_channel_get_state(event->channel) != FTDM_CHANNEL_STATE_DOWN) {
ftdm_set_state_locked(event->channel, FTDM_CHANNEL_STATE_RESTART);
}
ftdm_set_flag(event->channel, FTDM_CHANNEL_SUSPENDED);
ftdm_channel_get_alarms(event->channel, &alarmbits);
isdn_data->sig_cb(&sig);
ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) has alarms [%s]\n",
event->channel->span_id, event->channel->chan_id,
event->channel->physical_span_id, event->channel->physical_chan_id,
event->channel->last_error);
ftdm_channel_get_span_id(event->channel),
ftdm_channel_get_id(event->channel),
ftdm_channel_get_ph_span_id(event->channel),
ftdm_channel_get_ph_id(event->channel),
ftdm_channel_get_last_error(event->channel));
}
break;
case FTDM_OOB_ALARM_CLEAR:
......@@ -1708,7 +1738,7 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
{
const char * digit_str = (const char *)event->data;
if(digit_str) {
if (digit_str) {
fio_event_cb_t event_callback = NULL;
ftdm_channel_queue_dtmf(event->channel, digit_str);
......@@ -1739,14 +1769,13 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
static __inline__ void check_events(ftdm_span_t *span)
{
ftdm_status_t status;
ftdm_status_t status = ftdm_span_poll_event(span, 5, NULL);
status = ftdm_span_poll_event(span, 5, NULL);
switch(status) {
switch (status) {
case FTDM_SUCCESS:
{
ftdm_event_t *event;
while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) {
if (event->enum_id == FTDM_OOB_NOOP) {
continue;
......@@ -1776,6 +1805,7 @@ static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map
if (!dt_buffer) {
return -1;
}
wrote = teletone_mux_tones(ts, map);
ftdm_buffer_write(dt_buffer, ts->buffer, wrote * 2);
return 0;
......@@ -1802,9 +1832,11 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
ftdm_buffer_set_loops(dt_buffer, -1);
/* get a tone generation friendly interval to avoid distortions */
for (x = 1; x <= span->chan_count; x++) {
if (span->channels[x]->type != FTDM_CHAN_TYPE_DQ921) {
ftdm_channel_command(span->channels[x], FTDM_COMMAND_GET_INTERVAL, &interval);
for (x = 1; x <= ftdm_span_get_chan_count(span); x++) {
ftdm_channel_t *chan = ftdm_span_get_channel(span, x);
if (ftdm_channel_get_type(chan) != FTDM_CHAN_TYPE_DQ921) {
ftdm_channel_command(chan, FTDM_COMMAND_GET_INTERVAL, &interval);
break;
}
}
......@@ -1819,7 +1851,7 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
ts.duration = ts.rate;
/* main loop */
while(ftdm_running() && ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
while (ftdm_running() && ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
ftdm_wait_flag_t flags;
ftdm_status_t status;
int last_chan_state = 0;
......@@ -1829,11 +1861,11 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
/*
* check b-channel states and generate & send tones if neccessary
*/
for (x = 1; x <= span->chan_count; x++) {
ftdm_channel_t *ftdmchan = span->channels[x];
for (x = 1; x <= ftdm_span_get_chan_count(span); x++) {
ftdm_channel_t *chan = ftdm_span_get_channel(span, x);
ftdm_size_t len = sizeof(frame), rlen;
if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_DQ921) {
continue;
}
......@@ -1842,40 +1874,41 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
* (Recycle buffer content if succeeding channels share the
* same state, this saves some cpu cycles)
*/
switch (ftdmchan->state) {
switch (ftdm_channel_get_state(chan)) {
case FTDM_CHANNEL_STATE_DIALTONE:
{
ftdm_isdn_bchan_data_t *data = (ftdm_isdn_bchan_data_t *)ftdmchan->mod_data;
ftdm_isdn_bchan_data_t *data = (ftdm_isdn_bchan_data_t *)chan->mod_data;
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan);
/* check overlap dial timeout first before generating tone */
if (data && data->digit_timeout && data->digit_timeout <= now) {
if (strlen(ftdmchan->caller_data.dnis.digits) > 0) {
if (strlen(caller_data->dnis.digits) > 0) {
ftdm_log(FTDM_LOG_DEBUG, "Overlap dial timeout, advancing to RING state\n");
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING);
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RING);
} else {
/* no digits received, hangup */
ftdm_log(FTDM_LOG_DEBUG, "Overlap dial timeout, no digits received, going to HANGUP state\n");
ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE; /* TODO: probably wrong cause value */
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
caller_data->hangup_cause = FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE; /* TODO: probably wrong cause value */
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
}
data->digit_timeout = 0;
continue;
}
if (last_chan_state != ftdmchan->state) {
if (last_chan_state != ftdm_channel_get_state(chan)) {
ftdm_buffer_zero(dt_buffer);
teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_DIAL]);
last_chan_state = ftdmchan->state;
teletone_run(&ts, span->tone_map[FTDM_TONEMAP_DIAL]);
last_chan_state = ftdm_channel_get_state(chan);
}
}
break;
case FTDM_CHANNEL_STATE_RING:
{
if (last_chan_state != ftdmchan->state) {
if (last_chan_state != ftdm_channel_get_state(chan)) {
ftdm_buffer_zero(dt_buffer);
teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]);
last_chan_state = ftdmchan->state;
teletone_run(&ts, span->tone_map[FTDM_TONEMAP_RING]);
last_chan_state = ftdm_channel_get_state(chan);
}
}
break;
......@@ -1884,18 +1917,20 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
continue;
}
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
if (!ftdm_test_flag(chan, FTDM_CHANNEL_OPEN)) {
if (ftdm_channel_open_chan(chan) != FTDM_SUCCESS) {
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
continue;
}
ftdm_log(FTDM_LOG_NOTICE, "Successfully opened channel %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id);
ftdm_log(FTDM_LOG_NOTICE, "Successfully opened channel %d:%d\n",
ftdm_channel_get_span_id(chan),
ftdm_channel_get_id(chan));
}
flags = FTDM_READ;
status = ftdm_channel_wait(ftdmchan, &flags, (gated) ? 0 : interval);
switch(status) {
status = ftdm_channel_wait(chan, &flags, (gated) ? 0 : interval);
switch (status) {
case FTDM_FAIL:
continue;
......@@ -1910,12 +1945,12 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
}
gated = 1;
status = ftdm_channel_read(ftdmchan, frame, &len);
status = ftdm_channel_read(chan, frame, &len);
if (status != FTDM_SUCCESS || len <= 0) {
continue;
}
if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) {
if (chan->effective_codec != FTDM_CODEC_SLIN) {
len *= 2;
}
......@@ -1924,23 +1959,23 @@ static void *ftdm_isdn_tones_run(ftdm_thread_t *me, void *obj)
rlen = ftdm_buffer_read_loop(dt_buffer, frame, len);
if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) {
if (chan->effective_codec != FTDM_CODEC_SLIN) {
fio_codec_t codec_func = NULL;
if (ftdmchan->native_codec == FTDM_CODEC_ULAW) {
if (chan->native_codec == FTDM_CODEC_ULAW) {
codec_func = fio_slin2ulaw;
} else if (ftdmchan->native_codec == FTDM_CODEC_ALAW) {
} else if (chan->native_codec == FTDM_CODEC_ALAW) {
codec_func = fio_slin2alaw;
}
if (codec_func) {
status = codec_func(frame, sizeof(frame), &rlen);
} else {
snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!");
snprintf(chan->last_error, sizeof(chan->last_error), "codec error!");
goto done;
}
}
ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen);
ftdm_channel_write(chan, frame, sizeof(frame), &rlen);
}
/*
......@@ -1989,7 +2024,7 @@ static void *ftdm_isdn_run(ftdm_thread_t *me, void *obj)
Q921Start(&isdn_data->q921);
Q931Start(&isdn_data->q931);
while(ftdm_running() && ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
while (ftdm_running() && ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
ftdm_wait_flag_t flags = FTDM_READ;
ftdm_status_t status = ftdm_channel_wait(isdn_data->dchan, &flags, 100);
......@@ -2001,7 +2036,7 @@ static void *ftdm_isdn_run(ftdm_thread_t *me, void *obj)
/*
*
*/
switch(status) {
switch (status) {
case FTDM_FAIL:
{
ftdm_log(FTDM_LOG_ERROR, "D-Chan Read Error!\n");
......@@ -2040,8 +2075,8 @@ static void *ftdm_isdn_run(ftdm_thread_t *me, void *obj)
}
done:
ftdm_channel_close(&isdn_data->dchans[0]);
ftdm_channel_close(&isdn_data->dchans[1]);
// ftdm_channel_close(&isdn_data->dchans[0]);
// ftdm_channel_close(&isdn_data->dchans[1]);
ftdm_clear_flag(isdn_data, FTDM_ISDN_RUNNING);
#ifdef WIN32
......@@ -2223,11 +2258,11 @@ static ftdm_status_t ftdm_isdn_stop(ftdm_span_t *span)
ftdm_set_flag(isdn_data, FTDM_ISDN_STOP);
while(ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
while (ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
ftdm_sleep(100);
}
while(ftdm_test_flag(isdn_data, FTDM_ISDN_TONES_RUNNING)) {
while (ftdm_test_flag(isdn_data, FTDM_ISDN_TONES_RUNNING)) {
ftdm_sleep(100);
}
......@@ -2239,8 +2274,8 @@ static ftdm_status_t ftdm_isdn_stop(ftdm_span_t *span)
*/
static ftdm_status_t ftdm_isdn_start(ftdm_span_t *span)
{
ftdm_status_t ret;
ftdm_isdn_data_t *isdn_data = span->signal_data;
ftdm_status_t ret;
if (ftdm_test_flag(isdn_data, FTDM_ISDN_RUNNING)) {
return FTDM_FAIL;
......@@ -2266,9 +2301,8 @@ static ftdm_status_t ftdm_isdn_start(ftdm_span_t *span)
static int32_t parse_loglevel(const char *level)
{
if (!level) {
if (!level)
return -1;
}
if (!strcasecmp(level, "debug")) {
return FTDM_LOG_LEVEL_DEBUG;
......@@ -2291,53 +2325,53 @@ static int32_t parse_loglevel(const char *level)
}
}
static uint32_t parse_opts(const char *in)
static int parse_opts(const char *in, uint32_t *flags)
{
uint32_t flags = 0;
if (!in) {
return 0;
}
if (!in || !flags)
return -1;
if (strstr(in, "suggest_channel")) {
flags |= FTDM_ISDN_OPT_SUGGEST_CHANNEL;
*flags |= FTDM_ISDN_OPT_SUGGEST_CHANNEL;
}
if (strstr(in, "omit_display")) {
flags |= FTDM_ISDN_OPT_OMIT_DISPLAY_IE;
*flags |= FTDM_ISDN_OPT_OMIT_DISPLAY_IE;
}
if (strstr(in, "disable_tones")) {
flags |= FTDM_ISDN_OPT_DISABLE_TONES;
*flags |= FTDM_ISDN_OPT_DISABLE_TONES;
}
return flags;
return 0;
}
static uint32_t parse_dialect(const char *in)
static int parse_dialect(const char *in, uint32_t *dialect)
{
if (!in) {
return Q931_Dialect_Count;
}
if (!in || !dialect)
return -1;
#if __UNSUPPORTED__
if (!strcasecmp(in, "national")) {
return Q931_Dialect_National;
*dialect = Q931_Dialect_National;
return 0;
}
if (!strcasecmp(in, "dms")) {
return Q931_Dialect_DMS;
*dialect = Q931_Dialect_DMS;
return 0;
}
#endif
if (!strcasecmp(in, "5ess")) {
return Q931_Dialect_5ESS;
*dialect = Q931_Dialect_5ESS;
return 0;
}
if (!strcasecmp(in, "dss1")) {
return Q931_Dialect_DSS1;
if (!strcasecmp(in, "dss1") || !strcasecmp(in, "euroisdn")) {
*dialect = Q931_Dialect_DSS1;
return 0;
}
if (!strcasecmp(in, "q931")) {
return Q931_Dialect_Q931;
*dialect = Q931_Dialect_Q931;
return 0;
}
return Q931_Dialect_Count;
return -1;
}
......@@ -2425,7 +2459,7 @@ static FIO_API_FUNCTION(isdn_api)
if (!strcasecmp(argv[2], "q921")) {
layer = 0x01;
} else if(!strcasecmp(argv[2], "q931")) {
} else if (!strcasecmp(argv[2], "q931")) {
layer = 0x02;
} else if (!strcasecmp(argv[2], "all")) {
layer = 0x03;
......@@ -2513,7 +2547,7 @@ static FIO_API_FUNCTION(isdn_api)
stream->write_function(stream, "+OK capture started.\n");
goto done;
}
else if(!strcasecmp(argv[2], "stop")) {
else if (!strcasecmp(argv[2], "stop")) {
if (!isdn_pcap_is_open(isdn_data)) {
stream->write_function(stream, "-ERR capture is not running.\n");
......@@ -2526,7 +2560,7 @@ static FIO_API_FUNCTION(isdn_api)
stream->write_function(stream, "+OK capture stopped.\n");
goto done;
}
else if(!strcasecmp(argv[2], "suspend")) {
else if (!strcasecmp(argv[2], "suspend")) {
if (!isdn_pcap_is_open(isdn_data)) {
stream->write_function(stream, "-ERR capture is not running.\n");
......@@ -2537,7 +2571,7 @@ static FIO_API_FUNCTION(isdn_api)
stream->write_function(stream, "+OK capture suspended.\n");
goto done;
}
else if(!strcasecmp(argv[2], "resume")) {
else if (!strcasecmp(argv[2], "resume")) {
if (!isdn_pcap_is_open(isdn_data)) {
stream->write_function(stream, "-ERR capture is not running.\n");
......@@ -2563,44 +2597,80 @@ done:
return FTDM_SUCCESS;
}
static FIO_SIG_CONFIGURE_FUNCTION(isdn_configure_span)
static int parse_mode(const char *mode)
{
if (!mode)
return -1;
if (!strcasecmp(mode, "user") || !strcasecmp(mode, "cpe")) {
return Q931_TE;
}
if (!strcasecmp(mode, "net") || !strcasecmp(mode, "network")) {
return Q931_NT;
}
return -1;
}
static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(isdn_configure_span)
{
uint32_t i, x = 0;
ftdm_channel_t *dchans[2] = { 0 };
ftdm_isdn_data_t *isdn_data;
const char *tonemap = "us";
char *var, *val;
Q931Dialect_t dialect = Q931_Dialect_National;
ftdm_channel_t *dchan = NULL;
ftdm_isdn_data_t *isdn_data;
int32_t digit_timeout = 0;
const char *tonemap = "us";
int dchan_count = 0, bchan_count = 0;
int q921loglevel = -1;
int q931loglevel = -1;
uint32_t i;
if (span->signal_type) {
snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling [%d].", span->signal_type);
ftdm_log(FTDM_LOG_ERROR, "Span is already configured for signalling [%d]\n", span->signal_type);
snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling [%d]", span->signal_type);
return FTDM_FAIL;
}
if (span->trunk_type >= FTDM_TRUNK_NONE) {
ftdm_log(FTDM_LOG_WARNING, "Invalid trunk type '%s' defaulting to T1.\n", ftdm_trunk_type2str(span->trunk_type));
if (ftdm_span_get_trunk_type(span) >= FTDM_TRUNK_NONE) {
ftdm_log(FTDM_LOG_WARNING, "Invalid trunk type '%s' defaulting to T1\n", ftdm_span_get_trunk_type_str(span));
span->trunk_type = FTDM_TRUNK_T1;
}
for(i = 1; i <= span->chan_count; i++) {
if (span->channels[i]->type == FTDM_CHAN_TYPE_DQ921) {
if (x > 1) {
snprintf(span->last_error, sizeof(span->last_error), "Span has more than 2 D-Channels!");
for (i = 1; i <= ftdm_span_get_chan_count(span); i++) {
ftdm_channel_t *chan = ftdm_span_get_channel(span, i);
switch (ftdm_channel_get_type(chan)) {
case FTDM_CHAN_TYPE_DQ921:
if (dchan_count > 1) {
ftdm_log(FTDM_LOG_ERROR, "Span has more than 1 D-Channel!\n");
snprintf(span->last_error, sizeof(span->last_error), "Span has more than 1 D-Channel!");
return FTDM_FAIL;
}
if (ftdm_channel_open(span->span_id, i, &dchans[x]) == FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_DEBUG, "opening d-channel #%d %d:%d\n", x, dchans[x]->span_id, dchans[x]->chan_id);
dchans[x]->state = FTDM_CHANNEL_STATE_UP;
x++;
if (ftdm_channel_open(ftdm_span_get_id(span), i, &dchan) == FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_DEBUG, "opening d-channel #%d %d:%d\n", dchan_count,
ftdm_channel_get_span_id(dchan), ftdm_channel_get_id(dchan));
dchan->state = FTDM_CHANNEL_STATE_UP;
}
dchan_count++;
break;
case FTDM_CHAN_TYPE_B:
bchan_count++;
break;
default:
break;
}
}
if (!x) {
snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channels!");
if (!dchan_count) {
ftdm_log(FTDM_LOG_ERROR, "Span has no D-Channel!\n");
snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channel!");
return FTDM_FAIL;
}
if (!bchan_count) {
ftdm_log(FTDM_LOG_ERROR, "Span has no B-Channels!\n");
snprintf(span->last_error, sizeof(span->last_error), "Span has no B-Channels!");
return FTDM_FAIL;
}
......@@ -2611,52 +2681,55 @@ static FIO_SIG_CONFIGURE_FUNCTION(isdn_configure_span)
isdn_data->mode = Q931_TE;
dialect = Q931_Dialect_Q931;
while((var = va_arg(ap, char *))) {
for (i = 0; ftdm_parameters[i].var; i++) {
const char *var = ftdm_parameters[i].var;
const char *val = ftdm_parameters[i].val;
if (!val) {
ftdm_log(FTDM_LOG_ERROR, "Variable '%s' has no value\n", var);
return FTDM_FAIL;
}
if (!strcasecmp(var, "mode")) {
if (!(val = va_arg(ap, char *))) {
break;
if ((isdn_data->mode = parse_mode(val)) < 0) {
ftdm_log(FTDM_LOG_ERROR, "Invalid/unknown mode '%s'\n", val);
snprintf(span->last_error, sizeof(span->last_error), "Invalid/unknown mode [%s]!", val);
return FTDM_FAIL;
}
isdn_data->mode = strcasecmp(val, "net") ? Q931_TE : Q931_NT;
} else if (!strcasecmp(var, "dialect")) {
if (!(val = va_arg(ap, char *))) {
break;
}
dialect = parse_dialect(val);
if (dialect == Q931_Dialect_Count) {
if (parse_dialect(val, &dialect) < 0) {
ftdm_log(FTDM_LOG_ERROR, "Invalid/unknown dialect '%s'\n", val);
snprintf(span->last_error, sizeof(span->last_error), "Invalid/unknown dialect [%s]!", val);
return FTDM_FAIL;
}
} else if (!strcasecmp(var, "opts")) {
if (!(val = va_arg(ap, char *))) {
break;
if (parse_opts(val, &isdn_data->opts) < 0) {
ftdm_log(FTDM_LOG_ERROR, "Invalid/unknown options '%s'\n", val);
snprintf(span->last_error, sizeof(span->last_error), "Invalid/unknown options [%s]!", val);
return FTDM_FAIL;
}
isdn_data->opts = parse_opts(val);
} else if (!strcasecmp(var, "tonemap")) {
if (!(val = va_arg(ap, char *))) {
break;
}
tonemap = (const char *)val;
} else if (!strcasecmp(var, "digit_timeout")) {
int *optp;
if (!(optp = va_arg(ap, int *))) {
break;
digit_timeout = atoi(val);
if (digit_timeout < 3000 || digit_timeout > 30000) {
ftdm_log(FTDM_LOG_WARNING, "Digit timeout %d ms outside of range (3000 - 30000 ms), using default (10000 ms)\n", digit_timeout);
digit_timeout = DEFAULT_DIGIT_TIMEOUT;
}
digit_timeout = *optp;
} else if (!strcasecmp(var, "q921loglevel")) {
q921loglevel = va_arg(ap, int);
if (q921loglevel < Q921_LOG_NONE) {
q921loglevel = Q921_LOG_NONE;
} else if (q921loglevel > Q921_LOG_DEBUG) {
q921loglevel = Q921_LOG_DEBUG;
if ((q921loglevel = parse_loglevel(val)) < 0) {
ftdm_log(FTDM_LOG_ERROR, "Invalid/unknown loglevel '%s'\n", val);
snprintf(span->last_error, sizeof(span->last_error), "Invalid/unknown loglevel [%s]!", val);
return FTDM_FAIL;
}
} else if (!strcasecmp(var, "q931loglevel")) {
q931loglevel = va_arg(ap, int);
if (q931loglevel < Q931_LOG_NONE) {
q931loglevel = Q931_LOG_NONE;
} else if (q931loglevel > Q931_LOG_DEBUG) {
q931loglevel = Q931_LOG_DEBUG;
if ((q931loglevel = parse_loglevel(val)) < 0) {
ftdm_log(FTDM_LOG_ERROR, "Invalid/unknown loglevel '%s'\n", val);
snprintf(span->last_error, sizeof(span->last_error), "Invalid/unknown loglevel [%s]!", val);
return FTDM_FAIL;
}
} else {
ftdm_log(FTDM_LOG_ERROR, "Unknown parameter '%s'\n", var);
snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var);
return FTDM_FAIL;
}
......@@ -2665,39 +2738,35 @@ static FIO_SIG_CONFIGURE_FUNCTION(isdn_configure_span)
if (!digit_timeout) {
digit_timeout = DEFAULT_DIGIT_TIMEOUT;
}
else if (digit_timeout < 3000 || digit_timeout > 30000) {
ftdm_log(FTDM_LOG_WARNING, "Digit timeout %d ms outside of range (3000 - 30000 ms), using default (10000 ms)\n", digit_timeout);
digit_timeout = DEFAULT_DIGIT_TIMEOUT;
}
/* allocate per b-chan data */
if (isdn_data->mode == Q931_NT) {
ftdm_isdn_bchan_data_t *data;
data = malloc(span->chan_count * sizeof(ftdm_isdn_bchan_data_t));
data = malloc(bchan_count * sizeof(ftdm_isdn_bchan_data_t));
if (!data) {
return FTDM_FAIL;
}
for (i = 1; i <= span->chan_count; i++, data++) {
if (span->channels[i]->type == FTDM_CHAN_TYPE_B) {
span->channels[i]->mod_data = data;
for (i = 1; i <= ftdm_span_get_chan_count(span); i++, data++) {
ftdm_channel_t *chan = ftdm_span_get_channel(span, i);
if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_B) {
chan->mod_data = data;
memset(data, 0, sizeof(ftdm_isdn_bchan_data_t));
}
}
}
isdn_data->sig_cb = sig_cb;
isdn_data->dchans[0] = dchans[0];
isdn_data->dchans[1] = dchans[1];
isdn_data->dchan = isdn_data->dchans[0];
isdn_data->sig_cb = sig_cb;
isdn_data->dchan = dchan;
isdn_data->digit_timeout = digit_timeout;
Q921_InitTrunk(&isdn_data->q921,
0,
0,
isdn_data->mode,
span->trunk_type == FTDM_TRUNK_BRI_PTMP ? Q921_PTMP : Q921_PTP,
(ftdm_span_get_trunk_type(span) == FTDM_TRUNK_BRI_PTMP) ? Q921_PTMP : Q921_PTP,
0,
ftdm_isdn_921_21,
(Q921Tx23CB_t)ftdm_isdn_921_23,
......@@ -2798,7 +2867,7 @@ ftdm_module_t ftdm_module = {
.io_unload = NULL,
.sig_load = isdn_load,
.sig_unload = isdn_unload,
.sig_configure = isdn_configure_span
.configure_span_signaling = isdn_configure_span
};
/* For Emacs:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论