From 3d546f101d92f95e76e7be2de9c31c4254973e75 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 16 Dec 2009 05:05:47 +0100 Subject: Import chan_dect Re-import chan_dect due to a switch to the trunk branch. Signed-off-by: Patrick McHardy --- channels/Makefile | 2 + channels/chan_dect.c | 992 +++++++++++++++++++++++++++++++++++++++++++++++ configs/dect.conf.sample | 50 +++ configure.ac | 3 + 4 files changed, 1047 insertions(+) create mode 100644 channels/chan_dect.c create mode 100644 configs/dect.conf.sample diff --git a/channels/Makefile b/channels/Makefile index f9b5b3ad7..245344e9c 100644 --- a/channels/Makefile +++ b/channels/Makefile @@ -90,6 +90,8 @@ chan_h323.so: chan_h323.o h323/libchanh323.a endif endif +chan_dect.so: LIBS+=-ldect + chan_misdn.o: _ASTCFLAGS+=-Imisdn misdn_config.o: _ASTCFLAGS+=-Imisdn diff --git a/channels/chan_dect.c b/channels/chan_dect.c new file mode 100644 index 000000000..2d361c87b --- /dev/null +++ b/channels/chan_dect.c @@ -0,0 +1,992 @@ +/* + * Asterisk DECT Channel driver + * + * Copyright (c) 2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "asterisk.h" +#include "asterisk/lock.h" +#include "asterisk/channel.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/pbx.h" +#include "asterisk/sched.h" +#include "asterisk/io.h" +#include "asterisk/cli.h" +#include "asterisk/causes.h" +#include "asterisk/abstract_jb.h" + +#include +#include + +#define CONFIG_FILE "dect.conf" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision") + +static const struct ast_channel_tech dect_tech; +static struct dect_handle *dh; +static struct sched_context *sched; +static struct io_context *io; + +static const struct ast_jb_conf dect_default_jbconf = { + .max_size = -1, + .resync_threshold = -1, + .impl = "", +}; + +struct { + struct ast_jb_conf jbconf; + char default_context[AST_MAX_CONTEXT]; +} dect_cfg; + +static AST_LIST_HEAD_STATIC(dect_pt_list, dect_pt); + +struct dect_pt { + AST_LIST_ENTRY(dect_pt) list; + char name[64]; + struct dect_ipui ipui; + + AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(regexten); + AST_STRING_FIELD(context); + AST_STRING_FIELD(language); + AST_STRING_FIELD(cid_num); + AST_STRING_FIELD(cid_name); + AST_STRING_FIELD(ring_pattern); + ); + + unsigned int display_lines; + unsigned int display_columns; +}; + +struct dect_pvt { + struct dect_pt *pt; + struct dect_call *call; + struct ast_channel *chan; + + AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(cid_num); + AST_STRING_FIELD(cid_name); + AST_STRING_FIELD(display); + ); + int timer_id; +}; + +static int dect_release_reason_to_ast(enum dect_release_reasons reason) +{ + switch (reason) { + case DECT_RELEASE_NORMAL: + return AST_CAUSE_NORMAL_CLEARING; + case DECT_RELEASE_UNEXPECTED_MESSAGE: + case DECT_RELEASE_UNKNOWN_TRANSACTION_IDENTIFIER: + return AST_CAUSE_INVALID_MSG_UNSPECIFIED; + case DECT_RELEASE_MANDATORY_IE_MISSING: + return AST_CAUSE_MANDATORY_IE_MISSING; + case DECT_RELEASE_INVALID_IE_CONTENTS: + return AST_CAUSE_INVALID_IE_CONTENTS; + case DECT_RELEASE_INCOMPATIBLE_SERVICE: + case DECT_RELEASE_SERVICE_NOT_IMPLEMENTED: + case DECT_RELEASE_NEGOTIATION_NOT_SUPPORTED: + case DECT_RELEASE_INVALID_IDENTITY: + case DECT_RELEASE_AUTHENTICATION_FAILED: + case DECT_RELEASE_UNKNOWN_IDENTITY: + case DECT_RELEASE_NEGOTIATION_FAILED: + case DECT_RELEASE_TIMER_EXPIRY: + case DECT_RELEASE_PARTIAL_RELEASE: + case DECT_RELEASE_UNKNOWN: + return AST_CAUSE_NORMAL; + /* user values */ + case DECT_RELEASE_USER_DETACHED: + case DECT_RELEASE_USER_NOT_IN_RANGE: + return AST_CAUSE_SUBSCRIBER_ABSENT; + case DECT_RELEASE_USER_UNKNOWN: + return AST_CAUSE_NO_ROUTE_DESTINATION; + case DECT_RELEASE_USER_ALREADY_ACTIVE: + case DECT_RELEASE_USER_BUSY: + return AST_CAUSE_USER_BUSY; + case DECT_RELEASE_USER_REJECTION: + return AST_CAUSE_CALL_REJECTED; + case DECT_RELEASE_USER_CALL_MODIFY: + /* external handover values */ + case DECT_RELEASE_EXTERNAL_HANDOVER_NOT_SUPPORTED: + case DECT_RELEASE_NETWORK_PARAMETERS_MISSING: + case DECT_RELEASE_EXTERNAL_HANDOVER_RELEASE: + return AST_CAUSE_NORMAL; + /* temporary overload values */ + case DECT_RELEASE_OVERLOAD: + case DECT_RELEASE_INSUFFICIENT_RESOURCES: + case DECT_RELEASE_INSUFFICIENT_BEARERS_AVAILABLE: + return AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; + case DECT_RELEASE_IWU_CONGESTION: + return AST_CAUSE_SWITCH_CONGESTION; + default: + return AST_CAUSE_NORMAL; + } +} + +static struct dect_pt *dect_pt_get_by_name(const char *name) +{ + struct dect_pt *pt; + + AST_LIST_TRAVERSE(&dect_pt_list, pt, list) { + if (!strcasecmp(pt->name, name)) + return pt; + } + return NULL; +} + +static struct dect_pt *dect_pt_get_by_ipui(const struct dect_ipui *ipui) +{ + struct dect_pt *pt; + + AST_LIST_TRAVERSE(&dect_pt_list, pt, list) { + if (!dect_ipui_cmp(&pt->ipui, ipui)) + return pt; + } + return NULL; +} + +static struct dect_pt *dect_init_portable(const char *name, + const struct ast_variable *v) +{ + struct dect_pt *pt; + + pt = ast_malloc(sizeof(*pt)); + if (pt == NULL) + return NULL; + memset(pt, 0, sizeof(*pt)); + ast_copy_string(pt->name, name, sizeof(pt->name)); + + ast_string_field_init(pt, 512); + ast_string_field_set(pt, context, dect_cfg.default_context); + ast_string_field_set(pt, regexten, ""); + ast_string_field_set(pt, language, ""); + ast_string_field_set(pt, cid_num, ""); + ast_string_field_set(pt, cid_name, ""); + ast_string_field_set(pt, ring_pattern, "0"); + pt->ipui.put = DECT_IPUI_N; + + for (; v != NULL; v = v->next) { + if (!strcasecmp(v->name, "emc")) { + pt->ipui.pun.n.ipei.emc = strtoul(v->value, NULL, 16); + } else if (!strcasecmp(v->name, "psn")) { + pt->ipui.pun.n.ipei.psn = strtoul(v->value, NULL, 16); + } else if (!strcasecmp(v->name, "context")) { + ast_string_field_set(pt, context, v->value); + } else if (!strcasecmp(v->name, "regexten")) { + ast_string_field_set(pt, regexten, v->value); + } else if (!strcasecmp(v->name, "language")) { + ast_string_field_set(pt, language, v->value); + } else if (!strcasecmp(v->name, "cid_number")) { + ast_string_field_set(pt, cid_num, v->value); + } else if (!strcasecmp(v->name, "cid_name")) { + ast_string_field_set(pt, cid_name, v->value); + } else if (!strcasecmp(v->name, "ring_pattern")) { + ast_string_field_set(pt, ring_pattern, v->value); + } + } + + AST_LIST_INSERT_TAIL(&dect_pt_list, pt, list); + return pt; +} + +static void dect_register_extension(const struct dect_pt *pt, bool onoff) +{ + struct pbx_find_info q = { .stacklen = 0 }; + char *ext, *extenp, *context; + char exten[256]; + + if (ast_strlen_zero(dect_cfg.default_context)) + return; + + ast_copy_string(exten, S_OR(pt->regexten, pt->name), sizeof(exten)); + extenp = exten; + while ((ext = strsep(&extenp, "&")) != NULL) { + context = strchr(ext, '@'); + if (context != NULL) { + *context++ = '\0'; + if (!ast_context_find(context)) { + ast_log(LOG_WARNING, "Context %s for PT %s does not exist", + context, pt->name); + continue; + } + } else + context = dect_cfg.default_context; + + if (onoff) { + if (!ast_exists_extension(NULL, context, ext, 1, NULL)) { + ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop", + ast_strdup(pt->name), ast_free, "DECT"); + } + } else if (pbx_find_extension(NULL, NULL, &q, context, ext, 1, + NULL, "", E_MATCH)) { + ast_context_remove_extension(context, ext, 1, NULL); + } + } +} + +static int dect_init_call(struct dect_pvt *pvt, enum ast_channel_state state, + const char *exten) +{ + struct dect_pt *pt = pvt->pt; + struct ast_channel *chan; + + ast_string_field_init(pvt, 512); + + chan = ast_channel_alloc(1, state, pt->cid_num, pt->cid_name, "", + exten, pt->context, 0, 0, + "DECT/%s-%08lx", pt->name, (long)pvt); + if (chan == NULL) + return -1; + + chan->tech = &dect_tech; + chan->nativeformats = AST_FORMAT_G726_AAL2; + chan->readformat = AST_FORMAT_G726_AAL2; + chan->rawreadformat = AST_FORMAT_G726_AAL2; + chan->writeformat = AST_FORMAT_G726_AAL2; + chan->rawwriteformat = AST_FORMAT_G726_AAL2; + chan->tech_pvt = pvt; + + if (exten != NULL) + ast_copy_string(chan->exten, exten, sizeof(chan->exten)); + if (!ast_strlen_zero(pt->language)) + ast_string_field_set(chan, language, pt->language); + + ast_jb_configure(chan, &dect_cfg.jbconf); + + ast_module_ref(ast_module_info->self); + pvt->chan = chan; + + if (state != AST_STATE_RESERVED && state != AST_STATE_DIALING) { + if (ast_pbx_start(chan)) { + ast_log(LOG_WARNING, "Unable to start PBX\n"); + ast_hangup(chan); + } + } + return 0; +} + +static int dect_try_to_connect_call(struct dect_pvt *pvt) +{ + struct ast_channel *chan = pvt->chan; + struct dect_pt *pt = pvt->pt; + + if (!ast_exists_extension(NULL, pt->context, chan->exten, 1, NULL)) + return -1; + ast_log(LOG_NOTICE, "connecting call\n"); + if (ast_pbx_start(chan)) { + ast_log(LOG_WARNING, "Unable to start PBX\n"); + ast_hangup(chan); + } else + ast_setstate(chan, AST_STATE_RING); + return 0; +} + +static int dect_answer(struct ast_channel *chan) +{ + struct dect_pvt *pvt = chan->tech_pvt; + struct dect_mncc_connect_param connect = {}; + + ast_log(LOG_NOTICE, "answer call state %u\n", chan->_state); + if (chan->_state == AST_STATE_UP || chan->_state == AST_STATE_RINGING) + return 0; + + dect_mncc_connect_req(dh, pvt->call, &connect); + ast_setstate(chan, AST_STATE_UP); + return 0; +} + +static int dect_call(struct ast_channel *chan, char *dest, int timeout) +{ + struct dect_pvt *pvt = chan->tech_pvt; + struct dect_mncc_setup_param param; + struct dect_ie_basic_service service; + + /* Store CallerID for ALERTING state */ + ast_string_field_set(pvt, cid_num, chan->cid.cid_num); + ast_string_field_set(pvt, cid_name, chan->cid.cid_name); + + dect_ie_init(&service); + service.class = DECT_CALL_CLASS_NORMAL; + service.service = DECT_SERVICE_BASIC_SPEECH_DEFAULT; + + memset(¶m, 0, sizeof(param)); + param.basic_service = &service; + dect_mncc_setup_req(dh, pvt->call, &pvt->pt->ipui, ¶m); + + ast_setstate(pvt->chan, AST_STATE_DIALING); + chan->hangupcause = AST_CAUSE_NORMAL_CLEARING; + return 0; +} + +static struct ast_channel *dect_request_call(const char *type, format_t format, + const struct ast_channel *requestor, + void *data, int *cause) +{ + struct dect_pvt *pvt; + struct dect_call *call; + struct dect_pt *pt; + const char *name = data; + + pt = dect_pt_get_by_name(name); + if (pt == NULL) { + ast_log(LOG_NOTICE, "Call to unknown PT '%s' requested\n", name); + return NULL; + } + + call = dect_call_alloc(dh); + if (call == NULL) + return NULL; + pvt = dect_call_priv(call); + + ast_log(LOG_NOTICE, "Outgoing call to %s\n", pt->name); + pvt->pt = pt; + pvt->call = call; + if (dect_init_call(pvt, AST_STATE_RESERVED, NULL) < 0) + return NULL; + + return pvt->chan; +} + +static int dect_hangup(struct ast_channel *chan) +{ + struct dect_pvt *pvt = chan->tech_pvt; + struct dect_ie_release_reason release_reason; + struct dect_mncc_release_param param; + + ast_log(LOG_NOTICE, "Hangup\n"); + + if (chan->_state != AST_STATE_DOWN) { + dect_ie_init(&release_reason); + release_reason.reason = DECT_RELEASE_NORMAL; + + memset(¶m, 0, sizeof(param)); + param.release_reason = &release_reason; + dect_mncc_release_req(dh, pvt->call, ¶m); + } + + chan->tech_pvt = NULL; + ast_setstate(chan, AST_STATE_DOWN); + return 0; +} + +static int dect_indicate(struct ast_channel *chan, int condition, + const void *data, size_t datalen) +{ + struct dect_pvt *pvt = chan->tech_pvt; + struct dect_mncc_alert_param alert; + struct dect_ie_progress_indicator progress; + struct dect_ie_signal signal; + int res = 0; + + switch (condition) { + case AST_CONTROL_RINGING: + ast_log(LOG_NOTICE, "Ringing\n"); + memset(&alert, 0, sizeof(alert)); + if (0) { + alert.signal = dect_signal_init(&signal, DECT_SIGNAL_RING_BACK_TONE_ON); + } else { + dect_ie_init(&progress); + progress.location = DECT_LOCATION_PRIVATE_NETWORK_SERVING_LOCAL_USER; + progress.progress = DECT_PROGRESS_INBAND_INFORMATION_NOW_AVAILABLE; + dect_ie_list_add(&progress, &alert.progress_indicator); + } + dect_mncc_alert_req(dh, pvt->call, &alert); + ast_setstate(chan, AST_STATE_RINGING); + res = -1; + break; + case AST_CONTROL_BUSY: + case AST_CONTROL_CONGESTION: + case AST_CONTROL_PROCEEDING: + case AST_CONTROL_PROGRESS: + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: + case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_T38_PARAMETERS: + case AST_CONTROL_SRCUPDATE: + case -1: + res = -1; + ast_log(LOG_NOTICE, "Indicate %d\n", condition); + break; + default: + ast_log(LOG_NOTICE, "Indicate unknown condition %d\n", condition); + res = -1; + break; + } + return res; +} + +static int dect_write(struct ast_channel *chan, struct ast_frame *f) +{ + struct dect_pvt *pvt = chan->tech_pvt; + struct dect_msg_buf mb; + unsigned int len = f->datalen; + + mb.data = f->data.ptr; + mb.len = 40; + while (len >= 40) { + dect_dl_u_data_req(dh, pvt->call, &mb); + mb.data += 40; + len -= 40; + } + return 0; +} + +static struct ast_frame *dect_read(struct ast_channel *chan) +{ + ast_log(LOG_NOTICE, "read\n"); + return NULL; +} + +static int dect_fixup(struct ast_channel *old, struct ast_channel *new) +{ + ast_log(LOG_NOTICE, "fixup\n"); + return 0; +} + +static const struct ast_channel_tech dect_tech = { + .type = "DECT", + .description = "Digital Enhanced Cordless Telecommunications (DECT)", + .capabilities = AST_FORMAT_G726_AAL2, + .answer = dect_answer, + .requester = dect_request_call, + .call = dect_call, + .hangup = dect_hangup, + .indicate = dect_indicate, + .write = dect_write, + .read = dect_read, + .fixup = dect_fixup, +}; + +static void dect_mncc_setup_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_setup_param *param) +{ + struct dect_mncc_setup_ack_param setup_ack; + struct dect_mncc_call_proc_param call_proc; + struct dect_mncc_release_param release; + struct dect_ie_release_reason release_reason; + struct dect_ie_delimiter_request delimiter_request; + struct dect_ie_signal signal; + struct dect_pvt *pvt = dect_call_priv(call); + struct dect_pt *pt; + enum ast_channel_state state; + const char *exten; + + pt = dect_pt_get_by_ipui(dect_call_portable_identity(call)); + if (pt == NULL) { + ast_log(LOG_NOTICE, "Incoming call from unknown PT\n"); + + memset(&release, 0, sizeof(release)); + release.release_reason = dect_ie_init(&release_reason); + release_reason.reason = DECT_RELEASE_UNKNOWN_IDENTITY; + dect_mncc_reject_req(dh, call, &release); + return; + } + + ast_log(LOG_NOTICE, "Incoming call from %s\n", pt->name); + + /* If the phone supplies the entire number en-bloc, enter call + * proceeding state. A number is considered complete when contained + * in a called party number IE, or a multi-keypad IE and the extension + * exists. When no number is supplied and an 's' extension exists in + * the context, the call is also moved to call proceeding state and + * further decisions are left to the PBX. + * + * Otherwise enter overlap sending and wait for further information. + */ + state = AST_STATE_RING; + exten = NULL; + + if (param->called_party_number != NULL) + ; //ast_copy_string(exten, param->called_party_number.info, sizeof(exten)); + else if (param->keypad != NULL && param->keypad->info[0] < 0x80) + exten = (char *)param->keypad->info; + else { + exten = "s"; + if (!ast_exists_extension(NULL, pt->context, exten, 1, NULL)) { + state = AST_STATE_DIALING; + exten = ""; + } + } + + if (state == AST_STATE_RING) { + memset(&call_proc, 0, sizeof(call_proc)); + dect_mncc_call_proc_req(dh, call, &call_proc); + } else { + dect_ie_init(&delimiter_request); + dect_signal_init(&signal, DECT_SIGNAL_DIAL_TONE_ON); + + memset(&setup_ack, 0, sizeof(setup_ack)); + setup_ack.delimiter_request = &delimiter_request; + setup_ack.signal = &signal; + dect_mncc_setup_ack_req(dh, call, &setup_ack); + } + + pvt->pt = pt; + pvt->call = call; + dect_init_call(pvt, state, exten); + + /* WTF? Asterisk eats (actually leaks) the first queued frame when + * answering a channel. Give it something to eat :| */ + if (state == AST_STATE_DIALING) + ast_queue_control(pvt->chan, -1); +} + +static void dect_mncc_setup_ack_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_setup_ack_param *param) +{ + ast_log(LOG_NOTICE, "MNCC_SETUP_ACK-ind\n"); +} + +static void dect_mncc_reject_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_release_param *param) +{ + ast_log(LOG_NOTICE, "MNCC_REJECT-ind\n"); +} + +#if 0 +static int dect_display_timer(const void *data) +{ + struct dect_pvt *pvt = (struct dect_pvt *)data; + struct dect_ie_display display; + struct dect_mncc_info_param info = { .display = &display}; + + dect_display_init(&display); + dect_display_append_char(&display, DECT_C_RETURN); + dect_display_append_char(&display, DECT_C_RETURN); + + dect_mncc_info_req(dh, pvt->call, &info); + return 1; +} +#endif + +static void dect_mncc_alert_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_alert_param *param) +{ + struct dect_pvt *pvt = dect_call_priv(call); + struct dect_ie_display display; + struct dect_ie_signal signal; + struct dect_mncc_info_param info = { .signal = &signal, .display = &display}; + unsigned int name_len, num_len; + char pattern[16]; + const char *c; + + ast_log(LOG_NOTICE, "MNCC_ALERT-ind\n"); + + ast_copy_string(pattern, pvt->pt->ring_pattern, sizeof(pattern)); + c = pbx_builtin_getvar_helper(pvt->chan, "RING_PATTERN"); + if (c != NULL) + ast_copy_string(pattern, c, sizeof(pattern)); + + if (strcasecmp(pattern, "silent")) { + dect_ie_init(&signal); + signal.code = DECT_SIGNAL_ALERTING_BASE | (atoi(c) & 0xf); + } + + dect_display_init(&display); + name_len = strlen(pvt->cid_name); + num_len = strlen(pvt->cid_num); + if (name_len > 0 || num_len > 0) { + dect_display_append_char(&display, DECT_C_CLEAR_DISPLAY); + if (name_len > 0) { + dect_display_append(&display, pvt->cid_name, name_len); + dect_display_append(&display, " <", 2); + } + + if (num_len > 0) + dect_display_append(&display, pvt->cid_num, num_len); + + if (name_len > 0) + dect_display_append(&display, ">", 1); + //pvt->pt->timer_id = ast_sched_add(sched, 500, dect_display_timer, pvt); + } + + dect_mncc_info_req(dh, call, &info); + + ast_queue_control(pvt->chan, AST_CONTROL_RINGING); + ast_setstate(pvt->chan, AST_STATE_RINGING); +} + +static void dect_mncc_connect_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_connect_param *param) +{ + struct dect_pvt *pvt = dect_call_priv(call); + + ast_log(LOG_NOTICE, "MNCC_CONNECT-ind\n"); + ast_queue_control(pvt->chan, AST_CONTROL_ANSWER); + dect_mncc_connect_res(dh, call, param); +} + +static void dect_mncc_release_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_release_param *param) +{ + struct dect_pvt *pvt = dect_call_priv(call); + + ast_log(LOG_NOTICE, "MNCC_RELEASE-ind\n"); + ast_setstate(pvt->chan, AST_STATE_DOWN); + pvt->chan->hangupcause = + dect_release_reason_to_ast(param->release_reason->reason); + ast_queue_hangup(pvt->chan); + + dect_mncc_release_res(dh, call, param); +} + +static void dect_mncc_release_cfm(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_release_param *param) +{ + ast_log(LOG_NOTICE, "MNCC_RELEASE-cfm\n"); +} + +static void dect_dl_u_data_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_msg_buf *mb) +{ + struct dect_pvt *pvt = dect_call_priv(call); + struct ast_frame f; + + if (pvt->chan->_state == AST_STATE_DOWN) + return; + + memset(&f, 0, sizeof(f)); + f.frametype = AST_FRAME_VOICE; + f.subclass.integer = AST_FORMAT_G726_AAL2; + f.samples = mb->len * 2; + f.datalen = mb->len; + f.data.ptr = mb->data; + f.offset = AST_FRIENDLY_OFFSET; + + ast_queue_frame(pvt->chan, &f); +} + +static void dect_keypad_info(struct dect_pvt *pvt, + const struct dect_ie_keypad *keypad) +{ + struct ast_frame f = { .frametype = AST_FRAME_DTMF, }; + struct ast_channel *chan = pvt->chan; + unsigned int i; + + if (chan->_state == AST_STATE_DIALING) { + strncat(chan->exten, (char *)keypad->info, keypad->len); + dect_try_to_connect_call(pvt); + } else { + for (i = 0; i < keypad->len; i++) { + ast_verbose("keypad: '%c' (%x)\n", keypad->info[i], keypad->info[i]); + f.subclass.integer = keypad->info[i]; + ast_queue_frame(chan, &f); + } + } +} + +static void dect_mncc_info_ind(struct dect_handle *dh, struct dect_call *call, + struct dect_mncc_info_param *param) +{ + struct dect_pvt *pvt = dect_call_priv(call); + + if (param->keypad != NULL) + dect_keypad_info(pvt, param->keypad); +} + +static const struct dect_cc_ops dect_cc_ops = { + .priv_size = sizeof(struct dect_pvt), + .mncc_setup_ind = dect_mncc_setup_ind, + .mncc_setup_ack_ind = dect_mncc_setup_ack_ind, + .mncc_reject_ind = dect_mncc_reject_ind, + .mncc_call_proc_ind = NULL, + .mncc_alert_ind = dect_mncc_alert_ind, + .mncc_connect_ind = dect_mncc_connect_ind, + .mncc_connect_cfm = NULL, + .mncc_release_ind = dect_mncc_release_ind, + .mncc_release_cfm = dect_mncc_release_cfm, + .mncc_facility_ind = NULL, + .mncc_info_ind = dect_mncc_info_ind, + .mncc_modify_ind = NULL, + .mncc_modify_cfm = NULL, + .mncc_hold_ind = NULL, + .mncc_hold_cfm = NULL, + .mncc_retrieve_ind = NULL, + .mncc_retrieve_cfm = NULL, + .mncc_iwu_info_ind = NULL, + .dl_u_data_ind = dect_dl_u_data_ind, +}; + +static void dect_mm_locate_ind(struct dect_handle *dh, + struct dect_mm_endpoint *mme, + struct dect_mm_locate_param *param) +{ + ast_log(LOG_NOTICE, "MM_LOCATE-ind\n"); +} + +static void dect_mm_identity_assign_cfm(struct dect_handle *dh, + struct dect_mm_endpoint *mme, bool accept, + struct dect_mm_identity_assign_param *param) +{ + ast_log(LOG_NOTICE, "MN_IDENTITY_ASSIGN-cfm\n"); +} + +static const struct dect_mm_ops dect_mm_ops = { + .mm_locate_ind = dect_mm_locate_ind, + .mm_identity_assign_cfm = dect_mm_identity_assign_cfm, +}; + +static int dect_show_debug(const char *fmt, va_list ap) +{ + ast_verbose_ap(fmt, ap); + return 0; +} + +static int dect_load_config(void) +{ + struct ast_flags cfg_flags = {}; + struct ast_config *cfg; + struct ast_variable *v; + struct dect_pt *pt; + char *cat; + + memcpy(&dect_cfg.jbconf, &dect_default_jbconf, sizeof(struct ast_jb_conf)); + + cfg = ast_config_load(CONFIG_FILE, cfg_flags); + if (cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Config file %s is in an invalid format.\n", + CONFIG_FILE); + return AST_MODULE_LOAD_DECLINE; + } + + if (cfg == NULL) { + ast_log(LOG_ERROR, "Unable to load config %s\n", CONFIG_FILE); + return AST_MODULE_LOAD_DECLINE; + } + + for (v = ast_variable_browse(cfg, "general"); v != NULL; v = v->next) { + /* jitter buffer configuration */ + if (!ast_jb_read_conf(&dect_cfg.jbconf, v->name, v->value)) + continue; + + if (!strcasecmp(v->name, "context")) { + ast_copy_string(dect_cfg.default_context, v->value, + sizeof(dect_cfg.default_context)); + } + } + + /* Phone configuration */ + cat = NULL; + while ((cat = ast_category_browse(cfg, cat)) != NULL) { + if (!strcasecmp(cat, "general")) + continue; + pt = dect_init_portable(cat, ast_variable_browse(cfg, cat)); + if (pt == NULL) + return AST_MODULE_LOAD_DECLINE; + dect_register_extension(pt, true); + } + return 0; +} + +/* + * Asterisk CLI commands + */ + +static char *dect_cli_debug(struct ast_cli_entry *e, int cmd, + struct ast_cli_args *a) +{ + const char *arg; + + switch (cmd) { + case CLI_INIT: + e->command = "dect set debug {on|off}"; + e->usage = "Usage: dect set debug {on|off}\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + arg = a->argv[e->args - 1]; + if (!strcasecmp(arg, "on")) { + dect_set_debug_hook(dect_show_debug); + ast_cli(a->fd, "DECT debugging enabled\n"); + } else if (!strcasecmp(arg, "off")) { + dect_set_debug_hook(NULL); + ast_cli(a->fd, "DECT debugging disabled\n"); + } else + return CLI_SHOWUSAGE; + + return CLI_SUCCESS; +} + +static struct ast_cli_entry dect_cli_cmds[] = { + AST_CLI_DEFINE(dect_cli_debug, "Enable/Disable DECT debugging"), +}; + +/* + * libdect event ops + */ + +struct dect_fd_priv { + int *id; +}; + +static int dect_io_callback(int *id, int fd, short io_events, void *data) +{ + struct dect_fd *dfd = data; + uint32_t events = 0; + + if (io_events & (AST_IO_IN | AST_IO_ERR | AST_IO_HUP)) + events |= DECT_FD_READ; + if (io_events & AST_IO_OUT) + events |= DECT_FD_WRITE; + dfd->callback(dh, dfd, events); + return 1; +} + +static int dect_register_fd(const struct dect_handle *dh, struct dect_fd *dfd, + uint32_t events) +{ + struct dect_fd_priv *priv = (struct dect_fd_priv *)dfd->priv; + short io_events = 0; + + if (events & DECT_FD_READ) + io_events |= AST_IO_IN; + if (events & DECT_FD_WRITE) + io_events |= AST_IO_OUT; + + priv->id = ast_io_add(io, dfd->fd, dect_io_callback, io_events, dfd);; + return priv->id ? 0 : -1; +} + +static void dect_unregister_fd(const struct dect_handle *dh, struct dect_fd *dfd) +{ + struct dect_fd_priv *priv = (struct dect_fd_priv *)dfd->priv; + + ast_io_remove(io, priv->id); +} + +struct dect_timer_priv { + int id; +}; + +static int dect_timer_callback(const void *data) +{ + struct dect_timer *timer = (struct dect_timer *)data;; + + timer->callback(dh, timer); + return 0; +} + +static void dect_start_timer(const struct dect_handle *dh, + struct dect_timer *timer, + const struct timeval *tv) +{ + struct dect_timer_priv *priv = (struct dect_timer_priv *)timer->priv; + + priv->id = ast_sched_add(sched, tv->tv_sec * 1000 + tv->tv_usec / 1000, + dect_timer_callback, timer); +} + +static void dect_stop_timer(const struct dect_handle *dh, + struct dect_timer *timer) +{ + struct dect_timer_priv *priv = (struct dect_timer_priv *)timer->priv; + + AST_SCHED_DEL(sched, priv->id); +} + +static const struct dect_event_ops dect_event_ops = { + .fd_priv_size = sizeof(struct dect_fd_priv), + .register_fd = dect_register_fd, + .unregister_fd = dect_unregister_fd, + .timer_priv_size = sizeof(struct dect_timer_priv), + .start_timer = dect_start_timer, + .stop_timer = dect_stop_timer, +}; + +static struct dect_ops dect_ops = { + .event_ops = &dect_event_ops, + .cc_ops = &dect_cc_ops, + .mm_ops = &dect_mm_ops, +}; + +static pthread_t io_thread = AST_PTHREADT_NULL; + +static void *dect_io_thread(void *ignore) +{ + int timeout; + + while (1) { + pthread_testcancel(); + timeout = ast_sched_wait(sched); + if (timeout < 0) + timeout = 1000; + ast_io_wait(io, timeout); + ast_sched_runq(sched); + } + return NULL; +} + +static int dect_io_thread_start(void) +{ + int err; + + err = ast_pthread_create_background(&io_thread, NULL, dect_io_thread, NULL); + if (err < 0) { + ast_log(LOG_ERROR, "Unable to start IO thread\n"); + return err; + } + return 0; +} + +static int dect_load_module(void) +{ + sched = sched_context_create(); + if (sched == NULL) { + ast_log(LOG_ERROR, "Unable to create scheduler context\n"); + goto err1; + } + + io = io_context_create(); + if (io == NULL) { + ast_log(LOG_ERROR, "Unable to create IO context\n"); + goto err2; + } + + if (ast_channel_register(&dect_tech)) { + ast_log(LOG_ERROR, "Unable to register 'DECT' channel\n"); + goto err3; + } + + ast_cli_register_multiple(dect_cli_cmds, ARRAY_LEN(dect_cli_cmds)); + + dect_load_config(); + + if (dect_io_thread_start() < 0) + goto err4; + + dh = dect_alloc_handle(&dect_ops); + if (dh == NULL) { + ast_log(LOG_ERROR, "Unable to allocate DECT handle\n"); + goto err5; + } + + if (dect_init(dh) < 0) { + ast_log(LOG_ERROR, "Unable to initialize DECT handle\n"); + goto err6; + } + + return AST_MODULE_LOAD_SUCCESS; +err6: +err5: +err4: +err3: +err2: + sched_context_destroy(sched); +err1: + return AST_MODULE_LOAD_FAILURE; +} + +static int dect_unload_module(void) +{ + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "DECT", + .load = dect_load_module, + .unload = dect_unload_module, +); diff --git a/configs/dect.conf.sample b/configs/dect.conf.sample new file mode 100644 index 000000000..778df689b --- /dev/null +++ b/configs/dect.conf.sample @@ -0,0 +1,50 @@ +[general] +context = default + +;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- +; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an + ; DECT channel. Defaults to "no". An enabled jitterbuffer will + ; be used only if the sending side can create and the receiving + ; side can not accept jitter. The DECT channel can't accept jitter, + ; thus an enabled jitterbuffer on the receive DECT side will always + ; be used if the sending side can create jitter. + +; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. + +; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is + ; resynchronized. Useful to improve the quality of the voice, with + ; big jumps in/broken timestamps, usually sent from exotic devices + ; and programs. Defaults to 1000. + +; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of a DECT + ; channel. Two implementations are currently available - "fixed" + ; (with size always equals to jbmax-size) and "adaptive" (with + ; variable size, actually the new jb of IAX2). Defaults to fixed. + +; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". +;----------------------------------------------------------------------------------- + +[phone1] +emc = 08ae +psn = 83d1e +regexten = 601 + +language = de +cid_number = 601 +cid_name = kaber +ring_pattern = 3 + +display_lines = 1 +display_columns = 11 + +[phone2] +emc = 08ae +psn = 8969f +regexten = 602 +language = de + +[phone3] +emc = 08ae +psn = 5b9a0 +regexten = 603 +language = de diff --git a/configure.ac b/configure.ac index 9e8a2df81..6f65e0f7f 100644 --- a/configure.ac +++ b/configure.ac @@ -377,6 +377,7 @@ AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto]) AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi]) +AST_EXT_LIB_SETUP([DECT], [libdect], [dect]) AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec], [avcodec]) AST_EXT_LIB_SETUP([GSM], [External GSM], [gsm], [, use 'internal' GSM otherwise]) AST_EXT_LIB_SETUP([GTK2], [gtk2], [gtk2]) @@ -1895,6 +1896,8 @@ if test "${PBX_PWLIB}" = "1" -a "${USE_OPENH323}" != "no" ; then [${PWLIB_INCLUDE}], [${PWLIB_LIB}]) fi +AST_EXT_LIB_CHECK([DECT], [dect], [dect_alloc_handle]) + AST_EXT_LIB_CHECK([LUA], [lua5.1], [luaL_newstate], [lua5.1/lua.h], [-lm]) if test "x${PBX_LUA}" = "x1" ; then if test x"${LUA_DIR}" = x; then -- cgit v1.2.3