Add FXS support

This requires FXS support to mISDN too.
This commit is contained in:
Andreas Eversberg 2012-12-16 09:31:36 +01:00
parent fa1f601f49
commit acaf278f7f
16 changed files with 2020 additions and 70 deletions

View File

@ -46,7 +46,7 @@ INSTALLATION_DEFINES = \
if ENABLE_MISDN
MISDN_INCLUDE = -DWITH_MISDN -DWITH_CRYPT
MISDN_SOURCE = mISDN.cpp dss1.cpp crypt.cpp
MISDN_SOURCE = mISDN.cpp fxs.cpp dss1.cpp crypt.cpp
MISDN_LIB = -lmisdn
endif

View File

@ -2049,6 +2049,398 @@ void EndpointAppPBX::action_dialing_password_wr(void)
}
/* process pots-retrieve
*/
void EndpointAppPBX::action_init_pots_retrieve(void)
{
struct route_param *rparam;
struct port_list *portlist = ea_endpoint->ep_portlist;
class Port *port;
class Pfxs *ourfxs, *fxs;
int count = 0;
class Endpoint *epoint;
/* check given call */
if (!(rparam = routeparam(e_action, PARAM_POTS_CALL))) {
trace_header("ACTION pots-retrieve (no call given)", DIRECTION_NONE);
end_trace();
disconnect:
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "cause_3f");
e_action = NULL;
return;
}
/* find call */
port = find_port_id(portlist->port_id);
if (!port)
goto disconnect;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
trace_header("ACTION pots-retrieve (call not of FXS type)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
count++;
if (count == rparam->integer_value)
break;
}
}
port = port->next;
}
if (!port) {
trace_header("ACTION pots-retrieve (call # does not exist)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
#ifdef ISDN_P_FXS_POTS
/* release our call */
ourfxs->hangup_ind(0);
/* retrieve selected call */
fxs->retrieve_ind(0);
#endif
/* split if selected call is member of a 3pty */
epoint = find_epoint_id(ACTIVE_EPOINT(fxs->p_epointlist));
if (epoint && epoint->ep_app_type == EAPP_TYPE_PBX) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) try spliting 3pty. this may fail because we don't have a 3pty.\n", epoint->ep_serial);
((class EndpointAppPBX *)epoint->ep_app)->split_3pty();
}
}
/* process pots-release
*/
void EndpointAppPBX::action_init_pots_release(void)
{
struct route_param *rparam;
struct port_list *portlist = ea_endpoint->ep_portlist;
class Port *port;
class Pfxs *ourfxs, *fxs;
int count = 0;
/* check given call */
if (!(rparam = routeparam(e_action, PARAM_POTS_CALL))) {
trace_header("ACTION pots-release (no call given)", DIRECTION_NONE);
end_trace();
disconnect:
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "cause_3f");
e_action = NULL;
return;
}
/* find call */
port = find_port_id(portlist->port_id);
if (!port)
goto disconnect;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
trace_header("ACTION pots-release (call not of FXS type)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
count++;
if (count == rparam->integer_value)
break;
}
}
port = port->next;
}
if (!port) {
trace_header("ACTION pots-release (call # does not exist)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
#if 0
/* disconnect our call */
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "hangup");
e_action = NULL;
#endif
#ifdef ISDN_P_FXS_POTS
/* release selected call */
fxs->hangup_ind(0);
#endif
/* indicate timeout, so next action will be processed */
process_dialing(1);
}
/* process pots-reject
*/
void EndpointAppPBX::action_init_pots_reject(void)
{
struct port_list *portlist = ea_endpoint->ep_portlist;
class Port *port;
class Pfxs *ourfxs, *fxs;
/* find call */
port = find_port_id(portlist->port_id);
if (!port)
goto disconnect;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
trace_header("ACTION pots-reject (call not of FXS type)", DIRECTION_NONE);
end_trace();
disconnect:
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "cause_3f");
e_action = NULL;
return;
}
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (fxs->p_state == PORT_STATE_OUT_ALERTING)
break;
}
}
port = port->next;
}
if (!port) {
trace_header("ACTION pots-reject (no call waiting)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
#ifdef ISDN_P_FXS_POTS
/* reject alerting call */
fxs->reject_ind(0);
#endif
/* indicate timeout, so next action will be processed */
process_dialing(1);
}
/* process pots-answer
*/
void EndpointAppPBX::action_init_pots_answer(void)
{
struct port_list *portlist = ea_endpoint->ep_portlist;
class Port *port;
class Pfxs *ourfxs, *fxs;
/* find call */
port = find_port_id(portlist->port_id);
if (!port)
goto disconnect;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
trace_header("ACTION pots-answer (call not of FXS type)", DIRECTION_NONE);
end_trace();
disconnect:
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "cause_3f");
e_action = NULL;
return;
}
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (fxs->p_state == PORT_STATE_OUT_ALERTING)
break;
}
}
port = port->next;
}
if (!port) {
trace_header("ACTION pots-answer (no call waiting)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
#ifdef ISDN_P_FXS_POTS
/* release our call */
ourfxs->hangup_ind(0);
/* answer alerting call */
fxs->answer_ind(0);
#endif
}
/* process pots-3pty
*/
void EndpointAppPBX::action_init_pots_3pty(void)
{
struct port_list *portlist = ea_endpoint->ep_portlist;
class Port *port;
class Pfxs *ourfxs, *fxs, *fxs1 = NULL, *fxs2 = NULL;
class Endpoint *epoint;
int count = 0;
/* find call */
port = find_port_id(portlist->port_id);
if (!port)
goto disconnect;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
trace_header("ACTION pots-3pty (call not of FXS type)", DIRECTION_NONE);
end_trace();
disconnect:
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "cause_3f");
e_action = NULL;
return;
}
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (count == 0)
fxs1 = fxs;
if (count == 1)
fxs2 = fxs;
count++;
}
}
port = port->next;
}
if (count != 2) {
trace_header("ACTION pots-3pty (exactly two calls don't exist)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
#ifdef ISDN_P_FXS_POTS
/* release our call */
ourfxs->hangup_ind(0);
#endif
#ifdef ISDN_P_FXS_POTS
/* retrieve latest active call */
if (fxs2->p_m_fxs_age > fxs1->p_m_fxs_age) {
fxs2->retrieve_ind(0);
epoint = find_epoint_id(ACTIVE_EPOINT(fxs2->p_epointlist));
} else {
fxs1->retrieve_ind(0);
epoint = find_epoint_id(ACTIVE_EPOINT(fxs2->p_epointlist));
}
#else
epoint = NULL;
#endif
if (!epoint) {
trace_header("ACTION pots-3pty (interal error: no endpoint)", DIRECTION_NONE);
end_trace();
return;
}
if (epoint->ep_app_type != EAPP_TYPE_PBX) {
trace_header("ACTION pots-3pty (interal error: endpoint not PBX type)", DIRECTION_NONE);
end_trace();
return;
}
/* bridge calls */
if (((class EndpointAppPBX *)epoint->ep_app)->join_3pty_fxs()) {
trace_header("ACTION pots-3pty (interal error: join_3pty_fsx failed)", DIRECTION_NONE);
end_trace();
return;
}
}
/* process pots-transfer
*/
void EndpointAppPBX::action_init_pots_transfer(void)
{
struct route_param *rparam;
struct port_list *portlist = ea_endpoint->ep_portlist;
class Port *port;
class Pfxs *ourfxs, *fxs, *fxs1 = NULL, *fxs2 = NULL;
int count = 0;
/* check given call */
if (!(rparam = routeparam(e_action, PARAM_POTS_CALL))) {
trace_header("ACTION pots-transfer (no call given)", DIRECTION_NONE);
end_trace();
disconnect:
new_state(EPOINT_STATE_OUT_DISCONNECT);
message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
set_tone(portlist, "cause_3f");
e_action = NULL;
return;
}
/* find call */
port = find_port_id(portlist->port_id);
if (!port)
goto disconnect;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
trace_header("ACTION pots-transfer (call not of FXS type)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (count == 0)
fxs1 = fxs;
if (count == 1)
fxs2 = fxs;
count++;
}
}
port = port->next;
}
if (count != 2) {
trace_header("ACTION pots-transfer (exactly two calls don't exist)", DIRECTION_NONE);
end_trace();
goto disconnect;
}
#ifdef ISDN_P_FXS_POTS
/* retrieve call */
if (fxs2->p_m_fxs_age > fxs1->p_m_fxs_age)
fxs2->retrieve_ind(0);
else
fxs1->retrieve_ind(0);
#endif
/* bridge calls */
join_join_fxs();
}
/* general process dialing of incoming call
* depending on the detected prefix, subfunctions above (action_*) will be
* calles.

View File

@ -193,8 +193,13 @@ fail:
port = ss5_hunt_line(mISDNport);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
earlyb = mISDNport->earlyb;
#ifdef ISDN_P_FXS_POTS
if (mISDNport->pots)
port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
#else
trace_header("INTERFACE (has no function)", DIRECTION_NONE);
add_trace("interface", NULL, "%s", ifname);

View File

@ -500,6 +500,7 @@ void EndpointAppPBX::notify_active(void)
*/
void EndpointAppPBX::keypad_function(char digit)
{
class Port *port;
/* we must be in a call, in order to send messages to the call */
if (e_ext.number[0] == '\0') {
@ -515,7 +516,13 @@ void EndpointAppPBX::keypad_function(char digit)
break;
}
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) join call with call on hold\n", ea_endpoint->ep_serial);
join_join();
port = find_port_id(ea_endpoint->ep_portlist->port_id);
if (!port)
break;
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS)
join_join_fxs();
else if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1)
join_join_dss1();
break;
#ifdef WITH_CRYPT
@ -796,7 +803,12 @@ void EndpointAppPBX::out_setup(int cfnr)
port = ss5_hunt_line(mISDNport);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
#ifdef ISDN_P_FXS_POTS
if (mISDNport->pots)
port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
earlyb = mISDNport->earlyb;
#else
trace_header("INTERFACE (has no function)", DIRECTION_NONE);
@ -916,6 +928,11 @@ void EndpointAppPBX::out_setup(int cfnr)
if (mISDNport->ss5)
port = ss5_hunt_line(mISDNport);
else
#endif
#ifdef ISDN_P_FXS_POTS
if (mISDNport->pots)
port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
if (!port)
@ -1079,7 +1096,12 @@ void EndpointAppPBX::out_setup(int cfnr)
port = ss5_hunt_line(mISDNport);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
#ifdef ISDN_P_FXS_POTS
if (mISDNport->pots)
port = new Pfxs(PORT_TYPE_POTS_FXS_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, mode);
else
#endif
port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, mISDNport->ifport->interface, channel, mISDNport->ifport->channel_force, mode);
earlyb = mISDNport->earlyb;
#else
trace_header("INTERFACE (has no function)", DIRECTION_NONE);
@ -2320,23 +2342,13 @@ void EndpointAppPBX::port_3pty(struct port_list *portlist, int message_type, uni
struct lcr_msg *message;
int rc;
#if 0
/* bridge for real */
if (param->threepty.begin)
rc = join_join();
else if (param->threepty.end)
rc = -ENOTSUP;
else
return;
#else
/* 3PTY bridge */
if (param->threepty.begin)
rc = join_3pty();
rc = join_3pty_dss1();
else if (param->threepty.end)
rc = split_3pty();
else
return;
#endif
message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_3PTY);
message->param.threepty.begin = param->threepty.begin;
@ -2350,6 +2362,22 @@ void EndpointAppPBX::port_3pty(struct port_list *portlist, int message_type, uni
message_put(message);
}
/* port MESSAGE_TRANSFER */
void EndpointAppPBX::port_transfer(struct port_list *portlist, int message_type, union parameter *param)
{
logmessage(message_type, param, portlist->port_id, DIRECTION_IN);
class Port *port;
/* bridge for real */
if (!(port = find_port_id(portlist->port_id)))
return;
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS)
join_join_fxs();
else if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_DSS1)
join_join_dss1();
}
/* port MESSAGE_SUSPEND */
/* NOTE: before supending, the inactive-notification must be done in order to set call mixer */
void EndpointAppPBX::port_suspend(struct port_list *portlist, int message_type, union parameter *param)
@ -2470,6 +2498,11 @@ void EndpointAppPBX::ea_message_port(unsigned int port_id, int message_type, uni
port_3pty(portlist, message_type, param);
break;
case MESSAGE_TRANSFER:
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming TRANSFER request (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, e_ext.number, e_callerinfo.id);
port_transfer(portlist, message_type, param);
break;
/* PORT sends DTMF message */
case MESSAGE_DTMF: /* dtmf digits received */
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dtmf digit=%c (terminal '%s', caller id '%s')\n", ea_endpoint->ep_serial, param->dtmf, e_ext.number, e_callerinfo.id);
@ -3533,7 +3566,7 @@ reject:
/* join calls (look for a join that is on hold (same isdn interface/terminal))
*/
int EndpointAppPBX::join_join(void)
int EndpointAppPBX::join_join_dss1(void)
{
#ifdef WITH_MISDN
struct lcr_msg *message;
@ -3711,7 +3744,186 @@ int EndpointAppPBX::join_join(void)
return 0;
}
int EndpointAppPBX::join_3pty(void)
/* join calls (look for a join that is on hold (same fxs interface/terminal))
*/
int EndpointAppPBX::join_join_fxs(void)
{
#ifdef WITH_MISDN
struct lcr_msg *message;
struct join_relation *add_relation, *remove_relation;
struct join_relation **add_relation_pointer, **remove_relation_pointer;
class Join *our_join, *other_join, *add_join, *remove_join;
class JoinPBX *our_joinpbx, *other_joinpbx, *add_joinpbx, *remove_joinpbx;
class EndpointAppPBX *other_eapp, *remove_eapp;
class Port *our_port, *other_port;
class Pfxs *our_fxs, *other_fxs;
class Endpoint *temp_epoint;
/* are we a candidate to join a join? */
our_join = find_join_id(ea_endpoint->ep_join_id);
if (!our_join) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join doesn't exist anymore.\n", ea_endpoint->ep_serial);
return -1;
}
if (our_join->j_type != JOIN_TYPE_PBX) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: join is not a pbx join.\n", ea_endpoint->ep_serial);
return -1;
}
our_joinpbx = (class JoinPBX *)our_join;
if (!ea_endpoint->ep_portlist) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial);
return -1;
}
if (!e_ext.number[0]) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we are not internal extension.\n", ea_endpoint->ep_serial);
return -1;
}
our_port = find_port_id(ea_endpoint->ep_portlist->port_id);
if (!our_port) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial);
return -1;
}
if ((our_port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not fxs.\n", ea_endpoint->ep_serial);
return -1;
}
our_fxs = (class Pfxs *)our_port;
/* find an endpoint that has the same mISDNport that we are on */
other_eapp = apppbx_first;
while(other_eapp) {
if (other_eapp == this) {
other_eapp = other_eapp->next;
continue;
}
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id);
if (other_eapp->e_ext.number[0] /* has terminal */
&& other_eapp->ea_endpoint->ep_portlist /* has port */
&& other_eapp->ea_endpoint->ep_join_id) { /* has join */
other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id);
if (other_port) { /* port still exists */
if (other_port->p_type==PORT_TYPE_POTS_FXS_OUT
|| other_port->p_type==PORT_TYPE_POTS_FXS_IN) { /* port is FXS */
other_fxs = (class Pfxs *)other_port;
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type isdn! comparing our portnum=%d with other's portnum=%d hold=%s state=%d\n", ea_endpoint->ep_serial, our_fxs->p_m_mISDNport->portnum, other_fxs->p_m_mISDNport->portnum, (other_fxs->p_m_hold)?"YES":"NO", other_fxs->p_state);
if (1 //other_fxs->p_m_hold /* port is on hold */
&& other_fxs->p_m_mISDNport == our_fxs->p_m_mISDNport) /* same isdn interface */
break;
} else {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial);
}
} else {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port doesn't exist enymore.\n", ea_endpoint->ep_serial);
}
}
other_eapp = other_eapp->next;
}
if (!other_eapp) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same FXS terminal.\n", ea_endpoint->ep_serial);
return -1;
}
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port with same terminal found.\n", ea_endpoint->ep_serial);
/* if we have the same join */
if (other_eapp->ea_endpoint->ep_join_id == ea_endpoint->ep_join_id) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we and the other have the same join.\n", ea_endpoint->ep_serial);
return -1;
}
other_join = find_join_id(other_eapp->ea_endpoint->ep_join_id);
if (!other_join) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join doesn't exist anymore.\n", ea_endpoint->ep_serial);
return -1;
}
if (other_join->j_type != JOIN_TYPE_PBX) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join is not a pbx join.\n", ea_endpoint->ep_serial);
return -1;
}
other_joinpbx = (class JoinPBX *)other_join;
if (our_joinpbx->j_partyline && other_joinpbx->j_partyline) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: both joins are partylines.\n", ea_endpoint->ep_serial);
return -1;
}
/* now find out which is ACTIVE-IDLE and which is ACTIVE-HELD */
if (our_fxs->p_m_hold && !other_fxs->p_m_hold) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) our relation is on hold and other is active, so we move our relations to other relations\n", ea_endpoint->ep_serial);
remove_eapp = this;
remove_join = our_join;
remove_joinpbx = our_joinpbx;
add_join = other_join;
add_joinpbx = other_joinpbx;
} else {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) our relation is active or other is on hold, so we move ohter relations to our relations\n", ea_endpoint->ep_serial);
remove_eapp = other_eapp;
remove_join = other_join;
remove_joinpbx = other_joinpbx;
add_join = our_join;
add_joinpbx = our_joinpbx;
}
/* remove relation to endpoint for join on hold */
remove_relation = remove_joinpbx->j_relation;
remove_relation_pointer = &remove_joinpbx->j_relation;
while(remove_relation) {
if (remove_relation->epoint_id == remove_eapp->ea_endpoint->ep_serial) {
/* detach other endpoint */
*remove_relation_pointer = remove_relation->next;
FREE(remove_relation, sizeof(struct join_relation));
cmemuse--;
remove_relation = *remove_relation_pointer;
remove_eapp->ea_endpoint->ep_join_id = 0;
continue;
}
/* change join/hold pointer of endpoint to the new join */
temp_epoint = find_epoint_id(remove_relation->epoint_id);
if (temp_epoint) {
if (temp_epoint->ep_join_id == remove_join->j_serial)
temp_epoint->ep_join_id = add_join->j_serial;
}
remove_relation_pointer = &remove_relation->next;
remove_relation = remove_relation->next;
}
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) endpoint removed, other enpoints on join relinked.\n", ea_endpoint->ep_serial);
/* join call relations */
add_relation = add_joinpbx->j_relation;
add_relation_pointer = &add_joinpbx->j_relation;
while(add_relation) {
add_relation_pointer = &add_relation->next;
add_relation = add_relation->next;
}
*add_relation_pointer = remove_joinpbx->j_relation;
remove_joinpbx->j_relation = NULL;
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) relations joined.\n", ea_endpoint->ep_serial);
/* release endpoint */
message = message_create(remove_joinpbx->j_serial, remove_eapp->ea_endpoint->ep_serial, JOIN_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = CAUSE_NORMAL; /* normal */
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
/* if we are not a partyline, we get partyline state from other join */
add_joinpbx->j_partyline += remove_joinpbx->j_partyline;
/* remove empty join */
delete remove_join;
PDEBUG(DEBUG_EPOINT, "EPOINT(%d)join completely removed!\n", ea_endpoint->ep_serial);
/* mixer must update */
trigger_work(&add_joinpbx->j_updatebridge);
/* we send a retrieve to that endpoint */
// mixer will update the hold-state of the join and send it to the endpoints is changes
#else
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
#endif
return 0;
}
int EndpointAppPBX::join_3pty_dss1(void)
{
#ifdef WITH_MISDN
class Join *our_join, *other_join;
@ -3832,6 +4044,126 @@ int EndpointAppPBX::join_3pty(void)
return 0;
}
int EndpointAppPBX::join_3pty_fxs(void)
{
#ifdef WITH_MISDN
class Join *our_join, *other_join;
class JoinPBX *our_joinpbx, *other_joinpbx;
class EndpointAppPBX *other_eapp;
class Port *our_port, *other_port;
class Pfxs *our_fxs, *other_fxs;
/* are we a candidate to join a join? */
our_join = find_join_id(ea_endpoint->ep_join_id);
if (!our_join) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join doesn't exist anymore.\n", ea_endpoint->ep_serial);
return -1;
}
if (our_join->j_type != JOIN_TYPE_PBX) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: join is not a pbx join.\n", ea_endpoint->ep_serial);
return -1;
}
our_joinpbx = (class JoinPBX *)our_join;
if (!ea_endpoint->ep_portlist) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we have no port.\n", ea_endpoint->ep_serial);
return -1;
}
if (!e_ext.number[0]) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we are not internal extension.\n", ea_endpoint->ep_serial);
return -1;
}
our_port = find_port_id(ea_endpoint->ep_portlist->port_id);
if (!our_port) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port doesn't exist anymore.\n", ea_endpoint->ep_serial);
return -1;
}
if ((our_port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our port is not FXS pots.\n", ea_endpoint->ep_serial);
return -1;
}
our_fxs = (class Pfxs *)our_port;
/* find an endpoint that has the same mISDNport that we are on */
other_eapp = apppbx_first;
while(other_eapp) {
if (other_eapp == this) {
other_eapp = other_eapp->next;
continue;
}
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint candiate: (ep%d) terminal='%s' port=%s join=%d.\n", ea_endpoint->ep_serial, other_eapp->ea_endpoint->ep_serial, other_eapp->e_ext.number, (other_eapp->ea_endpoint->ep_portlist)?"YES":"NO", other_eapp->ea_endpoint->ep_join_id);
if (other_eapp->e_ext.number[0] /* has terminal */
&& other_eapp->ea_endpoint->ep_portlist /* has port */
&& other_eapp->ea_endpoint->ep_join_id) { /* has join */
other_port = find_port_id(other_eapp->ea_endpoint->ep_portlist->port_id);
if (other_port) { /* port still exists */
if (other_port->p_type==PORT_TYPE_POTS_FXS_OUT
|| other_port->p_type==PORT_TYPE_POTS_FXS_IN) { /* port is isdn nt-mode */
other_fxs = (class Pfxs *)other_port;
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of type FXS! comparing our portnum=%d with other's portnum=%d hold=%s state=%d\n", ea_endpoint->ep_serial, our_fxs->p_m_mISDNport->portnum, other_fxs->p_m_mISDNport->portnum, (other_fxs->p_m_hold)?"YES":"NO", other_fxs->p_state);
if (1 //other_fxs->p_m_hold /* port is on hold */
&& other_fxs->p_m_mISDNport == our_fxs->p_m_mISDNport) /* same pots interface */
break;
} else {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port is of other type!\n", ea_endpoint->ep_serial);
}
} else {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) comparing other endpoint's port doesn't exist enymore.\n", ea_endpoint->ep_serial);
}
}
other_eapp = other_eapp->next;
}
if (!other_eapp) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no other endpoint on same FXS terminal.\n", ea_endpoint->ep_serial);
return -1;
}
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) port with same terminal found.\n", ea_endpoint->ep_serial);
/* if we have the same join */
if (other_eapp->ea_endpoint->ep_join_id == ea_endpoint->ep_join_id) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: we and the other have the same join.\n", ea_endpoint->ep_serial);
return -1;
}
other_join = find_join_id(other_eapp->ea_endpoint->ep_join_id);
if (!other_join) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join doesn't exist anymore.\n", ea_endpoint->ep_serial);
return -1;
}
if (other_join->j_type != JOIN_TYPE_PBX) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join is not a pbx join.\n", ea_endpoint->ep_serial);
return -1;
}
other_joinpbx = (class JoinPBX *)other_join;
if (our_joinpbx->j_partyline && other_joinpbx->j_partyline) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: both joins are partylines.\n", ea_endpoint->ep_serial);
return -1;
}
if (our_joinpbx->j_3pty) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: our join already doing 3PTY.\n", ea_endpoint->ep_serial);
return -1;
}
if (other_joinpbx->j_3pty) {
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: other join already doing 3PTY.\n", ea_endpoint->ep_serial);
return -1;
}
/* set 3PTY bridge */
other_joinpbx->j_3pty = our_joinpbx->j_serial;
our_joinpbx->j_3pty = other_joinpbx->j_serial;
/* mixer must update */
trigger_work(&our_joinpbx->j_updatebridge);
trigger_work(&other_joinpbx->j_updatebridge);
/* we send a retrieve to that endpoint */
// mixer will update the hold-state of the join and send it to the endpoints is changes
#else
PDEBUG(DEBUG_EPOINT, "EPOINT(%d) cannot join: no mISDN support anyway.\n", ea_endpoint->ep_serial);
#endif
return 0;
}
int EndpointAppPBX::split_3pty(void)
{
#ifdef WITH_MISDN
@ -4391,6 +4723,11 @@ void EndpointAppPBX::logmessage(int message_type, union parameter *param, unsign
end_trace();
break;
case MESSAGE_TRANSFER:
trace_header("TRANSFER", dir);
end_trace();
break;
case MESSAGE_DISABLE_DEJITTER:
trace_header("DISBALE_DEJITTER", dir);
if (param->queue)

View File

@ -212,6 +212,7 @@ class EndpointAppPBX : public EndpointApp
void port_progress(struct port_list *portlist, int message_type, union parameter *param);
void port_facility(struct port_list *portlist, int message_type, union parameter *param);
void port_3pty(struct port_list *portlist, int message_type, union parameter *param);
void port_transfer(struct port_list *portlist, int message_type, union parameter *param);
void port_suspend(struct port_list *portlist, int message_type, union parameter *param);
void port_resume(struct port_list *portlist, int message_type, union parameter *param);
void port_enablekeypad(struct port_list *portlist, int message_type, union parameter *param);
@ -287,6 +288,12 @@ class EndpointAppPBX : public EndpointApp
void action_init_play(void);
void action_init_vbox_play(void);
void action_init_efi(void);
void action_init_pots_retrieve(void);
void action_init_pots_release(void);
void action_init_pots_reject(void);
void action_init_pots_answer(void);
void action_init_pots_3pty(void);
void action_init_pots_transfer(void);
void action_dialing_vbox_play(void);
void action_dialing_calculator(void);
void action_dialing_timer(void);
@ -310,8 +317,10 @@ class EndpointAppPBX : public EndpointApp
/* facility function */
void pick_join(char *extension);
int join_join(void);
int join_3pty(void);
int join_join_dss1(void);
int join_join_fxs(void);
int join_3pty_dss1(void);
int join_3pty_fxs(void);
int split_3pty(void);
void encrypt_shared(void);
void encrypt_keyex(void);

835
fxs.cpp Normal file
View File

@ -0,0 +1,835 @@
/*****************************************************************************\
** **
** LCR **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg **
** **
** mISDN fxs **
** **
\*****************************************************************************/
/* Procedures:
*****TBD: THIS TEXT IS OLD ******
off-hook indication:
no port instance:
Create port/endpoint instance and send SETUP message to endpoint.
port instance active:
Send CONNECT message to endpoint.
port instance inactive:
Put inactive port instance active. Send RETRIEVE message to endpoint.
on-hook indication:
Release active port instance. Send RELEASE message to endpoint if exists.
inactive port instance:
Send ring request. Use caller ID on incomming call or connected ID on outgoing call.
hookflash indication:
active port instance not connected:
Release active port instance. Send RELEASE message to endpoint if exists.
active port instance connected:
Put active port instance inactive. Send HOLD MESSAGE to endpoint.
inactive port instance:
Put inactive port instance active. Send RETRIEVE message to endpoint.
no inactive port instance:
Create port/endpoint instance and send SETUP message to endpoint.
keypulse indication:
active port instance in incomming overlap state:
Send INFORMATION message to endpoint.
active port instance in other state:
Send KEYPAD message to endpoint, if exists.
SETUP message:
no instance:
Create port instance and send ALERTING message to endpoint.
Send ring request. Use caller ID.
only one instance active:
Create port instance and send ALERTING message to endpoint.
Send knock sound. Send ALERTING message to endpoint.
one instance on hold:
Send RELEASE message (cause = BUSY) to endpoint.
PROCEEDING / ALERTING / CONNECT message:
(change state only)
DISCONNECT message:
is inactive port instance:
Release port instance. Send RELEASE message to endpoint.
RELEASE message:
is active port instance:
Create hangup tone (release tone)
is inactive port instance:
Release port instance.
*/
#include "main.h"
#include "myisdn.h"
// socket mISDN
//#include <sys/socket.h>
extern "C" {
}
#ifdef ISDN_P_FXS_POTS
static int fxs_age = 0;
static int delete_event(struct lcr_work *work, void *instance, int index);
static int dtmf_timeout(struct lcr_timer *timer, void *instance, int index)
{
class Pfxs *pfxs = (class Pfxs *)instance;
/* allow DTMF dialing now */
PDEBUG(DEBUG_ISDN, "%s: allow DTMF now\n", pfxs->p_name);
pfxs->p_m_fxs_allow_dtmf = 1;
return 0;
}
/*
* constructor
*/
Pfxs::Pfxs(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, struct interface *interface, int mode) : PmISDN(type, mISDNport, portname, settings, interface, 0, 0, mode)
{
p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN;
memset(&p_m_fxs_delete, 0, sizeof(p_m_fxs_delete));
add_work(&p_m_fxs_delete, delete_event, this, 0);
p_m_fxs_allow_dtmf = 0;
memset(&p_m_fxs_dtmf_timer, 0, sizeof(p_m_fxs_dtmf_timer));
add_timer(&p_m_fxs_dtmf_timer, dtmf_timeout, this, 0);
p_m_fxs_age = fxs_age++;
p_m_fxs_knocking = 0;
PDEBUG(DEBUG_ISDN, "Created new FXSPort(%s). Currently %d objects use, FXS port #%d\n", portname, mISDNport->use, p_m_portnum);
}
/*
* destructor
*/
Pfxs::~Pfxs()
{
del_timer(&p_m_fxs_dtmf_timer);
del_work(&p_m_fxs_delete);
}
/* deletes only if l3id is release, otherwhise it will be triggered then */
static int delete_event(struct lcr_work *work, void *instance, int index)
{
class Pfxs *pots = (class Pfxs *)instance;
delete pots;
return 0;
}
int Pfxs::hunt_bchannel(void)
{
if (p_m_mISDNport->b_num < 1)
return -47;
if (p_m_mISDNport->b_port[0])
return -17;
return 1;
}
int Pfxs::ph_control_pots(unsigned int cont, unsigned char *data, int len)
{
unsigned char buffer[MISDN_HEADER_LEN+sizeof(int)+len];
struct mISDNhead *ctrl = (struct mISDNhead *)buffer;
unsigned int *d = (unsigned int *)(buffer+MISDN_HEADER_LEN);
int ret;
ctrl->prim = PH_CONTROL_REQ;
ctrl->id = 0;
*d++ = cont;
if (len)
memcpy(d, data, len);
ret = sendto(p_m_mISDNport->pots_sock.fd, buffer, MISDN_HEADER_LEN+sizeof(int)+len, 0, NULL, 0);
if (ret <= 0)
PERROR("Failed to send to socket %d\n", p_m_mISDNport->pots_sock.fd);
return ret;
}
void Pfxs::pickup_ind(unsigned int cont)
{
struct interface *interface = p_m_mISDNport->ifport->interface;
class Endpoint *epoint;
struct lcr_msg *message;
int ret, channel;
p_m_fxs_age = fxs_age++;
if (p_m_fxs_knocking) {
ph_control_pots(POTS_CW_OFF, NULL, 0);
p_m_fxs_knocking = 0;
}
chan_trace_header(p_m_mISDNport, this, "PICKUP", DIRECTION_NONE);
if (interface->ifmsn && interface->ifmsn->msn[0]) {
SCPY(p_callerinfo.id, interface->ifmsn->msn);
add_trace("caller", "ID", "%s", p_callerinfo.id);
}
p_callerinfo.present = INFO_PRESENT_ALLOWED;
p_callerinfo.isdn_port = p_m_portnum;
SCPY(p_callerinfo.interface, p_m_mISDNport->ifport->interface->name);
p_capainfo.source_mode = B_MODE_TRANSPARENT;
p_capainfo.bearer_capa = INFO_BC_AUDIO;
p_capainfo.bearer_info1 = 0x80 + ((options.law=='a')?3:2);
p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
if ((cont & (~POTS_KP_MASK)) == POTS_KP_VAL) {
p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
p_dialinginfo.id[0] = cont & POTS_KP_MASK;
}
if (!p_m_b_channel) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) alloc bchannel\n", p_name);
/* hunt bchannel */
ret = channel = hunt_bchannel();
if (ret < 0)
goto no_channel;
/* open channel */
ret = seize_bchannel(channel, 1);
if (ret < 0) {
no_channel:
/*
* NOTE: we send MT_RELEASE_COMPLETE to "REJECT" the channel
* in response to the setup
*/
add_trace("error", NULL, "no b-channel");
end_trace();
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
return;
}
end_trace();
bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE);
} else {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) bchannel is already active\n", p_name);
}
/* 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");
epoint->ep_app = new_endpointapp(epoint, 0, p_m_mISDNport->ifport->interface->app); //incoming
epointlist_new(epoint->ep_serial);
/* indicate flash control */
if (cont == POTS_HOOK_FLASH || cont == POTS_EARTH_KEY)
p_dialinginfo.flash = 1;
/* 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.callerinfo, &p_callerinfo, sizeof(struct caller_info));
memcpy(&message->param.setup.dialinginfo, &p_dialinginfo, sizeof(struct dialing_info));
memcpy(&message->param.setup.capainfo, &p_capainfo, sizeof(struct capa_info));
message_put(message);
new_state(PORT_STATE_IN_OVERLAP);
schedule_timer(&p_m_fxs_dtmf_timer, 0, 500000);
}
void Pfxs::hangup_ind(unsigned int cont)
{
struct lcr_msg *message;
/* deactivate bchannel */
chan_trace_header(p_m_mISDNport, this, "HANGUP", DIRECTION_NONE);
end_trace();
drop_bchannel();
PDEBUG(DEBUG_ISDN, "Pfxs(%s) drop bchannel\n", p_name);
/* send release message, if not already */
if (p_epointlist) {
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 16;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
}
void Pfxs::answer_ind(unsigned int cont)
{
struct lcr_msg *message;
int ret, channel;
if (p_m_fxs_knocking) {
ph_control_pots(POTS_CW_OFF, NULL, 0);
p_m_fxs_knocking = 0;
}
chan_trace_header(p_m_mISDNport, this, "ANSWER", DIRECTION_NONE);
if (!p_m_b_channel) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) alloc bchannel\n", p_name);
/* hunt bchannel */
ret = channel = hunt_bchannel();
if (ret < 0)
goto no_channel;
/* open channel */
ret = seize_bchannel(channel, 1);
if (ret < 0) {
no_channel:
/*
* NOTE: we send MT_RELEASE_COMPLETE to "REJECT" the channel
* in response to the setup
*/
add_trace("error", NULL, "no b-channel");
end_trace();
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
return;
}
end_trace();
bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE);
} else {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) bchannel is already active\n", p_name);
}
if (p_m_hold) {
p_m_hold = 0;
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL;
message->param.notifyinfo.local = 1; /* call is held by supplementary service */
message_put(message);
} else {
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
message_put(message);
}
new_state(PORT_STATE_CONNECT);
}
void Pfxs::hold_ind(unsigned int cont)
{
struct lcr_msg *message;
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_HOLD;
message->param.notifyinfo.local = 1; /* call is held by supplementary service */
message_put(message);
/* deactivate bchannel */
chan_trace_header(p_m_mISDNport, this, "HOLD", DIRECTION_NONE);
end_trace();
drop_bchannel();
PDEBUG(DEBUG_ISDN, "Pfxs(%s) drop bchannel\n", p_name);
p_m_hold = 1;
}
void Pfxs::retrieve_ind(unsigned int cont)
{
struct lcr_msg *message;
int ret, channel;
p_m_fxs_age = fxs_age++;
if (p_m_fxs_knocking) {
ph_control_pots(POTS_CW_OFF, NULL, 0);
p_m_fxs_knocking = 0;
}
if (cont == POTS_ON_HOOK) {
const char *callerid;
if (p_state == PORT_STATE_CONNECT) {
new_state(PORT_STATE_OUT_ALERTING);
#if 0
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING);
message_put(message);
#endif
chan_trace_header(p_m_mISDNport, this, "RING (retrieve)", DIRECTION_NONE);
} else
chan_trace_header(p_m_mISDNport, this, "RING (after knocking)", DIRECTION_NONE);
if (p_type == PORT_TYPE_POTS_FXS_IN) {
if (p_connectinfo.id[0]) {
callerid = numberrize_callerinfo(p_connectinfo.id, p_connectinfo.ntype, options.national, options.international);
add_trace("connect", "number", callerid);
} else {
callerid = p_dialinginfo.id;
add_trace("dialing", "number", callerid);
}
} else {
callerid = numberrize_callerinfo(p_callerinfo.id, p_callerinfo.ntype, options.national, options.international);
add_trace("caller", "id", callerid);
}
ph_control_pots(POTS_RING_ON, (unsigned char *)callerid, strlen(callerid));
end_trace();
return;
}
chan_trace_header(p_m_mISDNport, this, "RETRIEVE", DIRECTION_NONE);
if (!p_m_b_channel) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) alloc bchannel\n", p_name);
/* hunt bchannel */
ret = channel = hunt_bchannel();
if (ret < 0)
goto no_channel;
/* open channel */
ret = seize_bchannel(channel, 1);
if (ret < 0) {
no_channel:
/*
* NOTE: we send MT_RELEASE_COMPLETE to "REJECT" the channel
* in response to the setup
*/
add_trace("error", NULL, "no b-channel");
end_trace();
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
return;
}
end_trace();
bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE);
} else {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) bchannel is already active\n", p_name);
}
p_m_hold = 0;
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_NOTIFY);
message->param.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL;
message->param.notifyinfo.local = 1; /* call is held by supplementary service */
message_put(message);
}
void Pfxs::keypulse_ind(unsigned int cont)
{
struct lcr_msg *message;
chan_trace_header(p_m_mISDNport, this, "PULSE", DIRECTION_NONE);
add_trace("KP", NULL, "%c", cont & DTMF_TONE_MASK);
end_trace();
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
message->param.information.id[0] = cont & POTS_KP_MASK;
PDEBUG(DEBUG_ISDN, "Pfxs(%s) PH_CONTROL INDICATION DTMF digit '%c'\n", p_name, message->param.dtmf);
message_put(message);
}
void Pfxs::reject_ind(unsigned int cont)
{
struct lcr_msg *message;
if (p_m_fxs_knocking) {
ph_control_pots(POTS_CW_OFF, NULL, 0);
p_m_fxs_knocking = 0;
}
/* deactivate bchannel */
chan_trace_header(p_m_mISDNport, this, "REJECT", DIRECTION_NONE);
end_trace();
drop_bchannel();
PDEBUG(DEBUG_ISDN, "Pfxs(%s) drop bchannel\n", p_name);
/* send release message, if not already */
if (p_epointlist) {
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 16;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
}
void Pfxs::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
{
struct lcr_msg *message;
class Port *port;
class Pfxs *pots;
struct epoint_list *epointlist;
const char *callerid;
int any_call = 0;
memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_ALERTING);
message_put(message);
new_state(PORT_STATE_OUT_ALERTING);
/* attach only if not already */
epointlist = p_epointlist;
while(epointlist) {
if (epointlist->epoint_id == epoint_id)
break;
epointlist = epointlist->next;
}
if (!epointlist)
epointlist_new(epoint_id);
/* find port in connected active state */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == p_m_mISDNport) {
if (pots != this)
any_call = 1;
if (pots->p_state == PORT_STATE_CONNECT && !pots->p_m_hold)
break; // found
}
}
port = port->next;
}
if (port) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) knock because there is an ongoing active call\n", p_name);
chan_trace_header(p_m_mISDNport, this, "KNOCK", DIRECTION_NONE);
callerid = numberrize_callerinfo(p_callerinfo.id, p_callerinfo.ntype, options.national, options.international);
add_trace("caller", "id", callerid);
end_trace();
ph_control_pots(POTS_CW_ON, (unsigned char *)callerid, strlen(callerid));
p_m_fxs_knocking = 1;
return;
}
if (any_call) {
/* reject call, because we have a call, but we are not connected */
PDEBUG(DEBUG_ISDN, "Pfxs(%s) reject because there is an ongoing and incomplete call\n", p_name);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 17; // busy
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
return;
}
PDEBUG(DEBUG_ISDN, "Pfxs(%s) ring because there is not calll\n", p_name);
chan_trace_header(p_m_mISDNport, this, "RING", DIRECTION_NONE);
callerid = numberrize_callerinfo(p_callerinfo.id, p_callerinfo.ntype, options.national, options.international);
add_trace("caller", "id", callerid);
end_trace();
ph_control_pots(POTS_RING_ON, (unsigned char *)callerid, strlen(callerid));
}
void Pfxs::message_proceeding(unsigned int epoint_id, int message_id, union parameter *param)
{
new_state(PORT_STATE_IN_PROCEEDING);
}
void Pfxs::message_alerting(unsigned int epoint_id, int message_id, union parameter *param)
{
new_state(PORT_STATE_IN_ALERTING);
}
void Pfxs::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
{
new_state(PORT_STATE_CONNECT);
memcpy(&p_connectinfo, &param->connectinfo, sizeof(struct connect_info));
}
void Pfxs::message_disconnect(unsigned int epoint_id, int message_id, union parameter *param)
{
if (p_state == PORT_STATE_OUT_ALERTING) {
if (p_m_fxs_knocking) {
ph_control_pots(POTS_CW_OFF, NULL, 0);
p_m_fxs_knocking = 0;
} else {
ph_control_pots(POTS_RING_OFF, NULL, 0);
}
if (p_epointlist) {
struct lcr_msg *message;
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 16;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
}
free_epointid(epoint_id);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_m_fxs_delete);
return;
}
new_state(PORT_STATE_OUT_DISCONNECT);
}
void Pfxs::message_release(unsigned int epoint_id, int message_id, union parameter *param)
{
chan_trace_header(p_m_mISDNport, this, "CLEAR", DIRECTION_NONE);
end_trace();
if (p_state == PORT_STATE_OUT_ALERTING) {
if (p_m_fxs_knocking) {
ph_control_pots(POTS_CW_OFF, NULL, 0);
p_m_fxs_knocking = 0;
} else {
ph_control_pots(POTS_RING_OFF, NULL, 0);
}
trigger_work(&p_m_fxs_delete);
}
if (p_state == PORT_STATE_CONNECT) {
if (!p_m_hold)
set_tone("", "release");
else
trigger_work(&p_m_fxs_delete);
}
new_state(PORT_STATE_RELEASE);
free_epointid(epoint_id);
}
/*
* endpoint sends messages to the port
*/
int Pfxs::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 */
message_setup(epoint_id, message_id, param);
break;
case MESSAGE_PROCEEDING: /* call of endpoint is proceeding */
message_proceeding(epoint_id, message_id, param);
break;
case MESSAGE_ALERTING: /* call of endpoint is ringing */
message_alerting(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 */
if (p_state==PORT_STATE_RELEASE) {
break;
}
message_release(epoint_id, message_id, param);
break;
}
return(1);
}
/*
* data from isdn-stack (layer-1) to pbx (port class)
*/
int stack2manager_fxs(struct mISDNport *mISDNport, unsigned int cont)
{
class Port *port;
class Pfxs *pots, *latest_pots = NULL, *alerting_pots = NULL;
int latest = -1;
char name[32];
PDEBUG(DEBUG_ISDN, "cont(0x%x)\n", cont);
if ((cont & (~POTS_KP_MASK)) == POTS_KP_VAL) {
/* find port in dialing state */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport
&& pots->p_state == PORT_STATE_IN_OVERLAP)
break; // found
}
port = port->next;
}
if (port) {
pots->keypulse_ind(cont);
return 0;
}
goto flash;
}
switch (cont) {
case POTS_OFF_HOOK:
/* find ringing */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport
&& pots->p_state == PORT_STATE_OUT_ALERTING)
break; // found
}
port = port->next;
}
if (port) {
pots->answer_ind(cont);
break;
}
setup:
/* creating port object */
SPRINT(name, "%s-%d-in", mISDNport->ifport->interface->name, mISDNport->portnum);
pots = new Pfxs(PORT_TYPE_POTS_FXS_IN, mISDNport, name, NULL, mISDNport->ifport->interface, B_MODE_TRANSPARENT);
if (!pots)
FATAL("Failed to create Port instance\n");
pots->pickup_ind(cont);
break;
case POTS_ON_HOOK:
if (mISDNport->ifport->pots_transfer) {
struct lcr_msg *message;
class Pfxs *pots1 = NULL, *pots2 = NULL;
int count = 0;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport) {
if (pots->p_state == PORT_STATE_CONNECT
|| pots->p_state == PORT_STATE_IN_PROCEEDING
|| pots->p_state == PORT_STATE_IN_ALERTING) {
if (count == 0)
pots1 = pots;
if (count == 1)
pots2 = pots;
count++;
}
}
}
port = port->next;
}
if (count == 2) {
if (pots1->p_state == PORT_STATE_CONNECT) {
message = message_create(pots1->p_serial, ACTIVE_EPOINT(pots1->p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
message_put(message);
}
else if (pots2->p_state == PORT_STATE_CONNECT) {
message = message_create(pots2->p_serial, ACTIVE_EPOINT(pots2->p_epointlist), PORT_TO_EPOINT, MESSAGE_TRANSFER);
message_put(message);
}
pots1->hangup_ind(cont);
pots2->hangup_ind(cont);
break;
}
}
if (mISDNport->ifport->pots_ring) {
/* release all except calls on hold, let the latest call ring */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport) {
if (pots->p_state == PORT_STATE_CONNECT && pots->p_m_hold) {
if (pots->p_m_fxs_age > latest) {
latest = pots->p_m_fxs_age;
latest_pots = pots;
}
}
if (pots->p_state == PORT_STATE_OUT_ALERTING)
alerting_pots = pots;
}
}
port = port->next;
}
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport) {
if ((pots->p_state != PORT_STATE_CONNECT || !pots->p_m_hold) && pots != alerting_pots) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) release because pots-ring-after-hangup set and call not on hold / alerting\n", pots->p_name);
pots->hangup_ind(cont);
}
}
}
port = port->next;
}
if (alerting_pots) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) answer because pots-ring-after-hangup set and call is alerting (knocking)\n", alerting_pots->p_name);
alerting_pots->retrieve_ind(cont);
break;
}
if (latest_pots) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) retrieve because pots-ring-after-hangup set and call is latest on hold\n", latest_pots->p_name);
latest_pots->retrieve_ind(cont);
break;
}
} else {
/* release all pots */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) release because pots-ring-after-hangup not set\n", pots->p_name);
pots->hangup_ind(cont);
}
}
port = port->next;
}
}
break;
case POTS_HOOK_FLASH:
case POTS_EARTH_KEY:
flash:
if (!mISDNport->ifport->pots_flash) {
PDEBUG(DEBUG_ISDN, "Pfxs flash key is disabled\n");
break;
}
/* hold active pots / release not active pots */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
pots = (class Pfxs *)port;
if (pots->p_m_mISDNport == mISDNport) {
if (pots->p_state == PORT_STATE_CONNECT) {
if (pots->p_m_hold) {
if (pots->p_m_fxs_age > latest) {
latest = pots->p_m_fxs_age;
latest_pots = pots;
}
} else {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) hold, because flash on active call\n", pots->p_name);
pots->hold_ind(cont);
}
} else if (pots->p_state == PORT_STATE_OUT_ALERTING) {
alerting_pots = pots;
} else {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) hangup, because flash on incomplete/released call\n", pots->p_name);
pots->hangup_ind(cont);
}
}
}
port = port->next;
}
#if 0
/* now we have our bchannel available, so we can look alerting port to answer */
if (alerting_pots) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) answer because call is alerting (knocking)\n", alerting_pots->p_name);
alerting_pots->answer_ind(cont);
break;
}
if (latest_pots) {
PDEBUG(DEBUG_ISDN, "Pfxs(%s) retrieve because call is latest on hold\n", latest_pots->p_name);
latest_pots->retrieve_ind(cont);
break;
}
#endif
goto setup;
default:
PERROR("unhandled message: xontrol(0x%x)\n", cont);
return(-EINVAL);
}
return(0);
}
#endif /* ISDN_P_FXS_POTS */

48
fxs.h Normal file
View File

@ -0,0 +1,48 @@
/*****************************************************************************\
** **
** PBX4Linux **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg **
** **
** fxs-port header file **
** **
\*****************************************************************************/
/* FXS port classes */
class Pfxs : public PmISDN
{
public:
Pfxs(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, struct interface *interface, int mode);
~Pfxs();
struct lcr_work p_m_fxs_delete;
struct lcr_timer p_m_fxs_dtmf_timer;
int p_m_fxs_allow_dtmf;
int p_m_fxs_age;
int p_m_fxs_knocking;
int ph_control_pots(unsigned int cont, unsigned char *data, int len);
int hunt_bchannel(void);
void pickup_ind(unsigned int cont);
void hangup_ind(unsigned int cont);
void answer_ind(unsigned int cont);
void hold_ind(unsigned int cont);
void retrieve_ind(unsigned int cont);
void keypulse_ind(unsigned int cont);
void flash_ind(unsigned int cont);
void reject_ind(unsigned int cont);
void message_setup(unsigned int epoint_id, int message_id, union parameter *param);
void message_information(unsigned int epoint_id, int message_id, union parameter *param);
void message_release(unsigned int epoint_id, int message_id, union parameter *param);
void message_proceeding(unsigned int epoint_id, int message_id, union parameter *param);
void message_alerting(unsigned int epoint_id, int message_id, union parameter *param);
void message_connect(unsigned int epoint_id, int message_id, union parameter *param);
void message_disconnect(unsigned int epoint_id, int message_id, union parameter *param);
int message_epoint(unsigned int epoint_id, int message, union parameter *param);
};
int stack2manager_fxs(struct mISDNport *mISDNport, unsigned int cont);

