forked from Mirrors/freeswitch
boost up
git-svn-id: http://svn.openzap.org/svn/openzap/trunk@416 a93c3328-9c30-0410-af19-c9cd2b2d52af
This commit is contained in:
parent
db534bd8f0
commit
61663af152
@ -29,7 +29,7 @@
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
CFLAGS=@COMP_VENDOR_CFLAGS@
|
||||
CFLAGS=@COMP_VENDOR_CFLAGS@ @DEFS@
|
||||
SRC=src
|
||||
SOURCES=\
|
||||
$(SRC)/hashtable.c \
|
||||
@ -68,6 +68,7 @@ $(SRC)/isdn/5ESSStateTE.c \
|
||||
$(SRC)/isdn/Q932mes.c \
|
||||
$(SRC)/zap_zt.c \
|
||||
$(SRC)/zap_wanpipe.c \
|
||||
$(SRC)/ss7_boost_client.c \
|
||||
$(SRC)/zap_ss7_boost.c
|
||||
|
||||
OBJS=\
|
||||
@ -107,6 +108,7 @@ $(SRC)/isdn/5ESSStateTE.o \
|
||||
$(SRC)/isdn/Q932mes.o \
|
||||
$(SRC)/zap_zt.o \
|
||||
$(SRC)/zap_wanpipe.o \
|
||||
$(SRC)/ss7_boost_client.o \
|
||||
$(SRC)/zap_ss7_boost.o
|
||||
|
||||
HEADERS= $(SRC)/include/fsk.h \
|
||||
|
@ -44,6 +44,9 @@ sun)
|
||||
COMP_VENDOR_CFLAGS="-std=c99 -Wall -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_CHECK_HEADERS([netinet/sctp.h])
|
||||
|
||||
# Enable debugging
|
||||
AC_ARG_ENABLE(debug,
|
||||
[AC_HELP_STRING([--enable-debug],[build with debug information])],[enable_debug="$enable_debug"],[enable_debug="yes"])
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "openzap.h"
|
||||
#include "zap_analog.h"
|
||||
#include "zap_isdn.h"
|
||||
#include "zap_ss7_boost.h"
|
||||
|
||||
#ifndef __FUNCTION__
|
||||
#define __FUNCTION__ __SWITCH_FUNC__
|
||||
@ -807,7 +808,8 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi
|
||||
char name[128];
|
||||
zap_status_t status;
|
||||
int direction = ZAP_TOP_DOWN;
|
||||
|
||||
zap_caller_data_t caller_data = {{ 0 }};
|
||||
|
||||
if (!outbound_profile) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n");
|
||||
return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
|
||||
@ -846,10 +848,14 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi
|
||||
chan_id = 0;
|
||||
}
|
||||
|
||||
zap_set_string(caller_data.ani, dest);
|
||||
zap_set_string(caller_data.cid_name, outbound_profile->caller_id_name);
|
||||
zap_set_string(caller_data.cid_num, outbound_profile->caller_id_number);
|
||||
|
||||
if (chan_id) {
|
||||
status = zap_channel_open(span_id, chan_id, &zchan);
|
||||
} else {
|
||||
status = zap_channel_open_any(span_id, direction, &zchan);
|
||||
status = zap_channel_open_any(span_id, direction, &caller_data, &zchan);
|
||||
}
|
||||
|
||||
if (status != ZAP_SUCCESS) {
|
||||
@ -875,9 +881,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi
|
||||
|
||||
snprintf(name, sizeof(name), "OPENZAP/%s", dest);
|
||||
switch_channel_set_name(channel, name);
|
||||
zap_set_string(zchan->caller_data.ani, dest);
|
||||
zap_set_string(zchan->caller_data.cid_name, outbound_profile->caller_id_name);
|
||||
zap_set_string(zchan->caller_data.cid_num, outbound_profile->caller_id_number);
|
||||
zchan->caller_data = caller_data;
|
||||
caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
|
||||
switch_channel_set_caller_profile(channel, caller_profile);
|
||||
tech_pvt->caller_profile = caller_profile;
|
||||
@ -1535,6 +1539,7 @@ static switch_status_t load_config(void)
|
||||
|
||||
if (!id) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
span_id = atoi(id);
|
||||
@ -1561,6 +1566,70 @@ static switch_status_t load_config(void)
|
||||
}
|
||||
}
|
||||
|
||||
if ((spans = switch_xml_child(cfg, "boost_spans"))) {
|
||||
for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) {
|
||||
char *id = (char *) switch_xml_attr_soft(myspan, "id");
|
||||
char *context = "default";
|
||||
char *dialplan = "XML";
|
||||
uint32_t span_id = 0;
|
||||
zap_span_t *span = NULL;
|
||||
char *tonegroup = NULL;
|
||||
char *local_ip = NULL;
|
||||
int local_port = 0;
|
||||
char *remote_ip = NULL;
|
||||
int remote_port = 0;
|
||||
|
||||
for (param = switch_xml_child(myspan, "param"); param; 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, "local-ip")) {
|
||||
local_ip = val;
|
||||
} else if (!strcasecmp(var, "local-port")) {
|
||||
local_port = atoi(val);
|
||||
} else if (!strcasecmp(var, "remote-ip")) {
|
||||
remote_ip = val;
|
||||
} else if (!strcasecmp(var, "remote-port")) {
|
||||
remote_port = atoi(val);
|
||||
} else if (!strcasecmp(var, "context")) {
|
||||
context = val;
|
||||
} else if (!strcasecmp(var, "dialplan")) {
|
||||
dialplan = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(id && local_ip && local_port && remote_ip && remote_port) ) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
span_id = atoi(id);
|
||||
|
||||
if (!tonegroup) {
|
||||
tonegroup = "us";
|
||||
}
|
||||
|
||||
if (zap_span_find(span_id, &span) != ZAP_SUCCESS) {
|
||||
zap_log(ZAP_LOG_ERROR, "Error finding OpenZAP span %d\n", span_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zap_ss7_boost_configure_span(span, local_ip, local_port, remote_ip, remote_port, on_isdn_signal) != ZAP_SUCCESS) {
|
||||
zap_log(ZAP_LOG_ERROR, "Error starting OpenZAP span %d error: %s\n", span_id, span->last_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
SPAN_CONFIG[span->span_id].span = span;
|
||||
switch_copy_string(SPAN_CONFIG[span->span_id].context, context, sizeof(SPAN_CONFIG[span->span_id].context));
|
||||
switch_copy_string(SPAN_CONFIG[span->span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span->span_id].dialplan));
|
||||
|
||||
zap_ss7_boost_start(span);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch_xml_free(xml);
|
||||
|
||||
|
@ -150,7 +150,9 @@
|
||||
#define assert(_Expression) ((void)(_Expression))
|
||||
#endif
|
||||
|
||||
#define ZAP_MAX_CHANNELS_SPAN 513
|
||||
#define ZAP_MAX_CHANNELS_PHYSICAL_SPAN 32
|
||||
#define ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN 16
|
||||
#define ZAP_MAX_CHANNELS_SPAN ZAP_MAX_CHANNELS_PHYSICAL_SPAN * ZAP_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN
|
||||
#define ZAP_MAX_SPANS_INTERFACE 33
|
||||
|
||||
#define GOTO_STATUS(label,st) status = st; goto label ;
|
||||
@ -506,7 +508,7 @@ zap_status_t zap_span_set_event_callback(zap_span_t *span, zio_event_cb_t event_
|
||||
zap_status_t zap_channel_set_event_callback(zap_channel_t *zchan, zio_event_cb_t event_callback);
|
||||
zap_status_t zap_channel_open(uint32_t span_id, uint32_t chan_id, zap_channel_t **zchan);
|
||||
zap_status_t zap_channel_open_chan(zap_channel_t *zchan);
|
||||
zap_status_t zap_channel_open_any(uint32_t span_id, zap_direction_t direction, zap_channel_t **zchan);
|
||||
zap_status_t zap_channel_open_any(uint32_t span_id, zap_direction_t direction, const zap_caller_data_t *caller_data, zap_channel_t **zchan);
|
||||
zap_status_t zap_channel_close(zap_channel_t **zchan);
|
||||
zap_status_t zap_channel_done(zap_channel_t *zchan);
|
||||
zap_status_t zap_channel_use(zap_channel_t *zchan);
|
||||
|
@ -44,7 +44,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#ifdef SS7BC_USE_SCTP
|
||||
#ifdef HAVE_NETINET_SCTP_H
|
||||
#include <netinet/sctp.h>
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
@ -77,13 +77,13 @@
|
||||
typedef t_sigboost ss7bc_event_t;
|
||||
typedef uint32_t ss7bc_event_id_t;
|
||||
|
||||
typedef struct smg_ip_cfg
|
||||
typedef struct ss7bc_ip_cfg
|
||||
{
|
||||
char local_ip[25];
|
||||
int local_port;
|
||||
char remote_ip[25];
|
||||
int remote_port;
|
||||
}smg_ip_cfg_t;
|
||||
}ss7bc_ip_cfg_t;
|
||||
|
||||
struct ss7bc_connection {
|
||||
zap_socket_t socket;
|
||||
@ -99,7 +99,7 @@ struct ss7bc_connection {
|
||||
unsigned int rxseq;
|
||||
unsigned int txwindow;
|
||||
unsigned int rxseq_reset;
|
||||
smg_ip_cfg_t cfg;
|
||||
ss7bc_ip_cfg_t cfg;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@ -111,7 +111,7 @@ typedef struct ss7bc_connection ss7bc_connection_t;
|
||||
/* disable nagle's algorythm */
|
||||
static inline void sctp_no_nagle(int socket)
|
||||
{
|
||||
#ifdef SS7BC_USE_SCTP
|
||||
#ifdef HAVE_NETINET_SCTP_H
|
||||
int flag = 1;
|
||||
setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int));
|
||||
#endif
|
||||
@ -123,8 +123,9 @@ ss7bc_event_t *ss7bc_connection_read(ss7bc_connection_t *mcon, int iteration);
|
||||
ss7bc_event_t *ss7bc_connection_readp(ss7bc_connection_t *mcon, int iteration);
|
||||
int ss7bc_connection_write(ss7bc_connection_t *mcon, ss7bc_event_t *event);
|
||||
void ss7bc_event_init(ss7bc_event_t *event, ss7bc_event_id_t event_id, int chan, int span);
|
||||
void ss7bc_call_init(ss7bc_event_t *event, char *calling, char *called, int setup_id);
|
||||
char *ss7bc_event_id_name(uint32_t event_id);
|
||||
void ss7bc_call_init(ss7bc_event_t *event, const char *calling, const char *called, int setup_id);
|
||||
const char *ss7bc_event_id_name(uint32_t event_id);
|
||||
int ss7bc_exec_command(ss7bc_connection_t *mcon, int span, int chan, int id, int cmd, int cause);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -33,21 +33,19 @@
|
||||
|
||||
#ifndef ZAP_SS7_BOOST_H
|
||||
#define ZAP_SS7_BOOST_H
|
||||
#include "ss7_boost_client.h"
|
||||
#include "openzap.h"
|
||||
|
||||
typedef enum {
|
||||
ZAP_SS7_BOOST_RUNNING = (1 << 0)
|
||||
} zap_isdn_flag_t;
|
||||
} zap_ss7_boost_flag_t;
|
||||
|
||||
struct zap_ss7_boost_data {
|
||||
const char *local_ip;
|
||||
int local_port;
|
||||
const char *remote_ip;
|
||||
int remote_port;
|
||||
typedef struct zap_ss7_boost_data {
|
||||
ss7bc_connection_t mcon;
|
||||
ss7bc_connection_t pcon;
|
||||
zio_signal_cb_t signal_cb;
|
||||
uint32_t flags;
|
||||
};
|
||||
typedef struct zap_ss7_boost_data zap_ss7_boost_data_t;
|
||||
} zap_ss7_boost_data_t;
|
||||
|
||||
zap_status_t zap_ss7_boost_start(zap_span_t *span);
|
||||
zap_status_t zap_ss7_boost_init(void);
|
||||
|
@ -336,8 +336,9 @@ typedef struct zap_channel zap_channel_t;
|
||||
typedef struct zap_event zap_event_t;
|
||||
typedef struct zap_sigmsg zap_sigmsg_t;
|
||||
typedef struct zap_span zap_span_t;
|
||||
typedef struct zap_caller_data zap_caller_data_t;
|
||||
|
||||
#define ZIO_CHANNEL_REQUEST_ARGS (zap_span_t *span, zap_direction_t direction, zap_channel_t **zchan)
|
||||
#define ZIO_CHANNEL_REQUEST_ARGS (zap_span_t *span, zap_direction_t direction, const zap_caller_data_t *caller_data, zap_channel_t **zchan)
|
||||
#define ZIO_CHANNEL_OUTGOING_CALL_ARGS (zap_channel_t *zchan)
|
||||
#define ZIO_SPAN_POLL_EVENT_ARGS (zap_span_t *span, uint32_t ms)
|
||||
#define ZIO_SPAN_NEXT_EVENT_ARGS (zap_span_t *span, zap_event_t **event)
|
||||
|
@ -43,7 +43,7 @@ extern int gethostbyname_r (__const char *__restrict __name,
|
||||
|
||||
struct ss7bc_map {
|
||||
uint32_t event_id;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct ss7bc_map ss7bc_table[] = {
|
||||
@ -72,7 +72,7 @@ static int create_conn_socket(ss7bc_connection_t *mcon, char *local_ip, int loca
|
||||
|
||||
memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp));
|
||||
memset(&mcon->local_hp, 0, sizeof(mcon->local_hp));
|
||||
#ifdef SS7BC_USE_SCTP
|
||||
#ifdef HAVE_NETINET_SCTP_H
|
||||
mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
|
||||
#else
|
||||
mcon->socket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
@ -96,7 +96,7 @@ static int create_conn_socket(ss7bc_connection_t *mcon, char *local_ip, int loca
|
||||
memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length);
|
||||
mcon->local_addr.sin_port = htons(local_port);
|
||||
|
||||
#ifdef SS7BC_USE_SCTP
|
||||
#ifdef HAVE_NETINET_SCTP_H
|
||||
setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY,
|
||||
(char *)&flag, sizeof(int));
|
||||
#endif
|
||||
@ -107,7 +107,7 @@ static int create_conn_socket(ss7bc_connection_t *mcon, char *local_ip, int loca
|
||||
close(mcon->socket);
|
||||
mcon->socket = -1;
|
||||
} else {
|
||||
#ifdef SS7BC_USE_SCTP
|
||||
#ifdef HAVE_NETINET_SCTP_H
|
||||
rc=listen(mcon->socket,100);
|
||||
if (rc) {
|
||||
close(mcon->socket);
|
||||
@ -123,9 +123,11 @@ static int create_conn_socket(ss7bc_connection_t *mcon, char *local_ip, int loca
|
||||
|
||||
int ss7bc_connection_close(ss7bc_connection_t *mcon)
|
||||
{
|
||||
close(mcon->socket);
|
||||
mcon->socket = -1;
|
||||
if (mcon->socket > -1) {
|
||||
close(mcon->socket);
|
||||
}
|
||||
memset(mcon, 0, sizeof(*mcon));
|
||||
mcon->socket = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -136,6 +138,41 @@ int ss7bc_connection_open(ss7bc_connection_t *mcon, char *local_ip, int local_po
|
||||
return mcon->socket;
|
||||
}
|
||||
|
||||
|
||||
int ss7bc_exec_command(ss7bc_connection_t *mcon, int span, int chan, int id, int cmd, int cause)
|
||||
{
|
||||
ss7bc_event_t oevent;
|
||||
int retry = 5;
|
||||
|
||||
ss7bc_event_init(&oevent, cmd, chan, span);
|
||||
oevent.release_cause = cause;
|
||||
|
||||
if (id >= 0) {
|
||||
oevent.call_setup_id = id;
|
||||
}
|
||||
isup_exec_cmd_retry:
|
||||
if (ss7bc_connection_write(mcon, &oevent) <= 0){
|
||||
|
||||
--retry;
|
||||
if (retry <= 0) {
|
||||
zap_log(ZAP_LOG_WARNING,
|
||||
"Critical System Error: Failed to tx on ISUP socket: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
zap_log(ZAP_LOG_WARNING,
|
||||
"System Warning: Failed to tx on ISUP socket: %s :retry %i\n",
|
||||
strerror(errno),retry);
|
||||
}
|
||||
|
||||
goto isup_exec_cmd_retry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ss7bc_event_t *ss7bc_connection_read(ss7bc_connection_t *mcon, int iteration)
|
||||
{
|
||||
unsigned int fromlen = sizeof(struct sockaddr_in);
|
||||
@ -360,7 +397,7 @@ int ss7bc_connection_write(ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
return err;
|
||||
}
|
||||
|
||||
void ss7bc_call_init(ss7bc_event_t *event, char *calling, char *called, int setup_id)
|
||||
void ss7bc_call_init(ss7bc_event_t *event, const char *calling, const char *called, int setup_id)
|
||||
{
|
||||
memset(event, 0, sizeof(ss7bc_event_t));
|
||||
event->event_id = SIGBOOST_EVENT_CALL_START;
|
||||
@ -387,10 +424,10 @@ void ss7bc_event_init(ss7bc_event_t *event, ss7bc_event_id_t event_id, int chan,
|
||||
event->span = span;
|
||||
}
|
||||
|
||||
char *ss7bc_event_id_name(uint32_t event_id)
|
||||
const char *ss7bc_event_id_name(uint32_t event_id)
|
||||
{
|
||||
unsigned int x;
|
||||
char *ret = NULL;
|
||||
const char *ret = NULL;
|
||||
|
||||
for (x = 0 ; x < sizeof(ss7bc_table)/sizeof(struct ss7bc_map); x++) {
|
||||
if (ss7bc_table[x].event_id == event_id) {
|
||||
|
@ -37,6 +37,7 @@
|
||||
#endif
|
||||
#include "openzap.h"
|
||||
#include "zap_isdn.h"
|
||||
#include "zap_ss7_boost.h"
|
||||
#include <stdarg.h>
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
@ -342,7 +343,7 @@ zap_status_t zap_span_close_all(void)
|
||||
span = &globals.spans[i];
|
||||
if (zap_test_flag(span, ZAP_SPAN_CONFIGURED)) {
|
||||
for(j = 0; j < span->chan_count; j++) {
|
||||
zap_channel_destroy(&span->channels[i]);
|
||||
zap_channel_destroy(&span->channels[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -679,7 +680,7 @@ zap_status_t zap_channel_set_state(zap_channel_t *zchan, zap_channel_state_t sta
|
||||
return ok ? ZAP_SUCCESS : ZAP_FAIL;
|
||||
}
|
||||
|
||||
zap_status_t zap_channel_open_any(uint32_t span_id, zap_direction_t direction, zap_channel_t **zchan)
|
||||
zap_status_t zap_channel_open_any(uint32_t span_id, zap_direction_t direction, const zap_caller_data_t *caller_data, zap_channel_t **zchan)
|
||||
{
|
||||
zap_status_t status = ZAP_FAIL;
|
||||
zap_channel_t *check;
|
||||
@ -690,7 +691,7 @@ zap_status_t zap_channel_open_any(uint32_t span_id, zap_direction_t direction, z
|
||||
zap_mutex_lock(globals.mutex);
|
||||
|
||||
if (span_id && globals.spans[span_id].channel_request) {
|
||||
status = globals.spans[span_id].channel_request(&globals.spans[span_id], direction, zchan);
|
||||
status = globals.spans[span_id].channel_request(&globals.spans[span_id], direction, caller_data, zchan);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1949,6 +1950,7 @@ zap_status_t zap_global_init(void)
|
||||
|
||||
time_init();
|
||||
zap_isdn_init();
|
||||
zap_ss7_boost_init();
|
||||
|
||||
memset(&interfaces, 0, sizeof(interfaces));
|
||||
globals.interface_hash = create_hashtable(16, zap_hash_hashfromstring, zap_hash_equalkeys);
|
||||
|
@ -35,30 +35,493 @@
|
||||
#include "ss7_boost_client.h"
|
||||
#include "zap_ss7_boost.h"
|
||||
|
||||
typedef uint16_t ss7_boost_request_id_t;
|
||||
|
||||
typedef enum {
|
||||
BST_FREE,
|
||||
BST_WAITING,
|
||||
BST_READY,
|
||||
BST_FAIL
|
||||
} ss7_boost_request_status_t;
|
||||
|
||||
typedef struct {
|
||||
ss7_boost_request_status_t status;
|
||||
ss7bc_event_t event;
|
||||
zap_span_t *span;
|
||||
zap_channel_t *zchan;
|
||||
} ss7_boost_request_t;
|
||||
|
||||
static ss7_boost_request_id_t current_request = 0;
|
||||
|
||||
static ss7_boost_request_t OUTBOUND_REQUESTS[ZAP_MAX_CHANNELS_SPAN] = {{ 0 }};
|
||||
|
||||
static zap_mutex_t *request_mutex = NULL;
|
||||
static zap_mutex_t *signal_mutex = NULL;
|
||||
|
||||
static ss7_boost_request_id_t next_request_id(void)
|
||||
{
|
||||
ss7_boost_request_id_t r;
|
||||
|
||||
zap_mutex_lock(request_mutex);
|
||||
if (current_request == ZAP_MAX_CHANNELS_SPAN) {
|
||||
current_request = 0;
|
||||
}
|
||||
r = current_request++;
|
||||
zap_mutex_unlock(request_mutex);
|
||||
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
static zap_channel_t *find_zchan(zap_span_t *span, ss7bc_event_t *event)
|
||||
{
|
||||
int i;
|
||||
zap_channel_t *zchan = NULL;
|
||||
|
||||
for(i = 0; i < span->chan_count; i++) {
|
||||
if (span->channels[i].physical_span_id == event->span+1 && span->channels[i].physical_chan_id == event->chan+1) {
|
||||
zchan = &span->channels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return zchan;
|
||||
}
|
||||
|
||||
static ZIO_CHANNEL_REQUEST_FUNCTION(ss7_boost_channel_request)
|
||||
{
|
||||
zap_ss7_boost_data_t *ss7_boost_data = span->signal_data;
|
||||
zap_status_t status = ZAP_SUCCESS;
|
||||
ss7_boost_request_id_t r = next_request_id();
|
||||
ss7bc_event_t event = {0};
|
||||
int sanity = 60000;
|
||||
|
||||
ss7bc_call_init(&event, caller_data->cid_num, caller_data->ani, r);
|
||||
OUTBOUND_REQUESTS[r].status = BST_WAITING;
|
||||
OUTBOUND_REQUESTS[r].span = span;
|
||||
|
||||
if (ss7bc_connection_write(&ss7_boost_data->mcon, &event) <= 0) {
|
||||
zap_log(ZAP_LOG_CRIT, "Failed to tx on ISUP socket [%s]\n", strerror(errno));
|
||||
status = ZAP_FAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
while(OUTBOUND_REQUESTS[r].status == BST_WAITING) {
|
||||
zap_sleep(1);
|
||||
if (!--sanity) {
|
||||
status = ZAP_FAIL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (OUTBOUND_REQUESTS[r].status == BST_READY) {
|
||||
*zchan = OUTBOUND_REQUESTS[r].zchan;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
OUTBOUND_REQUESTS[r].status = BST_FREE;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static ZIO_CHANNEL_OUTGOING_CALL_FUNCTION(ss7_boost_outgoing_call)
|
||||
{
|
||||
zap_status_t status = ZAP_SUCCESS;
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void handle_call_start_ack(ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
zap_channel_t *zchan;
|
||||
|
||||
OUTBOUND_REQUESTS[event->call_setup_id].event = *event;
|
||||
|
||||
if ((zchan = find_zchan(OUTBOUND_REQUESTS[event->call_setup_id].span, event))) {
|
||||
OUTBOUND_REQUESTS[event->call_setup_id].status = BST_READY;
|
||||
if (zap_channel_open_chan(zchan) != ZAP_SUCCESS) {
|
||||
zap_log(ZAP_LOG_ERROR, "OPEN ERROR [%s]\n", zchan->last_error);
|
||||
} else {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DIALING);
|
||||
OUTBOUND_REQUESTS[event->call_setup_id].zchan = zchan;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ss7bc_exec_command(mcon,
|
||||
event->span,
|
||||
event->chan,
|
||||
event->call_setup_id,
|
||||
SIGBOOST_EVENT_CALL_STOPPED,
|
||||
0);
|
||||
OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL;
|
||||
|
||||
}
|
||||
|
||||
static void handle_call_start_nack(ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
OUTBOUND_REQUESTS[event->call_setup_id].event = *event;
|
||||
OUTBOUND_REQUESTS[event->call_setup_id].status = BST_FAIL;
|
||||
|
||||
ss7bc_exec_command(mcon,
|
||||
event->span,
|
||||
event->chan,
|
||||
event->call_setup_id,
|
||||
SIGBOOST_EVENT_CALL_START_NACK_ACK,
|
||||
0);
|
||||
}
|
||||
|
||||
static void handle_call_stop(zap_span_t *span, ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
zap_channel_t *zchan;
|
||||
|
||||
if ((zchan = find_zchan(span, event))) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_TERMINATING);
|
||||
} else {
|
||||
ss7bc_exec_command(mcon,
|
||||
event->span,
|
||||
event->chan,
|
||||
0,
|
||||
SIGBOOST_EVENT_CALL_STOPPED,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_call_answer(zap_span_t *span, ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
zap_channel_t *zchan;
|
||||
|
||||
if ((zchan = find_zchan(span, event))) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_UP);
|
||||
} else {
|
||||
ss7bc_exec_command(mcon,
|
||||
event->span,
|
||||
event->chan,
|
||||
0,
|
||||
SIGBOOST_EVENT_CALL_STOPPED,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_call_start(zap_span_t *span, ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
zap_channel_t *zchan;
|
||||
|
||||
if ((zchan = find_zchan(span, event))) {
|
||||
ss7bc_exec_command(mcon,
|
||||
event->span,
|
||||
event->chan,
|
||||
0,
|
||||
SIGBOOST_EVENT_CALL_START_ACK,
|
||||
0);
|
||||
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_RING);
|
||||
} else {
|
||||
ss7bc_exec_command(mcon,
|
||||
event->span,
|
||||
event->chan,
|
||||
0,
|
||||
SIGBOOST_EVENT_CALL_START_NACK,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handle_heartbeat(ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
int err = ss7bc_connection_write(mcon, event);
|
||||
|
||||
if (err <= 0) {
|
||||
zap_log(ZAP_LOG_CRIT, "Failed to tx on ISUP socket [%s]: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_restart_ack(ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
mcon->rxseq_reset = 0;
|
||||
}
|
||||
|
||||
static int parse_ss7_event(zap_span_t *span, ss7bc_connection_t *mcon, ss7bc_event_t *event)
|
||||
{
|
||||
zap_mutex_lock(signal_mutex);
|
||||
|
||||
zap_log(ZAP_LOG_DEBUG,
|
||||
"RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
|
||||
ss7bc_event_id_name(event->event_id),
|
||||
event->event_id,
|
||||
event->span+1,
|
||||
event->chan+1,
|
||||
event->release_cause,
|
||||
event->call_setup_id,
|
||||
event->fseqno,
|
||||
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
|
||||
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
|
||||
);
|
||||
|
||||
|
||||
switch(event->event_id) {
|
||||
|
||||
case SIGBOOST_EVENT_CALL_START:
|
||||
handle_call_start(span, mcon, event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_CALL_STOPPED:
|
||||
handle_call_stop(span, mcon, event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_CALL_START_ACK:
|
||||
handle_call_start_ack(mcon, event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_CALL_START_NACK:
|
||||
handle_call_start_nack(mcon, event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_CALL_ANSWERED:
|
||||
handle_call_answer(span, mcon, event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_HEARTBEAT:
|
||||
handle_heartbeat(mcon, event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_CALL_START_NACK_ACK:
|
||||
//handle_call_start_nack_ack(event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_CALL_STOPPED_ACK:
|
||||
//handle_call_stop_ack(event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_INSERT_CHECK_LOOP:
|
||||
//handle_call_loop_start(event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_REMOVE_CHECK_LOOP:
|
||||
//handle_call_stop(event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_SYSTEM_RESTART_ACK:
|
||||
handle_restart_ack(mcon,event);
|
||||
break;
|
||||
case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE:
|
||||
//handle_gap_abate(event);
|
||||
break;
|
||||
default:
|
||||
zap_log(ZAP_LOG_WARNING, "No handler implemented for [%s]\n", ss7bc_event_id_name(event->event_id));
|
||||
break;
|
||||
}
|
||||
|
||||
zap_mutex_unlock(signal_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __inline__ void state_advance(zap_channel_t *zchan)
|
||||
{
|
||||
|
||||
zap_ss7_boost_data_t *ss7_boost_data = zchan->span->signal_data;
|
||||
ss7bc_connection_t *mcon = &ss7_boost_data->mcon;
|
||||
zap_sigmsg_t sig;
|
||||
zap_status_t status;
|
||||
|
||||
zap_log(ZAP_LOG_ERROR, "%d:%d STATE [%s]\n",
|
||||
zchan->span_id, zchan->chan_id, zap_channel_state2str(zchan->state));
|
||||
|
||||
memset(&sig, 0, sizeof(sig));
|
||||
sig.chan_id = zchan->chan_id;
|
||||
sig.span_id = zchan->span_id;
|
||||
sig.channel = zchan;
|
||||
|
||||
switch (zchan->state) {
|
||||
case ZAP_CHANNEL_STATE_DOWN:
|
||||
{
|
||||
zap_channel_done(zchan);
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_PROGRESS:
|
||||
{
|
||||
if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
|
||||
sig.event_id = ZAP_SIGEVENT_PROGRESS;
|
||||
if ((status = ss7_boost_data->signal_cb(&sig) != ZAP_SUCCESS)) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
|
||||
}
|
||||
} else {
|
||||
//send progress
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_RING:
|
||||
{
|
||||
if (!zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
|
||||
sig.event_id = ZAP_SIGEVENT_START;
|
||||
if ((status = ss7_boost_data->signal_cb(&sig) != ZAP_SUCCESS)) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_RESTART:
|
||||
{
|
||||
if (zchan->last_state > ZAP_CHANNEL_STATE_HANGUP) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_PROGRESS_MEDIA:
|
||||
{
|
||||
if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
|
||||
sig.event_id = ZAP_SIGEVENT_PROGRESS_MEDIA;
|
||||
if ((status = ss7_boost_data->signal_cb(&sig) != ZAP_SUCCESS)) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
|
||||
}
|
||||
} else {
|
||||
// send alerting
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_UP:
|
||||
{
|
||||
if (zap_test_flag(zchan, ZAP_CHANNEL_OUTBOUND)) {
|
||||
sig.event_id = ZAP_SIGEVENT_UP;
|
||||
if ((status = ss7_boost_data->signal_cb(&sig) != ZAP_SUCCESS)) {
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_HANGUP);
|
||||
}
|
||||
} else {
|
||||
// send connect
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_DIALING:
|
||||
{
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_HANGUP:
|
||||
{
|
||||
ss7bc_exec_command(mcon,
|
||||
zchan->physical_span_id-1,
|
||||
zchan->physical_chan_id-1,
|
||||
0,
|
||||
SIGBOOST_EVENT_CALL_STOPPED,
|
||||
0);
|
||||
zap_set_state_locked(zchan, ZAP_CHANNEL_STATE_DOWN);
|
||||
|
||||
}
|
||||
break;
|
||||
case ZAP_CHANNEL_STATE_TERMINATING:
|
||||
{
|
||||
sig.event_id = ZAP_SIGEVENT_STOP;
|
||||
status = ss7_boost_data->signal_cb(&sig);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static __inline__ void check_state(zap_span_t *span)
|
||||
{
|
||||
|
||||
|
||||
if (zap_test_flag(span, ZAP_SPAN_STATE_CHANGE)) {
|
||||
uint32_t j;
|
||||
for(j = 1; j <= span->chan_count; j++) {
|
||||
if (zap_test_flag((&span->channels[j]), ZAP_CHANNEL_STATE_CHANGE)) {
|
||||
zap_mutex_lock(signal_mutex);
|
||||
state_advance(&span->channels[j]);
|
||||
zap_mutex_unlock(signal_mutex);
|
||||
zap_clear_flag_locked((&span->channels[j]), ZAP_CHANNEL_STATE_CHANGE);
|
||||
}
|
||||
}
|
||||
zap_clear_flag_locked(span, ZAP_SPAN_STATE_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void *zap_ss7_boost_run(zap_thread_t *me, void *obj)
|
||||
{
|
||||
zap_span_t *span = (zap_span_t *) obj;
|
||||
zap_ss7_boost_data_t *ss7_boost_data = span->signal_data;
|
||||
ss7bc_connection_t *mcon, *pcon;
|
||||
|
||||
|
||||
ss7_boost_data->pcon = ss7_boost_data->mcon;
|
||||
|
||||
if (ss7bc_connection_open(&ss7_boost_data->mcon,
|
||||
ss7_boost_data->mcon.cfg.local_ip,
|
||||
ss7_boost_data->mcon.cfg.local_port,
|
||||
ss7_boost_data->mcon.cfg.remote_ip,
|
||||
ss7_boost_data->mcon.cfg.remote_port) < 0) {
|
||||
zap_log(ZAP_LOG_DEBUG, "Error: Opening MCON Socket [%d] %s\n", ss7_boost_data->mcon.socket, strerror(errno));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ss7bc_connection_open(&ss7_boost_data->pcon,
|
||||
ss7_boost_data->pcon.cfg.local_ip,
|
||||
++ss7_boost_data->pcon.cfg.local_port,
|
||||
ss7_boost_data->pcon.cfg.remote_ip,
|
||||
ss7_boost_data->pcon.cfg.remote_port) < 0) {
|
||||
zap_log(ZAP_LOG_DEBUG, "Error: Opening PCON Socket [%d] %s\n", ss7_boost_data->pcon.socket, strerror(errno));
|
||||
goto end;
|
||||
}
|
||||
|
||||
mcon = &ss7_boost_data->mcon;
|
||||
pcon = &ss7_boost_data->pcon;
|
||||
|
||||
mcon->rxseq_reset = 1;
|
||||
|
||||
ss7bc_exec_command(mcon,
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
SIGBOOST_EVENT_SYSTEM_RESTART,
|
||||
0);
|
||||
|
||||
while (zap_running() && zap_test_flag(ss7_boost_data, ZAP_SS7_BOOST_RUNNING)) {
|
||||
break;
|
||||
fd_set rfds, efds;
|
||||
struct timeval tv = { 0, 100000 };
|
||||
int max, activity, i = 0;
|
||||
ss7bc_event_t *event = NULL;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&efds);
|
||||
FD_SET(mcon->socket, &rfds);
|
||||
FD_SET(mcon->socket, &efds);
|
||||
FD_SET(pcon->socket, &rfds);
|
||||
FD_SET(pcon->socket, &efds);
|
||||
|
||||
max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1;
|
||||
|
||||
if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!activity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (FD_ISSET(pcon->socket, &rfds)) {
|
||||
if ((event = ss7bc_connection_readp(pcon, i))) {
|
||||
parse_ss7_event(span, pcon, event);
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(mcon->socket, &rfds)) {
|
||||
if ((event = ss7bc_connection_read(mcon, i))) {
|
||||
parse_ss7_event(span, mcon, event);
|
||||
}
|
||||
}
|
||||
|
||||
check_state(span);
|
||||
|
||||
}
|
||||
|
||||
goto end;
|
||||
|
||||
error:
|
||||
zap_log(ZAP_LOG_CRIT, "Socket Error!\n");
|
||||
|
||||
end:
|
||||
|
||||
ss7bc_connection_close(&ss7_boost_data->mcon);
|
||||
ss7bc_connection_close(&ss7_boost_data->pcon);
|
||||
|
||||
zap_clear_flag(ss7_boost_data, ZAP_SS7_BOOST_RUNNING);
|
||||
|
||||
zap_log(ZAP_LOG_DEBUG, "SS7_BOOST thread ended.\n");
|
||||
@ -67,6 +530,9 @@ static void *zap_ss7_boost_run(zap_thread_t *me, void *obj)
|
||||
|
||||
zap_status_t zap_ss7_boost_init(void)
|
||||
{
|
||||
zap_mutex_create(&request_mutex);
|
||||
zap_mutex_create(&signal_mutex);
|
||||
|
||||
return ZAP_SUCCESS;
|
||||
}
|
||||
|
||||
@ -84,14 +550,18 @@ zap_status_t zap_ss7_boost_configure_span(zap_span_t *span,
|
||||
{
|
||||
zap_ss7_boost_data_t *ss7_boost_data = NULL;
|
||||
|
||||
if (!local_ip && local_port && remote_ip && remote_port && sig_cb) {
|
||||
return ZAP_FAIL;
|
||||
}
|
||||
|
||||
ss7_boost_data = malloc(sizeof(*ss7_boost_data));
|
||||
assert(ss7_boost_data);
|
||||
memset(ss7_boost_data, 0, sizeof(*ss7_boost_data));
|
||||
|
||||
ss7_boost_data->local_ip = local_ip;
|
||||
ss7_boost_data->local_port = local_port;
|
||||
ss7_boost_data->remote_ip = remote_ip;
|
||||
ss7_boost_data->remote_port = remote_port;
|
||||
zap_set_string(ss7_boost_data->mcon.cfg.local_ip, local_ip);
|
||||
ss7_boost_data->mcon.cfg.local_port = local_port;
|
||||
zap_set_string(ss7_boost_data->mcon.cfg.remote_ip, remote_ip);
|
||||
ss7_boost_data->mcon.cfg.remote_port = remote_port;
|
||||
ss7_boost_data->signal_cb = sig_cb;
|
||||
|
||||
span->signal_data = ss7_boost_data;
|
||||
|
@ -358,13 +358,14 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
|
||||
sockfd = tdmv_api_open_span_chan(spanno, x);
|
||||
|
||||
if (sockfd != WP_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) {
|
||||
wanpipe_tdm_api_t tdm_api;
|
||||
zap_log(ZAP_LOG_INFO, "configuring device s%dc%d as OpenZAP device %d:%d fd:%d\n", spanno, x, chan->span_id, chan->chan_id, sockfd);
|
||||
chan->physical_span_id = spanno;
|
||||
chan->physical_chan_id = x;
|
||||
chan->rate = 8000;
|
||||
|
||||
if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) {
|
||||
wanpipe_tdm_api_t tdm_api;
|
||||
|
||||
|
||||
#if 1
|
||||
if (type == ZAP_CHAN_TYPE_FXO) {
|
||||
@ -390,8 +391,9 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
|
||||
tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_mode = WP_TDMAPI_EVENT_ENABLE;
|
||||
wp_tdm_cmd_exec(chan, &tdm_api);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_B) {
|
||||
tdm_api.wp_tdm_cmd.cmd = SIOC_WP_TDM_GET_HW_CODING;
|
||||
wp_tdm_cmd_exec(chan, &tdm_api);
|
||||
if (tdm_api.wp_tdm_cmd.hw_tdm_coding) {
|
||||
@ -400,6 +402,7 @@ static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start,
|
||||
chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW;
|
||||
}
|
||||
}
|
||||
|
||||
if (!zap_strlen_zero(name)) {
|
||||
zap_copy_string(chan->chan_name, name, sizeof(chan->chan_name));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user