INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) $(SIP_INCLUDE) -Wall $(INSTALLATION_DEFINES)
-lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) $(SIP_SOURCE) select.c action.cpp mISDN.cpp \
- tones.c loop.c remote.cpp action_efi.cpp crypt.cpp mail.c trace.c \
- action_vbox.cpp dss1.cpp main.c \
- vbox.cpp alawulaw.c endpoint.cpp interface.c message.c \
- apppbx.cpp endpointapp.cpp join.cpp options.c \
- extension.c joinpbx.cpp port.cpp \
- callerid.c joinremote.cpp route.c \
- cause.c socket_server.c
+lcr_SOURCES = \
+ main.c select.c trace.c \ options.c \ tones.c \ alawulaw.c \ cause.c \ interface.c \ message.c \ callerid.c \ socket_server.c \
+ port.cpp \ vbox.cpp \ mISDN.cpp \ dss1.cpp \ loop.c remote.cpp \
+ $(GSM_SOURCE) $(SS5_SOURCE) $(SIP_SOURCE) \
+ endpoint.cpp \ endpointapp.cpp \
+ apppbx.cpp \ route.c \ action.cpp action_efi.cpp \ action_vbox.cpp \ extension.c \ crypt.cpp \ mail.c \
+ join.cpp \ joinpbx.cpp \ joinremote.cpp
lcr_LDADD = $(LIBCRYPTO) -lmisdn -lpthread $(GSM_LIB) $(SIP_LIB)
// PDEBUG(DEBUG_EPOINT, "received message %d (terminal %s, caller id %s)\n", message, e_ext.number, e_callerinfo.id);
switch(message_type) {
- case MESSAGE_DATA: /* data from port */
- /* check if there is a call */
- if (!ea_endpoint->ep_join_id)
- break;
- /* continue if only one portlist */
- if (ea_endpoint->ep_portlist->next != NULL)
- break;
- /* forward message */
- message_forward(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, param);
- break;
-
case MESSAGE_TONE_EOF: /* tone is end of file */
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) current tone is now end of file.\n", ea_endpoint->ep_serial);
if (e_action) {
}
}
+/* join MESSAGE_BRIDE */
+void EndpointAppPBX::join_bridge(struct port_list *portlist, int message_type, union parameter *param)
+{
+ struct lcr_msg *message;
+
+ while(portlist) {
+ message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_BRIDGE);
+ memcpy(&message->param, param, sizeof(union parameter));
+ message_put(message);
+ portlist = portlist->next;
+ }
+}
+
/* join MESSAGE_NOTIFY */
void EndpointAppPBX::join_notify(struct port_list *portlist, int message_type, union parameter *param)
{
portlist = ea_endpoint->ep_portlist;
- /* send MESSAGE_DATA to port */
- if (message_type == MESSAGE_DATA) {
- if (join_id == ea_endpoint->ep_join_id) { // still linked with JOIN
- /* skip if no port relation */
- if (!portlist)
- return;
- /* skip if more than one port relation */
- if (portlist->next)
- return;
- /* forward audio data to port */
- message_forward(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, param);
- return;
- }
- }
-
// PDEBUG(DEBUG_EPOINT, "EPOINT(%d) received message %d for active JOIN (terminal %s, caller id %s state=%d)\n", ea_endpoint->ep_serial, message, e_ext.number, e_callerinfo.id, e_state);
switch(message_type) {
/* JOIN SENDS TONE message */
join_mISDNsignal(portlist, message_type, param);
break;
-#if 0
- kann nach dem test gelöscht werden, da eine direkte funktion im join und im mISDN zum austausch der message existiert
- /* JOIN requests bchannel */
- case MESSAGE_BCHANNEL: /* indicates the need of own bchannel access */
- PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received bchannel assignment %d from join.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id, param->bchannel.type);
- /* only one port is expected to be connected to bchannel */
- if (!portlist)
- break;
- if (portlist->next)
- break;
- e_join_pattern = 1;
- SCPY(e_tone, "");
- set_tone(portlist, NULL);
- message = message_forward(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, param);
- logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT);
+ /* JOIN sends bridge message */
+ case MESSAGE_BRIDGE: /* bride message to port */
+ PDEBUG(DEBUG_EPOINT, "EPOINT(%d) epoint with terminal '%s' (caller id '%s') received bridge message.\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
+ join_bridge(portlist, message_type, param);
break;
-#endif
/* JOIN has pattern available */
case MESSAGE_PATTERN: /* indicating pattern available */
void ea_message_join(unsigned int join_id, int message, union parameter *param);
void join_crypt(struct port_list *portlist, int message_type, union parameter *param);
void join_mISDNsignal(struct port_list *portlist, int message_type, union parameter *param);
+ void join_bridge(struct port_list *portlist, int message_type, union parameter *param);
void join_setup(struct port_list *portlist, int message_type, union parameter *param);
void join_information(struct port_list *portlist, int message_type, union parameter *param);
void join_overlap(struct port_list *portlist, int message_type, union parameter *param);
struct isdn_cause isdn_cause[128];
struct isdn_location isdn_location[16];
-void _printdebug(const char *function, int line, unsigned int mask, const char *fmt, ...)
+void _printdebug(const char *file, const char *function, int line, unsigned int mask, const char *fmt, ...)
{
}
-void _printerror(const char *function, int line, const char *fmt, ...)
+void _printerror(const char *file, const char *function, int line, const char *fmt, ...)
{
char buffer[4096];
va_list args;
}
/*
- * request data from endpoint/port if:
- * - two relations
- * - any without mISDN
- * in this case we bridge
+ * Bridge between port instances if:
+ * - one or all are not mISDN
*/
- message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_mISDNSIGNAL);
- message->param.mISDNsignal.message = mISDNSIGNAL_JOINDATA;
- message->param.mISDNsignal.joindata = (relations==2 && !allmISDN);
- PDEBUG(DEBUG_JOIN, "join%d EP%d set joindata=%d\n", j_serial, relation->epoint_id, message->param.mISDNsignal.joindata);
+ message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_BRIDGE);
+ message->param.bridge_id = j_serial;
+ PDEBUG(DEBUG_JOIN, "join%u EP%u requests bridge=%u\n", j_serial, relation->epoint_id, message->param.bridge_id);
message_put(message);
relation = relation->next;
}
}
-/*
- * bridging is only possible with two connected endpoints
- */
-void JoinPBX::bridge_data(unsigned int epoint_from, struct join_relation *relation_from, union parameter *param)
-{
- struct join_relation *relation_to;
-
- /* if we are alone */
- if (!j_relation->next)
- return;
-
- /* if we are more than two */
- if (j_relation->next->next)
- return;
-
- /* skip if source endpoint has NOT audio mode CONNECT */
- if (relation_from->channel_state != 1)
- return;
-
- /* get destination relation */
- relation_to = j_relation;
- if (relation_to == relation_from) {
- /* oops, we are the first, so destination is: */
- relation_to = relation_to->next;
- }
-
- /* skip if destination endpoint has NOT audio mode CONNECT */
- if (relation_to->channel_state != 1)
- return;
-
- /* now we may send our data to the endpoint where it
- * will be delivered to the port
- */
-//printf("from %d, to %d\n", relation_from->epoint_id, relation_to->epoint_id);
- message_forward(j_serial, relation_to->epoint_id, JOIN_TO_EPOINT, param);
-}
-
/* release join from endpoint
* if the join has two relations, all relations are freed and the join will be
* destroyed
// joinpbx_debug(join,"Join::message_epoint");
// }
if (options.deb & DEBUG_JOIN) {
- if (message_type != MESSAGE_DATA) {
+ if (message_type) {
cl = join_first;
while(cl) {
if (cl->j_type == JOIN_TYPE_PBX)
}
return;
- /* audio data */
- case MESSAGE_DATA:
- /* now send audio data to the other endpoint */
- bridge_data(epoint_id, relation, param);
- return;
-
/* relations sends a connect */
case MESSAGE_CONNECT:
/* outgoing setup type becomes connected */
int j_partyline_jingle; /* also play jingle on join/leave */
void bridge(void);
- void bridge_data(unsigned int epoint_from, struct join_relation *relation_from, union parameter *param);
void remove_relation(struct join_relation *relation);
struct join_relation *add_relation(void);
int out_setup(unsigned int epoint_id, int message, union parameter *param, char *newnumber, char *newkeypad);
p_m_echo = 0;
p_m_tone = 0;
p_m_rxoff = 0;
- p_m_joindata = 0;
p_m_inband_send_on = 0;
p_m_inband_receive_on = 0;
p_m_dtmf = !mISDNport->ifport->nodtmf;
}
if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on || p_m_load) {
- schedule_timer(&p_m_loadtimer, 0, ISDN_TRANSMIT*125);
+ schedule_timer(&p_m_loadtimer, 0, PORT_TRANSMIT * 125);
}
}
void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len)
{
unsigned int cont = *((unsigned int *)data);
- unsigned char *data_temp;
- unsigned int length_temp;
struct lcr_msg *message;
unsigned char *p;
int l;
cryptman_listen_bch(data, len);
}
- p = data;
-
- /* send data to epoint */
- if (p_m_joindata && ACTIVE_EPOINT(p_epointlist)) { /* only if we have an epoint object */
- length_temp = len;
- data_temp = p;
- while(length_temp) {
- message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
- message->param.data.len = (length_temp>sizeof(message->param.data.data))?sizeof(message->param.data.data):length_temp;
- memcpy(message->param.data.data, data_temp, message->param.data.len);
- message_put(message);
- if (length_temp <= sizeof(message->param.data.data))
- break;
- data_temp += sizeof(message->param.data.data);
- length_temp -= sizeof(message->param.data.data);
- }
- }
+ /* send to remote, if bridged */
+ bridge_tx(data, len);
}
set_conf(oldconf, newconf);
break;
- case mISDNSIGNAL_JOINDATA:
- if (p_m_joindata != param->mISDNsignal.joindata) {
- p_m_joindata = param->mISDNsignal.joindata;
- PDEBUG(DEBUG_BCHANNEL, "we change to joindata=%d.\n", p_m_joindata);
- update_rxoff();
- } else
- PDEBUG(DEBUG_BCHANNEL, "we already have joindata=%d.\n", p_m_joindata);
- break;
-
case mISDNSIGNAL_DELAY:
if (p_m_delay != param->mISDNsignal.delay) {
p_m_delay = param->mISDNsignal.delay;
*/
int PmISDN::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
{
- if (Port::message_epoint(epoint_id, message_id, param))
- return(1);
+ if (Port::message_epoint(epoint_id, message_id, param)) {
+ if (message_id == MESSAGE_BRIDGE)
+ update_rxoff();
+ return 1;
+ }
switch(message_id) {
- case MESSAGE_DATA: /* tx-data from upper layer */
- txfromup(param->data.data, param->data.len);
- return(1);
-
case MESSAGE_mISDNSIGNAL: /* user command */
PDEBUG(DEBUG_ISDN, "PmISDN(%s) received special ISDN SIGNAL %d.\n", p_name, param->mISDNsignal.message);
message_mISDNsignal(epoint_id, message_id, param);
- return(1);
+ return 1;
case MESSAGE_CRYPT: /* crypt control command */
PDEBUG(DEBUG_ISDN, "PmISDN(%s) received encryption command '%d'.\n", p_name, param->crypt.type);
message_crypt(epoint_id, message_id, param);
- return(1);
+ return 1;
}
- return(0);
+ return 0;
}
void PmISDN::update_rxoff(void)
{
/* call bridges in user space OR crypto OR recording */
- if (p_m_joindata || p_m_crypt_msg_loops || p_m_crypt_listen || p_record || p_m_inband_receive_on) {
+ if (p_bridge || p_m_crypt_msg_loops || p_m_crypt_listen || p_record || p_m_inband_receive_on) {
/* rx IS required */
if (p_m_rxoff) {
/* turn on RX */
/*
* enque data from upper buffer
*/
-void PmISDN::txfromup(unsigned char *data, int length)
+int PmISDN::bridge_rx(unsigned char *data, int length)
{
unsigned char buf[MISDN_HEADER_LEN+((length>ISDN_LOAD)?length:ISDN_LOAD)];
struct mISDNhead *hh = (struct mISDNhead *)buf;
int ret;
if (p_m_b_index < 0)
- return;
+ return -EIO;
if (p_m_mISDNport->b_state[p_m_b_index] != B_STATE_ACTIVE)
- return;
+ return -EINVAL;
/* check if high priority tones exist
* ignore data in this case
*/
if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on)
- return;
+ return -EBUSY;
/* preload procedure
* if transmit buffer in DSP module is empty,
if (ret <= 0)
PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_sock[p_m_b_index].fd);
p_m_load += ISDN_LOAD;
- schedule_timer(&p_m_loadtimer, 0, ISDN_TRANSMIT*125);
+ schedule_timer(&p_m_loadtimer, 0, PORT_TRANSMIT * 125);
}
/* drop if load would exceed ISDN_MAXLOAD
* this keeps the delay not too high
*/
if (p_m_load+length > ISDN_MAXLOAD)
- return;
+ return -EINVAL;
/* make and send frame */
hh->prim = PH_DATA_REQ;
if (ret <= 0)
PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_sock[p_m_b_index].fd);
p_m_load += length;
+
+ return 0;
}
int PmISDN::inband_send(unsigned char *buffer, int len)
int p_m_mute; /* if set, conf is disconnected */
int p_m_tone; /* current kernel space tone */
int p_m_rxoff; /* rx from driver is disabled */
-// int p_m_nodata; /* all parties within a conf are isdn ports, so pure bridging is possible */
int p_m_txdata; /* get what we transmit */
int p_m_dtmf; /* dtmf decoding is enabled */
- int p_m_joindata; /* the call requires data due to no briging capability */
+
+ int bridge_rx(unsigned char *data, int len);
struct lcr_timer p_m_loadtimer; /* timer for audio transmission */
virtual void update_load(void);
int p_m_load; /* current data in dsp tx buffer */
unsigned int p_m_last_tv_sec; /* time stamp of last tx_load call, (to sync audio data */
unsigned int p_m_last_tv_msec;
-// int p_m_fromup_buffer_readp; /* buffer for audio from remote endpoint */
-// int p_m_fromup_buffer_writep;
-// unsigned char p_m_fromup_buffer[FROMUP_BUFFER_SIZE];
- void txfromup(unsigned char *data, int length);
int p_m_crypt; /* encryption is enabled */
int p_m_crypt_msg_loops; /* sending a message */
int p_m_b_channel; /* number 1,2 1..15,17... */
int p_m_b_exclusive; /* if bchannel is exclusive */
int p_m_b_reserve; /* set if channel is reserved */
-// long long p_m_jittercheck; /* time of audio data */
-// long long p_m_jitterdropped; /* number of bytes dropped */
int p_m_b_mode; /* bchannel mode */
int p_m_hold; /* if port is on hold */
struct lcr_timer p_m_timeout; /* timeout of timers */
#define UNPRINT snprintf
#define VUNPRINT vsnprintf
+#define FATAL(fmt, arg...) _fatal(__FILE__, __FUNCTION__, __LINE__, fmt, ##arg)
/* fatal error with error message and exit */
-#define FATAL(fmt, arg...) fatal(__FUNCTION__, __LINE__, fmt, ##arg)
-static inline void fatal(const char *function, int line, const char *fmt, ...)
+static inline void _fatal(const char *file, const char *function, int line, const char *fmt, ...)
{
va_list args;
char buffer[256];
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
buffer[sizeof(buffer)-1] = '\0';
- fprintf(stderr, "FATAL ERROR in function %s, line %d: %s", function, line, buffer);
+ fprintf(stderr, "FATAL ERROR in function %s/%s, line %d: %s", file, function, line, buffer);
fprintf(stderr, "This error is not recoverable, must exit here.\n");
#ifdef DEBUG_FUNC
- debug(function, line, "FATAL", buffer);
- debug(function, line, "FATAL", (char *)"This error is not recoverable, must exit here.\n");
+ debug(file, function, line, "FATAL", buffer);
+ debug(file, function, line, "FATAL", (char *)"This error is not recoverable, must exit here.\n");
#endif
exit(EXIT_FAILURE);
}
/* memory allocation with setting to zero */
-#define MALLOC(size) _malloc(size, __FUNCTION__, __LINE__)
-static inline void *_malloc(unsigned int size, const char *function, int line)
+#define MALLOC(size) _malloc(size, __FILE__, __FUNCTION__, __LINE__)
+static inline void *_malloc(unsigned int size, const char *file, const char *function, int line)
{
void *addr;
addr = malloc(size);
if (!addr)
- fatal(function, line, "No memory for %d bytes.\n", size);
+ _fatal(file, function, line, "No memory for %d bytes.\n", size);
memset(addr, 0, size);
return addr;
}
int debug_newline = 1;
int nooutput = 0;
-void debug(const char *function, int line, const char *prefix, char *buffer)
+void debug(const char *file, const char *function, int line, const char *prefix, char *buffer)
{
time_t now;
struct tm *now_tm;
if (debug_newline)
printf("\033[32m%06d %s\033[37m%s", debug_count%1000000, prefix?prefix:"", prefix?" ":"");
if (function)
- printf("(in %s() line %d): %s", function, line, buffer);
+ printf("(in %s/%s() line %d): %s", file, function, line, buffer);
else
printf("%s", buffer);
}
}
-void _printdebug(const char *function, int line, unsigned int mask, const char *fmt, ...)
+void _printdebug(const char *file, const char *function, int line, unsigned int mask, const char *fmt, ...)
{
char buffer[4096];
va_list args;
buffer[sizeof(buffer)-1]=0;
va_end(args);
- debug(function, line, "DEBUG", buffer);
+ debug(file, function, line, "DEBUG", buffer);
pthread_mutex_unlock(&mutexd);
}
-void _printerror(const char *function, int line, const char *fmt, ...)
+void _printerror(const char *file, const char *function, int line, const char *fmt, ...)
{
char buffer[4096];
va_list args;
va_end(args);
if (options.deb)
- debug(function, line, "ERROR", buffer);
+ debug(file, function, line, "ERROR", buffer);
else { /* only if we do not debug */
if (function)
fprintf(stderr, "ERROR (in %s() line %d) %s", function, line, buffer);
struct sched_param schedp;
int created_mutexd = 0,/* created_mutext = 0,*/ created_mutexe = 0,
created_lock = 0, created_signal = 0, created_debug = 0,
- created_misdn = 0;
+ created_misdn = 0, created_message = 0;
char tracetext[256], lock[128];
char options_error[256];
int polling = 0;
/* init message */
init_message();
+ created_message = 1;
/*** main loop ***/
SPRINT(tracetext, "%s %s started, waiting for calls...", NAME, VERSION_STRING);
end_trace();
ret=0;
- /* clean messacleane */
- cleanup_message();
-
/* free all */
free:
}
/* destroy objects */
-
while(port_first) {
debug_count++;
delete port_first;
PDEBUG(DEBUG_MSG, "freed %d pending messages\n", i);
}
+ /* clean messages */
+ if (created_message)
+ cleanup_message();
+
/* free tones */
if (toneset_first)
free_tones();
extern FILE *debug_fp;
-#define PDEBUG(mask, fmt, arg...) _printdebug(__FUNCTION__, __LINE__, mask, fmt, ## arg)
-#define PERROR(fmt, arg...) _printerror(__FUNCTION__, __LINE__, fmt, ## arg)
-#define PDEBUG_RUNTIME(mask, fmt, arg...) _printdebug(NULL, 0, mask, fmt, ## arg)
-#define PERROR_RUNTIME(fmt, arg...) _printerror(NULL, 0, fmt, ## arg)
-void _printdebug(const char *function, int line, unsigned int mask, const char *fmt, ...);
-void _printerror(const char *function, int line, const char *fmt, ...);
+#define PDEBUG(mask, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, mask, fmt, ## arg)
+#define PERROR(fmt, arg...) _printerror(__FILE__, __FUNCTION__, __LINE__, fmt, ## arg)
+#define PDEBUG_RUNTIME(mask, fmt, arg...) _printdebug(NULL, NULL, 0, mask, fmt, ## arg)
+#define PERROR_RUNTIME(fmt, arg...) _printerror(NULL, NULL, 0, fmt, ## arg)
+void _printdebug(const char *file, const char *function, int line, unsigned int mask, const char *fmt, ...);
+void _printerror(const char *file, const char *function, int line, const char *fmt, ...);
#define DEBUG_FUNC
-void debug(const char *function, int line, const char *prefix, char *buffer);
+void debug(const char *file, const char *function, int line, const char *prefix, char *buffer);
#define DEBUG_CONFIG 0x0001
#define DEBUG_MSG 0x0002
}
/* attaches a message to the end of the message chain */
-void message_put(struct lcr_msg *message)
+void _message_put(struct lcr_msg *message, const char *file, int line)
{
if (message->id_to == 0) {
PDEBUG(DEBUG_MSG, "message %s not written, because destination is 0.\n", messages_txt[message->type]);
return;
}
- if ((options.deb&DEBUG_MSG) && message->type != MESSAGE_DATA)
- PDEBUG(DEBUG_MSG, "message %s written from %ld to %ld (memory %x)\n", messages_txt[message->type], message->id_from, message->id_to, message);
+ if ((options.deb & DEBUG_MSG))
+ PDEBUG(DEBUG_MSG, "message %s written from %ld to %ld (memory %x at file %s, line %d)\n", messages_txt[message->type], message->id_from, message->id_to, message, file, line);
*messagepointer_end = message;
messagepointer_end = &(message->next);
message->keep = 0;
- if ((options.deb&DEBUG_MSG) && message->type != MESSAGE_DATA)
-
+ if ((options.deb & DEBUG_MSG))
PDEBUG(DEBUG_MSG, "message %s reading from %ld to %ld (memory %x)\n", messages_txt[message->type], message->id_from, message->id_to, message);
return(message);
enum { /* isdnsignal */
mISDNSIGNAL_VOLUME, /* change volume */
mISDNSIGNAL_CONF, /* joint/split conference */
- mISDNSIGNAL_JOINDATA, /* data required by join instance */
mISDNSIGNAL_ECHO, /* enable/disable echoe */
mISDNSIGNAL_DELAY, /* use delay or adaptive jitter */
};
int len;
};
-#define ISDN_TRANSMIT 256
-/* DATA */
-struct param_data {
- unsigned char data[ISDN_TRANSMIT]; /* audio data */
- int len; /* audio data */
-};
-
struct param_play {
char file[512]; /* file name */
int offset; /* offset to start file at (in seconds) */
int tx_gain;
int rx_gain;
int conf;
- int joindata;
int tone;
int echo;
int delay;
int state; /* MESSAGE_TIMEOUT */
int knock; /* MESSAGE_KNOCK 0=off !0=on */
int audiopath; /* MESSAGE_audiopath see RELATION_CHANNEL_* (join.h) */
- struct param_data data; /* MESSAGE_DATA */
struct param_play play; /* MESSAGE_VBOX_PLAY */
int speed; /* MESSAGE_VBOX_PLAY_SPEED */
struct param_counter counter; /* MESSAGE_TONE_COUNTER */
struct param_hello hello; /* MESSAGE_HELLO */
struct param_bchannel bchannel; /* MESSAGE_BCHANNEL */
struct param_newref newref; /* MESSAGE_NEWREF */
+ unsigned int bridge_id; /* MESSAGE_BRIDGE */
};
enum { /* message flow */
MESSAGE_SUSPEND, /* suspend port */
MESSAGE_RESUME, /* resume port */
MESSAGE_AUDIOPATH, /* set status of audio path to endpoint (to call, audio is also set) */
-// MESSAGE_REMOTE_AUDIO, /* tell remote to set audio status */
MESSAGE_PATTERN, /* pattern information tones available */
MESSAGE_NOPATTERN, /* pattern information tones unavailable */
MESSAGE_CRYPT, /* encryption message */
- MESSAGE_DATA, /* audio/hdlc data */
MESSAGE_VBOX_PLAY, /* play recorded file */
MESSAGE_VBOX_PLAY_SPEED,/* change speed of file */
MESSAGE_VBOX_TONE, /* set answering VBOX tone */
MESSAGE_BCHANNEL, /* request/assign/remove bchannel */
MESSAGE_HELLO, /* hello message for remote application */
MESSAGE_NEWREF, /* special message to create and inform ref */
+ MESSAGE_BRIDGE, /* control port bridge */
};
#define MESSAGES static const char *messages_txt[] = { \
"MESSAGE_SUSPEND", \
"MESSAGE_RESUME", \
"MESSAGE_AUDIOPATH", \
-/* "MESSAGE_REMOTE_AUDIO",*/ \
"MESSAGE_PATTERN", \
"MESSAGE_NOPATTERN", \
"MESSAGE_CRYPT", \
- "MESSAGE_DATA", \
"MESSAGE_VBOX_PLAY", \
"MESSAGE_VBOX_PLAY_SPEED", \
"MESSAGE_VBOX_TONE", \
"MESSAGE_BCHANNEL", \
"MESSAGE_HELLO", \
"MESSAGE_NEWREF", \
+ "MESSAGE_BRIDGE", \
};
struct lcr_msg *message_create(int id_from, int id_to, int flow, int type);
-void message_put(struct lcr_msg *message);
+#define message_put(m) _message_put(m, __FILE__, __LINE__)
+void _message_put(struct lcr_msg *message, const char *file, int line);
struct lcr_msg *message_forward(int id_from, int id_to, int flow, union parameter *param);
struct lcr_msg *message_get(void);
void message_free(struct lcr_msg *message);
unsigned int port_serial = 1; /* must be 1, because 0== no port */
+struct port_bridge *p_bridge_first;
+
+static void remove_bridge(struct port_bridge *bridge, class Port *port);
/* free epointlist relation
*/
{
class Port *temp, **tempp;
- PDEBUG(DEBUG_PORT, "new port of type %d, name '%s'\n", type, portname);
+ PDEBUG(DEBUG_PORT, "new port of type 0x%x, name '%s'\n", type, portname);
/* initialize object */
if (settings)
memset(&p_redirinfo, 0, sizeof(p_redirinfo));
memset(&p_capainfo, 0, sizeof(p_capainfo));
p_echotest = 0;
+ p_bridge = 0;
/* call recording */
p_record = NULL;
class Port *temp, **tempp;
struct lcr_msg *message;
+ PDEBUG(DEBUG_PORT, "removing port of type 0x%x, name '%s'\n", p_type, p_name);
+
+ if (p_bridge) {
+ PDEBUG(DEBUG_PORT, "Removing us from bridge %u\n", p_bridge->bridge_id);
+ remove_bridge(p_bridge, this);
+ }
+
if (p_record)
close_record(0, 0);
classuse--;
- PDEBUG(DEBUG_PORT, "removing port of type %d, name '%s'\n", p_type, p_name);
-
/* disconnect port from endpoint */
while(p_epointlist) {
/* send disconnect */
}
-/* endpoint sends messages to the port
- * this is called by the message_epoint inherited by child classes
- * therefor a return=1 means: stop, no more processing
+/* Endpoint sends messages to the port
+ * This is called by the message_epoint, inherited by child classes.
+ * Therefor a return 1 means: "already handled here"
*/
//extern struct lcr_msg *dddebug;
int Port::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
{
/* check if we got audio data from one remote port */
switch(message_id) {
- case MESSAGE_TONE: /* play tone */
- PDEBUG(DEBUG_PORT, "PORT(%s) isdn port with (caller id %s) setting tone '%s' dir '%s'\n", p_name, p_callerinfo.id, param->tone.name, param->tone.dir);
+ case MESSAGE_TONE: /* play tone */
+ PDEBUG(DEBUG_PORT, "PORT(%s) setting tone '%s' dir '%s'\n", p_name, param->tone.name, param->tone.dir);
set_tone(param->tone.dir,param->tone.name);
- return(1);
+ return 1;
- case MESSAGE_VBOX_TONE: /* play tone of answering machine */
+ case MESSAGE_VBOX_TONE: /* play tone of answering machine */
PDEBUG(DEBUG_PORT, "PORT(%s) set answering machine tone '%s' '%s'\n", p_name, param->tone.dir, param->tone.name);
set_vbox_tone(param->tone.dir, param->tone.name);
- return(1);
+ return 1;
- case MESSAGE_VBOX_PLAY: /* play recording of answering machine */
+ case MESSAGE_VBOX_PLAY: /* play recording of answering machine */
PDEBUG(DEBUG_PORT, "PORT(%s) set answering machine file to play '%s' (offset %d seconds)\n", p_name, param->play.file, param->play.offset);
set_vbox_play(param->play.file, param->play.offset);
- return(1);
+ return 1;
- case MESSAGE_VBOX_PLAY_SPEED: /* set speed of playback (recording of answering machine) */
+ case MESSAGE_VBOX_PLAY_SPEED: /* set speed of playback (recording of answering machine) */
PDEBUG(DEBUG_PORT, "PORT(%s) set answering machine playback speed %d (times)\n", p_name, param->speed);
set_vbox_speed(param->speed);
- return(1);
+ return 1;
+ case MESSAGE_BRIDGE: /* create / join / leave / destroy bridge */
+ PDEBUG(DEBUG_PORT, "PORT(%s) bridging to id %d\n", p_name, param->bridge_id);
+ bridge(param->bridge_id);
+ return 1;
}
- return(0);
+ return 0;
}
{
}
+
+/*
+ * bridge handling
+ */
+
+static void remove_bridge(struct port_bridge *bridge, class Port *port)
+{
+ struct port_bridge **temp = &p_bridge_first;
+ while (*temp) {
+ if (*temp == bridge) {
+ int remove = 0;
+
+ /* Remove us from bridge. If bridge is empty, remove it completely. */
+ if (bridge->sunrise == port) {
+ bridge->sunrise = NULL;
+ if (!bridge->sunset)
+ remove = 1;
+ }
+ if (bridge->sunset == port) {
+ bridge->sunset = NULL;
+ if (!bridge->sunrise)
+ remove = 1;
+ }
+ if (remove) {
+ PDEBUG(DEBUG_PORT, "Remove bridge %u\n", bridge->bridge_id);
+ *temp = bridge->next;
+ FREE(bridge, sizeof(struct port_bridge));
+ memuse--;
+ }
+ return;
+ }
+ temp = &((*temp)->next);
+ }
+ PERROR("Bridge %p not found in list\n", bridge);
+}
+
+void Port::bridge(unsigned int bridge_id)
+{
+ /* Remove bridge, if we leave bridge or if we join a different bridge. */
+ if (p_bridge && bridge_id != p_bridge->bridge_id) {
+ PDEBUG(DEBUG_PORT, "Remove port %u from bridge %u, because out new bridge is %u\n", p_serial, p_bridge->bridge_id, bridge_id);
+ remove_bridge(p_bridge, this);
+ p_bridge = NULL;
+ }
+
+ /* if we leave bridge */
+ if (!bridge_id)
+ return;
+
+ /* find bridge */
+ if (!p_bridge) {
+ struct port_bridge *temp = p_bridge_first;
+
+ while (temp) {
+ if (temp->bridge_id == bridge_id)
+ break;
+ temp = temp->next;
+ }
+ p_bridge = temp;
+ if (p_bridge)
+ PDEBUG(DEBUG_PORT, "Port %d found existing bridge %u.\n", p_serial, p_bridge->bridge_id);
+ }
+
+ /* create bridge */
+ if (!p_bridge) {
+ struct port_bridge **temp = &p_bridge_first;
+
+ p_bridge = (struct port_bridge *) MALLOC(sizeof(struct port_bridge));
+ memuse++;
+ p_bridge->bridge_id = bridge_id;
+ p_bridge->sunrise = this;
+
+ /* attach bridge instance to list */
+ while (*temp)
+ temp = &((*temp)->next);
+ *temp = p_bridge;
+ PDEBUG(DEBUG_PORT, "Port %d creating not existing bridge %u.\n", p_serial, p_bridge->bridge_id);
+ }
+
+ /* already joined */
+ if (p_bridge->sunrise == this || p_bridge->sunset == this)
+ return;
+
+ /* join bridge */
+ if (!p_bridge->sunrise) {
+ p_bridge->sunrise = this;
+ return;
+ }
+ if (!p_bridge->sunset) {
+ p_bridge->sunset = this;
+ return;
+ }
+
+ PERROR("Bridge ID %u cannot be joined by port %u, because it is already occupied by ports %u and %u.\n", p_bridge->bridge_id, p_serial, p_bridge->sunrise->p_serial, p_bridge->sunset->p_serial);
+ p_bridge = NULL;
+}
+
+/* send data to remote Port */
+int Port::bridge_tx(unsigned char *data, int len)
+{
+ class Port *to_port = NULL;
+
+ /* get remote port from bridge */
+ if (!p_bridge)
+ return -EINVAL;
+ if (p_bridge->sunrise == this)
+ to_port = p_bridge->sunset;
+ if (p_bridge->sunset == this)
+ to_port = p_bridge->sunrise;
+ if (!to_port)
+ return -EINVAL;
+
+// printf("Traffic: %u -> %u (bridge %u)\n", p_serial, to_port->p_serial, p_bridge->bridge_id);
+ return to_port->bridge_rx(data, len);
+}
+
+/* receive data from remote Port (dummy, needs to be inherited) */
+int Port::bridge_rx(unsigned char *data, int len)
+{
+ return 0; /* datenklo */
+}
+
#define RECORD_BUFFER_LENGTH 1024 // must be a binary border & must be greater 256, because 256 will be written if buffer overflows
#define RECORD_BUFFER_MASK 1023
+#define PORT_TRANSMIT 256 // how much to transmit via bridge, if it is not defined by received data length
+
/* structure of epoint_list */
struct epoint_list {
struct epoint_list *next;
int no_seconds;
};
+/* port bridge instance */
+struct port_bridge {
+ struct port_bridge *next; /* next bridge node */
+ unsigned int bridge_id; /* unique ID to identify bridge */
+ class Port *sunrise; /* one side of the bridge */
+ class Port *sunset; /* other side of the bridge */
+};
+
+extern struct port_bridge *p_bridge_first;
+
/* generic port class */
class Port
{
/* endpoint relation */
struct epoint_list *p_epointlist; /* endpoint relation */
+ /* audio bridging */
+ struct port_bridge *p_bridge; /* linked to a port bridge or NULL */
+ void bridge(unsigned int bridge_id); /* join a bridge */
+ int bridge_tx(unsigned char *data, int len); /* used to transmit data to remote port */
+ virtual int bridge_rx(unsigned char *data, int len); /* function to be inherited, so data is received */
+
/* state */
int p_state; /* state of port */
void new_state(int state); /* set new state */
#endif
}
-void trigger_work(struct lcr_work *work)
+void _trigger_work(struct lcr_work *work, const char *func)
{
if (!work->inuse) {
- FATAL("Work not added\n");
+ FATAL("Work not added, (called from func %s)\n", func);
}
/* event already triggered */
int _add_work(struct lcr_work *work, int (*cb)(struct lcr_work *work, void *instance, int index), void *instance, int index, const char *func);
#define del_work(a) _del_work(a, __func__);
void _del_work(struct lcr_work *work, const char *func);
-void trigger_work(struct lcr_work *work);
+#define trigger_work(a) _trigger_work(a, __func__);
+void _trigger_work(struct lcr_work *work, const char *func);
class Endpoint *epoint;
if (PmISDN::message_epoint(epoint_id, message_id, param))
- return(1);
+ return 1;
epoint = find_epoint_id(epoint_id);
if (!epoint) {
PDEBUG(DEBUG_SIP, "PORT(%s) no endpoint object found where the message is from.\n", p_name);
- return(0);
+ return 0;
}
switch(message_id) {
- case MESSAGE_DATA:
- return(1);
-
case MESSAGE_ALERTING: /* call is ringing on LCR side */
if (p_state != PORT_STATE_IN_SETUP
&& p_state != PORT_STATE_IN_PROCEEDING)
add_trace("respond", "value", "180 Ringing");
end_trace();
new_state(PORT_STATE_IN_ALERTING);
- return(1);
+ return 1;
case MESSAGE_CONNECT: /* call is connected on LCR side */
if (p_state != PORT_STATE_IN_SETUP
&& p_state != PORT_STATE_IN_ALERTING)
return 0;
message_connect(epoint_id, message_id, param);
- return(1);
+ return 1;
case MESSAGE_DISCONNECT: /* call has been disconnected */
case MESSAGE_RELEASE: /* call has been released */
message_release(epoint_id, message_id, param);
- return(1);
+ return 1;
case MESSAGE_SETUP: /* dial-out command received from epoint */
message_setup(epoint_id, message_id, param);
- return(1);
+ return 1;
default:
PDEBUG(DEBUG_SIP, "PORT(%s) SP port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
}
- return(0);
+ return 0;
}
int Psip::parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port)
if (string) {
/* process debug */
if (options.deb)
- debug(NULL, 0, "TRACE", string);
+ debug(NULL, NULL, 0, "TRACE", string);
/* process log */
if (options.log[0]) {
fp = fopen(options.log, "a");
{
struct lcr_msg *message;
unsigned int tosend;
- unsigned char buffer[ISDN_TRANSMIT];
+ unsigned char buffer[PORT_TRANSMIT + PORT_TRANSMIT]; /* use twice of the buffer, so we can send more in case of delayed main loop execution */
class Endpoint *epoint;
int temp;
struct timeval current_time;
/* set time the first time */
if (!p_vbox_audio_start)
- p_vbox_audio_start = now - ISDN_TRANSMIT;
+ p_vbox_audio_start = now - PORT_TRANSMIT;
/* calculate the number of bytes */
tosend = (unsigned int)(now - p_vbox_audio_start) - p_vbox_audio_transferred;
tosend = sizeof(buffer);
/* schedule next event */
- temp = ISDN_TRANSMIT + ISDN_TRANSMIT - tosend;
+ temp = PORT_TRANSMIT + PORT_TRANSMIT - tosend;
if (temp < 0)
temp = 0;
schedule_timer(&p_vbox_announce_timer, 0, temp*125);
} else {
if (p_record)
record(buffer, tosend, 0); // from down
- message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA);
- message->param.data.len = tosend;
- memcpy(message->param.data.data, buffer, tosend);
- message_put(message);
+ /* send to remote, if bridged */
+ bridge_tx(buffer, tosend);
}
}
+int VBoxPort::bridge_rx(unsigned char *data, int len)
+{
+ if (p_record)
+ record(data, len, 1); // from up
+ return 0;
+}
/*
* endpoint sends messages to the vbox port
}
switch(message_id) {
- case MESSAGE_DATA:
- record(param->data.data, param->data.len, 1); // from up
- return(1);
-
case MESSAGE_DISCONNECT: /* call has been disconnected */
new_state(PORT_STATE_OUT_DISCONNECT);
vbox_trace_header(this, "DISCONNECT to VBox", DIRECTION_OUT);
int message_epoint(unsigned int epoint_id, int message, union parameter *param);
void send_announcement(void);
+ int bridge_rx(unsigned char *data, int len);
+
private:
struct EndpointAppPBX *p_vbox_apppbx; /* pbx application */
unsigned int p_vbox_timeout; /* timeout for recording */