Skinny: rewrite of the skinny state machine

- for incoming calls, go CS_ROUTING only when number is dialed.
  CS_HIBERNATE before
- start media when both side have answered

Also:
- send tone for UNALLOCATED_NUMBER and USER_BUSY
- if channel variables are not sufficent to set call info, ask the
  partner channel
This commit is contained in:
Mathieu Parent 2010-05-20 15:10:33 +02:00
parent f8f91362f0
commit 8cc89ab042
6 changed files with 156 additions and 101 deletions

View File

@ -561,24 +561,104 @@ void tech_init(private_t *tech_pvt, skinny_profile_t *profile, switch_core_sessi
switch_status_t channel_on_init(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
switch_set_flag_locked(tech_pvt, TFLAG_IO);
/* Move channel's state machine to ROUTING. This means the call is trying
to get from the initial start where the call because, to the point
where a destination has been identified. If the channel is simply
left in the initial state, nothing will happen. */
switch_channel_set_state(channel, CS_ROUTING);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL INIT\n", switch_channel_get_name(channel));
return SWITCH_STATUS_SUCCESS;
}
struct channel_on_routing_helper {
private_t *tech_pvt;
listener_t *listener;
uint32_t line_instance;
};
int channel_on_routing_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct channel_on_routing_helper *helper = pArg;
listener_t *listener = NULL;
char *device_name = argv[0];
uint32_t device_instance = atoi(argv[1]);
/* uint32_t position = atoi(argv[2]); */
uint32_t line_instance = atoi(argv[3]);
/* char *label = argv[4]; */
/* char *value = argv[5]; */
/* char *caller_name = argv[6]; */
/* uint32_t ring_on_idle = atoi(argv[7]); */
/* uint32_t ring_on_active = atoi(argv[8]); */
/* uint32_t busy_trigger = atoi(argv[9]); */
/* char *forward_all = argv[10]; */
/* char *forward_busy = argv[11]; */
/* char *forward_noanswer = argv[12]; */
/* uint32_t noanswer_duration = atoi(argv[13]); */
/* char *channel_uuid = argv[14]; */
/* uint32_t call_id = atoi(argv[15]); */
/* uint32_t call_state = atoi(argv[16]); */
skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener);
if(listener) {
if(!strcmp(device_name, helper->listener->device_name)
&& (device_instance == helper->listener->device_instance)
&& (line_instance == helper->line_instance)) {/* the calling line */
helper->tech_pvt->caller_profile->dialplan = switch_core_strdup(helper->tech_pvt->caller_profile->pool, listener->profile->dialplan);
helper->tech_pvt->caller_profile->context = switch_core_strdup(helper->tech_pvt->caller_profile->pool, listener->profile->context);
send_dialed_number(listener, helper->tech_pvt->caller_profile->destination_number, line_instance, helper->tech_pvt->call_id);
skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_PROCEED);
skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
} else {
send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON);
skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY);
send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0xffff);
send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE,
line_instance, helper->tech_pvt->call_id);
skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
}
}
return 0;
}
switch_status_t channel_on_routing(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
skinny_action_t action;
private_t *tech_pvt = switch_core_session_get_private(session);
char *data = NULL;
listener_t *listener = NULL;
struct channel_on_routing_helper helper = {0};
if(switch_test_flag(tech_pvt, TFLAG_FORCE_ROUTE)) {
action = SKINNY_ACTION_ROUTE;
switch_clear_flag_locked(tech_pvt, TFLAG_FORCE_ROUTE);
} else {
action = skinny_session_dest_match_pattern(session, &data);
}
switch(action) {
case SKINNY_ACTION_ROUTE:
skinny_profile_find_listener_by_device_name_and_instance(tech_pvt->profile,
switch_channel_get_variable(channel, "skinny_device_name"),
atoi(switch_channel_get_variable(channel, "skinny_device_instance")), &listener);
if (listener) {
helper.tech_pvt = tech_pvt;
helper.listener = listener;
helper.line_instance = atoi(switch_channel_get_variable(channel, "skinny_line_instance"));
skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), channel_on_routing_callback, &helper);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Could not find listener %s:%s for Channel %s\n",
switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"),
switch_channel_get_name(channel));
}
break;
case SKINNY_ACTION_WAIT:
/* for now, wait forever */
switch_channel_set_state(channel, CS_HIBERNATE);
break;
case SKINNY_ACTION_DROP:
default:
switch_channel_hangup(channel, SWITCH_CAUSE_UNALLOCATED_NUMBER);
}
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel));
@ -591,7 +671,6 @@ switch_status_t channel_on_execute(switch_core_session_t *session)
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
return SWITCH_STATUS_SUCCESS;
}
@ -651,11 +730,14 @@ int channel_on_hangup_callback(void *pArg, int argc, char **argv, char **columnN
send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_OFF);
switch (helper->cause) {
case SWITCH_CAUSE_UNALLOCATED_NUMBER:
send_start_tone(listener, SKINNY_TONE_REORDER, 0, line_instance, call_id);
skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
send_display_prompt_status(listener, 0, SKINNY_DISP_UNKNOWN_NUMBER, line_instance, call_id);
break;
case SWITCH_CAUSE_USER_BUSY:
send_start_tone(listener, SKINNY_TONE_BUSYTONE, 0, line_instance, call_id);
send_display_prompt_status(listener, 0, SKINNY_DISP_BUSY, line_instance, call_id);
break;
break;
case SWITCH_CAUSE_NORMAL_CLEARING:
send_clear_prompt_status(listener, line_instance, call_id);
break;
@ -842,6 +924,20 @@ switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame
switch_status_t channel_answer_channel(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
private_t *tech_pvt = switch_core_session_get_private(session);
listener_t *listener = NULL;
skinny_profile_find_listener_by_device_name_and_instance(tech_pvt->profile,
switch_channel_get_variable(channel, "skinny_device_name"),
atoi(switch_channel_get_variable(channel, "skinny_device_instance")), &listener);
if (listener) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Bli!\n");
skinny_session_start_media(session, listener, atoi(switch_channel_get_variable(channel, "skinny_line_instance")));
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Unable to find listener to answer %s:%s\n",
switch_channel_get_variable(channel, "skinny_device_name"), switch_channel_get_variable(channel, "skinny_device_instance"));
}
return SWITCH_STATUS_SUCCESS;
}
@ -925,7 +1021,6 @@ switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, swi
tech_pvt->caller_profile = caller_profile;
switch_channel_set_flag(channel, CF_OUTBOUND);
switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
if ((sql = switch_mprintf(
"INSERT INTO skinny_active_lines "

View File

@ -146,8 +146,6 @@ typedef switch_status_t (*skinny_listener_callback_func_t) (listener_t *listener
/*****************************************************************************/
typedef enum {
TFLAG_IO = (1 << 0),
TFLAG_INBOUND = (1 << 1),
TFLAG_OUTBOUND = (1 << 2),
TFLAG_DTMF = (1 << 3),
TFLAG_VOICE = (1 << 4),
TFLAG_HANGUP = (1 << 5),
@ -155,7 +153,8 @@ typedef enum {
TFLAG_CODEC = (1 << 7),
TFLAG_READING = (1 << 9),
TFLAG_WRITING = (1 << 10)
TFLAG_WRITING = (1 << 10),
TFLAG_FORCE_ROUTE = (1 << 11)
} TFLAGS;
typedef enum {

View File

@ -242,6 +242,15 @@ switch_status_t skinny_device_event(listener_t *listener, switch_event_t **ev, s
return SWITCH_STATUS_SUCCESS;
}
switch_status_t skinny_set_channel_variables(switch_channel_t *channel, listener_t *listener, uint32_t line_instance)
{
switch_channel_set_variable(channel, "skinny_profile_name", listener->profile->name);
switch_channel_set_variable(channel, "skinny_device_name", listener->device_name);
switch_channel_set_variable_printf(channel, "skinny_device_instance", "%d", listener->device_instance);
switch_channel_set_variable_printf(channel, "skinny_line_instance", "%d", line_instance);
return SWITCH_STATUS_SUCCESS;
}
/*****************************************************************************/
/*****************************************************************************/
switch_status_t skinny_session_walk_lines(skinny_profile_t *profile, char *channel_uuid, switch_core_db_callback_func_t callback, void *data)
@ -916,8 +925,9 @@ switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file
ptr = (char *) reply;
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_DEBUG,
"Sending %s (type=%x,length=%d).\n",
skinny_message_type2str(reply->type), reply->type, reply->length);
"Sending %s (type=%x,length=%d) to %s:%d.\n",
skinny_message_type2str(reply->type), reply->type, reply->length,
listener->device_name, listener->device_instance);
switch_socket_send(listener->sock, ptr, &len);
return SWITCH_STATUS_SUCCESS;

View File

@ -637,6 +637,7 @@ char* skinny_codec2string(enum skinny_codecs skinnycodec);
switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req);
switch_status_t skinny_device_event(listener_t *listener, switch_event_t **ev, switch_event_types_t event_id, const char *subclass_name);
switch_status_t skinny_set_channel_variables(switch_channel_t *channel, listener_t *listener, uint32_t line_instance);
switch_status_t skinny_session_walk_lines(skinny_profile_t *profile, char *channel_uuid, switch_core_db_callback_func_t callback, void *data);

View File

@ -117,6 +117,7 @@ switch_status_t skinny_create_incoming_session(listener_t *listener, uint32_t *l
snprintf(name, sizeof(name), "SKINNY/%s/%s:%d/%d", listener->profile->name,
listener->device_name, listener->device_instance, *line_instance_p);
switch_channel_set_name(channel, name);
skinny_set_channel_variables(channel, listener, *line_instance_p);
if (switch_core_session_thread_launch(nsession) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT,
@ -163,6 +164,12 @@ switch_status_t skinny_create_incoming_session(listener_t *listener, uint32_t *l
send_display_prompt_status(listener, 0, "\200\000",
*line_instance_p, tech_pvt->call_id);
send_activate_call_plane(listener, *line_instance_p);
if (switch_channel_get_state(channel) == CS_NEW) {
switch_channel_set_state(channel, CS_HIBERNATE);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(nsession), SWITCH_LOG_CRIT,
"Wow! this channel should be in CS_NEW state, but it is not!\n");
}
goto done;
error:
@ -246,60 +253,10 @@ found:
}
struct skinny_session_process_dest_helper {
private_t *tech_pvt;
listener_t *listener;
uint32_t line_instance;
};
int skinny_session_process_dest_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct skinny_session_process_dest_helper *helper = pArg;
listener_t *listener = NULL;
char *device_name = argv[0];
uint32_t device_instance = atoi(argv[1]);
/* uint32_t position = atoi(argv[2]); */
uint32_t line_instance = atoi(argv[3]);
/* char *label = argv[4]; */
/* char *value = argv[5]; */
/* char *caller_name = argv[6]; */
/* uint32_t ring_on_idle = atoi(argv[7]); */
/* uint32_t ring_on_active = atoi(argv[8]); */
/* uint32_t busy_trigger = atoi(argv[9]); */
/* char *forward_all = argv[10]; */
/* char *forward_busy = argv[11]; */
/* char *forward_noanswer = argv[12]; */
/* uint32_t noanswer_duration = atoi(argv[13]); */
/* char *channel_uuid = argv[14]; */
/* uint32_t call_id = atoi(argv[15]); */
/* uint32_t call_state = atoi(argv[16]); */
skinny_profile_find_listener_by_device_name_and_instance(helper->tech_pvt->profile, device_name, device_instance, &listener);
if(listener) {
if(!strcmp(device_name, helper->listener->device_name)
&& (device_instance == helper->listener->device_instance)
&& (line_instance == helper->line_instance)) {/* the calling line */
/* nothing */
} else {
send_set_lamp(listener, SKINNY_BUTTON_LINE, line_instance, SKINNY_LAMP_ON);
skinny_line_set_state(listener, line_instance, helper->tech_pvt->call_id, SKINNY_IN_USE_REMOTELY);
send_select_soft_keys(listener, line_instance, helper->tech_pvt->call_id, 10, 0xffff);
send_display_prompt_status(listener, 0, SKINNY_DISP_IN_USE_REMOTE,
line_instance, helper->tech_pvt->call_id);
skinny_session_send_call_info(helper->tech_pvt->session, listener, line_instance);
}
}
return 0;
}
switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace)
{
skinny_action_t action;
switch_channel_t *channel = NULL;
private_t *tech_pvt = NULL;
char *data = NULL;
struct skinny_session_process_dest_helper helper = {0};
switch_assert(session);
switch_assert(listener);
@ -333,34 +290,10 @@ switch_status_t skinny_session_process_dest(switch_core_session_t *session, list
} else {
tech_pvt->caller_profile->destination_number = switch_core_strdup(tech_pvt->caller_profile->pool,
dest);
switch_set_flag_locked(tech_pvt, TFLAG_FORCE_ROUTE);
}
if(dest) {
action = SKINNY_ACTION_ROUTE;
} else {
action = skinny_session_dest_match_pattern(session, &data);
}
switch(action) {
case SKINNY_ACTION_ROUTE:
tech_pvt->caller_profile->dialplan = switch_core_strdup(tech_pvt->caller_profile->pool, listener->profile->dialplan);
tech_pvt->caller_profile->context = switch_core_strdup(tech_pvt->caller_profile->pool, listener->profile->context);
send_dialed_number(listener, tech_pvt->caller_profile->destination_number, line_instance, tech_pvt->call_id);
skinny_line_set_state(listener, line_instance, tech_pvt->call_id, SKINNY_PROCEED);
skinny_session_send_call_info(session, listener, line_instance);
skinny_session_start_media(session, listener, line_instance);
helper.tech_pvt = tech_pvt;
helper.listener = listener;
helper.line_instance = line_instance;
skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_process_dest_callback, &helper);
break;
case SKINNY_ACTION_WAIT:
/* for now, wait forever */
break;
case SKINNY_ACTION_DROP:
default:
switch_channel_hangup(channel, SWITCH_CAUSE_UNALLOCATED_NUMBER);
}
switch_channel_set_state(channel, CS_ROUTING);
return SWITCH_STATUS_SUCCESS;
}
@ -383,20 +316,28 @@ switch_status_t skinny_session_send_call_info(switch_core_session_t *session, li
/* Calling party */
if (zstr((caller_party_name = switch_channel_get_variable(channel, "effective_caller_id_name"))) &&
zstr((caller_party_name = switch_channel_get_variable(channel, "caller_id_name")))) {
zstr((caller_party_name = switch_channel_get_variable(channel, "caller_id_name"))) &&
zstr((caller_party_name = switch_channel_get_variable_partner(channel, "effective_caller_id_name"))) &&
zstr((caller_party_name = switch_channel_get_variable_partner(channel, "caller_id_name")))) {
caller_party_name = SWITCH_DEFAULT_CLID_NAME;
}
if (zstr((caller_party_number = switch_channel_get_variable(channel, "effective_caller_id_number"))) &&
zstr((caller_party_number = switch_channel_get_variable(channel, "caller_id_number")))) {
zstr((caller_party_number = switch_channel_get_variable(channel, "caller_id_number"))) &&
zstr((caller_party_number = switch_channel_get_variable_partner(channel, "effective_caller_id_number"))) &&
zstr((caller_party_number = switch_channel_get_variable_partner(channel, "caller_id_number")))) {
caller_party_number = "0000000000";
}
/* Called party */
if (zstr((called_party_name = switch_channel_get_variable(channel, "effective_callee_id_name"))) &&
zstr((called_party_name = switch_channel_get_variable(channel, "callee_id_name")))) {
zstr((called_party_name = switch_channel_get_variable(channel, "callee_id_name"))) &&
zstr((called_party_name = switch_channel_get_variable_partner(channel, "effective_callee_id_name"))) &&
zstr((called_party_name = switch_channel_get_variable_partner(channel, "callee_id_name")))) {
called_party_name = SWITCH_DEFAULT_CLID_NAME;
}
if (zstr((called_party_number = switch_channel_get_variable(channel, "effective_callee_id_number"))) &&
zstr((called_party_number = switch_channel_get_variable(channel, "callee_id_number"))) &&
zstr((called_party_number = switch_channel_get_variable_partner(channel, "effective_callee_id_number"))) &&
zstr((called_party_number = switch_channel_get_variable_partner(channel, "callee_id_number"))) &&
zstr((called_party_number = switch_channel_get_variable(channel, "destination_number")))) {
called_party_number = "0000000000";
}
@ -615,6 +556,9 @@ switch_status_t skinny_session_answer(switch_core_session_t *session, listener_t
skinny_session_walk_lines(tech_pvt->profile, switch_core_session_get_uuid(session), skinny_session_answer_callback, &helper);
if (switch_channel_get_state(channel) == CS_INIT) {
switch_channel_set_state(channel, CS_ROUTING);
}
skinny_session_start_media(session, listener, line_instance);
return SWITCH_STATUS_SUCCESS;
@ -763,6 +707,8 @@ switch_status_t skinny_session_stop_media(switch_core_session_t *session, listen
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
switch_clear_flag_locked(tech_pvt, TFLAG_IO);
send_close_receive_channel(listener,
tech_pvt->call_id, /* uint32_t conference_id, */
tech_pvt->party_id, /* uint32_t pass_thru_party_id, */
@ -1565,10 +1511,12 @@ switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *liste
0 /* uint32_t g723_bitrate */
);
if (switch_channel_get_state(channel) == CS_NEW) {
switch_channel_set_state(channel, CS_INIT);
}
switch_set_flag_locked(tech_pvt, TFLAG_IO);
switch_channel_mark_answered(channel);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Unable to find session for device %s:%d (call id=%d).\n",
listener->device_name, listener->device_instance, request->data.open_receive_channel_ack.pass_thru_party_id);
}
end:
if(session) {
@ -1819,7 +1767,8 @@ switch_status_t skinny_handle_feature_stat_request(listener_t *listener, skinny_
switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *request)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length);
"Received %s (type=%x,length=%d) from %s:%d.\n", skinny_message_type2str(request->type), request->type, request->length,
listener->device_name, listener->device_instance);
if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"Device should send a register message first.\n");

View File

@ -36,6 +36,7 @@
/* SESSION FUNCTIONS */
switch_status_t skinny_create_ingoing_session(listener_t *listener, uint32_t *line_instance, switch_core_session_t **session);
skinny_action_t skinny_session_dest_match_pattern(switch_core_session_t *session, char **data);
switch_status_t skinny_session_process_dest(switch_core_session_t *session, listener_t *listener, uint32_t line_instance, char *dest, char append_dest, uint32_t backspace);
switch_status_t skinny_session_send_call_info(switch_core_session_t *session, listener_t *listener, uint32_t line_instance);
switch_call_cause_t skinny_ring_lines(private_t *tech_pvt);