Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
F
freeswitch
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
张华
freeswitch
Commits
2cfd09c3
提交
2cfd09c3
authored
1月 07, 2011
作者:
Moises Silva
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
freetdm: initial glare handling code
上级
40aa1d90
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
181 行增加
和
32 行删除
+181
-32
glare.txt
libs/freetdm/docs/glare.txt
+29
-0
ftdm_io.c
libs/freetdm/src/ftdm_io.c
+60
-12
ftdm_state.c
libs/freetdm/src/ftdm_state.c
+53
-0
ftmod_r2.c
libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
+33
-20
ftdm_state.h
libs/freetdm/src/include/private/ftdm_state.h
+6
-0
没有找到文件。
libs/freetdm/docs/glare.txt
0 → 100644
浏览文件 @
2cfd09c3
Glare is a PITA.
Although configuration of ISDN links can be done to minimize glare, to be pedantic we must have a clear policy
on how the FreeTDM API is supposed to behave on glare across signaling modules.
There is a well-known race in the FreeTDM API since the beginning. When a user wants to place a call there is 2 APIs that
must be used:
1. ftdm_channel_open_xx (to hunt the channel by group, span or select a channel individually)
2. ftdm_channel_call_place() to place the actual call.
Since the user has no access to channel locking, between opening a channel and placing a call, an incoming call could be
received. Therefore the user needs to be aware of the following:
1. Between ftdm_channel_open_xx and ftdm_channel_call_place() a SIGEVENT_START can be received, if the user application
is smart enough, upon receive of SIGEVENT_START it can avoid doing anything else with the channel (from an outgoing call perspective)
since that channel is now a channel owned by the incoming call. It can for example hunt another channel using
ftdm_channel_open_xx again and avoid calling ftdm_channel_call_place. However, if the app is not smart enough and still calls
ftdm_channel_call_place even though already received FTDM_SIGEVENT_START on that channel, ftdm_channel_call_place will return
FTDM_BREAK to inform the user the outgoing call could not be placed and that there is already an incoming call on that channel.
2. If SIGEVENT_START was not received before calling ftdm_channel_call_place, it could still come while ftdm_channel_call_place()
is being executed, in such situation ftdm_channel_place_call() will return FTDM_BREAK to inform the user the call could
not be placed due to glare and the incoming call won the channel, he user should back off since the channel is
now owned by the incoming call (it can touch the channel having in mind there is now an incoming call on it)
3. After ftdm_channel_call_place returns, if glare is detected and the signaling stack decides to drop the local call, a regular
SIGEVENT_STOP will be sent with the hangup cause FTDM_CAUSE_REQUESTED_CHAN_UNAVAIL.
libs/freetdm/src/ftdm_io.c
浏览文件 @
2cfd09c3
...
...
@@ -2142,6 +2142,10 @@ static ftdm_status_t _ftdm_channel_call_hangup_nl(ftdm_channel_t *chan, const ch
ftdm_sched_cancel_timer
(
globals
.
timingsched
,
chan
->
hangup_timer
);
}
ftdm_set_flag
(
chan
,
FTDM_CHANNEL_USER_HANGUP
);
/* if a state change requested by the user was pending, a hangup certainly cancels that request */
if
(
ftdm_test_flag
(
chan
,
FTDM_CHANNEL_STATE_CHANGE
))
{
ftdm_channel_cancel_state
(
file
,
func
,
line
,
chan
);
}
status
=
ftdm_channel_set_state
(
file
,
func
,
line
,
chan
,
FTDM_CHANNEL_STATE_HANGUP
,
1
);
}
else
{
/* the signaling stack did not touch the state,
...
...
@@ -2372,19 +2376,57 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char
ftdm_channel_lock
(
ftdmchan
);
if
(
ftdmchan
->
span
->
outgoing_call
)
{
status
=
ftdmchan
->
span
->
outgoing_call
(
ftdmchan
);
}
else
{
status
=
FTDM_NOTIMPL
;
ftdm_log
(
FTDM_LOG_ERROR
,
"outgoing_call method not implemented in this span!
\n
"
);
if
(
!
ftdmchan
->
span
->
outgoing_call
)
{
ftdm_log_chan_msg
(
ftdmchan
,
FTDM_LOG_ERROR
,
"outgoing_call method not implemented in this span!
\n
"
);
status
=
FTDM_ENOSYS
;
goto
done
;
}
if
(
status
==
FTDM_SUCCESS
)
{
ftdm_set_flag
(
ftdmchan
,
FTDM_CHANNEL_CALL_STARTED
);
ftdm_call_set_call_id
(
ftdmchan
,
&
ftdmchan
->
caller_data
);
if
(
!
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_OPEN
))
{
ftdm_log_chan_msg
(
ftdmchan
,
FTDM_LOG_ERROR
,
"Cannot place call in channel that is not open!
\n
"
);
goto
done
;
}
if
(
!
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_OUTBOUND
))
{
if
(
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_CALL_STARTED
))
{
status
=
FTDM_BREAK
;
/* we set the outbound flag when the user open a channel, but if the signaling stack sends an
* incoming call we clear it, which indicates the inbound call was received before we could try
* to place the outbound call */
ftdm_log_chan_msg
(
ftdmchan
,
FTDM_LOG_WARNING
,
"Inbound call won the race, you should hunt in another channel!
\n
"
);
goto
done
;
}
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_ERROR
,
"Cannot place call in non outbound channel in state %s!
\n
"
,
ftdm_channel_state2str
(
ftdmchan
->
state
));
goto
done
;
}
if
(
ftdmchan
->
state
!=
FTDM_CHANNEL_STATE_DOWN
)
{
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_ERROR
,
"Cannot place call in channel in state %s!
\n
"
,
ftdm_channel_state2str
(
ftdmchan
->
state
));
goto
done
;
}
status
=
ftdmchan
->
span
->
outgoing_call
(
ftdmchan
);
if
(
status
==
FTDM_BREAK
)
{
/* the signaling module detected glare on time */
ftdm_log_chan_msg
(
ftdmchan
,
FTDM_LOG_WARNING
,
"Glare detected, you should hunt in another channel!
\n
"
);
goto
done
;
}
if
(
status
!=
FTDM_SUCCESS
)
{
ftdm_log_chan_msg
(
ftdmchan
,
FTDM_LOG_ERROR
,
"Failed to place call!
\n
"
);
goto
done
;
}
/* in case of success, *before* unlocking the channel, we must set the call started flag and the call id
* that is a guarantee that signaling modules expect from us */
ftdm_set_flag
(
ftdmchan
,
FTDM_CHANNEL_CALL_STARTED
);
ftdm_call_set_call_id
(
ftdmchan
,
&
ftdmchan
->
caller_data
);
if
(
!
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_NONBLOCK
))
{
/* be aware this waiting unlocks the channel and locks it back when done */
ftdm_wait_for_flag_cleared
(
ftdmchan
,
FTDM_CHANNEL_STATE_CHANGE
,
100
);
}
done
:
ftdm_channel_unlock
(
ftdmchan
);
#ifdef __WINDOWS__
...
...
@@ -5355,7 +5397,10 @@ static void execute_safety_hangup(void *data)
FT_DECLARE
(
ftdm_status_t
)
ftdm_span_send_signal
(
ftdm_span_t
*
span
,
ftdm_sigmsg_t
*
sigmsg
)
{
if
(
sigmsg
->
channel
)
{
ftdm_mutex_lock
(
sigmsg
->
channel
->
mutex
);
ftdm_mutex_lock
(
sigmsg
->
channel
->
mutex
);
sigmsg
->
chan_id
=
sigmsg
->
channel
->
chan_id
;
sigmsg
->
span_id
=
sigmsg
->
channel
->
span_id
;
sigmsg
->
call_id
=
sigmsg
->
channel
->
caller_data
.
call_id
;
}
/* some core things to do on special events */
...
...
@@ -5373,6 +5418,12 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t
case
FTDM_SIGEVENT_START
:
{
ftdm_assert
(
!
ftdm_test_flag
(
sigmsg
->
channel
,
FTDM_CHANNEL_CALL_STARTED
),
"Started call twice!"
);
if
(
ftdm_test_flag
(
sigmsg
->
channel
,
FTDM_CHANNEL_OUTBOUND
))
{
ftdm_log_chan_msg
(
sigmsg
->
channel
,
FTDM_LOG_WARNING
,
"Inbound call taking over outbound channel
\n
"
);
ftdm_clear_flag
(
sigmsg
->
channel
,
FTDM_CHANNEL_OUTBOUND
);
}
ftdm_set_flag
(
sigmsg
->
channel
,
FTDM_CHANNEL_CALL_STARTED
);
ftdm_call_set_call_id
(
sigmsg
->
channel
,
&
sigmsg
->
channel
->
caller_data
);
ftdm_set_echocancel_call_begin
(
sigmsg
->
channel
);
...
...
@@ -5410,9 +5461,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t
}
if
(
sigmsg
->
channel
)
{
sigmsg
->
call_id
=
sigmsg
->
channel
->
caller_data
.
call_id
;
}
/* if the signaling module uses a queue for signaling notifications, then enqueue it */
if
(
ftdm_test_flag
(
span
,
FTDM_SPAN_USE_SIGNALS_QUEUE
))
{
ftdm_span_queue_signal
(
span
,
sigmsg
);
...
...
libs/freetdm/src/ftdm_state.c
浏览文件 @
2cfd09c3
...
...
@@ -164,6 +164,59 @@ static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t s
return
ok
;
}
FT_DECLARE
(
ftdm_status_t
)
ftdm_channel_cancel_state
(
const
char
*
file
,
const
char
*
func
,
int
line
,
ftdm_channel_t
*
fchan
)
{
ftdm_time_t
diff
;
ftdm_channel_state_t
state
;
ftdm_channel_state_t
last_state
;
uint8_t
hindex
=
0
;
if
(
!
ftdm_test_flag
(
fchan
,
FTDM_CHANNEL_STATE_CHANGE
))
{
ftdm_log_chan
(
fchan
,
FTDM_LOG_WARNING
,
"Cannot cancel state change from %s to %s, it was already processed
\n
"
,
ftdm_channel_state2str
(
fchan
->
last_state
),
ftdm_channel_state2str
(
fchan
->
state
));
return
FTDM_FAIL
;
}
if
(
fchan
->
state_status
!=
FTDM_STATE_STATUS_NEW
)
{
ftdm_log_chan
(
fchan
,
FTDM_LOG_WARNING
,
"Failed to cancel state change from %s to %s, state is not new anymore
\n
"
,
ftdm_channel_state2str
(
fchan
->
last_state
),
ftdm_channel_state2str
(
fchan
->
state
));
return
FTDM_FAIL
;
}
/* compute the last history index */
hindex
=
(
fchan
->
hindex
==
0
)
?
(
ftdm_array_len
(
fchan
->
history
)
-
1
)
:
(
fchan
->
hindex
-
1
);
diff
=
fchan
->
history
[
hindex
].
end_time
-
fchan
->
history
[
hindex
].
time
;
/* go back in time and revert the state to the previous state */
state
=
fchan
->
state
;
last_state
=
fchan
->
last_state
;
fchan
->
state
=
fchan
->
last_state
;
fchan
->
state_status
=
FTDM_STATE_STATUS_COMPLETED
;
fchan
->
last_state
=
fchan
->
history
[
hindex
].
last_state
;
fchan
->
hindex
=
hindex
;
/* clear the state change flag */
ftdm_clear_flag
(
fchan
,
FTDM_CHANNEL_STATE_CHANGE
);
/* ack any pending indications as cancelled */
ftdm_ack_indication
(
fchan
,
fchan
->
indication
,
FTDM_ECANCELED
);
/* wake up anyone sleeping waiting for the state change to complete, it won't ever be completed */
if
(
ftdm_test_flag
(
fchan
,
FTDM_CHANNEL_BLOCKING
))
{
ftdm_clear_flag
(
fchan
,
FTDM_CHANNEL_BLOCKING
);
ftdm_interrupt_signal
(
fchan
->
state_completed_interrupt
);
}
/* NOTE
* we could potentially also take out the channel from the pendingchans queue, but I believe is easier just leave it,
* the only side effect will be a call to ftdm_channel_advance_states() for a channel that has nothing to advance */
ftdm_log_chan_ex
(
fchan
,
file
,
func
,
line
,
FTDM_LOG_LEVEL_DEBUG
,
"Cancelled state change from %s to %s in %llums
\n
"
,
ftdm_channel_state2str
(
last_state
),
ftdm_channel_state2str
(
state
),
diff
);
return
FTDM_SUCCESS
;
}
/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
#define DEFAULT_WAIT_TIME 1000
FT_DECLARE
(
ftdm_status_t
)
ftdm_channel_set_state
(
const
char
*
file
,
const
char
*
func
,
int
line
,
ftdm_channel_t
*
ftdmchan
,
ftdm_channel_state_t
state
,
int
waitrq
)
...
...
libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
浏览文件 @
2cfd09c3
...
...
@@ -71,7 +71,6 @@ typedef struct ftdm_r2_call_t {
int
accepted
:
1
;
int
answer_pending
:
1
;
int
disconnect_rcvd
:
1
;
int
ftdm_call_started
:
1
;
int
protocol_error
:
1
;
ftdm_size_t
dnis_index
;
ftdm_size_t
ani_index
;
...
...
@@ -293,6 +292,9 @@ static ftdm_call_cause_t ftdm_r2_cause_to_ftdm_cause(ftdm_channel_t *fchan, open
case
OR2_CAUSE_FORCED_RELEASE
:
return
FTDM_CAUSE_NORMAL_CLEARING
;
case
OR2_CAUSE_GLARE
:
return
FTDM_CAUSE_REQUESTED_CHAN_UNAVAIL
;
}
ftdm_log_chan
(
fchan
,
FTDM_LOG_WARNING
,
"Mapping openr2 cause %d to unspecified
\n
"
,
cause
);
return
FTDM_CAUSE_NORMAL_UNSPECIFIED
;
...
...
@@ -345,7 +347,6 @@ static void ft_r2_clean_call(ftdm_r2_call_t *call)
call
->
accepted
=
0
;
call
->
answer_pending
=
0
;
call
->
disconnect_rcvd
=
0
;
call
->
ftdm_call_started
=
0
;
call
->
protocol_error
=
0
;
call
->
dnis_index
=
0
;
call
->
ani_index
=
0
;
...
...
@@ -443,13 +444,6 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call)
r2data
=
ftdmchan
->
span
->
signal_data
;
if
(
ftdmchan
->
state
!=
FTDM_CHANNEL_STATE_DOWN
)
{
/* collision, an incoming seized the channel between our take and use timing */
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_CRIT
,
"R2 cannot dial out in channel in state %s, try another channel!.
\n
"
,
ftdm_channel_state2str
(
ftdmchan
->
state
));
return
FTDM_FAIL
;
}
ft_r2_clean_call
(
ftdmchan
->
call_data
);
if
(
ftdmchan
->
caller_data
.
cpc
==
FTDM_CPC_INVALID
||
ftdmchan
->
caller_data
.
cpc
==
FTDM_CPC_UNKNOWN
)
{
...
...
@@ -475,7 +469,6 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call)
return
FTDM_FAIL
;
}
R2CALL
(
ftdmchan
)
->
ftdm_call_started
=
1
;
ftdm_set_state
(
ftdmchan
,
FTDM_CHANNEL_STATE_DIALING
);
ftdm_channel_set_feature
(
ftdmchan
,
FTDM_CHANNEL_FEATURE_IO_STATS
);
...
...
@@ -625,7 +618,23 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
}
if
(
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_INUSE
))
{
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_CRIT
,
"Cannot start call when channel is in use (state = %s)
\n
"
,
ftdm_channel_state2str
(
ftdmchan
->
state
));
if
(
ftdmchan
->
state
==
FTDM_CHANNEL_STATE_DOWN
&&
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_OUTBOUND
))
{
if
(
!
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_CALL_STARTED
))
{
/* The user requested this channel but has not yet placed a call on it, we can take it over
* and the user will receive FTDM_BREAK if attempts to place a call in the channel
* informing him that the channel was taken over by an incoming call, although he may know
* that already anyways since we sent a SIGEVENT_START on the channel */
ftdm_clear_flag
(
ftdmchan
,
FTDM_CHANNEL_OUTBOUND
);
}
else
{
/* The user requested the channel and placed the call, apparently openr2 could not detect the
* glare on time, but this should not happen with our locking/thread model since we always
* check for state changes before processing network events (like CAS change) therefore
* openr2 should at this time be aware of the call that we placed on this channel and should
* have initiated the release of the call per ITU R2 spec */
}
}
else
{
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_CRIT
,
"Cannot start call when channel is in use (state = %s)
\n
"
,
ftdm_channel_state2str
(
ftdmchan
->
state
));
}
return
;
}
...
...
@@ -1007,8 +1016,19 @@ static void ftdm_r2_on_call_log_created(openr2_chan_t *r2chan, const char *logna
snprintf
(
r2call
->
logname
,
sizeof
(
r2call
->
logname
),
"%s"
,
logname
);
}
static
void
ftdm_r2_on_call_proceed
(
openr2_chan_t
*
r2chan
)
{
ftdm_sigmsg_t
sigev
;
ftdm_channel_t
*
fchan
=
openr2_chan_get_client_data
(
r2chan
);
memset
(
&
sigev
,
0
,
sizeof
(
sigev
));
sigev
.
event_id
=
FTDM_SIGEVENT_PROCEED
;
sigev
.
channel
=
fchan
;
ftdm_span_send_signal
(
fchan
->
span
,
&
sigev
);
}
static
openr2_event_interface_t
ftdm_r2_event_iface
=
{
/* .on_call_init */
ftdm_r2_on_call_init
,
/* .on_call_proceed */
ftdm_r2_on_call_proceed
,
/* .on_call_offered */
ftdm_r2_on_call_offered
,
/* .on_call_accepted */
ftdm_r2_on_call_accepted
,
/* .on_call_answered */
ftdm_r2_on_call_answered
,
...
...
@@ -1691,8 +1711,7 @@ static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
uint32_t
interval
=
0
;
ftdm_channel_command
(
ftdmchan
,
FTDM_COMMAND_GET_INTERVAL
,
&
interval
);
ftdm_assert
(
interval
!=
0
,
"Invalid interval!"
);
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_DEBUG
,
"Starting processing of outgoing call in channel with interval %d
\n
"
,
interval
);
ftdm_log_chan
(
ftdmchan
,
FTDM_LOG_DEBUG
,
"Starting outgoing call with interval %d
\n
"
,
interval
);
openr2_chan_enable_read
(
r2chan
);
}
break
;
...
...
@@ -1702,10 +1721,7 @@ static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
/* notify the user about the new call */
sigev
.
event_id
=
FTDM_SIGEVENT_START
;
ftdm_span_send_signal
(
ftdmchan
->
span
,
&
sigev
);
r2call
->
ftdm_call_started
=
1
;
break
;
/* the call is making progress */
...
...
@@ -1719,9 +1735,6 @@ static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
}
}
else
{
ftdm_log_chan_msg
(
ftdmchan
,
FTDM_LOG_DEBUG
,
"Notifying progress
\n
"
);
sigev
.
event_id
=
FTDM_SIGEVENT_PROCEED
;
ftdm_span_send_signal
(
ftdmchan
->
span
,
&
sigev
);
sigev
.
event_id
=
FTDM_SIGEVENT_PROGRESS_MEDIA
;
ftdm_span_send_signal
(
ftdmchan
->
span
,
&
sigev
);
}
...
...
@@ -1772,7 +1785,7 @@ static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
case
FTDM_CHANNEL_STATE_TERMINATING
:
{
/* if the call has not been started yet we must go to HANGUP right here */
if
(
!
r2call
->
ftdm_call_started
)
{
if
(
!
ftdm_test_flag
(
ftdmchan
,
FTDM_CHANNEL_CALL_STARTED
)
)
{
ftdm_set_state
(
ftdmchan
,
FTDM_CHANNEL_STATE_HANGUP
);
}
else
{
openr2_call_disconnect_cause_t
disconnect_cause
=
ftdm_r2_ftdm_cause_to_openr2_cause
(
ftdmchan
);
...
...
libs/freetdm/src/include/private/ftdm_state.h
浏览文件 @
2cfd09c3
...
...
@@ -177,6 +177,12 @@ struct ftdm_state_map {
};
typedef
struct
ftdm_state_map
ftdm_state_map_t
;
/*!\brief Cancel the state processing for a channel (the channel must be locked when calling this function)
* \note Only the core should use this function
*/
FT_DECLARE
(
ftdm_status_t
)
ftdm_channel_cancel_state
(
const
char
*
file
,
const
char
*
func
,
int
line
,
ftdm_channel_t
*
ftdmchan
);
/*!\brief Set the state for a channel (the channel must be locked when calling this function)
* \note Signaling modules should use ftdm_set_state macro instead
* \note If this function is called with the wait parameter set to a non-zero value, the recursivity
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论