View File

@ -1110,6 +1110,57 @@ static int inter_context(struct interface *interface, char *filename, int line,
return(0);
}
static int inter_pots_flash(struct interface *interface, char *filename, int line, char *parameter, char *value)
{
struct interface_port *ifport;
/* port in chain ? */
if (!interface->ifport) {
SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
return(-1);
}
/* goto end of chain */
ifport = interface->ifport;
while(ifport->next)
ifport = ifport->next;
ifport->pots_flash = 1;
return(0);
}
static int inter_pots_ring(struct interface *interface, char *filename, int line, char *parameter, char *value)
{
struct interface_port *ifport;
/* port in chain ? */
if (!interface->ifport) {
SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
return(-1);
}
/* goto end of chain */
ifport = interface->ifport;
while(ifport->next)
ifport = ifport->next;
ifport->pots_ring = 1;
return(0);
}
static int inter_pots_transfer(struct interface *interface, char *filename, int line, char *parameter, char *value)
{
struct interface_port *ifport;
/* port in chain ? */
if (!interface->ifport) {
SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
return(-1);
}
/* goto end of chain */
ifport = interface->ifport;
while(ifport->next)
ifport = ifport->next;
ifport->pots_transfer = 1;
return(0);
}
static int inter_shutdown(struct interface *interface, char *filename, int line, char *parameter, char *value)
{
interface->shutdown = 1;
@ -1309,6 +1360,21 @@ struct interface_param interface_param[] = {
{"context", &inter_context, "<context>",
"Give context for calls to application."},
{"pots-flash", &inter_pots_flash, "",
"Allow flash button to hold an active call and setup a new call.\n"
"Ihis parameter only appies to POTS type of interfaces\n"
"This parameter must follow a 'port' parameter.\n"},
{"pots-ring-after-hangup", &inter_pots_ring, "",
"Allow ringing of last hold call after hangup. Other calls on hold will not be\n"
"released.\n"
"Ihis parameter only appies to POTS type of interfaces\n"
"This parameter must follow a 'port' parameter.\n"},
{"pots-transfer-after-hangup", &inter_pots_transfer, "",
"If two calls on hold, both are connected after hangup.\n"
"If one call is on hold and another one alerting, call on hold is tranfered.\n"
"Ihis parameter only appies to POTS type of interfaces\n"
"This parameter must follow a 'port' parameter.\n"},
{"shutdown", &inter_shutdown, "",
"Interface will not be loaded when processing interface.conf"},

View File

@ -67,6 +67,9 @@ struct interface_port {
int dialmax; /* maximum number of digits to dial */
char tones_dir[128];
int nonotify; /* blocks outgoing notify messages */
int pots_flash; /* allow flash button / keypulse to hold active call */
int pots_ring; /* after hangup let calls on hold ring the phone */
int pots_transfer; /* after hangup, two calls are transfered */
};
struct interface_msn {

201
mISDN.cpp
View File

@ -1057,10 +1057,23 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
end_trace();
if (!p_m_dtmf)
return;
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
message->param.dtmf = cont & DTMF_TONE_MASK;
PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION DTMF digit '%c'\n", p_name, message->param.dtmf);
message_put(message);
if (p_type == PORT_TYPE_POTS_FXS_IN && p_state == PORT_STATE_IN_OVERLAP) {
class Pfxs *pfxs = (class Pfxs *)this;
if (!pfxs->p_m_fxs_allow_dtmf) {
PDEBUG(DEBUG_PORT, "PmISDN(%s) DTMF for FXS not yet allowed\n", p_name);
return;
}
SCCAT(p_dialinginfo.id, cont & DTMF_TONE_MASK);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
message->param.information.id[0] = cont & DTMF_TONE_MASK;
PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION INFORMATION digit '%s'\n", p_name, message->param.information.id);
message_put(message);
} else {
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
message->param.dtmf = cont & DTMF_TONE_MASK;
PDEBUG(DEBUG_PORT, "PmISDN(%s) PH_CONTROL INDICATION DTMF digit '%c'\n", p_name, message->param.dtmf);
message_put(message);
}
return;
}
switch(cont) {
@ -1777,6 +1790,43 @@ int mISDN_getportbyname(int sock, int cnt, char *portname)
return (port);
}
/* handle frames from pots */
static int pots_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int i)
{
struct mISDNport *mISDNport = (struct mISDNport *)instance;
unsigned char buffer[2048+MISDN_HEADER_LEN];
struct mISDNhead *hh = (struct mISDNhead *)buffer;
unsigned int cont;
int ret;
ret = recv(fd->fd, buffer, sizeof(buffer), 0);
if (ret < 0) {
PERROR("read error frame, errno %d\n", errno);
return 0;
}
if (ret < (int)MISDN_HEADER_LEN) {
PERROR("read short frame, got %d, expected %d\n", ret, (int)MISDN_HEADER_LEN);
return 0;
}
switch(hh->prim) {
case PH_CONTROL_IND:
cont = *((unsigned int *)(buffer + MISDN_HEADER_LEN));
/* l1-control is sent to LCR */
if (mISDNport->ntmode)
stack2manager_fxs(mISDNport, cont);
else
PERROR("FXO not supported!\n");
break;
case PH_ACTIVATE_REQ:
break;
default:
PERROR("child message not handled: prim(0x%x) socket(%d) msg->len(%d)\n", hh->prim, fd->fd, ret-MISDN_HEADER_LEN);
}
return 0;
}
/*
* global function to add a new card (port)
*/
@ -1845,32 +1895,29 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
pri = 1;
nt = 1;
}
#ifdef ISDN_P_FXS
if (devinfo.Dprotocols & (1 << ISDN_P_FXS)) {
#ifdef ISDN_P_FXS_POTS
if (devinfo.Dprotocols & (1 << ISDN_P_FXO_POTS)) {
pots = 1;
te = 1;
}
#endif
#ifdef ISDN_P_FXO
if (devinfo.Dprotocols & (1 << ISDN_P_FXO)) {
if (devinfo.Dprotocols & (1 << ISDN_P_FXS_POTS)) {
pots = 1;
nt = 1;
}
#endif
if (force_nt && !nt) {
PERROR_RUNTIME("Port %d does not support NT-mode\n", port);
if (!pots)
PERROR_RUNTIME("Port %d does not support NT-mode\n", port);
else
PERROR_RUNTIME("Port %d does not support FXS-mode\n", port);
return(NULL);
}
if (bri && pri) {
PERROR_RUNTIME("Port %d supports BRI and PRI?? What kind of controller is that?. (Can't use this!)\n", port);
return(NULL);
}
if (pots && !bri && !pri) {
PERROR_RUNTIME("Port %d supports POTS, LCR does not!\n", port);
return(NULL);
}
if (!bri && !pri) {
PERROR_RUNTIME("Port %d does not support BRI nor PRI!\n", port);
if (!bri && !pri && !pots) {
PERROR_RUNTIME("Port %d does not support BRI nor PRI nor POTS!\n", port);
return(NULL);
}
if (!nt && !te) {
@ -1883,6 +1930,10 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
/* if TE an NT is supported (and not forced to NT), turn off NT */
if (te && nt)
nt = 0;
if (pots && te) {
PERROR_RUNTIME("Port %d uses FXO-mode, but not supported by LCR!\n", port);
return(NULL);
}
/* check for double use of port */
if (nt) {
@ -1963,40 +2014,85 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
}
/* allocate ressources of port */
protocol = (nt)?L3_PROTOCOL_DSS1_NET:L3_PROTOCOL_DSS1_USER;
prop = (1 << MISDN_FLG_L2_CLEAN);
if (ptp) // ptp forced
prop |= (1 << MISDN_FLG_PTP);
if (nt) // supports hold/retrieve on nt-mode
prop |= (1 << MISDN_FLG_NET_HOLD);
if (l1hold) // supports layer 1 hold
prop |= (1 << MISDN_FLG_L1_HOLD);
if (l2hold) // supports layer 2 hold
prop |= (1 << MISDN_FLG_L2_HOLD);
/* open layer 3 and init upqueue */
/* queue must be initializes, because l3-thread may send messages during open_layer3() */
mqueue_init(&mISDNport->upqueue);
mISDNport->ml3 = open_layer3(port, protocol, prop , do_layer3, mISDNport);
if (!mISDNport->ml3) {
mqueue_purge(&mISDNport->upqueue);
PERROR_RUNTIME("open_layer3() failed for port %d\n", port);
start_trace(port,
ifport->interface,
NULL,
NULL,
DIRECTION_NONE,
CATEGORY_CH,
0,
"PORT (open failed)");
end_trace();
mISDNport_close(mISDNport);
return(NULL);
if (!pots) {
/* ISDN */
protocol = (nt)?L3_PROTOCOL_DSS1_NET:L3_PROTOCOL_DSS1_USER;
prop = (1 << MISDN_FLG_L2_CLEAN);
if (ptp) // ptp forced
prop |= (1 << MISDN_FLG_PTP);
if (nt) // supports hold/retrieve on nt-mode
prop |= (1 << MISDN_FLG_NET_HOLD);
if (l1hold) // supports layer 1 hold
prop |= (1 << MISDN_FLG_L1_HOLD);
if (l2hold) // supports layer 2 hold
prop |= (1 << MISDN_FLG_L2_HOLD);
/* open layer 3 and init upqueue */
/* queue must be initializes, because l3-thread may send messages during open_layer3() */
mqueue_init(&mISDNport->upqueue);
mISDNport->ml3 = open_layer3(port, protocol, prop , do_layer3, mISDNport);
if (!mISDNport->ml3) {
mqueue_purge(&mISDNport->upqueue);
PERROR_RUNTIME("open_layer3() failed for port %d\n", port);
start_trace(port,
ifport->interface,
NULL,
NULL,
DIRECTION_NONE,
CATEGORY_CH,
0,
"PORT (open failed)");
end_trace();
mISDNport_close(mISDNport);
return(NULL);
}
} else {
#ifdef ISDN_P_FXS_POTS
/* POTS */
int sock, ret;
struct sockaddr_mISDN addr;
struct mISDNhead act;
/* open socket */
/* queue must be initializes, because even pots interfaces are checked at mISDN_upqueue loop */
mqueue_init(&mISDNport->upqueue);
sock = socket(PF_ISDN, SOCK_DGRAM, (nt) ? ISDN_P_FXS_POTS : ISDN_P_FXO_POTS);
if (sock < 0) {
PERROR_RUNTIME("Cannot open mISDN due to '%s'.\n", strerror(errno));
return NULL;
}
/* bind socket to dchannel */
addr.family = AF_ISDN;
addr.dev = port;
addr.channel = 0;
ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
PERROR_RUNTIME("Error: Failed to bind pots control channel\n");
start_trace(port,
ifport->interface,
NULL,
NULL,
DIRECTION_NONE,
CATEGORY_CH,
0,
"PORT (open failed)");
end_trace();
return(NULL);
}
act.prim = PH_ACTIVATE_REQ;
act.id = 0;
ret = sendto(sock, &act, MISDN_HEADER_LEN, 0, NULL, 0);
if (ret <= 0)
PERROR("Failed to send to socket %d\n", sock);
mISDNport->pots_sock.fd = sock;
register_fd(&mISDNport->pots_sock, LCR_FD_READ, pots_sock_callback, mISDNport, i);
#endif
}
SCPY(mISDNport->name, devinfo.name);
mISDNport->b_num = devinfo.nrbchan;
mISDNport->portnum = port;
mISDNport->ntmode = nt;
mISDNport->pots = pots;
mISDNport->tespecial = ifport->tespecial;
mISDNport->pri = pri;
mISDNport->ptp = ptp;
@ -2012,7 +2108,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
}
/* if ptp, pull up the link */
if (mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) {
if (!pots && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) {
mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
l1l2l3_trace_header(mISDNport, NULL, L2_ESTABLISH_REQ, DIRECTION_OUT);
add_trace("tei", NULL, "%d", 0);
@ -2020,8 +2116,8 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
schedule_timer(&mISDNport->l2establish, 5, 0); /* 5 seconds */
}
/* for nt-mode ptmp the link is always up */
if (mISDNport->ntmode && !mISDNport->ptp)
/* for POTS or nt-mode ptmp the link is always up */
if (pots || (mISDNport->ntmode && !mISDNport->ptp))
mISDNport->l2link = 1;
PDEBUG(DEBUG_BCHANNEL, "using 'mISDN_dsp.o' module\n");
@ -2034,7 +2130,10 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
CATEGORY_CH,
0,
"PORT (open)");
add_trace("mode", NULL, (mISDNport->ntmode)?"network":"terminal");
if (!pots)
add_trace("mode", NULL, (mISDNport->ntmode)?"network":"terminal");
else
add_trace("mode", NULL, (mISDNport->ntmode)?"FXS":"FXO");
add_trace("channels", NULL, "%d", mISDNport->b_num);
if (mISDNport->ss5)
add_trace("ccitt#5", NULL, "enabled");
@ -2129,6 +2228,12 @@ void mISDNport_close(struct mISDNport *mISDNport)
close_layer3(mISDNport->ml3);
}
/* close layer 1, if open */
if (mISDNport->pots_sock.fd) {
unregister_fd(&mISDNport->pots_sock);
close(mISDNport->pots_sock.fd);
}
/* purge upqueue */
mqueue_purge(&mISDNport->upqueue);

View File

@ -45,6 +45,8 @@ struct mISDNport {
int ntmode; /* is TRUE if port is NT mode */
int tespecial; /* is TRUE if port uses special TE mode */
int pri; /* is TRUE if port is a primary rate interface */
int pots; /* is TRUE if port is of type POTS */
struct lcr_fd pots_sock; /* socket for L1 */
int tones; /* TRUE if tones are sent outside connect state */
int earlyb; /* TRUE if tones are received outside connect state */
int b_num; /* number of bchannels */

1
main.h
View File

@ -150,6 +150,7 @@ void debug(const char *file, const char *function, int line, const char *prefix,
#ifdef WITH_MISDN
#include "mISDN.h"
#include "dss1.h"
#include "fxs.h"
#endif
#if defined WITH_GSM_BS || defined WITH_GSM_MS
#include "gsm.h"

View File

@ -185,6 +185,7 @@ struct dialing_info {
char display[84]; /* display information */
char keypad[33]; /* send keypad facility */
char context[32]; /* asterisk context */
int flash; /* flash key caused setup of call */
};
/* call-info structure CONNECT */
@ -433,6 +434,7 @@ enum { /* messages between entities */
MESSAGE_BRIDGE, /* control port bridge */
MESSAGE_TRAFFIC, /* exchange bchannel traffic */
MESSAGE_3PTY, /* 3PTY call invoke */
MESSAGE_TRANSFER, /* call transfer invoke */
MESSAGE_DISABLE_DEJITTER/* tell (mISDN) port not to dejitter */
};
@ -471,6 +473,7 @@ enum { /* messages between entities */
"MESSAGE_BRIDGE", \
"MESSAGE_TRAFFIC", \
"MESSAGE_3PTY", \
"MESSAGE_TRANSFER", \
"MESSAGE_DISABLE_DEJITTER", \
};

10
port.h
View File

@ -18,6 +18,9 @@
#define PORT_CLASS_DSS1 0x1100
#define PORT_CLASS_DSS1_NT 0x1110
#define PORT_CLASS_DSS1_TE 0x1120
#define PORT_CLASS_POTS 0x1200
#define PORT_CLASS_POTS_FXS 0x1210
#define PORT_CLASS_POTS_FXO 0x1220
#define PORT_CLASS_SS5 0x1300
#define PORT_CLASS_SIP 0x2000
#define PORT_CLASS_GSM 0x3000
@ -27,6 +30,7 @@
#define PORT_CLASS_MASK 0xf000
#define PORT_CLASS_mISDN_MASK 0xff00
#define PORT_CLASS_DSS1_MASK 0xfff0
#define PORT_CLASS_POTS_MASK 0xfff0
#define PORT_CLASS_GSM_MASK 0xff00
#define PORT_CLASS_DIR_MASK 0x000f
#define PORT_CLASS_DIR_IN 0x0001
@ -37,6 +41,12 @@
/* te-mode */
#define PORT_TYPE_DSS1_TE_IN 0x1121
#define PORT_TYPE_DSS1_TE_OUT 0x1122
/* FXS-mode */
#define PORT_TYPE_POTS_FXS_IN 0x1211
#define PORT_TYPE_POTS_FXS_OUT 0x1212
/* FXO-mode */
#define PORT_TYPE_POTS_FXO_IN 0x1221
#define PORT_TYPE_POTS_FXO_OUT 0x1222
/* gsm */
#define PORT_TYPE_GSM_BS_IN 0x3101
#define PORT_TYPE_GSM_BS_OUT 0x3102

127
route.c
View File

@ -94,6 +94,14 @@ struct cond_defs cond_defs[] = {
"remote=<application name>","Matches if remote application is running."},
{ "notremote", MATCH_NOTREMOTE,COND_TYPE_STRING,
"notremote=<application name>","Matches if remote application is not running."},
{ "pots-flash", MATCH_POTS_FLASH,COND_TYPE_NULL,
"pots-flash","When using POTS: Matches if call was invoked by flash/earth button."},
{ "pots-cw", MATCH_POTS_CW, COND_TYPE_NULL,
"pots-cw","When using POTS: Matches if a call is waiting."},
{ "pots-calls", MATCH_POTS_CALLS,COND_TYPE_INTEGER,
"pots-calls=<total number>","When using POTS: Matches if given number of calls are held."},
{ "pots-last", MATCH_POTS_LAST,COND_TYPE_INTEGER,
"pots-last=<call number>","When using POTS: Matches if given call number (1=oldest) was the last active call."},
{ NULL, 0, 0, NULL}
};
@ -245,6 +253,9 @@ struct param_defs param_defs[] = {
{ PARAM_KEYPAD,
"keypad", PARAM_TYPE_NULL,
"keypad", "Use 'keypad facility' for dialing, instead of 'called number'."},
{ PARAM_POTS_CALL,
"pots-call", PARAM_TYPE_INTEGER,
"pots-call=<call #>", "Select call number. The oldest call is number 1."},
{ 0, NULL, 0, NULL, NULL}
};
@ -380,6 +391,30 @@ struct action_defs action_defs[] = {
"efi", &EndpointAppPBX::action_init_efi, NULL, NULL,
PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT,
"Elektronische Fernsprecher Identifikation - announces caller ID."},
{ ACTION_POTS_RETRIEVE,
"pots-retrieve", &EndpointAppPBX::action_init_pots_retrieve, NULL, NULL,
PARAM_POTS_CALL,
"When using POTS: Select call on hold to retrieve."},
{ ACTION_POTS_RELEASE,
"pots-release", &EndpointAppPBX::action_init_pots_release, NULL, NULL,
PARAM_POTS_CALL,
"When using POTS: Select call on hold to release."},
{ ACTION_POTS_REJECT,
"pots-reject", &EndpointAppPBX::action_init_pots_reject, NULL, NULL,
0,
"When using POTS: Reject incomming waiting call."},
{ ACTION_POTS_ANSWER,
"pots-answer", &EndpointAppPBX::action_init_pots_answer, NULL, NULL,
0,
"When using POTS: Answer incomming waiting call."},
{ ACTION_POTS_3PTY,
"pots-3pty", &EndpointAppPBX::action_init_pots_3pty, NULL, NULL,
0,
"When using POTS: Invoke 3PTY call of two calls on hold"},
{ ACTION_POTS_TRANSFER,
"pots-transfer", &EndpointAppPBX::action_init_pots_transfer, NULL, NULL,
0,
"When using POTS: Interconnect two calls on hold"},
{ -1,
NULL, NULL, NULL, NULL, 0, NULL}
};
@ -983,7 +1018,7 @@ struct route_ruleset *ruleset_parse(void)
while(*p!=':' && *p!='\0') {
/* read item text */
i = 0;
while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9')) {
while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || *p == '-') {
if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
key[i++] = *p++;
if (i == sizeof(key)) i--; /* limit */
@ -1427,11 +1462,15 @@ struct route_ruleset *ruleset_parse(void)
while(*p != 0) {
/* read param text */
i = 0;
while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9')) {
while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || *p == '-') {
if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
key[i++] = *p++;
if (i == sizeof(key)) i--; /* limit */
}
if (*p == ':') {
p++;
goto nextaction;
}
key[i] = 0;
if (key[0] == '\0') {
SPRINT(failure, "Expecting parameter name.");
@ -1919,6 +1958,10 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset)
int avail,
any;
int jj;
class Port *port;
class Pfxs *ourfxs, *fxs;
int fxs_count;
int fxs_age;
#endif
struct admin_list *admin;
time_t now;
@ -2237,6 +2280,86 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset)
istrue = 1;
break;
#ifdef WITH_MISDN
case MATCH_POTS_FLASH:
if (e_dialinginfo.flash)
istrue = 1;
break;
case MATCH_POTS_CW:
port = find_port_id(ea_endpoint->ep_portlist->port_id);
if (!port)
break;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS)
break;
ourfxs = (class Pfxs *)port;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (fxs->p_state == PORT_STATE_OUT_ALERTING) {
istrue = 1;
break;
}
}
}
port = port->next;
}
break;
case MATCH_POTS_CALLS:
port = find_port_id(ea_endpoint->ep_portlist->port_id);
if (!port)
break;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS)
break;
ourfxs = (class Pfxs *)port;
integer = 0;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (fxs->p_state == PORT_STATE_CONNECT) {
integer++;
}
}
}
port = port->next;
}
goto match_integer;
case MATCH_POTS_LAST:
port = find_port_id(ea_endpoint->ep_portlist->port_id);
if (!port)
break;
if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS)
break;
ourfxs = (class Pfxs *)port;
/* integer gets the call number that has been the least active call on hold */
fxs_age = -1;
fxs_count = 0;
integer = 0;
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
fxs = (class Pfxs *)port;
if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
if (fxs->p_state == PORT_STATE_CONNECT) {
fxs_count++;
if (fxs->p_m_fxs_age > fxs_age) {
fxs_age = fxs->p_m_fxs_age;
integer = fxs_count;
}
}
}
}
port = port->next;
}
goto match_integer;
#endif
default:
PERROR("Software error: MATCH_* %d not parsed in function '%s'", cond->match, __FUNCTION__);
break;

