parent
fa1f601f49
commit
acaf278f7f
|
@ -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
|
||||
|
|
392
action.cpp
392
action.cpp
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
365
apppbx.cpp
365
apppbx.cpp
|
@ -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
|
||||
|
@ -795,6 +802,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);
|
||||
earlyb = mISDNport->earlyb;
|
||||
|
@ -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)
|
||||
|
@ -1078,6 +1095,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);
|
||||
earlyb = mISDNport->earlyb;
|
||||
|
@ -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)
|
||||
|
|
13
apppbx.h
13
apppbx.h
|
@ -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);
|
||||
|
|
|
@ -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, ¶m->setup.callerinfo, sizeof(p_callerinfo));
|
||||
memcpy(&p_redirinfo, ¶m->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, ¶m->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 */
|
|
@ -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);
|
||||
|
66
interface.c
66
interface.c
|
@ -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"},
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
133
mISDN.cpp
133
mISDN.cpp
|
@ -1057,10 +1057,23 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
|
|||
end_trace();
|
||||
if (!p_m_dtmf)
|
||||
return;
|
||||
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) {
|
||||
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,6 +2014,8 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
|
|||
}
|
||||
|
||||
/* allocate ressources of port */
|
||||
if (!pots) {
|
||||
/* ISDN */
|
||||
protocol = (nt)?L3_PROTOCOL_DSS1_NET:L3_PROTOCOL_DSS1_USER;
|
||||
prop = (1 << MISDN_FLG_L2_CLEAN);
|
||||
if (ptp) // ptp forced
|
||||
|
@ -1992,11 +2045,54 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport)
|
|||
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)");
|
||||
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);
|
||||
|
||||
|
|
2
mISDN.h
2
mISDN.h
|
@ -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
1
main.h
|
@ -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"
|
||||
|
|
|
@ -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
10
port.h
|
@ -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
127
route.c
|
@ -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
11
route.h
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue