summaryrefslogtreecommitdiffstats
path: root/ss5.cpp
diff options
context:
space:
mode:
authorAndreas Eversberg2009-09-26 13:20:29 +0200
committerAndreas Eversberg2009-09-26 13:20:29 +0200
commit323cbc387b1a068f8e2bcfd1034666406ba18c93 (patch)
treeee251a238dde61870ef4bef687e7fcb6fff1f749 /ss5.cpp
parentSome minor corrections on default/interface.conf (diff)
downloadlcr-323cbc387b1a068f8e2bcfd1034666406ba18c93.tar.gz
lcr-323cbc387b1a068f8e2bcfd1034666406ba18c93.tar.xz
lcr-323cbc387b1a068f8e2bcfd1034666406ba18c93.zip
Added support for signalling system no. 5.
More infos will follow on the isdn4linux mailing list. modified: Makefile.am modified: Makefile.in modified: README modified: apppbx.cpp modified: configure modified: configure.ac modified: default/options.conf modified: dss1.cpp modified: ie.cpp modified: interface.c modified: interface.h modified: lcradmin.c modified: lcrsocket.h modified: mISDN.cpp modified: mISDN.h modified: main.c modified: main.h modified: port.h modified: socket_server.c new file: ss5.cpp new file: ss5.h new file: ss5_decode.c new file: ss5_decode.h new file: ss5_encode.c new file: ss5_encode.h
Diffstat (limited to 'ss5.cpp')
-rw-r--r--ss5.cpp1972
1 files changed, 1972 insertions, 0 deletions
diff --git a/ss5.cpp b/ss5.cpp
new file mode 100644
index 0000000..d038769
--- /dev/null
+++ b/ss5.cpp
@@ -0,0 +1,1972 @@
+/*****************************************************************************\
+** **
+** LCR **
+** **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg **
+** **
+** mISDN ss5 **
+** **
+\*****************************************************************************/
+
+/*
+ * STATES:
+ *
+ * there are three types of states
+ *
+ * - the port state (p_state): used for current call state
+ * - the ss5 state (p_m_s_state): used for current tone
+ * - the ss5 signal state (p_m_s_signal): used for current signal state of current tone
+ *
+ * the port state differs from isdn state:
+ *
+ * - PORT_STATE_IDLE: used until number is complete. outgoing overlap dialing is received in this state.
+ * - PORT_STATE_OUT_SETUP: the seizing procedure is started.
+ * - PORT_STATE_OUT_OVERLAP: the transmitter is sending the digits.
+ * - PORT_STATE_OUT_PROCEEDING: the digits are sent, we wait until someone answers.
+ * - PORT_STATE_CONNECT: a call is answered on either side.
+ * - PORT_STATE_IN_SETUP: the seizing is received, we wait for the first digit.
+ * - PORT_STATE_IN_OVERLAP: the digits are received.
+ * - PORT_STATE_IN_PROCEEDING: the number is complete, a SETUP is indicated.
+ * - PORT_STATE_IN_DISCONNECT: a clear-back was received, an DISCONNECT is indicated.
+ * - PORT_STATE_RELEASE: the clear forward procedure is started.
+ *
+ */
+
+#include "main.h"
+
+/* ss5 signal states */
+enum {
+ SS5_STATE_IDLE, /* no signal */
+ SS5_STATE_SEIZING, /* seizing */
+ SS5_STATE_PROCEED_TO_SEND, /* proceed-to-send */
+ SS5_STATE_BUSY_FLASH, /* busy-flash / clear back */
+ SS5_STATE_ACK_BUSY_FLASH, /* acknowledge of busy/answer/clear-back */
+ SS5_STATE_ANSWER, /* answer */
+ SS5_STATE_ACK_ANSWER, /* acknowledge of busy/answer/clear-back */
+ SS5_STATE_FORWARD_TRANSFER, /* forward transfer */
+ SS5_STATE_CLEAR_BACK, /* clear-back */
+ SS5_STATE_ACK_CLEAR_BACK, /* acknowledge of busy/answer/clear-back */
+ SS5_STATE_CLEAR_FORWARD, /* clear-forward */
+ SS5_STATE_RELEASE_GUARD, /* release-guard */
+ SS5_STATE_DIAL_OUT, /* dialing state (transmitter) */
+ SS5_STATE_DIAL_IN, /* dialing state (receiver) */
+ SS5_STATE_DIAL_IN_PULSE, /* dialing state (receiver with pulses) */
+ SS5_STATE_DELAY, /* after signal wait until next signal can be sent */
+ SS5_STATE_DOUBLE_SEIZE, /* in case of a double seize, we make the remote size recognize it */
+};
+const char *ss5_state_name[] = {
+ "STATE_IDLE",
+ "STATE_SEIZING",
+ "STATE_PROCEED_TO_SEND",
+ "STATE_BUSY_FLASH",
+ "STATE_ACK_BUSY_FLASH",
+ "STATE_ANSWER",
+ "STATE_ACK_ANSWER",
+ "STATE_FORWARD_TRANSFER",
+ "STATE_CLEAR_BACK",
+ "STATE_ACK_CLEAR_BACK",
+ "STATE_CLEAR_FORWARD",
+ "STATE_RELEASE_GUARD",
+ "STATE_DIAL_OUT",
+ "STATE_DIAL_IN",
+ "STATE_DIAL_IN_PULSE",
+ "STATE_DELAY",
+ "STATE_DOUBLE_SEIZE",
+};
+
+enum {
+ SS5_SIGNAL_NULL,
+ /* sending signal states */
+ SS5_SIGNAL_SEND_ON, /* sending signal, waiting for acknowledge */
+ SS5_SIGNAL_SEND_ON_RECOG, /* sending signal, receiving ack, waiting for recogition timer */
+ SS5_SIGNAL_SEND_OFF, /* silence, receiving ack, waiting for stop */
+ /* receiving signal states */
+ SS5_SIGNAL_RECEIVE_RECOG, /* receiving signal, waiting for recognition timer */
+ SS5_SIGNAL_RECEIVE, /* receiving signal / send ack, waiting for stop */
+ SS5_SIGNAL_DELAY, /* delay after release guard to prevent ping-pong */
+ /* sending / receiving digit states */
+ SS5_SIGNAL_DIGIT_PAUSE, /* pausing before sending (next) digit */
+ SS5_SIGNAL_DIGIT_ON, /* sending digit */
+ SS5_SIGNAL_PULSE_OFF, /* make */
+ SS5_SIGNAL_PULSE_ON, /* break */
+};
+const char *ss5_signal_name[] = {
+ "NULL",
+ "SIGNAL_SEND_ON",
+ "SIGNAL_SEND_ON_RECOG",
+ "SIGNAL_SEND_OFF",
+ "SIGNAL_RECEIVE_RECOG",
+ "SIGNAL_RECEIVE",
+ "SIGNAL_DELAY",
+ "SIGNAL_DIGIT_PAUSE",
+ "SIGNAL_DIGIT_ON",
+ "SIGNAL_PULSE_OFF",
+ "SIGNAL_PULSE_ON",
+};
+
+/* ss5 signal timers (in samples) */
+#define SS5_TIMER_AFTER_SIGNAL (100*8) /* wait after signal is terminated */
+#define SS5_TIMER_KP (100*8) /* duration of KP1 or KP2 digit */
+#define SS5_TIMER_DIGIT (55*8) /* duration of all other digits */
+#define SS5_TIMER_PAUSE (55*8) /* pause between digits */
+#define SS5_TIMER_FORWARD (850*8) /* forward transfer length */
+#define SS5_TIMER_RECOG_SEIZE (40*8) /* recognition time of seizing / proceed-to-send signal */
+#define SS5_TIMER_RECOG_OTHER (125*8) /* recognition time of all other f1/f2 signals */
+#define SS5_TIMER_SIGNAL_LOSS (15*8) /* minimum time of signal loss for a continous signal */
+#define SS5_TIMER_DOUBLE_SEIZE (850*8) /* double seize length */
+#define SS5_TIMER_RELEASE_GUARD (850*8) /* be sure to release after clear-forward */
+#define SS5_TIMER_RELEASE_MAX (2000*8)/* maximum time for release guard to prevent 'double-releasing' */
+#define SS5_TIMER_RELEASE_DELAY (4000*8)/* wait after release guard to prevent ping-pong */
+#define BELL_TIMER_BREAK (50*8) /* loop open, tone */
+#define BELL_TIMER_MAKE (50*8) /* loop closed, no tone */
+#define BELL_TIMER_PAUSE (800*8) /* interdigit delay */
+#define BELL_TIMER_RECOG_HANGUP (200*8) /* time to recognize hangup */
+#define BELL_TIMER_RECOG_END (300*8) /* recognize end of digit */
+
+/* ss5 timers */
+#define SS5_TIMER_OVERLAP 5.0 /* timeout for overlap digits received on outgoing exchange */
+
+
+/*
+ * ss5 trace header
+ */
+enum { /* even values are indications, odd values are requests */
+ SS5_SEIZING_IND,
+ SS5_SEIZING_REQ,
+ SS5_PROCEED_TO_SEND_IND,
+ SS5_PROCEED_TO_SEND_REQ,
+ SS5_BUSY_FLASH_IND,
+ SS5_BUSY_FLASH_REQ,
+ SS5_ANSWER_IND,
+ SS5_ANSWER_REQ,
+ SS5_CLEAR_BACK_IND,
+ SS5_CLEAR_BACK_REQ,
+ SS5_CLEAR_FORWARD_IND,
+ SS5_CLEAR_FORWARD_REQ,
+ SS5_RELEASE_GUARD_IND,
+ SS5_RELEASE_GUARD_REQ,
+ SS5_ACKNOWLEDGE_IND,
+ SS5_ACKNOWLEDGE_REQ,
+ SS5_DOUBLE_SEIZURE_IND,
+ SS5_DOUBLE_SEIZURE_REQ,
+ SS5_DIALING_IND,
+ SS5_DIALING_REQ,
+ SS5_FORWARD_TRANSFER_IND,
+ SS5_FORWARD_TRANSFER_REQ,
+};
+static struct isdn_message {
+ const char *name;
+ unsigned int value;
+} ss5_message[] = {
+ {"SEIZING RECEIVED", SS5_SEIZING_IND},
+ {"SEIZING SENDING", SS5_SEIZING_REQ},
+ {"PROCEED-TO-SEND RECEIVED", SS5_PROCEED_TO_SEND_IND},
+ {"PROCEED-TO-SEND SENDING", SS5_PROCEED_TO_SEND_REQ},
+ {"BUSY-FLASH RECEIVED", SS5_BUSY_FLASH_IND},
+ {"BUSY-FLASH SENDING", SS5_BUSY_FLASH_REQ},
+ {"ANSWER RECEIVED", SS5_ANSWER_IND},
+ {"ANSWER SENDING", SS5_ANSWER_REQ},
+ {"CLEAR-BACK RECEIVED", SS5_CLEAR_BACK_IND},
+ {"CLEAR-BACK SENDING", SS5_CLEAR_BACK_REQ},
+ {"CLEAR-FORWARD RECEIVED", SS5_CLEAR_FORWARD_IND},
+ {"CLEAR-FORWARD SENDING", SS5_CLEAR_FORWARD_REQ},
+ {"RELEASE-GUARD RECEIVED", SS5_RELEASE_GUARD_IND},
+ {"RELEASE-GUARD SENDING", SS5_RELEASE_GUARD_REQ},
+ {"ACKNOWLEDGE RECEIVED", SS5_ACKNOWLEDGE_IND},
+ {"ACKNOWLEDGE SENDING", SS5_ACKNOWLEDGE_REQ},
+ {"DOUBLE-SEIZURE RECEIVED", SS5_DOUBLE_SEIZURE_IND},
+ {"DOUBLE-SEIZURE SENDING", SS5_DOUBLE_SEIZURE_REQ},
+ {"DIALING RECEIVED", SS5_DIALING_IND},
+ {"DIALING SENDING", SS5_DIALING_REQ},
+ {"FORWARD-TRANSFER RECEIVED", SS5_FORWARD_TRANSFER_IND},
+ {"FORWARD-TRANSFER SENDING", SS5_FORWARD_TRANSFER_REQ},
+ {NULL, 0},
+};
+static void ss5_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned int msg, int channel)
+{
+ int i;
+ char msgtext[64];
+
+ SCPY(msgtext, "<<UNKNOWN MESSAGE>>");
+ /* select message and primitive text */
+ i = 0;
+ while(ss5_message[i].name) {
+// if (msg == L3_NOTIFY_REQ) printf("val = %x %s\n", isdn_message[i].value, isdn_message[i].name);
+ if (ss5_message[i].value == msg) {
+ SCPY(msgtext, ss5_message[i].name);
+ break;
+ }
+ i++;
+ }
+
+ /* init trace with given values */
+ start_trace(mISDNport?mISDNport->portnum:-1,
+ mISDNport?(mISDNport->ifport?mISDNport->ifport->interface:NULL):NULL,
+ port?numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype, options.national, options.international):NULL,
+ port?port->p_dialinginfo.id:NULL,
+ (msg&1)?DIRECTION_OUT:DIRECTION_IN,
+ CATEGORY_CH,
+ port?port->p_serial:0,
+ msgtext);
+ add_trace("channel", NULL, "%d", channel);
+ switch (port->p_type) {
+ case PORT_TYPE_SS5_OUT:
+ add_trace("state", NULL, "outgoing");
+ break;
+ case PORT_TYPE_SS5_IN:
+ add_trace("state", NULL, "incomming");
+ break;
+ default:
+ add_trace("state", NULL, "idle");
+ break;
+ }
+}
+
+
+/*
+ * changes release tone int busy signal
+ * this makes the line more authentic
+ */
+void Pss5::set_tone(const char *dir, const char *name)
+{
+ if (name && !strcmp(name, "cause_10"))
+ name = "busy";
+
+ PmISDN::set_tone(dir, name);
+}
+
+/*
+ * creation of static channels
+ */
+void ss5_create_channel(struct mISDNport *mISDNport, int i)
+{
+ class Pss5 *ss5port;
+ char portname[32];
+ struct port_settings port_settings;
+
+ SPRINT(portname, "%s-%d", mISDNport->name, i+1);
+
+ memset(&port_settings, 0, sizeof(port_settings));
+ SCPY(port_settings.tones_dir, options.tones_dir);
+
+ ss5port = new Pss5(PORT_TYPE_SS5_IDLE, mISDNport, portname, &port_settings, i + (i>=15) + 1, 1, B_MODE_TRANSPARENT);
+ if (!ss5port)
+ FATAL("No memory for Pss5 class.\n");
+ if (!ss5port->p_m_b_channel)
+ FATAL("No bchannel on given index.\n");
+
+ /* connect channel */
+ bchannel_event(mISDNport, ss5port->p_m_b_index, B_EVENT_USE);
+
+}
+
+
+/*
+ * hunt for a free line
+ * this function returns a port object in idle state.
+ */
+class Pss5 *ss5_hunt_line(struct mISDNport *mISDNport)
+{
+ int i;
+ class Port *port;
+ class Pss5 *ss5port = NULL;
+ struct select_channel *selchannel;
+
+ PDEBUG(DEBUG_SS5, "Entered name=%s\n", mISDNport->name);
+ selchannel = mISDNport->ifport->out_channel;
+ while(selchannel) {
+ switch(selchannel->channel) {
+ case CHANNEL_FREE: /* free channel */
+ case CHANNEL_ANY: /* any channel */
+ for (i = 0; i < mISDNport->b_num; i++) {
+ port = mISDNport->b_port[i];
+ PDEBUG(DEBUG_SS5, "Checking port %p on index\n", port, i);
+ if (!port)
+ continue;
+ if (port->p_type == PORT_TYPE_SS5_IN || port->p_type == PORT_TYPE_SS5_OUT)
+ PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because port not idle type.\n",
+ mISDNport->name, i);
+ if (port->p_type != PORT_TYPE_SS5_IDLE)
+ continue;
+ ss5port = (class Pss5 *)port;
+ /* is really idle ? */
+ if (ss5port->p_state == PORT_STATE_IDLE
+ && ss5port->p_m_s_state == SS5_STATE_IDLE)
+ return ss5port;
+ PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because p_state=%d, ss5_state=%d.\n",
+ mISDNport->name, i, ss5port->p_state,ss5port->p_m_s_state);
+ }
+ PDEBUG(DEBUG_SS5, "no free interface\n");
+ return NULL;
+
+ case CHANNEL_NO:
+ break;
+
+ default:
+ if (selchannel->channel<1 || selchannel->channel==16)
+ break;
+ i = selchannel->channel-1-(selchannel->channel>=17);
+ if (i >= mISDNport->b_num)
+ break;
+ port = mISDNport->b_port[i];
+ if (!port)
+ break;
+ if (port->p_type == PORT_TYPE_SS5_IN || port->p_type == PORT_TYPE_SS5_OUT)
+ PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because port not idle type.\n",
+ mISDNport->name, i);
+ if (port->p_type != PORT_TYPE_SS5_IDLE)
+ break;
+ ss5port = (class Pss5 *)port;
+ /* is really idle ? */
+ if (ss5port->p_state == PORT_STATE_IDLE
+ && ss5port->p_m_s_state == SS5_STATE_IDLE)
+ return ss5port;
+ PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because p_state=%d, ss5_state=%d.\n",
+ mISDNport->name, i, ss5port->p_state,ss5port->p_m_s_state);
+ }
+ selchannel = selchannel->next;
+ }
+ PDEBUG(DEBUG_SS5, "no free interface in channel list\n");
+ return NULL;
+}
+
+
+/*
+ * constructor
+ */
+Pss5::Pss5(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : PmISDN(type, mISDNport, portname, settings, channel, exclusive, mode)
+{
+ p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN;
+ p_m_s_state = SS5_STATE_IDLE;
+ p_m_s_signal = SS5_SIGNAL_NULL;
+ p_m_s_dial[0] = '\0';
+ p_m_s_digit_i = 0;
+ p_m_s_pulsecount = 0;
+ p_m_s_last_digit = ' ';
+ p_m_s_signal_loss = 0;
+ p_m_s_decoder_count = 0;
+ //p_m_s_decoder_buffer;
+ p_m_s_sample_nr = 0;
+ p_m_s_recog = 0;
+ p_m_s_timer = 0.0;
+ p_m_s_timer_fn = NULL;
+ p_m_s_answer = 0;
+ p_m_s_busy_flash = 0;
+ p_m_s_clear_back = 0;
+ memset(p_m_s_delay_digits, ' ', sizeof(p_m_s_delay_digits));
+ memset(p_m_s_delay_mute, ' ', sizeof(p_m_s_delay_mute));
+
+ /* turn on signalling receiver */
+ inband_receive_on();
+
+ PDEBUG(DEBUG_SS5, "Created new mISDNPort(%s). Currently %d objects use.\n", portname, mISDNport->use);
+}
+
+
+/*
+ * destructor
+ */
+Pss5::~Pss5()
+{
+}
+
+
+/*
+ * change ss5 states
+ */
+void Pss5::_new_ss5_state(int state, const char *func, int line)
+{
+ PDEBUG(DEBUG_SS5, "%s(%s:%d): changing SS5 state from %s to %s\n", p_name, func, line, ss5_state_name[p_m_s_state], ss5_state_name[state]);
+ p_m_s_state = state;
+ p_m_s_signal = SS5_SIGNAL_NULL;
+}
+void Pss5::_new_ss5_signal(int signal, const char *func, int line)
+{
+ if (p_m_s_signal)
+ PDEBUG(DEBUG_SS5, "%s(%s:%d): changing SS5 signal state from %s to %s\n", p_name, func, line, ss5_signal_name[p_m_s_signal], ss5_signal_name[signal]);
+ else
+ PDEBUG(DEBUG_SS5, "%s(%s:%d): changing SS5 signal state to %s\n", p_name, func, line, ss5_signal_name[signal]);
+ p_m_s_signal = signal;
+}
+
+
+/*
+ * signalling receiver
+ *
+ * this function will be called for every audio received.
+ */
+void Pss5::inband_receive(unsigned char *buffer, int len)
+{
+ int count = 0, tocopy, space;
+ char digit;
+
+ again:
+ /* how much to copy ? */
+ tocopy = len - count;
+ space = SS5_DECODER_NPOINTS - p_m_s_decoder_count;
+ if (space < 0)
+ FATAL("p_m_s_decoder_count overflows\n");
+ if (space < tocopy)
+ tocopy = space;
+ /* copy an count */
+ memcpy(p_m_s_decoder_buffer+p_m_s_decoder_count, buffer+count, tocopy);
+ p_m_s_decoder_count += tocopy;
+ count += tocopy;
+ /* decoder buffer not completely filled ? */
+ if (tocopy < space)
+ return;
+
+ /* decode one frame */
+ digit = ss5_decode(p_m_s_decoder_buffer, SS5_DECODER_NPOINTS);
+ p_m_s_decoder_count = 0;
+
+#ifdef DEBUG_DETECT
+ if (p_m_s_last_digit != digit && digit != ' ')
+ PDEBUG(DEBUG_SS5, "%s: detecting signal '%c' start (state=%s signal=%s)\n", p_name, digit, ss5_state_name[p_m_s_state], ss5_signal_name[p_m_s_signal]);
+#endif
+
+ /* ignore short loss of signal */
+ if (p_m_s_signal_loss) {
+ if (digit == ' ') {
+ /* still lost */
+ if (p_m_s_signal_loss >= SS5_TIMER_SIGNAL_LOSS) {
+#ifdef DEBUG_DETECT
+ PDEBUG(DEBUG_SS5, "%s: signal '%c' lost too long\n", p_name, p_m_s_last_digit);
+#endif
+ /* long enough, we stop loss-timer */
+ p_m_s_signal_loss = 0;
+ } else {
+ /* not long enough, so we use last signal */
+ p_m_s_signal_loss += SS5_DECODER_NPOINTS;
+ digit = p_m_s_last_digit;
+ }
+ } else {
+ /* signal is back, we stop timer and store */
+#ifdef DEBUG_DETECT
+ PDEBUG(DEBUG_SS5, "%s: signal '%c' lost, but continues with '%c'\n", p_name, p_m_s_last_digit, digit);
+#endif
+ p_m_s_signal_loss = 0;
+ p_m_s_last_digit = digit;
+ }
+ } else {
+ if (p_m_s_last_digit != ' ' && digit == ' ') {
+#ifdef DEBUG_DETECT
+ PDEBUG(DEBUG_SS5, "%s: signal '%c' lost\n", p_name, p_m_s_last_digit);
+#endif
+ /* restore last digit until signal is really lost */
+ p_m_s_last_digit = digit;
+ /* starting to loose signal */
+ p_m_s_signal_loss = SS5_DECODER_NPOINTS;
+ } else {
+ /* storing last signal, in case it is lost */
+ p_m_s_last_digit = digit;
+ }
+ }
+
+ /* update mute */
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_SUPPRESS)) {
+ int mdigit;
+ memcpy(p_m_s_delay_mute, p_m_s_delay_mute+1, sizeof(p_m_s_delay_mute)-1);
+ p_m_s_delay_mute[sizeof(p_m_s_delay_mute)-1] = digit;
+ mdigit = p_m_s_delay_mute[0];
+ if (p_m_mute) {
+ /* mute is on */
+ if (mdigit != 'A' && mdigit != 'B' && mdigit != 'C')
+ mute_off();
+ } else {
+ /* mute is off */
+ if (mdigit == 'A' || mdigit == 'B' || mdigit == 'C')
+ mute_on();
+ }
+ }
+
+ /* delay decoded tones */
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_DELAY)) {
+ /* shift buffer */
+ memcpy(p_m_s_delay_digits, p_m_s_delay_digits+1, sizeof(p_m_s_delay_digits)-1);
+ /* first in */
+ p_m_s_delay_digits[sizeof(p_m_s_delay_digits)-1] = digit;
+ /* first out */
+ digit = p_m_s_delay_digits[0];
+ }
+
+ /* clear forward is always recognized */
+ if (digit == 'C' && p_m_s_state != SS5_STATE_CLEAR_FORWARD && p_m_s_state != SS5_STATE_RELEASE_GUARD) {
+ switch (p_type) {
+ case PORT_TYPE_SS5_OUT:
+ PDEBUG(DEBUG_SS5, "%s: received release-guard, waiting for recognition\n", p_name);
+ break;
+ case PORT_TYPE_SS5_IN:
+ PDEBUG(DEBUG_SS5, "%s: received clear-forward, waiting for recognition\n", p_name);
+ break;
+ default:
+ PDEBUG(DEBUG_SS5, "%s: received clear-forward in idle state, waiting for recognition\n", p_name);
+ break;
+ }
+ new_state(PORT_STATE_RELEASE);
+ new_ss5_state(SS5_STATE_RELEASE_GUARD);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+ p_m_s_recog = 0;
+ } else
+ switch(p_m_s_state) {
+ case SS5_STATE_IDLE:
+ /* seizing only recognized in port idle state */
+ if (p_state == PORT_STATE_IDLE) {
+ if (digit != 'A')
+ break;
+ seize:
+ PDEBUG(DEBUG_SS5, "%s: received seize, waiting for recognition\n", p_name);
+ p_type = PORT_TYPE_SS5_IN;
+ new_ss5_state(SS5_STATE_PROCEED_TO_SEND);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+ p_m_s_recog = 0;
+ break;
+ }
+ /* other signals */
+ if (digit == 'A') {
+ if (p_type != PORT_TYPE_SS5_OUT)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: received answer, waiting for recognition\n", p_name);
+ new_ss5_state(SS5_STATE_ACK_ANSWER);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+ p_m_s_recog = 0;
+ break;
+ }
+ if (digit == 'B') {
+ if (p_type == PORT_TYPE_SS5_IN) {
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_BELL)) {
+ new_ss5_state(SS5_STATE_DIAL_IN_PULSE); /* go pulsing state */
+ new_ss5_signal(SS5_SIGNAL_PULSE_OFF); /* we are starting with pulse off */
+ p_m_s_pulsecount = 0; /* init pulse counter */
+ p_m_s_dial[0] = '\0'; /* init dial string */
+ pulse_ind(1); /* also inits recogition timer... */
+ break;
+ }
+ PDEBUG(DEBUG_SS5, "%s: received forward-transfer, waiting for recognition\n", p_name);
+ /* forward transfer on incomming lines */
+ new_ss5_state(SS5_STATE_FORWARD_TRANSFER);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+ p_m_s_recog = 0;
+ break;
+ }
+ if (p_state == PORT_STATE_CONNECT) {
+ PDEBUG(DEBUG_SS5, "%s: received clear-back, waiting for recognition\n", p_name);
+ new_ss5_state(SS5_STATE_ACK_CLEAR_BACK);
+ } else {
+ PDEBUG(DEBUG_SS5, "%s: received busy-flash, waiting for recognition\n", p_name);
+ new_ss5_state(SS5_STATE_ACK_BUSY_FLASH);
+ }
+ new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+ p_m_s_recog = 0;
+ break;
+ }
+ /* dialing only allowed in incomming setup state */
+ if (p_state == PORT_STATE_IN_SETUP) {
+ if (!strchr("1234567890*#abc", digit))
+ break;
+ PDEBUG(DEBUG_SS5, "%s: received dialing start with '%c'\n", p_name, digit);
+ new_ss5_state(SS5_STATE_DIAL_IN);
+ new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+ p_m_s_dial[0] = '\0';
+ digit_ind(digit);
+ break;
+ }
+ break;
+ /* sending seizing */
+ case SS5_STATE_SEIZING:
+ switch (p_m_s_signal) {
+ case SS5_SIGNAL_SEND_ON:
+ if (digit == 'A') { /* double seize */
+ PDEBUG(DEBUG_SS5, "%s: received double seizure\n", p_name, digit);
+ double_seizure_ind();
+ break;
+ }
+ if (digit == 'B') {
+ PDEBUG(DEBUG_SS5, "%s: received answer to outgoing seize, waiting for recognition\n", p_name);
+ /* set recognition timer */
+ new_ss5_signal(SS5_SIGNAL_SEND_ON_RECOG);
+ p_m_s_recog = 0;
+ }
+ break;
+ case SS5_SIGNAL_SEND_ON_RECOG:
+ if (digit != 'B') { /* seize */
+ PDEBUG(DEBUG_SS5, "%s: answer to outgoing seize is gone before recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_SEND_ON);
+// p_m_s_sample_nr = 0;
+// inband_send_on();
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_SEIZE)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: answer to outgoing seize recognized, turning off, waiting for recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_SEND_OFF);
+ break;
+ case SS5_SIGNAL_SEND_OFF:
+ if (digit == 'B')
+ break;
+ PDEBUG(DEBUG_SS5, "%s: outgoing seizure is complete, proceeding...\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ proceed_to_send_ind();
+ break;
+ }
+ break;
+ /* answer to seize */
+ case SS5_STATE_PROCEED_TO_SEND:
+ if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+ if (digit != 'A') {
+ PDEBUG(DEBUG_SS5, "%s: incomming seize is gone before recognition\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ p_type = PORT_TYPE_SS5_IDLE;
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_SEIZE)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: incomming seize is recognized, responding...\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE);
+ p_m_s_sample_nr = 0;
+ inband_send_on();
+ break;
+ }
+ if (digit != 'A') {
+ PDEBUG(DEBUG_SS5, "%s: incomming seize is gone after responding\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ seizing_ind();
+ }
+ break;
+ /* sending busy flash / answer / clear-back */
+ case SS5_STATE_BUSY_FLASH:
+ case SS5_STATE_ANSWER:
+ case SS5_STATE_CLEAR_BACK:
+ switch (p_m_s_signal) {
+ case SS5_SIGNAL_SEND_ON:
+ if (digit == 'A') {
+ PDEBUG(DEBUG_SS5, "%s: received acknowledge, waiting for recognition\n", p_name);
+ /* set recognition timer */
+ new_ss5_signal(SS5_SIGNAL_SEND_ON_RECOG);
+ p_m_s_recog = 0;
+ }
+ break;
+ case SS5_SIGNAL_SEND_ON_RECOG:
+ if (digit != 'A') {
+ PDEBUG(DEBUG_SS5, "%s: acknowledge is gone before recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_SEND_ON);
+// p_m_s_sample_nr = 0;
+// inband_send_on();
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: acknowledge recognized, turning off, waiting for recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_SEND_OFF);
+ break;
+ case SS5_SIGNAL_SEND_OFF:
+ if (digit == 'A')
+ break;
+ PDEBUG(DEBUG_SS5, "%s: outgoing signal is complete\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ break;
+ }
+ break;
+ /* answer to busy-flash / clear back */
+ case SS5_STATE_ACK_BUSY_FLASH:
+ case SS5_STATE_ACK_CLEAR_BACK:
+ if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+ if (digit != 'B') {
+ PDEBUG(DEBUG_SS5, "%s: incomming clear-back/busy-flash is gone before recognition\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: incomming clear-back/busy-flash is recognized, responding...\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE);
+ p_m_s_sample_nr = 0;
+ inband_send_on();
+ break;
+ }
+ if (digit != 'B') {
+ PDEBUG(DEBUG_SS5, "%s: incomming clear-back/busy-flash is gone after responding\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ if (p_m_s_state == SS5_STATE_ACK_BUSY_FLASH)
+ busy_flash_ind();
+ else
+ clear_back_ind();
+ }
+ break;
+ /* answer to answer */
+ case SS5_STATE_ACK_ANSWER:
+ if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+ if (digit != 'A') {
+ PDEBUG(DEBUG_SS5, "%s: incomming answer is gone before recognition\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: incomming answer is recognized, responding...\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE);
+ p_m_s_sample_nr = 0;
+ inband_send_on();
+ break;
+ }
+ if (digit != 'A') {
+ PDEBUG(DEBUG_SS5, "%s: incomming answer is gone after responding\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ answer_ind();
+ }
+ break;
+ /* sending clear-forward */
+ case SS5_STATE_CLEAR_FORWARD:
+ switch (p_m_s_signal) {
+ case SS5_SIGNAL_SEND_ON:
+ if (digit == 'C') {
+ PDEBUG(DEBUG_SS5, "%s: received answer to clear-forward, waiting for recognition\n", p_name);
+ /* set recognition timer */
+ new_ss5_signal(SS5_SIGNAL_SEND_ON_RECOG);
+ p_m_s_recog = 0;
+ }
+ break;
+ case SS5_SIGNAL_SEND_ON_RECOG:
+ if (digit != 'C') {
+ PDEBUG(DEBUG_SS5, "%s: answer to clear-forward is gone before recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_SEND_ON);
+// p_m_s_sample_nr = 0;
+// inband_send_on();
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: answer to clear-forward recognized, turning off, waiting for recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_SEND_OFF);
+ break;
+ case SS5_SIGNAL_SEND_OFF:
+ if (digit == 'A') {
+ PDEBUG(DEBUG_SS5, "%s: received seize right after clear-forward answer, continue with seize\n", p_name);
+ new_state(PORT_STATE_IDLE);
+ goto seize;
+ }
+ if (digit == 'C')
+ break;
+ PDEBUG(DEBUG_SS5, "%s: answer to clear-forward is complete\n", p_name);
+ release_guard_ind();
+ new_ss5_signal(SS5_SIGNAL_DELAY);
+ p_m_s_recog = 0; /* use recog to delay */
+ PDEBUG(DEBUG_SS5, "%s: answer to clear-forward on outgoing interface starting delay to prevent ping-pong\n", p_name);
+ break;
+ case SS5_SIGNAL_DELAY:
+ if (digit == 'A') {
+ PDEBUG(DEBUG_SS5, "%s: received seize right after clear-forward answer, continue with seize\n", p_name);
+ new_state(PORT_STATE_IDLE);
+ goto seize;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RELEASE_DELAY)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: delay time over, going idle\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ new_state(PORT_STATE_IDLE);
+ p_type = PORT_TYPE_SS5_IDLE;
+ break;
+ }
+ break;
+ /* answer to release-guard*/
+ case SS5_STATE_RELEASE_GUARD:
+ switch (p_m_s_signal) {
+ case SS5_SIGNAL_RECEIVE_RECOG:
+ if (digit != 'C') {
+ if (p_type == PORT_TYPE_SS5_OUT)
+ PDEBUG(DEBUG_SS5, "%s: incomming release-guard is gone before recognition\n", p_name);
+ else
+ PDEBUG(DEBUG_SS5, "%s: incomming clear forward is gone before recognition\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+ break;
+ if (p_type == PORT_TYPE_SS5_OUT)
+ PDEBUG(DEBUG_SS5, "%s: incomming release-guard is recognized, responding...\n", p_name);
+ else
+ PDEBUG(DEBUG_SS5, "%s: incomming clear-forward is recognized, responding...\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE);
+ p_m_s_sample_nr = 0;
+ inband_send_on();
+ break;
+ case SS5_SIGNAL_RECEIVE:
+ if (digit == 'C'
+ || p_m_s_sample_nr < 256) /* small hack to keep answer for at least some time */
+ break;
+#if 0
+ if (digit == 'A') {
+ PDEBUG(DEBUG_SS5, "%s: received seize right after clear-forward is received\n", p_name);
+ new_state(PORT_STATE_IDLE);
+ goto seize;
+ }
+#endif
+ /* if clear forward stops right after recognition on the incomming side,
+ * the release guard signal stops and may be too short to be recognized at the outgoing side.
+ * to prevent this, a timer can be started to force a release guard that is long
+ * enough to be recognized on the outgoing side.
+ * this will prevent braking via blueboxing (other tricks may still be possible).
+ */
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_RELEASEGUARDTIMER)
+ && p_m_s_sample_nr < SS5_TIMER_RELEASE_GUARD)
+ break;
+ if (p_type == PORT_TYPE_SS5_OUT)
+ PDEBUG(DEBUG_SS5, "%s: incomming release-guard is gone after responding\n", p_name);
+ else
+ PDEBUG(DEBUG_SS5, "%s: incomming clear-forward is gone after responding\n", p_name);
+ if (p_type == PORT_TYPE_SS5_OUT) {
+ release_guard_ind();
+ new_ss5_signal(SS5_SIGNAL_DELAY);
+ p_m_s_recog = 0; /* use recog to delay */
+ PDEBUG(DEBUG_SS5, "%s: incomming release-guard on outgoing interface starting delay to prevent ping-pong\n", p_name);
+ } else {
+ clear_forward_ind();
+ new_ss5_state(SS5_STATE_IDLE);
+ }
+ break;
+ case SS5_SIGNAL_DELAY:
+ if (digit == 'A') {
+ PDEBUG(DEBUG_SS5, "%s: received seize right after release guard is gone, continue with seize\n", p_name);
+ new_state(PORT_STATE_IDLE);
+ goto seize;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RELEASE_DELAY)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: delay time over, going idle\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ new_state(PORT_STATE_IDLE);
+ p_type = PORT_TYPE_SS5_IDLE;
+ break;
+ }
+ break;
+ /* wait time to recognize forward transfer */
+ case SS5_STATE_FORWARD_TRANSFER:
+ if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+ if (digit != 'B') {
+ PDEBUG(DEBUG_SS5, "%s: incomming forward-transfer is gone before recognition\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ break;
+ }
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: incomming forward-transfer is recognized, responding, if BELL feature was selected...\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_RECEIVE);
+#if 0
+ p_m_s_sample_nr = 0;
+ inband_send_on();
+#endif
+ break;
+ }
+ if (digit != 'B') {
+ PDEBUG(DEBUG_SS5, "%s: incomming forward-transfer is gone after recognition\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ forward_transfer_ind();
+ break;
+ }
+ break;
+ /* dialing is received */
+ case SS5_STATE_DIAL_IN:
+ if (strchr("1234567890*#abc", digit)) {
+ if (p_m_s_signal != SS5_SIGNAL_DIGIT_PAUSE)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: incomming digit '%c' is recognized\n", p_name, digit);
+ new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+ digit_ind(digit);
+ } else {
+ if (p_m_s_signal != SS5_SIGNAL_DIGIT_ON)
+ break;
+ PDEBUG(DEBUG_SS5, "%s: incomming digit is gone after recognition\n", p_name);
+ new_ss5_signal(SS5_SIGNAL_DIGIT_PAUSE);
+ }
+ break;
+ case SS5_STATE_DIAL_IN_PULSE:
+ if (digit == 'B')
+ pulse_ind(1);
+ else
+ pulse_ind(0);
+ break;
+ }
+
+ /* something more to decode ? */
+ if (count != len)
+ goto again;
+}
+
+
+/*
+ * signalling sender
+ *
+ * this function generates tones and assembles dial string with digits and pause
+ * the result is sent to mISDN. it uses the ss5_encode() function.
+ * except for dialing and forward-transfer, tones are continuous and will not change state.
+ */
+int Pss5::inband_send(unsigned char *buffer, int len)
+{
+ int count = 0; /* sample counter */
+ int duration;
+ char digit;
+ int tocode, tosend;
+
+ switch(p_m_s_state) {
+ /* turn off transmitter in idle state */
+ case SS5_STATE_IDLE:
+ inband_send_off();
+ break;
+
+ case SS5_STATE_SEIZING:
+ if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+ && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+ break;
+ duration = -1; /* continuous */
+ digit = 'A';
+ send:
+ /* how much samples do we have left */
+ if (duration < 0)
+ tocode = len;
+ else
+ tocode = duration - p_m_s_sample_nr;
+ if (tocode > 0) {
+ if (tocode > len)
+ tocode = len;
+ ss5_encode(buffer, tocode, digit, p_m_s_sample_nr);
+ /* increase counters */
+ p_m_s_sample_nr += tocode;
+ count += tocode;
+ }
+ /* more to come ? */
+ if (duration > 0 && p_m_s_sample_nr >= duration) {
+ PDEBUG(DEBUG_SS5, "%s: sending tone '%c' complete, starting delay\n", p_name, digit);
+ if (p_m_s_state == SS5_STATE_DOUBLE_SEIZE) {
+ do_release(CAUSE_NOCHANNEL, LOCATION_BEYOND);
+ break;
+ }
+ new_ss5_state(SS5_STATE_DELAY);
+ p_m_s_sample_nr = 0;
+ }
+#if 0
+ /* stop sending if too long */
+ if (duration < 0 && p_m_s_sample_nr >= SS5_TIMER_MAX_SIGNAL) {
+ PDEBUG(DEBUG_SS5, "%s: sending tone '%c' too long, stopping\n", p_name, digit);
+ inband_send_off();
+ break;
+ }
+#endif
+ break;
+ /* incomming seizing */
+ case SS5_STATE_PROCEED_TO_SEND:
+ if (p_m_s_signal != SS5_SIGNAL_RECEIVE)
+ break;
+ duration = -1; /* continuous */
+ digit = 'B';
+ goto send;
+
+ case SS5_STATE_BUSY_FLASH:
+ case SS5_STATE_CLEAR_BACK:
+ if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+ && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+ break;
+ duration = -1; /* continuous */
+ digit = 'B';
+ goto send;
+
+ case SS5_STATE_ANSWER:
+ if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+ && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+ break;
+ duration = -1; /* continuous */
+ digit = 'A';
+ goto send;
+
+ case SS5_STATE_ACK_BUSY_FLASH:
+ case SS5_STATE_ACK_ANSWER:
+ case SS5_STATE_ACK_CLEAR_BACK:
+ if (p_m_s_signal != SS5_SIGNAL_RECEIVE)
+ break;
+ duration = -1; /* continuous */
+ digit = 'A';
+ goto send;
+
+#if 0
+ case SS5_STATE_FORWARD_TRANSFER:
+ if (p_m_s_signal != SS5_SIGNAL_RECEIVE)
+ break;
+ /* only on bell systems continue and acknowledge tone */
+ if (!(p_m_mISDNport->ss5 & SS5_FEATURE_BELL))
+ break;
+ duration = SS5_TIMER_FORWARD;
+ digit = 'B';
+ goto send;
+#endif
+
+ case SS5_STATE_CLEAR_FORWARD:
+ if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+ && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+ break;
+ duration = -1; /* continuous */
+ digit = 'C';
+ goto send;
+
+ case SS5_STATE_RELEASE_GUARD:
+ if (p_m_s_signal != SS5_SIGNAL_RECEIVE
+ && p_m_s_signal != SS5_SIGNAL_DELAY)
+ break;
+ /* prevent from sending release guard too long */
+ if (p_m_s_sample_nr >= SS5_TIMER_RELEASE_MAX)
+ break;
+ duration = -1; /* continuous */
+ digit = 'C';
+ goto send;
+
+ case SS5_STATE_DIAL_OUT:
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_PULSEDIALING))
+ count = inband_dial_pulse(buffer, len, count);
+ else
+ count = inband_dial_mf(buffer, len, count);
+ break;
+ break;
+
+ case SS5_STATE_DELAY:
+ tosend = len - count;
+ memset(buffer+count, audio_s16_to_law[0], tosend);
+ p_m_s_sample_nr += tosend;
+ count += tosend;
+ if (p_m_s_sample_nr >= SS5_TIMER_AFTER_SIGNAL) {
+ PDEBUG(DEBUG_SS5, "%s: delay done, ready for next signal\n", p_name);
+ new_ss5_state(SS5_STATE_IDLE);
+ inband_send_off();
+ }
+ break;
+
+ case SS5_STATE_DOUBLE_SEIZE:
+ duration = SS5_TIMER_DOUBLE_SEIZE;
+ digit = 'A';
+ goto send;
+
+ /* nothing to send */
+ default:
+ PERROR("inband signalling is turned on, but no signal is processed here.");
+ new_ss5_state(SS5_STATE_IDLE);
+ inband_send_off();
+ return 0;
+ }
+
+ /* return (partly) filled buffer */
+ return count;
+}
+
+
+int Pss5::inband_dial_mf(unsigned char *buffer, int len, int count)
+{
+ int duration;
+ int tocode, tosend;
+ char digit;
+
+ /* dialing
+ *
+ * p_m_s_dial: digits to be dialed
+ * p_m_s_digit_i: current digit counter
+ * p_m_s_signal: current signal state
+ * p_m_s_sample_nr: current sample number
+ */
+ again:
+ /* get digit and duration */
+ digit = p_m_s_dial[p_m_s_digit_i];
+ if (!digit) { /* if end of string reached */
+ new_ss5_state(SS5_STATE_DELAY);
+ p_m_s_sample_nr = 0;
+ return count;
+ }
+ if (p_m_s_signal == SS5_SIGNAL_DIGIT_ON) {
+ if (!p_m_s_digit_i) // first digit
+ duration = SS5_TIMER_KP;
+ else
+ duration = SS5_TIMER_DIGIT;
+ } else {
+ duration = SS5_TIMER_PAUSE;
+ }
+ /* end of digit/pause ? */
+ if (p_m_s_sample_nr >= duration) {
+ p_m_s_sample_nr = 0;
+ if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE)
+ new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+ else {
+ new_ss5_signal(SS5_SIGNAL_DIGIT_PAUSE);
+ p_m_s_digit_i++;
+ goto again;
+ }
+ }
+ /* how much samples do we have left */
+ tosend = len - count;
+ tocode = duration - p_m_s_sample_nr;
+ if (tocode < 0)
+ FATAL("sample_nr overrun duration");
+ if (tosend < tocode)
+ tocode = tosend;
+ /* digit or pause */
+ if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE) {
+ memset(buffer+count, audio_s16_to_law[0], tocode);
+// printf("coding pause %d bytes\n", tocode);
+ } else {
+ ss5_encode(buffer+count, tocode, digit, p_m_s_sample_nr);
+// printf("coding digit '%c' %d bytes\n", digit, tocode);
+ }
+ /* increase counters */
+ p_m_s_sample_nr += tocode;
+ count += tocode;
+ /* can we take more ? */
+ if (len != count)
+ goto again;
+ return count;
+}
+
+
+int Pss5::inband_dial_pulse(unsigned char *buffer, int len, int count)
+{
+ int tocode, tosend;
+ int duration;
+ char digit;
+
+ /* dialing
+ *
+ * p_m_s_dial: digits to be dialed
+ * p_m_s_digit_i: current digit counter
+ * p_m_s_signal: current signal state
+ * p_m_s_sample_nr: current sample number
+ */
+ again:
+ /* get digit */
+ digit = p_m_s_dial[p_m_s_digit_i];
+ if (!digit) { /* if end of string reached */
+ new_ss5_state(SS5_STATE_DELAY);
+ p_m_s_sample_nr = 0;
+ return count;
+ }
+ /* convert digit to pulse */
+ switch (digit) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ digit -= '0';
+ break;
+ case '0':
+ digit = 10;
+ break;
+ case '*':
+ digit = 11;
+ break;
+ case '#':
+ digit = 12;
+ break;
+ default:
+ p_m_s_digit_i++;
+ goto again;
+ }
+ /* get duration */
+ if (p_m_s_signal == SS5_SIGNAL_DIGIT_ON) {
+ if (p_m_s_pulsecount & 1)
+ duration = BELL_TIMER_MAKE; /* loop closed */
+ else
+ duration = BELL_TIMER_BREAK; /* loop open, tone */
+ } else {
+ duration = BELL_TIMER_PAUSE;
+ }
+ /* end of digit/pause ? */
+ if (p_m_s_sample_nr >= duration) {
+ p_m_s_sample_nr = 0;
+ if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE) {
+ new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+ PDEBUG(DEBUG_SS5, "%s: starting pusling digit '%c'\n", p_name, digit);
+ } else {
+ p_m_s_pulsecount++; /* toggle pulse */
+ if (!(p_m_s_pulsecount & 1)) {
+ /* pulse now on again, but if end is reached... */
+ if (p_m_s_pulsecount == (digit<<1)) {
+ new_ss5_signal(SS5_SIGNAL_DIGIT_PAUSE);
+ p_m_s_pulsecount = 0;
+ p_m_s_digit_i++;
+ goto again;
+ }
+ }
+ }
+ }
+ /* how much samples do we have left */
+ tosend = len - count;
+ tocode = duration - p_m_s_sample_nr;
+ if (tocode < 0)
+ FATAL("sample_nr overrun duration");
+ if (tosend < tocode)
+ tocode = tosend;
+ /* digit or pause */
+ if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE
+ || (p_m_s_pulsecount&1)) /* ...or currently on and no pulse */
+ memset(buffer+count, audio_s16_to_law[0], tocode);
+ else
+ ss5_encode(buffer+count, tocode, 'B', p_m_s_sample_nr);
+ /* increase counters */
+ p_m_s_sample_nr += tocode;
+ count += tocode;
+ /* can we take more ? */
+ if (len != count)
+ goto again;
+ return count;
+}
+
+
+/*
+ * start signal
+ */
+void Pss5::start_signal(int state)
+{
+ PDEBUG(DEBUG_SS5, "%s: starting singal '%s'\n", p_name, ss5_state_name[state]);
+ /* start signal */
+ new_ss5_state(state);
+ if (state == SS5_STATE_DIAL_OUT) {
+ p_m_s_digit_i = 0;
+ p_m_s_pulsecount = 0;
+ new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+ } else
+ new_ss5_signal(SS5_SIGNAL_SEND_ON);
+
+ /* double seize must continue the current seize tone, so don't reset sample_nr */
+ if (state != SS5_STATE_DOUBLE_SEIZE) {
+ /* (re)set sound phase to 0 */
+ p_m_s_sample_nr = 0;
+ }
+
+ /* turn on inband transmitter */
+ inband_send_on();
+}
+
+
+/*
+ * handles all indications
+ */
+void Pss5::seizing_ind(void)
+{
+ ss5_trace_header(p_m_mISDNport, this, SS5_SEIZING_IND, p_m_b_channel);
+ end_trace();
+
+ new_state(PORT_STATE_IN_SETUP);
+}
+
+void Pss5::digit_ind(char digit)
+{
+ int i;
+ char string[128] = "", dial[128] = "";
+ int dash, first_digit, last_was_digit;
+
+ /* add digit */
+ SCCAT(p_m_s_dial, digit);
+
+ if (p_state == PORT_STATE_IN_SETUP)
+ new_state(PORT_STATE_IN_OVERLAP);
+
+ /* not last digit ? */
+ if (digit != 'c')
+ return;
+
+ /* parse string */
+ dash = 0; /* dash must be used next time */
+ first_digit = 1;
+ last_was_digit = 0;
+ p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
+ for (i = 0; p_m_s_dial[i]; i++) {
+ if (dash || (last_was_digit && (p_m_s_dial[i]<'0' || p_m_s_dial[i]>'9')))
+ SCCAT(string, '-');
+ dash = 0;
+ last_was_digit = 0;
+ switch(p_m_s_dial[i]) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ if (first_digit)
+ dash = 1;
+ first_digit = 0;
+ last_was_digit = 1;
+ SCCAT(string, p_m_s_dial[i]);
+ SCCAT(dial, p_m_s_dial[i]);
+ break;
+ case '*':
+ SCAT(string, "C11");
+ SCCAT(dial, p_m_s_dial[i]);
+ dash = 1;
+ break;
+ case '#':
+ SCAT(string, "C12");
+ SCCAT(dial, p_m_s_dial[i]);
+ dash = 1;
+ break;
+ case 'a':
+ SCAT(string, "KP1");
+ SCCAT(dial, p_m_s_dial[i]);
+ dash = 1;
+ break;
+ case 'b':
+ SCAT(string, "KP2");
+ SCCAT(dial, p_m_s_dial[i]);
+ dash = 1;
+ break;
+ case 'c':
+ SCAT(string, "ST");
+ dash = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+ add_trace("string", NULL, "%s", string);
+ add_trace("number", NULL, "%s", dial);
+ end_trace();
+ new_ss5_state(SS5_STATE_IDLE);
+
+ do_setup(dial);
+ new_state(PORT_STATE_IN_PROCEEDING);
+}
+
+void Pss5::pulse_ind(int on)
+{
+ struct lcr_msg *message;
+ char dial[3] = "a.";
+
+ if (p_m_s_signal == SS5_SIGNAL_PULSE_OFF) {
+ if (on) {
+ /* pulse turns on */
+ p_m_s_recog = 0;
+ new_ss5_signal(SS5_SIGNAL_PULSE_ON);
+ /* pulse turns of, count it */
+ p_m_s_pulsecount++;
+ PDEBUG(DEBUG_SS5, "%s: pulse turns on, counting\n", p_name);
+ } else {
+ /* pulse remains off */
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ /* not recognized end of digit, we wait... */
+ if (p_m_s_recog < BELL_TIMER_RECOG_END)
+ return;
+ PDEBUG(DEBUG_SS5, "%s: pulse remains off, counted %d pulses\n", p_name, p_m_s_pulsecount);
+ if (p_m_s_pulsecount >= 12)
+ dial[1] = '#';
+ else if (p_m_s_pulsecount == 11)
+ dial[1] = '*';
+ else if (p_m_s_pulsecount == 10)
+ dial[1] = '0';
+ else
+ dial[1] = p_m_s_pulsecount + '0';
+ ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+ add_trace("digit", NULL, "%s", dial+1);
+ add_trace("pulses", NULL, "%d", p_m_s_pulsecount);
+ end_trace();
+ /* special star release feature */
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_STAR_RELEASE) && dial[1] == '*') {
+ ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+ add_trace("star", NULL, "releases call");
+ end_trace();
+ goto star_release;
+ }
+ if (p_state == PORT_STATE_IN_SETUP) {
+ /* sending digit as setup */
+ do_setup(dial); /* include 'a' == KP1 */
+ new_state(PORT_STATE_IN_OVERLAP);
+ } else {
+ /* sending digit as information */
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
+ SCPY(message->param.information.id, dial+1);
+ message_put(message);
+ }
+ new_ss5_state(SS5_STATE_IDLE);
+ /* done rx pulses, return to idle */
+ new_ss5_state(SS5_STATE_IDLE);
+ }
+ } else {
+ if (on) {
+ /* pulse remains on */
+ p_m_s_recog += SS5_DECODER_NPOINTS;
+ } else {
+ /* pulse turns off */
+ if (p_m_s_recog >= BELL_TIMER_RECOG_HANGUP) {
+ PDEBUG(DEBUG_SS5, "%s: long pulse turns off, releasing\n", p_name);
+ ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+ add_trace("longtone", NULL, "releases call");
+ end_trace();
+ star_release:
+ /* long pulse is gone, release current connection, if any */
+ while(p_epointlist) {
+ message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = LOCATION_BEYOND;
+ message->param.disconnectinfo.cause = CAUSE_NORMAL;
+ message_put(message);
+ free_epointlist(p_epointlist);
+ }
+ set_tone("", NULL);
+ /* return to setup state */
+ new_state(PORT_STATE_IN_SETUP);
+ new_ss5_state(SS5_STATE_IDLE);
+ return;
+ }
+ PDEBUG(DEBUG_SS5, "%s: short pulse turns off, releasing\n", p_name);
+ p_m_s_recog = 0;
+ new_ss5_signal(SS5_SIGNAL_PULSE_OFF);
+ }
+ }
+}
+
+void Pss5::proceed_to_send_ind(void)
+{
+ ss5_trace_header(p_m_mISDNport, this, SS5_PROCEED_TO_SEND_IND, p_m_b_channel);
+ end_trace();
+
+ SCPY(p_m_s_dial, p_dialinginfo.id);
+ start_signal(SS5_STATE_DIAL_OUT);
+
+ new_state(PORT_STATE_OUT_OVERLAP);
+}
+
+void Pss5::busy_flash_ind(void)
+{
+ struct lcr_msg *message;
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_BUSY_FLASH_IND, p_m_b_channel);
+ end_trace();
+
+ /* busy before dialing ? */
+ if (!p_epointlist)
+ return;
+
+ if (!(p_m_mISDNport->ss5 & SS5_FEATURE_NODISCONNECT)) {
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DISCONNECT);
+ message->param.disconnectinfo.location = LOCATION_BEYOND;
+ message->param.disconnectinfo.cause = CAUSE_BUSY;
+ message_put(message);
+ }
+
+ new_state(PORT_STATE_IN_DISCONNECT);
+}
+
+void Pss5::answer_ind(void)
+{
+ struct lcr_msg *message;
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_ANSWER_IND, p_m_b_channel);
+ end_trace();
+
+ /* answer before dialing ? */
+ if (!p_epointlist)
+ return;
+
+ /* already connected */
+ if (!(p_m_mISDNport->ss5 & SS5_FEATURE_CONNECT)) {
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
+ message_put(message);
+ }
+
+ new_state(PORT_STATE_CONNECT);
+}
+
+void Pss5::forward_transfer_ind(void)
+{
+// struct lcr_msg *message;
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_FORWARD_TRANSFER_IND, p_m_b_channel);
+ end_trace();
+
+#if 0
+ /* if BELL flavor bluebox flag is set, use it to seize a new line */
+ if (!(p_m_mISDNport->ss5 & SS5_FEATURE_BELL))
+ return;
+
+ /* special BELL flavor hack to clear a line and seize a new one */
+ while(p_epointlist) {
+ message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = LOCATION_BEYOND;
+ message->param.disconnectinfo.cause = CAUSE_NORMAL;
+ message_put(message);
+ free_epointlist(p_epointlist);
+ }
+ set_tone("", NULL);
+ new_state(PORT_STATE_IN_SETUP);
+#endif
+}
+
+void Pss5::clear_back_ind(void)
+{
+ struct lcr_msg *message;
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_BACK_IND, p_m_b_channel);
+ end_trace();
+
+ /* nobody? */
+ if (!p_epointlist)
+ return;
+
+ if (!(p_m_mISDNport->ss5 & SS5_FEATURE_NODISCONNECT)) {
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DISCONNECT);
+ message->param.disconnectinfo.location = LOCATION_BEYOND;
+ message->param.disconnectinfo.cause = CAUSE_NORMAL;
+ message_put(message);
+ }
+
+ new_state(PORT_STATE_IN_DISCONNECT);
+}
+
+void Pss5::clear_forward_ind(void)
+{
+ struct lcr_msg *message;
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_FORWARD_IND, p_m_b_channel);
+ end_trace();
+
+ new_state(PORT_STATE_IDLE);
+ set_tone("", NULL);
+ p_type = PORT_TYPE_SS5_IDLE;
+
+ /* someone ? */
+ if (!p_epointlist)
+ return;
+
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = LOCATION_BEYOND;
+ message->param.disconnectinfo.cause = CAUSE_NORMAL;
+ message_put(message);
+ free_epointlist(p_epointlist);
+
+}
+
+void Pss5::release_guard_ind(void)
+{
+ struct lcr_msg *message;
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_RELEASE_GUARD_IND, p_m_b_channel);
+ end_trace();
+
+ set_tone("", NULL);
+
+ /* someone ? */
+ if (!p_epointlist)
+ return;
+
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = LOCATION_BEYOND;
+ message->param.disconnectinfo.cause = CAUSE_NORMAL;
+ message_put(message);
+ free_epointlist(p_epointlist);
+}
+
+void Pss5::double_seizure_ind(void)
+{
+ ss5_trace_header(p_m_mISDNport, this, SS5_DOUBLE_SEIZURE_IND, p_m_b_channel);
+ end_trace();
+ ss5_trace_header(p_m_mISDNport, this, SS5_DOUBLE_SEIZURE_REQ, p_m_b_channel);
+ end_trace();
+
+ /* start double seizure sequence, so remote exchange will recognize it */
+ start_signal(SS5_STATE_DOUBLE_SEIZE);
+}
+
+
+/*
+ * shuts down by sending a clear forward and releasing endpoint
+ */
+void Pss5::do_release(int cause, int location)
+{
+ struct lcr_msg *message;
+
+ /* sending release to endpoint */
+ while(p_epointlist) {
+ message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = location;
+ message->param.disconnectinfo.cause = cause;
+ message_put(message);
+ free_epointlist(p_epointlist);
+ }
+
+ /* start clear-forward */
+ ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_FORWARD_REQ, p_m_b_channel);
+ end_trace();
+ start_signal(SS5_STATE_CLEAR_FORWARD);
+
+ new_state(PORT_STATE_RELEASE);
+}
+
+
+/*
+ * create endpoint and send setup
+ */
+void Pss5::do_setup(char *dial)
+{
+ class Endpoint *epoint;
+ struct lcr_msg *message;
+
+ SCPY(p_dialinginfo.id, dial);
+ p_dialinginfo.sending_complete = 1;
+ p_callerinfo.present = INFO_PRESENT_NOTAVAIL;
+ p_callerinfo.screen = INFO_SCREEN_NETWORK;
+ p_callerinfo.ntype = INFO_NTYPE_NOTPRESENT;
+ p_callerinfo.isdn_port = p_m_portnum;
+ SCPY(p_callerinfo.interface, p_m_mISDNport->ifport->interface->name);
+
+ p_capainfo.bearer_capa = INFO_BC_AUDIO;
+ p_capainfo.bearer_info1 = (options.law=='a')?3:2;
+ p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
+ p_capainfo.hlc = INFO_HLC_NONE;
+ p_capainfo.exthlc = INFO_HLC_NONE;
+ p_capainfo.source_mode = B_MODE_TRANSPARENT;
+
+ /* create endpoint */
+ if (p_epointlist)
+ FATAL("Incoming call but already got an endpoint.\n");
+ if (!(epoint = new Endpoint(p_serial, 0)))
+ FATAL("No memory for Endpoint instance\n");
+ if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint, 0))) //incoming
+ FATAL("No memory for Endpoint Application instance\n");
+ epointlist_new(epoint->ep_serial);
+
+ /* send setup message to endpoit */
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_SETUP);
+ message->param.setup.isdn_port = p_m_portnum;
+ message->param.setup.port_type = p_type;
+// message->param.setup.dtmf = !p_m_mISDNport->ifport->nodtmf;
+ memcpy(&message->param.setup.dialinginfo, &p_dialinginfo, sizeof(struct dialing_info));
+ memcpy(&message->param.setup.callerinfo, &p_callerinfo, sizeof(struct caller_info));
+ memcpy(&message->param.setup.redirinfo, &p_redirinfo, sizeof(struct redir_info));
+ memcpy(&message->param.setup.capainfo, &p_capainfo, sizeof(struct capa_info));
+ message_put(message);
+
+}
+
+
+/*
+ * handler
+ */
+int Pss5::handler(void)
+{
+ int ret;
+
+ if ((ret = PmISDN::handler()))
+ return(ret);
+
+ /* handle timer */
+ if (p_m_s_timer && p_m_s_timer < now) {
+ p_m_s_timer = 0.0;
+ (this->*(p_m_s_timer_fn))();
+ }
+
+ /* if answer signal is queued */
+ if (p_m_s_answer && p_m_s_state == SS5_STATE_IDLE) {
+ p_m_s_answer = 0;
+ /* start answer */
+ ss5_trace_header(p_m_mISDNport, this, SS5_ANSWER_REQ, p_m_b_channel);
+ end_trace();
+ start_signal(SS5_STATE_ANSWER);
+ }
+
+ /* if busy-flash signal is queued */
+ if (p_m_s_busy_flash && p_m_s_state == SS5_STATE_IDLE) {
+ p_m_s_busy_flash = 0;
+ /* start busy-flash */
+ ss5_trace_header(p_m_mISDNport, this, SS5_BUSY_FLASH_REQ, p_m_b_channel);
+ end_trace();
+ start_signal(SS5_STATE_BUSY_FLASH);
+ }
+
+ /* if clear-back signal is queued */
+ if (p_m_s_clear_back && p_m_s_state == SS5_STATE_IDLE) {
+ p_m_s_clear_back = 0;
+ /* start clear-back */
+ ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_BACK_REQ, p_m_b_channel);
+ end_trace();
+ start_signal(SS5_STATE_CLEAR_BACK);
+ }
+
+ return(0);
+}
+
+
+/*
+ * handles all messages from endpoint
+ */
+
+/* MESSAGE_SETUP */
+void Pss5::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
+{
+ struct lcr_msg *message;
+ int i;
+ char string[128] = "", dial[128] = "";
+ int dash, first_digit, last_was_digit;
+
+ if (p_epointlist) {
+ PERROR("endpoint already exist.\n");
+ message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ message->param.disconnectinfo.cause = CAUSE_UNSPECIFIED;
+ message_put(message);
+ return;
+ }
+
+ /* copy setup infos to port */
+ memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
+ memcpy(&p_dialinginfo, &param->setup.dialinginfo, sizeof(p_dialinginfo));
+ memcpy(&p_capainfo, &param->setup.capainfo, sizeof(p_capainfo));
+ memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
+ /* screen outgoing caller id */
+ do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, p_m_mISDNport->ifport->interface);
+ do_screen(1, p_callerinfo.id2, sizeof(p_callerinfo.id2), &p_callerinfo.ntype2, &p_callerinfo.present2, p_m_mISDNport->ifport->interface);
+
+ /* parse dial string */
+ dash = 0; /* dash must be used next time */
+ first_digit = 1;
+ last_was_digit = 0;
+ for (i = 0; p_dialinginfo.id[i]; i++) {
+ if (dash || (last_was_digit && (p_dialinginfo.id[i]<'0' || p_dialinginfo.id[i]>'9')))
+ SCCAT(string, '-');
+ dash = 0;
+ last_was_digit = 0;
+ switch(p_dialinginfo.id[i]) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ if (i && first_digit)
+ dash = 1;
+ first_digit = 0;
+ last_was_digit = 1;
+ SCCAT(string, p_dialinginfo.id[i]);
+ SCCAT(dial, p_dialinginfo.id[i]);
+ break;
+ case '*':
+ SCAT(string, "C11");
+ SCCAT(dial, '*');
+ dash = 1;
+ break;
+ case '#':
+ SCAT(string, "C12");
+ SCCAT(dial, '#');
+ dash = 1;
+ break;
+ case 'a':
+ SCAT(string, "KP1");
+ SCCAT(dial, 'a');
+ dash = 1;
+ break;
+ case 'b':
+ SCAT(string, "KP2");
+ SCCAT(dial, 'b');
+ dash = 1;
+ break;
+ case 'c':
+ SCAT(string, "ST");
+ SCCAT(dial, 'c');
+ dash = 1;
+ case 'K':
+ i++;
+ if (p_dialinginfo.id[i] != 'P')
+ goto dial_error;
+ i++;
+ if (p_dialinginfo.id[i] == '1') {
+ SCAT(string, "KP1");
+ SCCAT(dial, 'a');
+ dash = 1;
+ break;
+ }
+ if (p_dialinginfo.id[i] == '2') {
+ SCAT(string, "KP2");
+ SCCAT(dial, 'b');
+ dash = 1;
+ break;
+ }
+ goto dial_error;
+ case 'C':
+ i++;
+ if (p_dialinginfo.id[i] != '1')
+ goto dial_error;
+ i++;
+ if (p_dialinginfo.id[i] == '1') {
+ SCAT(string, "C11");
+ SCCAT(dial, 'a');
+ dash = 1;
+ break;
+ }
+ if (p_dialinginfo.id[i] == '2') {
+ SCAT(string, "C12");
+ SCCAT(dial, 'b');
+ dash = 1;
+ break;
+ }
+ goto dial_error;
+ case 'S':
+ i++;
+ if (p_dialinginfo.id[i] != 'T')
+ goto dial_error;
+ SCAT(string, "ST");
+ SCCAT(dial, 'c');
+ dash = 1;
+ break;
+ default:
+ break;
+ }
+ /* stop, if ST */
+ if (dial[0] && dial[strlen(dial)-1] == 'c')
+ break;
+ }
+ /* terminate */
+ if (dial[0] && dial[strlen(dial)-1]!='c') {
+ SCCAT(string, '-');
+ SCAT(string, "ST");
+ SCCAT(dial, 'c');
+ }
+
+ /* error in dial string */
+ if (!dial[0]) {
+ dial_error:
+ ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_REQ, p_m_b_channel);
+ add_trace("string", NULL, "%s", p_dialinginfo.id);
+ if (!dial[0])
+ add_trace("error", NULL, "no number", dial);
+ else if (dial[0]!='a' && dial[0]!='b')
+ add_trace("error", NULL, "number must start with KP1/KP2", dial);
+ else
+ add_trace("error", NULL, "illegal format", dial);
+ end_trace();
+ message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ message->param.disconnectinfo.cause = CAUSE_INVALID;
+ message_put(message);
+ return;
+ }
+
+ /* copy new dial string */
+ SCPY(p_dialinginfo.id, dial);
+
+ /* attach only if not already */
+ epointlist_new(epoint_id);
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_REQ, p_m_b_channel);
+ add_trace("string", NULL, "%s", string);
+ add_trace("type", NULL, "%s", (dial[0]=='b')?"international":"national");
+ add_trace("number", NULL, "%s", dial);
+ end_trace();
+ /* connect auto path */
+ if ((p_m_mISDNport->ss5 & SS5_FEATURE_CONNECT)) {
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
+ message_put(message);
+ } else {
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING);
+ message_put(message);
+ }
+
+ /* start seizing */
+ ss5_trace_header(p_m_mISDNport, this, SS5_SEIZING_REQ, p_m_b_channel);
+ end_trace();
+ new_ss5_state(SS5_STATE_SEIZING);
+ new_ss5_signal(SS5_SIGNAL_SEND_ON);
+ p_m_s_sample_nr = 0;
+ inband_send_on();
+
+ p_type = PORT_TYPE_SS5_OUT;
+ new_state(PORT_STATE_OUT_SETUP);
+}
+
+/* MESSAGE_CONNECT */
+void Pss5::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
+{
+ memcpy(&p_connectinfo, &param->connectinfo, sizeof(p_connectinfo));
+
+ if (p_state != PORT_STATE_CONNECT) {
+ new_state(PORT_STATE_CONNECT);
+ p_m_s_answer = 1;
+ }
+
+ set_tone("", NULL);
+}
+
+/* MESSAGE_DISCONNECT */
+void Pss5::message_disconnect(unsigned int epoint_id, int message_id, union parameter *param)
+{
+ /* disconnect and clear forward (release guard) */
+// if ((p_type==PORT_TYPE_SS5_IN && !p_m_mISDNport->tones) /* incomming exchange with no tones */
+if (0 || p_type==PORT_TYPE_SS5_OUT) { /* outgoing exchange */
+ do_release(param->disconnectinfo.cause, param->disconnectinfo.location);
+ return;
+ }
+
+ ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_BACK_REQ, p_m_b_channel);
+ end_trace();
+ start_signal(SS5_STATE_CLEAR_BACK);
+
+ new_state(PORT_STATE_OUT_DISCONNECT);
+}
+
+/* MESSAGE_RELEASE */
+void Pss5::message_release(unsigned int epoint_id, int message_id, union parameter *param)
+{
+ do_release(param->disconnectinfo.cause, param->disconnectinfo.location);
+}
+
+
+/*
+ * endpoint sends messages to the port
+ */
+int Pss5::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
+{
+ if (PmISDN::message_epoint(epoint_id, message_id, param))
+ return(1);
+
+ switch(message_id) {
+ case MESSAGE_SETUP: /* dial-out command received from epoint */
+ if (p_state!=PORT_STATE_IDLE) {
+ PERROR("Pss5(%s) ignoring setup because isdn port is not in idle state (or connected for sending display info).\n", p_name);
+ break;
+ }
+ if (p_epointlist && p_state==PORT_STATE_IDLE)
+ FATAL("Pss5(%s): epoint pointer is set in idle state, how bad!!\n", p_name);
+ message_setup(epoint_id, message_id, param);
+ break;
+
+ case MESSAGE_CONNECT: /* call of endpoint is connected */
+ message_connect(epoint_id, message_id, param);
+ break;
+
+ case MESSAGE_DISCONNECT: /* call has been disconnected */
+ message_disconnect(epoint_id, message_id, param);
+ break;
+
+ case MESSAGE_RELEASE: /* release isdn port */
+ message_release(epoint_id, message_id, param);
+ break;
+
+ default:
+ PDEBUG(DEBUG_SS5, "Pss5(%s) ss5 port with (caller id %s) received an unhandled message: %d\n", p_name, p_callerinfo.id, message_id);
+ }
+
+ return(1);
+}
+