11
route.h
View File

@ -76,6 +76,10 @@ enum { /* what to check during runtime */
MATCH_IDLE,
MATCH_REMOTE,
MATCH_NOTREMOTE,
MATCH_POTS_FLASH,
MATCH_POTS_CW,
MATCH_POTS_CALLS,
MATCH_POTS_LAST,
};
enum { /* how to parse text file during startup */
@ -149,6 +153,7 @@ enum { /* defines when a statement should be executed */
#define PARAM_EXTEN (1LL<<46)
#define PARAM_ON (1LL<<47)
#define PARAM_KEYPAD (1LL<<48)
#define PARAM_POTS_CALL (1LL<<49)
/* action index
* NOTE: The given index is the actual entry number of action_defs[], so add/remove both lists!!!
@ -185,6 +190,12 @@ enum { /* defines when a statement should be executed */
#define ACTION_PASSWORD_WRITE 29
#define ACTION_NOTHING 30
#define ACTION_EFI 31
#define ACTION_POTS_RETRIEVE 32
#define ACTION_POTS_RELEASE 33
#define ACTION_POTS_REJECT 34
#define ACTION_POTS_ANSWER 35
#define ACTION_POTS_3PTY 36
#define ACTION_POTS_TRANSFER 37
struct route_cond { /* an item */
struct route_cond *next; /* next entry */