diff --git a/channels/Makefile b/channels/Makefile
index b641173fe85304fb7c7388b3eed6a3ccb1b7d51e..5558ee546a0e6dcc08a22a22caffa3a11fa563be 100644
--- a/channels/Makefile
+++ b/channels/Makefile
@@ -36,6 +36,7 @@ chan_mgcp.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
 chan_unistim.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
 chan_phone.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
 chan_sip.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
+chan_brcm.so: LIBS+=-lubus -lubox -lpicoevent
 
 chan_misdn.o: _ASTCFLAGS+=-Imisdn
 misdn_config.o: _ASTCFLAGS+=-Imisdn
diff --git a/channels/chan_brcm.c b/channels/chan_brcm.c
new file mode 100644
index 0000000000000000000000000000000000000000..4d2f615a3e7c0d036c3a6633f2e299fbdb0e9e73
--- /dev/null
+++ b/channels/chan_brcm.c
@@ -0,0 +1,3822 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \author Benjamin Larsson <benjamin@southpole.se>
+ * \author Jonas Höglund <jonash@southpole.se>
+ *
+ * \ingroup channel_drivers
+ */
+
+//	#define BRCM_LOCK_DEBUG		/* If defined we will log lock events to the asterisk debug channel */
+
+/* TODO:
+ * Prefered codec order mulaw/alaw/g729/g723.1/g726_24/g726_32
+ * Enable T38 support
+ * Enable V18 support
+ */
+
+
+
+#include "asterisk.h"
+
+#include <math.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <sys/stat.h>															// mkfifo()
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/utils.h"
+#include "asterisk/callerid.h"
+#include "asterisk/causes.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/indications.h"
+#include "asterisk/sched.h"
+#include "asterisk/app.h"
+#include "asterisk/format_cap.h"
+#include "asterisk/format_compatibility.h"
+#include "asterisk/format_cache.h"
+#include "asterisk/logger.h"
+#include "asterisk/bridge.h"
+
+#include "asterisk/stasis_system.h"
+#include "asterisk/stasis_channels.h"
+
+#include <libubus.h>
+#include <libpicoevent.h>
+
+#include "chan_brcm.h"
+
+#ifndef AST_MODULE
+#define AST_MODULE "chan_brcm"
+#endif
+
+static void brcm_dialtone_set(struct brcm_pvt *p, dialtone_state state);
+static int brcm_signal_dialtone(struct brcm_pvt *p);
+static int brcm_in_conference(const struct brcm_pvt *p);
+static int cwtimeout_cb(const void *data);
+static int r4hanguptimeout_cb(const void *data);
+static void brcm_generate_rtp_packet(struct brcm_subchannel *p, uint8_t *packet_buf, int type, int marker, int dtmf_timestamp);
+static int brcm_mute_connection(struct brcm_subchannel *p);
+static int brcm_unmute_connection(struct brcm_subchannel *p);
+static int brcm_close_connection(struct brcm_subchannel *p);
+static int brcm_create_conference(struct brcm_pvt *p);
+static int brcm_stop_conference(struct brcm_subchannel *p);
+static int brcm_finish_transfer(struct ast_channel *owner, struct brcm_subchannel *p, int result);
+static int brcm_call(struct ast_channel *ast, const char *dest, int timeout);
+static int brcm_hangup(struct ast_channel *ast);
+static int brcm_answer(struct ast_channel *ast);
+static struct ast_frame *brcm_read(struct ast_channel *ast);
+static int brcm_write(struct ast_channel *ast, struct ast_frame *frame);
+static int brcm_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int brcm_transfer(struct ast_channel *ast, const char *newdest);
+static int brcm_senddigit_begin(struct ast_channel *ast, char digit);
+static int brcm_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int brcm_in_call(const struct brcm_pvt *p);
+static int brcm_in_callwaiting(const struct brcm_pvt *p);
+static int brcm_in_onhold(const struct brcm_pvt *p);
+static int brcm_subchannel_is_idle(const struct brcm_subchannel *sub);
+static struct ast_channel *brcm_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause);
+static void brcm_lock_pvts(void);
+static void brcm_unlock_pvts(void);
+static struct brcm_pvt* brcm_get_next_pvt(struct brcm_pvt *p);
+static void *pe_base_run(void *unused);
+static int brcm_create_connection(struct brcm_subchannel *p);
+static int brcm_signal_congestion(struct brcm_pvt *p);
+static int brcm_stop_dialtone(struct brcm_pvt *p);
+static int brcm_signal_ringing(struct brcm_pvt *p);
+static int brcm_stop_ringing(struct brcm_pvt *p);
+static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p);
+static int brcm_stop_ringing_callerid_pending(struct brcm_pvt *p);
+static int brcm_signal_callwaiting(const struct brcm_pvt *p);
+static int brcm_stop_callwaiting(const struct brcm_pvt *p);
+static int brcm_signal_callerid(struct ast_channel *chan, struct brcm_subchannel *sub);
+static struct brcm_subchannel *brcm_get_idle_subchannel(const struct brcm_pvt *p);
+static struct brcm_subchannel* brcm_get_active_subchannel(const struct brcm_pvt *p);
+static struct brcm_subchannel *brcm_subchannel_get_peer(const struct brcm_subchannel *sub);
+static struct brcm_pvt* brcm_get_pvt_from_lineid(struct brcm_pvt *p, int line_id);
+static void handle_dtmf_calling(struct brcm_subchannel *sub);
+static void brcm_cancel_dialing_timeouts(struct brcm_pvt *p);
+static int brcm_should_relay_dtmf(const struct brcm_subchannel *sub);
+static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, const char *ext, const char *context,
+		const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor,
+		struct ast_format_cap *format);
+
+/* Global brcm channel parameters */
+static const char tdesc[] = "Broadcom SLIC Driver";
+static const char config[] = "chan_telephony.conf";
+
+static line_settings line_config[MAX_NUM_LINEID];
+static int current_connection_id = 0;
+static int num_fxs_endpoints = -1;
+static int num_fxo_endpoints = -1;
+static int num_dect_endpoints = -1;
+static int num_endpoints = -1;
+static int clip = 1; // Caller ID presentation
+/* driver scheduler */
+static struct ast_sched_context *sched = NULL;
+
+/* Call waiting */
+static int cwtimeout = DEFAULT_CALL_WAITING_TIMEOUT;
+
+/* R4 transfer */
+static int r4hanguptimeout = DEFAULT_R4_HANGUP_TIMEOUT;
+
+/* Automatic call on hold hangup */
+static int onholdhanguptimeout = DEFAULT_ONHOLD_HANGUP_TIMEOUT;
+
+/* Boolean, controls whether the transferor puts the transfer target on-hold before sending
+ * REFER to the transferee */
+static int hold_target_before_refer = 1;
+
+/* Boolean, controls whether the conference call shall be terminated when the initiator hangs up.
+ * The conference call terminates until there is only one participant remaining by default. The
+ * option is not configurable for now */
+static int terminate_conference = 0;
+
+/* Global jitterbuffer configuration */
+static struct ast_jb_conf global_jbconf;
+
+//TODO change AST_MAX_EXTENSION to something shorter
+/* Structure for feature access codes */
+struct feature_access_code {
+	AST_LIST_ENTRY(feature_access_code) list;
+	char code[AST_MAX_EXTENSION];
+};
+
+/* List of configured feature access codes */
+static AST_LIST_HEAD_NOLOCK_STATIC(feature_access_codes, feature_access_code);
+
+/* Format a string of feature access codes */
+static const char *feature_access_code_string(char *buffer, unsigned int buffer_length);
+
+/* Add FAC to list */
+static int feature_access_code_add(const char *code);
+
+/* Clear list of FAC */
+static int feature_access_code_clear(void);
+
+/* Match dialed digits against feature access codes */
+static int feature_access_code_match(char *sequence);
+
+/*  Send dialed DTMF codes during call */
+static void send_outgoing_dtmf(struct ast_channel *owner, char dtmf_button, int frametype);
+
+/* Boolean value whether the monitoring thread shall continue. */
+static unsigned int monitor;
+static unsigned int packets;
+
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+static pthread_t packet_thread = AST_PTHREADT_NULL;
+static pthread_t ubus_thread = AST_PTHREADT_NULL;
+
+static struct ubus_context *ctx;
+static struct ubus_event_handler ObjAddListener;								// Event handler for new ubus object events
+static struct ubus_event_handler ObjRmListener;									// Event handler for removed ubus object events
+static int base;
+static volatile uint32_t endpt_id = 0;
+static const char endpt_ubus_path[] = "endpt";
+static const char audio_rx_str[] = "/tmp/endpt_audio_tx";
+static const char audio_tx_str[] = "/tmp/endpt_audio_rx";
+static const char ubusStrObjAdd[] = "ubus.object.add";							// UBUS objects added to global context
+static const char ubusStrObjRm[] = "ubus.object.remove";						// UBUS objects added removed from global context
+static int audio_rx_fd = 0, audio_tx_fd = 0;
+static pe_bus_t *audio_rx_bus, *audio_tx_bus;
+static pe_stream_t *audio_rx_stream;
+
+#define RTP_HEADER_SIZE 12
+#define RTP_DTMF_SIZE 4
+
+enum {
+	EVENT_TYPE,
+	EVENT_LINE_ID,
+	__EVENT_MAX,
+};
+
+enum {																			// New ubus objects in global context
+	OBJ_ID,																		// UBUS ID of object
+	OBJ_PATH,																	// String path of object
+};
+
+enum {
+	STATE_OFF = 0,
+	STATE_ON,
+	STATE_LAST,
+};
+
+struct endpt_event {
+	const char *name;
+	enum LINE_EVENT event;
+	int line;
+};
+
+typedef struct __attribute__((__packed__)) {
+	int32_t line;
+	int32_t cnx_id;
+	uint32_t rtp_size;
+	uint8_t rtp[1];
+} audio_packet_t;
+
+static struct endpt_event event_map[] = {
+	{ .name = "DTMF0", .event = EVENT_DTMF0 },
+	{ .name = "DTMF1", .event = EVENT_DTMF1 },
+	{ .name = "DTMF2", .event = EVENT_DTMF2 },
+	{ .name = "DTMF3", .event = EVENT_DTMF3 },
+	{ .name = "DTMF4", .event = EVENT_DTMF4 },
+	{ .name = "DTMF5", .event = EVENT_DTMF5 },
+	{ .name = "DTMF6", .event = EVENT_DTMF6 },
+	{ .name = "DTMF7", .event = EVENT_DTMF7 },
+	{ .name = "DTMF8", .event = EVENT_DTMF8 },
+	{ .name = "DTMF9", .event = EVENT_DTMF9 },
+	{ .name = "DTMFA", .event = EVENT_DTMFA },
+	{ .name = "DTMFB", .event = EVENT_DTMFB },
+	{ .name = "DTMFC", .event = EVENT_DTMFC },
+	{ .name = "DTMFD", .event = EVENT_DTMFD },
+	{ .name = "DTMFS", .event = EVENT_DTMFS },
+	{ .name = "DTMFH", .event = EVENT_DTMFH },
+	{ .name = "OFFHOOK", .event = EVENT_OFFHOOK },
+	{ .name = "ONHOOK", .event = EVENT_ONHOOK },
+	{ .name = "EARLY_OFFHOOK", .event = EVENT_EARLY_OFFHOOK },
+	{ .name = "EARLY_ONHOOK", .event = EVENT_EARLY_ONHOOK },
+	{ .name = "FLASH", .event = EVENT_FLASH },
+	{ .name = "", .event = EVENT_LAST },
+};
+
+static const struct blobmsg_policy asterisk_event_policy[] = {
+	[EVENT_TYPE] = { .name = "event", .type = BLOBMSG_TYPE_STRING },
+	[EVENT_LINE_ID] = { .name = "line", .type = BLOBMSG_TYPE_INT32 },
+};
+
+static const struct blobmsg_policy new_obj_policy[] = {
+	[OBJ_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },					// UBUS ID of object
+	[OBJ_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },				// String path of object
+};
+
+static struct ast_channel_tech *cur_tech;
+
+static struct stasis_subscription **registration_change_sub = NULL; /*!< subscription id for SIP registration change events for every line*/
+
+static const DTMF_CHARNAME_MAP dtmf_to_charname[] =
+{
+	{EVENT_DTMF0, "DTMF0", '0', 0},
+	{EVENT_DTMF1, "DTMF1", '1', 1},
+	{EVENT_DTMF2, "DTMF2", '2', 2},
+	{EVENT_DTMF3, "DTMF3", '3', 3},
+	{EVENT_DTMF4, "DTMF4", '4', 4},
+	{EVENT_DTMF5, "DTMF5", '5', 5},
+	{EVENT_DTMF6, "DTMF6", '6', 6},
+	{EVENT_DTMF7, "DTMF7", '7', 7},
+	{EVENT_DTMF8, "DTMF8", '8', 8},
+	{EVENT_DTMF9, "DTMF9", '9', 9},
+	{EVENT_DTMFA, "DTMFA", 'A', 12},
+	{EVENT_DTMFB, "DTMFB", 'B', 13},
+	{EVENT_DTMFC, "DTMFC", 'C', 14},
+	{EVENT_DTMFD, "DTMFD", 'D', 15},
+	{EVENT_DTMFH, "DTMFH", 0x23, 11}, //#
+	{EVENT_DTMFS, "DTMFS", 0x2A, 10}, //*
+	{EVENT_LAST,  "", '-', -1}
+};
+
+/* Linked list of pvt:s */
+static struct brcm_pvt *iflist;
+
+/* Protect the interface list (of brcm_pvt's) */
+AST_MUTEX_DEFINE_STATIC(iflock);
+
+/* Protect the monitoring thread, so only one process can kill or start it, and not
+   when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+static struct ast_format_cap *default_cap;
+
+static int load_common_settings(struct ast_config **cfg);
+static void load_lines_settings(struct ast_config *cfg);
+static char *state2str(enum channel_state state);
+
+/* exported capabilities */
+static struct ast_channel_tech brcm_tech = {
+	.type = "TELCHAN",
+	.description = tdesc,
+	.requester = brcm_request,			//No lock held (no channel yet)
+	.call = brcm_call,				//Channel is locked
+	.hangup = brcm_hangup,				//Channel is locked
+	.answer = brcm_answer,				//Channel is locked
+	.read = brcm_read,				//Channel is locked
+	.write = brcm_write,				//Channel is locked
+	.send_digit_begin = brcm_senddigit_begin,	//Channel is NOT locked
+	.send_digit_end = brcm_senddigit_end,		//Channel is NOT locked
+	.indicate = brcm_indicate,			//Channel is locked
+	.transfer = brcm_transfer			// Channel is locked
+};
+
+static struct brcm_channel_tech fxs_tech = {
+	.signal_ringing = brcm_signal_ringing,
+	.signal_ringing_callerid_pending = brcm_signal_ringing_callerid_pending,
+	.signal_callerid = brcm_signal_callerid,
+	.stop_ringing = brcm_stop_ringing,
+	.stop_ringing_callerid_pending = brcm_stop_ringing_callerid_pending,
+	.release = NULL,
+};
+
+/* Tries to lock 10 timees, then gives up */
+/* static int pvt_trylock(struct brcm_pvt *pvt, const char *reason) */
+/* { */
+/* 	int i = 10; */
+/* 	while (i--) { */
+/* 		if (!ast_mutex_trylock(&pvt->lock)) { */
+/* 			ast_debug(9, "----> Successfully locked pvt port %d - reason %s\n", pvt->line_id, reason); */
+/* 			return 1; */
+/* 		} */
+/* 	} */
+/* 	ast_debug(9, "----> Failed to lock port %d - %s\n", pvt->line_id, reason); */
+/* 	return 0; */
+/* } */
+
+#ifdef BRCM_LOCK_DEBUG
+static int pvt_lock(struct brcm_pvt *pvt, const char *reason)
+{
+	ast_debug(9, "----> Trying to lock port %d - %s\n", pvt->line_id, reason);
+	ast_mutex_lock(&pvt->lock);
+	ast_debug(9, "----> Locked pvt port %d - reason %s\n", pvt->line_id, reason);
+	return 1;
+}
+
+
+static int pvt_lock_silent(struct brcm_pvt *pvt)
+{
+	ast_mutex_lock(&pvt->lock);
+	return 1;
+}
+
+
+static int pvt_unlock(struct brcm_pvt *pvt)
+{
+	ast_mutex_unlock(&pvt->lock);
+	ast_debug(10, "----> Unlocking pvt port %d\n", pvt->line_id);
+	return 1;
+}
+
+static int pvt_unlock_silent(struct brcm_pvt *pvt)
+{
+	ast_mutex_unlock(&pvt->lock);
+	return 1;
+}
+
+#else
+#define pvt_lock(pvt, reason)		ast_mutex_lock(&(pvt)->lock)
+#define pvt_lock_silent(pvt)		ast_mutex_lock(&(pvt)->lock)
+#define pvt_unlock(pvt)			ast_mutex_unlock(&(pvt)->lock)
+#define pvt_unlock_silent(pvt)		ast_mutex_unlock(&(pvt)->lock)
+#endif
+
+// Get the endptmngr ubus address. It's dynamically updated
+// by another thread without locking. Thus we read it twice
+// here to ensure we get it correct since it might have been
+// written behind our back.
+static uint32_t get_ubus_endpt_id(void) {
+	uint32_t local_endpt_id[2];
+
+	do {
+		local_endpt_id[0] = endpt_id;
+		local_endpt_id[1] = endpt_id;
+	} while(local_endpt_id[0] != local_endpt_id[1]);
+
+	return local_endpt_id[0];
+}
+
+//-------------------------------------------------------------
+// Callback for: a ubus call (invocation) has replied with some data
+static void ubus_call_answer(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+	ast_debug(3, "Got answer on ubus call.\n");
+}
+
+
+//-------------------------------------------------------------
+// Callback for: a ubus call (invocation) has finished
+static void ubus_call_complete(struct ubus_request *req, int ret)
+{
+	ast_debug(3, "Ubus call completed.\n");
+	free(req);
+}
+
+
+static void endpt_signal(int line, char *signal, char *state, char *data) {
+	struct blob_buf bb;
+	struct ubus_request *req;
+	uint32_t local_endpt_id;
+	int res;
+
+	//ast_debug(5, "line = %d, signal = %s, state = %s, data = %s\n",  line, signal, state, data);
+
+	local_endpt_id = get_ubus_endpt_id();
+
+	if (local_endpt_id) {
+		memset(&bb, 0, sizeof(bb));
+		blob_buf_init(&bb, 0);
+
+		blobmsg_add_u32(&bb, "line", line);
+		blobmsg_add_string(&bb, "signal", signal);
+		blobmsg_add_string(&bb, "state", state);
+		if (data)
+			blobmsg_add_string(&bb, "data", data);
+
+		req = calloc(1, sizeof(struct ubus_request));
+		if (!req) return;
+
+		res = ubus_invoke_async(ctx, local_endpt_id, "signal", bb.head, req);
+
+		if(res != UBUS_STATUS_OK) {
+			ast_log(LOG_ERROR, "Error invoking method: %s %d\n", "signal", res);
+			free(req);
+			return;
+		}
+
+		req->data_cb = ubus_call_answer;
+		req->complete_cb = ubus_call_complete;
+		req->priv = NULL;
+		ubus_complete_request_async(ctx, req);
+	}
+}
+
+static void endpt_connection(int line, int id, char *action) {
+	struct blob_buf bb;
+	struct ubus_request *req;
+	uint32_t local_endpt_id;
+	int res;
+
+	local_endpt_id = get_ubus_endpt_id();
+
+	if (local_endpt_id) {
+		memset(&bb, 0, sizeof(bb));
+		blob_buf_init(&bb, 0);
+
+		blobmsg_add_u32(&bb, "line", line);
+		blobmsg_add_u32(&bb, "id", line);
+		blobmsg_add_string(&bb, "action", action);
+
+		req = calloc(1, sizeof(struct ubus_request));
+		if (!req) return;
+		res = ubus_invoke_async(ctx, local_endpt_id, "connection", bb.head, req);
+
+		if(res != UBUS_STATUS_OK) {
+			ast_log(LOG_ERROR, "Error invoking method: %s %d\n", "connection", res);
+			free(req);
+			return;
+		}
+
+		req->data_cb = ubus_call_answer;
+		req->complete_cb = ubus_call_complete;
+		req->priv = NULL;
+		ubus_complete_request_async(ctx, req);
+	}
+}
+
+static int brcm_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+	struct brcm_subchannel *sub = ast_channel_tech_pvt(ast);
+	struct ast_bridge_channel *play_bridge_channel;
+	struct ast_bridge *myBridge;
+	struct ast_frame astFrame;
+	int res = 0;
+	pvt_lock(sub->parent, "indicate");
+	//ast_mutex_lock(&sub->parent->lock);
+	switch(condition) {
+	case AST_CONTROL_UNHOLD:
+		res = 0; //We still want asterisk core to play tone
+		brcm_stop_dialtone(sub->parent);
+
+		// Play a beep when unholding.
+		ast_channel_lock(ast);
+		play_bridge_channel = ast_channel_get_bridge_channel(ast);
+		ast_channel_unlock(ast);
+		ast_bridge_channel_queue_playfile(play_bridge_channel, NULL, "beep", NULL);
+
+		sub->channel_state = INCALL;
+
+		// Tell all participants to re-sync RTP stream.
+		myBridge = ast_channel_internal_bridge(ast);
+		memset(&astFrame, 0, sizeof astFrame);
+		astFrame.frametype = AST_FRAME_CONTROL;
+		astFrame.subclass.integer = AST_CONTROL_SRCUPDATE;
+		ast_bridge_queue_everyone_else(myBridge, NULL, &astFrame);
+		break;
+
+	case AST_CONTROL_UPDATE_RTP_PEER:
+	case AST_CONTROL_SRCUPDATE:
+	case AST_CONTROL_SRCCHANGE:
+		break;
+	case AST_CONTROL_RINGING:
+		sub->channel_state = RINGBACK;
+		endpt_signal(sub->parent->line_id, "ringback", "on", NULL);
+		res = 0;
+		break;
+	case AST_CONTROL_TRANSFER:
+		res = -1;
+		if (datalen != sizeof(enum ast_control_transfer)) {
+			ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_TRANSFER. Expected %d, got %d\n", (int) sizeof(enum ast_control_transfer), (int) datalen);
+		} else {
+			enum ast_control_transfer *message = (enum ast_control_transfer *) data;
+			brcm_finish_transfer(ast, sub, *message);
+		}
+		break;
+	case AST_CONTROL_CONGESTION:
+		ast_debug(4, "Got CONGESTION on %s\n", ast_channel_name(ast));
+		/* The other end is out of network resources */
+		if (ast_channel_state(ast) != AST_STATE_UP) {
+			/* If state is UP, we can't do anything */
+			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+			brcm_hangup(ast);
+			break;
+		}
+		res = -1;
+		break;
+	case AST_CONTROL_CONNECTED_LINE:
+		ast_debug(4, "Got CONNECTED LINE UPDATE on %s\n", ast_channel_name(ast));
+		/* Update caller IDs on display - dect ? */
+		//TODO Ronny: perhaps reverse loop current here?
+		res = -1;
+		break;
+
+	case AST_CONTROL_BUSY:
+		ast_debug(4, "Got BUSY on %s\n", ast_channel_name(ast));
+		/* The other end is busy */
+		if (ast_channel_state(ast) != AST_STATE_UP) {
+			/* XXX We should play a busy tone here!! */
+			ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+			brcm_hangup(ast);
+			break;
+		}
+		res = -1;
+		break;
+	case AST_CONTROL_PROGRESS:
+		ast_debug(4, "Got PROGRESS on %s\n", ast_channel_name(ast));
+		/* Early media is coming our way */
+		/* What do we do with that? */
+		res = -1;
+		break;
+	case AST_CONTROL_NORMAL_DIALTONE:
+		brcm_dialtone_set(sub->parent, DIALTONE_ON);
+		break;
+	case AST_CONTROL_SPECIAL_DIALTONE:
+		brcm_dialtone_set(sub->parent, DIALTONE_SPECIAL_CONDITION);
+		break;
+	case AST_CONTROL_PVT_CAUSE_CODE: {
+		  const struct ast_control_pvt_cause_code *cause_code = data;
+		  int ast_cause = cause_code->ast_cause;
+		  ast_debug(2, "AST_CONTROL_PVT_CAUSE_CODE = %d, chan_name = %s\n", ast_cause, cause_code->chan_name);
+
+		  switch (ast_cause) {
+		  case AST_CAUSE_NO_USER_RESPONSE:
+		  case AST_CAUSE_NO_ANSWER:
+			  if (ast_channel_state(ast) != AST_STATE_UP) {
+				  endpt_signal(sub->parent->line_id, "congestion", "on", NULL);
+				  break;
+			  }
+			  res = -1;
+			  break;
+		  case AST_CAUSE_NORMAL_CLEARING:
+			  	  // This is just fine.
+			  	  break;
+		  case AST_CAUSE_USER_BUSY:
+				if (ast_channel_state(ast) != AST_STATE_UP) {
+					/* XXX We should play a busy tone here!! */
+					endpt_signal(sub->parent->line_id, "busy", "on", NULL);
+					break;
+				}
+				res = -1;
+		  default:
+			  ast_debug(1, "Don't know how to handle cause code %d\n", ast_cause);
+			  break;
+		  }
+		}
+		break;
+	default:
+		res = -1;
+		ast_debug(1, "Don't know how to indicate condition %d\n", condition);
+		break;
+	}
+	pvt_unlock(sub->parent);
+	return res;
+}
+
+static int brcm_transfer(struct ast_channel *ast, const char *newdest)
+{
+	struct brcm_pvt *pvt;
+	struct brcm_subchannel *subchan_active, *subchan_inactive;
+	struct ast_channel *chan_new;
+	char ext[strlen(newdest) + 1], *replaces;
+
+	ast_debug(1, "Transfer the existing call to [%s]\n", newdest);
+
+	// Get the other subchannel
+	subchan_active = ast_channel_tech_pvt(ast);
+	if (!subchan_active || !(pvt = subchan_active->parent) ||
+		!(subchan_inactive = brcm_subchannel_get_peer(subchan_active)) ||
+		!brcm_subchannel_is_idle(subchan_inactive)) {
+		return -1;
+	}
+
+	strncpy(ext, newdest, sizeof(ext));
+	replaces = strcasestr(ext, "?Replaces=");
+	if (replaces) {
+		*replaces = '\0';
+		replaces += strlen("?Replaces=");
+	}
+
+	if (!subchan_inactive->connection_init) {
+		subchan_inactive->connection_id = ast_atomic_fetchadd_int((int *)&current_connection_id, +1);
+		brcm_create_connection(subchan_inactive);
+	}
+
+	chan_new = brcm_new(subchan_inactive, AST_STATE_DOWN, ext, pvt->context, NULL, NULL, NULL);
+	if (!chan_new) {
+		ast_log(LOG_ERROR, "couldn't create channel\n");
+		return -1;
+	}
+
+	if (replaces)
+		pbx_builtin_setvar_helper(chan_new, "SIPTRANSFER_REPLACES", replaces);
+
+	ast_setstate(chan_new, AST_STATE_RING);
+	subchan_inactive->channel_state = TRANSFERING;
+
+	if (ast_pbx_start(chan_new)) {
+		ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(chan_new));
+		ast_hangup(chan_new);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int brcm_finish_transfer(struct ast_channel *owner, struct brcm_subchannel *p, int result)
+{
+	struct brcm_subchannel* peer_sub;
+	/*
+	 * We have received the result of a transfer operation.
+	 * This could be:
+	 * - Result of a Transfer-On-Hangup (Remote Transfer), in which case
+	 *   we should hangup the subchannel, no matter the result
+	 * - Result of a R4 Attended Transfer (Remote Transfer), in which case
+	 *   we should wait for hangup on both subchannels, or resume calls if failed
+	 *   Hangup should be received immediately, but we start a timer to hangup
+	 *   everything ourselves just to be sure.
+	 * - Probably nothing else - the built-in transfer should never let this
+	 *   control frame propagate to here
+	 */
+	if (p->channel_state != TRANSFERING) {
+		ast_log(LOG_WARNING, "Received AST_CONTROL_TRANSFER while in state %s\n", state2str(p->channel_state));
+		return -1;
+	}
+
+	peer_sub = brcm_subchannel_get_peer(p);
+	if (!peer_sub) {
+		ast_log(LOG_ERROR, "Failed to get peer subchannel\n");
+		return -1;
+	}
+
+	if (brcm_subchannel_is_idle(peer_sub)) {
+		// In the case of Transfer-On-Hangup peer sub should be a idle
+		if (result == AST_TRANSFER_SUCCESS) {
+			ast_log(LOG_NOTICE, "Remote transfer completed successfully, hanging up\n");
+		}
+		else {
+			ast_log(LOG_NOTICE, "Remote transfer failed, hanging up\n");
+		}
+		ast_queue_control(owner, AST_CONTROL_HANGUP);
+		p->channel_state = CALLENDED;
+	} else if (peer_sub->channel_state == ONHOLD) {
+		// In the case of R4 transfer peer sub should be on hold
+		if (result == AST_TRANSFER_SUCCESS) {
+			ast_log(LOG_NOTICE, "Remote transfer completed successfully, wait for remote hangup\n");
+			p->r4_hangup_timer_id = ast_sched_add(sched, r4hanguptimeout, r4hanguptimeout_cb, p);
+		} else {
+			//Do nothing. Let calls be up as they were before R4 was attempted (first call on hold, second call active)
+			ast_log(LOG_NOTICE, "Remote transfer failed\n");
+			p->channel_state = INCALL;
+		}
+	} else {
+		ast_log(LOG_WARNING, "AST_CONTROL_TRANSFER received in unexpected state\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*! \brief Incoming DTMF begin event */
+static int brcm_senddigit_begin(struct ast_channel *ast, char digit)
+{
+	struct brcm_subchannel *sub;
+	char signal[10];
+
+	//ast_debug(5, "DTMF send begin: %c\n", digit);
+
+	sub = ast_channel_tech_pvt(ast);
+	pvt_lock(sub->parent, "DTMF senddigit_begin");
+
+	snprintf(signal, sizeof(signal), "dtmf%c", digit);
+	endpt_signal(sub->parent->line_id, signal, "on", NULL);
+
+	pvt_unlock(sub->parent);
+	return 0;
+}
+
+/*! \brief Incoming DTMF end */
+static int brcm_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+	struct brcm_subchannel *sub;
+	char signal[10];
+
+	//ast_debug(5, "DTMF send end: %c, %d ms\n", digit, duration);
+
+	sub = ast_channel_tech_pvt(ast);
+	pvt_lock(sub->parent, "DTMF senddigit_end");
+
+	snprintf(signal, sizeof(signal), "dtmf%c", digit);
+	endpt_signal(sub->parent->line_id, signal, "off", NULL);
+
+	pvt_unlock(sub->parent);
+	return 0;
+}
+
+
+static int brcm_call(struct ast_channel *chan, const char *dest, int timeout)
+{
+	struct brcm_pvt *p;
+	struct brcm_subchannel *sub;
+	struct brcm_subchannel *sub_peer;
+
+	struct timeval UtcTime = ast_tvnow();
+	struct ast_tm tm;
+
+	sub = ast_channel_tech_pvt(chan);
+
+	ast_debug(1, "%s(): line %d\n", __func__, sub->parent->line_id);
+	ast_localtime(&UtcTime, &tm, NULL);
+
+	if ((ast_channel_state(chan) != AST_STATE_DOWN) && (ast_channel_state(chan) != AST_STATE_RESERVED)) {
+		ast_log(LOG_WARNING, "brcm_call called on %s, neither down nor reserved\n", ast_channel_name(chan));
+		return -1;
+	}
+
+	//ast_mutex_lock(&sub->parent->lock);
+	pvt_lock(sub->parent, "brcm_call");
+
+	p = sub->parent;
+	sub_peer = brcm_subchannel_get_peer(sub);
+	if (brcm_in_call(p) &&                          // a call is established
+			line_config[p->line_id].callwaiting &&  // call waiting active
+			!sub_peer->cw_rejected) {      // a previous call has not been rejected using R0
+		ast_debug(1, "Call waiting\n");
+		sub->channel_state = CALLWAITING;
+		brcm_signal_callwaiting(p);
+		int cwtimeout_ms = cwtimeout * 1000;
+		sub->cw_timer_id = ast_sched_add(sched, cwtimeout_ms, cwtimeout_cb, sub);
+	  	ast_setstate(chan, AST_STATE_RINGING);
+		ast_queue_control(chan, AST_CONTROL_RINGING);
+	}
+	else if (!brcm_subchannel_is_idle(sub_peer)) {
+		ast_debug(1, "Line is busy\n");
+		ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY);
+		ast_queue_control(chan, AST_CONTROL_BUSY);
+	}
+	else {
+		ast_debug(1, "Not call waiting\n");
+		sub->channel_state = RINGING;
+		if (!clip) {
+			p->tech->signal_ringing(p);
+		} else {
+			p->tech->signal_ringing_callerid_pending(p);
+			p->tech->signal_callerid(chan, sub);
+		}
+	  	ast_setstate(chan, AST_STATE_RINGING);
+		ast_queue_control(chan, AST_CONTROL_RINGING);
+	}
+	//ast_mutex_unlock(&sub->parent->lock);
+	pvt_unlock(sub->parent);
+
+	return 0;
+}
+
+static int brcm_hangup(struct ast_channel *ast)
+{
+	struct brcm_pvt *p;
+	struct brcm_subchannel *sub, *sub_peer;
+	sub = ast_channel_tech_pvt(ast);
+
+	if (!ast_channel_tech_pvt(ast)) {
+		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+		return 0;
+	}
+	pvt_lock(sub->parent, "TELCHAN hangup");
+
+	p = sub->parent;
+	sub_peer = brcm_subchannel_get_peer(sub);
+	ast_debug(1, "brcm_hangup(%s) line_id=%d connection_id=%d\n", ast_channel_name(ast), p->line_id, sub->connection_id);
+
+	if (sub->channel_state == CALLWAITING) {
+		brcm_stop_callwaiting(p);
+	} else if (sub_peer->channel_state == ONHOLD &&
+			sub_peer->onhold_hangup_timer_id != -1 && sub->channel_state == RINGING) {
+		ast_debug(2, "There is a forgotten onhold call, not releasing channel\n");
+		endpt_signal(sub->parent->line_id, "ringback", "off", NULL);
+	} else if (sub->channel_state == RINGING || sub->onhold_hangup_timer_id != -1) {
+		//Stop ringing if other end hungup before we answered
+		if (!clip) {
+			p->tech->stop_ringing(p);
+		} else {
+			p->tech->stop_ringing_callerid_pending(p);
+		}
+	} else if (brcm_subchannel_is_idle(sub_peer) && p->tech->release) {
+		//No active subchannel left, release
+		p->tech->release(p);
+	}
+
+	if (sub->cw_timer_id != -1) {
+		if (ast_sched_del(sched, sub->cw_timer_id)) {
+			ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n");
+		}
+		sub->cw_timer_id = -1;
+	}
+
+	if(sub->r4_hangup_timer_id != -1) {
+		if (ast_sched_del(sched, sub->r4_hangup_timer_id)) {
+			ast_log(LOG_WARNING, "Failed to remove scheduled r4 hangup timer\n");
+		}
+		sub->r4_hangup_timer_id = -1;
+	}
+
+	if(sub->onhold_hangup_timer_id != -1) {
+		if (ast_sched_del(sched, sub->onhold_hangup_timer_id)) {
+			ast_log(LOG_WARNING, "Failed to remove scheduled onhold hangup timer\n");
+		}
+		sub->onhold_hangup_timer_id = -1;
+	}
+	ast_setstate(ast, AST_STATE_DOWN);
+
+	p->lastformat = NULL;
+	p->lastinput = NULL;
+	p->hf_detected = 0;
+	sub->channel_state = CALLENDED;
+	if (terminate_conference && sub->conference_initiator && brcm_in_conference(p)) {
+		/* Switch still active call leg out of conference mode */
+		brcm_stop_conference(sub);
+		brcm_stop_conference(sub_peer);
+	}
+
+	memset(p->ext, 0, sizeof(p->ext));
+	sub->owner = NULL;
+	sub->conference_initiator = 0;
+	free(sub->conference_id);
+	sub->conference_id = NULL;
+	sub->cw_rejected = 0;
+	ast_module_unref(ast_module_info->self);
+	ast_verb(3, "Hungup '%s'\n", ast_channel_name(ast));
+	ast_channel_tech_pvt_set(ast, NULL);
+	brcm_close_connection(sub);
+
+	pvt_unlock(p);
+	return 0;
+}
+
+static int brcm_answer(struct ast_channel *ast)
+{
+	struct brcm_subchannel *sub = ast_channel_tech_pvt(ast);
+	struct brcm_pvt *pvt = sub->parent;
+
+	ast_debug(1, "brcm_answer(%s)\n", ast_channel_name(ast));
+
+	pvt_lock(pvt, "TELCHAN answer");
+
+	/* For attended call transfer, if the transfer target has accepted the call initiated by
+	 * us as the tranferee, we can terminate the original call with the transferor */
+	if (sub->channel_state == TRANSFERING) {
+		struct brcm_subchannel *peer = brcm_subchannel_get_peer(sub);
+
+		if (peer->owner) {
+			ast_debug(1, "Transferee terminates the original call with the transferor\n");
+			ast_queue_control(peer->owner, AST_CONTROL_HANGUP);
+			peer->owner = NULL;
+			peer->channel_state = CALLENDED;
+			brcm_close_connection(peer);
+		}
+	}
+
+	if (ast_channel_state(ast) != AST_STATE_UP) {
+		ast_setstate(ast, AST_STATE_UP);
+		ast_debug(2, "brcm_answer(%s) set state to up\n", ast_channel_name(ast));
+	}
+	ast_channel_rings_set(ast, 0);
+	sub->channel_state = INCALL;
+	endpt_signal(pvt->line_id, "ringback", "off", NULL);
+
+	pvt_unlock(pvt);
+
+	return 0;
+}
+
+
+// Hangup ALL brcm lines currently ringing with busy casue.
+static int brcm_busy_all_ringers(void)
+{
+	struct brcm_pvt *pvt;
+	int i;
+
+	brcm_lock_pvts();
+	pvt = iflist;
+
+	while(pvt) {	
+		for (i=0; i<NUM_SUBCHANNELS; i++) {
+			if (pvt->sub[i] && pvt->sub[i]->owner && pvt->sub[i]->channel_state == RINGING) {
+				ast_debug(4, "Hangup BUSY on %s\n", ast_channel_name(pvt->sub[i]->owner));
+				ast_queue_control(pvt->sub[i]->owner, AST_CONTROL_BUSY);
+				ast_queue_hangup_with_cause(pvt->sub[i]->owner, AST_CAUSE_USER_BUSY);
+			}
+		}
+
+		pvt = brcm_get_next_pvt(pvt);
+	}
+
+	brcm_unlock_pvts();
+
+	return 0;
+}
+
+
+/*
+* Map RTP data header value to a codec name
+*/
+static char* brcm_get_codec_string(int id) {
+	switch (id) {
+			case PCMA:		return "alaw"; break;
+			case PCMU:		return "ulaw"; break;
+			case G723:		return "g723.1"; break;
+			case G726:		return "g726"; break;
+			case G729:		return "g729"; break;
+			case G722:		return "g722"; break;
+			case -1:		return "none set"; break;
+			default: 		return "unknown id"; break;
+	}
+}
+
+static int brcm_classify_rtp_packet(int id) {
+
+	//ast_debug(5, "RTP Packet ID = %d\n", id);
+
+	switch (id) {
+		case PCMU: return BRCM_AUDIO;
+		case G726: return BRCM_AUDIO;
+		case G723: return BRCM_AUDIO;
+		case PCMA: return BRCM_AUDIO;
+		case G729: return BRCM_AUDIO;
+		case G722: return BRCM_AUDIO;
+		case RTCP: return BRCM_UNKNOWN;											// Ignored packet.
+		default:
+			ast_verbose("Unknown rtp packet id %d\n", id);
+			return BRCM_UNKNOWN;
+	}
+}
+
+static int map_ast_codec_id_to_rtp(const struct ast_format *astcodec)
+{
+	if (ast_format_cmp(astcodec, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
+		return PCMA;
+	} else if (ast_format_cmp(astcodec, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
+		return PCMU;
+	} else if (ast_format_cmp(astcodec, ast_format_g722) == AST_FORMAT_CMP_EQUAL) {
+		return G722;
+	} else if (ast_format_cmp(astcodec, ast_format_g723) == AST_FORMAT_CMP_EQUAL) {
+		return G723;
+	} else if (ast_format_cmp(astcodec, ast_format_g729) == AST_FORMAT_CMP_EQUAL) {
+		return G729;
+	} else if (ast_format_cmp(astcodec, ast_format_g726) == AST_FORMAT_CMP_EQUAL) {
+		return G726;
+	} else {
+		ast_verbose("Unknown asterisk format/codec id\n");
+		return PCMA;
+	}
+}
+
+
+static struct ast_frame  *brcm_read(struct ast_channel *ast)
+{
+	return &ast_null_frame;
+}
+
+/* Handle stream events on audio_bus. Parses raw stream and calls 
+   registered packet handlers. */
+static void audio_rx_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event __attribute__((unused))) {
+	if (pe_bus_receive(audio_rx_bus, event) < 0) {
+		printf("audio_bus rx buffer full");
+		return;																	// Drop packets if we can't cope the pace
+	}
+
+	pe_bus_dispatch(audio_rx_bus);
+}
+
+static int brcm_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+	struct brcm_subchannel *sub = ast_channel_tech_pvt(ast);
+	int packet_size;
+	audio_packet_t *ap;
+
+	if (ast_channel_state(ast) != AST_STATE_UP && ast_channel_state(ast) != AST_STATE_RING) {
+		/* Silently ignore packets until channel is up */
+		ast_debug(5, "error: channel not up\n");
+		return 0;
+	}
+
+	/* Ignore if on hold */
+	if (sub->channel_state == ONHOLD) {
+		return 0;
+	}
+
+	if(frame->frametype == AST_FRAME_VOICE) {
+		packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen;
+		ap = calloc(1, packet_size);
+		if (!ap) {
+			printf("Out of memory\n");
+			return -1;
+		}
+
+		ap->line = sub->parent->line_id;
+		ap->cnx_id = sub->parent->line_id;
+		ap->rtp_size = RTP_HEADER_SIZE + frame->datalen;
+
+		/* copy frame data to audio packet */
+		memcpy(ap->rtp + RTP_HEADER_SIZE, frame->data.ptr, frame->datalen);
+
+		//ast_mutex_lock(&sub->parent->lock);
+		pvt_lock(sub->parent, "TELCHAN write frame");
+
+		/* generate the rtp header */
+		brcm_generate_rtp_packet(sub, ap->rtp, map_ast_codec_id_to_rtp(frame->subclass.format), 0, 0);
+
+		/* set rtp id sent to endpoint */
+		sub->codec = map_ast_codec_id_to_rtp(frame->subclass.format);
+
+		//ast_mutex_unlock(&sub->parent->lock);
+		pvt_unlock(sub->parent);
+
+		pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size);
+		free(ap);
+	}
+
+	return 0;
+}
+
+static void brcm_reset_dtmf_buffer(struct brcm_pvt *p)
+{
+	memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf));
+	p->dtmf_len = 0;
+	p->dtmf_first = -1;
+	p->dtmfbuf[p->dtmf_len] = '\0';
+}
+
+static char *state2str(enum channel_state state)
+{
+	switch (state) {
+	case ONHOOK:		return "ONHOOK";
+	case OFFHOOK:		return "OFFHOOK";
+	case DIALING:		return "DIALING";
+	case CALLING:		return "CALLING";
+	case INCALL:		return "INCALL";
+	case ANSWER:		return "ANSWER";
+	case CALLENDED:		return "CALLENDED";
+	case RINGING:		return "RINGING";
+	case CALLWAITING:	return "CALLWAITING";
+	case ONHOLD:		return "ONHOLD";
+	case TRANSFERING:	return "TRANSFERING";
+	case RINGBACK:		return "RINGBACK";
+	case AWAITONHOOK:	return "AWAITONHOOK";
+	default:			return "UNKNOWN";
+	}
+}
+
+static int brcm_subchannel_is_idle(const struct brcm_subchannel *sub)
+{
+	if (sub->channel_state == ONHOOK || sub->channel_state == CALLENDED) {
+		return 1;
+	}
+	return 0;
+}
+
+struct brcm_subchannel *brcm_subchannel_get_peer(const struct brcm_subchannel *sub)
+{
+	struct brcm_subchannel *peer_sub;
+	peer_sub = (sub->parent->sub[0] == sub) ? sub->parent->sub[1] : sub->parent->sub[0];
+	return peer_sub;
+}
+
+/* Tell endpoint to play country specific dialtone. */
+static int brcm_signal_dialtone(struct brcm_pvt *p) {
+	
+	switch (p->dialtone) {
+		case DIALTONE_OFF:
+			endpt_signal(p->line_id, "dial", "off", NULL);
+			endpt_signal(p->line_id, "stutter", "off", NULL);
+			break;
+		case DIALTONE_ON:
+			endpt_signal(p->line_id, "dial", "on", NULL);
+			break;
+		case DIALTONE_CONGESTION:
+			endpt_signal(p->line_id, "congestion", "on", NULL);
+			break;
+		case DIALTONE_SPECIAL_CONDITION:
+			endpt_signal(p->line_id, "stutter", "on", NULL);
+			break;
+		default:
+			ast_log(LOG_ERROR, "Requested to signal unknown dialtone\n");
+			return -1;
+	}
+	return 0;
+}
+
+int brcm_stop_dialtone(struct brcm_pvt *p) {
+	endpt_signal(p->line_id, "dial", "off", NULL);
+	endpt_signal(p->line_id, "stutter", "off", NULL);
+
+	return 0;
+}
+
+/* Tell endpoint to play country specific congestion tone */
+int brcm_signal_congestion(struct brcm_pvt *p) {
+	endpt_signal(p->line_id, "stutter", "on", NULL);
+
+	return 0;
+}
+
+static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, const char *ext, const char *context,
+		const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor,
+		struct ast_format_cap *format)
+{
+	struct ast_channel *chan;
+	struct ast_format *fmt;
+	struct ast_format_cap *caps;
+
+	caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (!caps) {
+		return NULL;
+	}
+	
+	chan = ast_channel_alloc(1, state, subchan->parent->cid_num, subchan->parent->cid_name, "", ext, context,
+			assignedids, requestor, 0, "TELCHAN/%d/%d", subchan->parent->line_id, subchan->connection_id);
+	if (chan) {
+		ast_channel_tech_set(chan, cur_tech);
+
+		ast_format_cap_append_from_cap(caps, default_cap, AST_MEDIA_TYPE_UNKNOWN);	
+		ast_channel_nativeformats_set(chan, caps);
+		ao2_ref(caps, -1);
+		fmt = ast_format_cap_get_format(ast_channel_nativeformats(chan), 0);
+
+		line_settings *s = &line_config[subchan->parent->line_id];
+
+		/* set codecs */
+		ast_channel_set_writeformat(chan, fmt);
+		ast_channel_set_rawwriteformat(chan, fmt);
+		ast_channel_set_readformat(chan, fmt);
+		ast_channel_set_rawreadformat(chan, fmt);
+
+		/* no need to call ast_setstate: the channel_alloc already did its job */
+		if (state == AST_STATE_RING)
+			ast_channel_rings_set(chan, 1);
+		ast_channel_tech_pvt_set(chan, subchan);
+
+		/* Don't use ast_set_callerid() here because it will
+		 * generate a NewCallerID event before the NewChannel event */
+		if (!ast_strlen_zero(subchan->parent->cid_num)) {
+			ast_channel_caller(chan)->ani.number.valid = 1;
+			ast_channel_caller(chan)->ani.number.str = ast_strdup(subchan->parent->cid_num);
+		}
+
+		ast_channel_caller(chan)->id.number.presentation = s->clir ? AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED : AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+		ast_channel_caller(chan)->id.name.presentation = s->clir ? AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED : AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+
+		//Setup jitter buffer
+		ast_jb_configure(chan, &global_jbconf);
+		subchan->owner = chan;
+
+		ast_module_ref(ast_module_info->self);
+
+		if (state != AST_STATE_DOWN) {
+			if (ast_pbx_start(chan)) {
+				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(chan));
+				ast_hangup(chan);
+				return NULL;
+			}
+		}
+	} else
+		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+
+	ast_channel_unlock(chan);
+
+	return chan;
+}
+
+
+static struct brcm_pvt* brcm_get_next_pvt(struct brcm_pvt *p) {
+	if (p && p->next)
+		return p->next;
+	else
+		return NULL;
+}
+
+struct brcm_pvt* brcm_get_pvt_from_lineid(struct brcm_pvt *p, int line_id)
+{
+	struct brcm_pvt *tmp = p;
+	if (p && p->line_id == line_id) return p;
+	tmp = brcm_get_next_pvt(tmp);
+
+	while(tmp) {
+		if (tmp->line_id == line_id) return tmp;
+		tmp = brcm_get_next_pvt(tmp);
+	}
+	return NULL;
+}
+
+struct brcm_subchannel* brcm_get_active_subchannel(const struct brcm_pvt *p)
+{
+	struct brcm_subchannel *sub = NULL;
+	int i;
+
+	if(!p) return NULL;
+
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		switch (p->sub[i]->channel_state) {
+			case INCALL:
+			case DIALING:
+			case CALLING:
+			case OFFHOOK:
+			case AWAITONHOOK:
+			case RINGING:
+			case TRANSFERING:
+			case RINGBACK:
+				sub = p->sub[i];
+				return sub;
+			case CALLWAITING:
+			case ONHOLD:
+				break;
+			case ONHOOK:
+			case ANSWER: //Remove this state
+			case CALLENDED:
+				if (!sub) {
+					sub = p->sub[i];
+				}
+				break;
+			default:
+				ast_log(LOG_WARNING, "Unhandled channel state %d\n", sub->channel_state);
+				break;
+		}
+	}
+
+	return sub;
+}
+
+static struct brcm_subchannel *brcm_get_onhold_subchannel(const struct brcm_pvt *p)
+{
+	struct brcm_subchannel *sub;
+	int i;
+	for(i=0; i<NUM_SUBCHANNELS; i++) {
+			sub = p->sub[i];
+			if (sub->channel_state == ONHOLD) {
+				return sub;
+			}
+	}
+	return NULL;
+}
+
+/* Hangup incoming call after call waiting times out */
+static int cwtimeout_cb(const void *data)
+{
+	struct brcm_subchannel *sub;
+	struct ast_channel *owner = NULL;
+
+	ast_debug(2, "No response to call waiting, hanging up\n");
+
+	sub = (struct brcm_subchannel *) data;
+	//ast_mutex_lock(&sub->parent->lock);
+	pvt_lock(sub->parent, "Cwtimeout callback");
+	sub->cw_timer_id = -1;
+	if (sub->owner) {
+		ast_channel_ref(sub->owner);
+		owner = sub->owner;
+	}
+	//ast_mutex_unlock(&sub->parent->lock);
+	pvt_unlock(sub->parent);
+
+	if (owner) {
+		ast_channel_lock(owner);
+		ast_channel_hangupcause_set(owner, AST_CAUSE_USER_BUSY);
+		ast_queue_control(owner, AST_CONTROL_BUSY);
+		ast_channel_unlock(owner);
+		ast_channel_unref(owner);
+	}
+
+	return 0;
+}
+
+/* Hangup calls if not done by remote after R4 transfer */
+static int r4hanguptimeout_cb(const void *data)
+{
+	struct brcm_subchannel *sub;
+	struct brcm_subchannel *peer_sub;
+
+	struct ast_channel *sub_owner = NULL;
+	struct ast_channel *peer_sub_owner = NULL;
+
+	ast_debug(2, "No hangup from remote after remote transfer using R4, hanging up\n");
+
+	sub = (struct brcm_subchannel *) data;
+	peer_sub = brcm_subchannel_get_peer(sub);
+
+	//ast_mutex_lock(&sub->parent->lock);
+	pvt_lock(sub->parent, "r4hanguptimeout callback");
+
+	sub->r4_hangup_timer_id = -1;
+	peer_sub->channel_state = CALLENDED;
+	sub->channel_state = CALLENDED;
+
+	if (sub->owner) {
+		ast_channel_ref(sub->owner);
+		sub_owner = sub->owner;
+	}
+	if (peer_sub->owner) {
+		ast_channel_ref(peer_sub->owner);
+		peer_sub_owner = peer_sub->owner;
+	}
+	//ast_mutex_unlock(&sub->parent->lock);
+	pvt_unlock(sub->parent);
+
+	if (sub_owner) {
+		ast_queue_control(sub_owner, AST_CONTROL_HANGUP);
+		ast_channel_unref(sub_owner);
+	}
+
+	if (peer_sub_owner) {
+		ast_queue_control(peer_sub_owner, AST_CONTROL_HANGUP);
+		ast_channel_unref(peer_sub_owner);
+	}
+
+	return 0;
+}
+
+/* Hangup call onhold if user does not pick up after reminder ringing */
+static int onholdhanguptimeout_cb(const void *data)
+{
+	struct brcm_subchannel *sub;
+	struct ast_channel *sub_owner = NULL;
+
+	ast_debug(2, "No pickup after reminder ringing for call on hold, hanging up\n");
+	sub = (struct brcm_subchannel *) data;
+
+	//ast_mutex_lock(&sub->parent->lock);
+	pvt_lock(sub->parent, "onholdhanguptimeout callback");
+
+	sub->onhold_hangup_timer_id = -1;
+
+	if (sub->owner) {
+		ast_channel_ref(sub->owner);
+		sub_owner = sub->owner;
+	}
+	//ast_mutex_unlock(&sub->parent->lock);
+	pvt_unlock(sub->parent);
+
+	if (sub_owner) {
+		ast_queue_control(sub_owner, AST_CONTROL_HANGUP);
+		ast_channel_unref(sub_owner);
+	}
+
+	return 0;
+}
+
+/*
+ * Helper function that tells asterisk to start a call on the provided pvt/sub/context
+ * using the content of the dtmf buffer.
+ */
+static void brcm_start_calling(struct brcm_pvt *p, struct brcm_subchannel *sub, char* context)
+{
+	struct ast_channel *c = (struct ast_channel *)sub->parent;
+
+	sub->channel_state = CALLING;
+	strncpy(p->ext, p->dtmfbuf, sizeof(p->dtmfbuf));
+	ast_channel_exten_set(c, p->ext);
+	ast_channel_context_set(c, context);
+	ast_debug(1, "Calling %s@%s\n", ast_channel_exten(c), ast_channel_context(c));
+
+	/* Reset the dtmf buffer */
+	brcm_reset_dtmf_buffer(p);
+
+	/* Reset hook flash state */
+	p->hf_detected = 0;
+
+	/* Start the pbx */
+	if (!sub->connection_init) {
+		sub->connection_id = ast_atomic_fetchadd_int((int *)&current_connection_id, +1);
+		brcm_create_connection(sub);
+	}
+
+	/* Changed state from AST_STATE_UP to AST_STATE_RING ito get the brcm_answer callback
+	 * which is needed for call waiting. */
+	brcm_new(sub, AST_STATE_RING, sub->parent->ext, sub->parent->context, NULL, NULL, NULL);
+}
+
+/*
+ * Start calling if we have a (partial) match in asterisks dialplan after an interdigit timeout.
+ * Called on scheduler thread.
+ */
+static int handle_interdigit_timeout(const void *data)
+{
+	ast_debug(9, "Interdigit timeout\n");
+	struct brcm_pvt *p = (struct brcm_pvt *) data;
+	//ast_mutex_lock(&p->lock);
+	pvt_lock(p, "interdigit callback");
+	p->interdigit_timer_id = -1;
+	struct brcm_subchannel *sub = brcm_get_active_subchannel(p);
+
+	if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num))
+	{
+		//We have at least one matching extension in "normal" context,
+		//and interdigit timeout has passed, so have asterisk start calling.
+		//Asterisk will select the best matching extension if there are more than one possibility.
+		ast_debug(9, "Interdigit timeout, extension(s) matching %s found\n", p->dtmfbuf);
+		brcm_start_calling(p, sub, p->context);
+	}
+	//ast_mutex_unlock(&p->lock);
+	pvt_unlock(p);
+	return 0;
+}
+
+/*
+ * Reset hook flash state after an interdigit timeout.
+ * Called on scheduler thread.
+ */
+static int handle_hookflash_timeout(const void *data)
+{
+	ast_debug(9, "Hook flash timeout, clear hook flash\n");
+	struct brcm_pvt *p = (struct brcm_pvt *) data;
+
+	//ast_mutex_lock(&p->lock);
+	pvt_lock(p, "hookflash callback");
+	p->interdigit_timer_id = -1;
+	p->hf_detected = 0;
+	//ast_mutex_unlock(&p->lock);
+	pvt_unlock(p);
+
+	return 0;
+}
+
+/*
+ * Start autodialing if we have an autodial extension.
+ * Called on scheduler thread.
+ */
+static int handle_autodial_timeout(const void *data)
+{
+	ast_debug(9, "Autodial timeout\n");
+	struct brcm_pvt *p = (struct brcm_pvt *) data;
+	pvt_lock(p, "autodial timeout");
+	//ast_mutex_lock(&p->lock);
+	p->autodial_timer_id = -1;
+	struct brcm_subchannel *sub = brcm_get_active_subchannel(p);
+	line_settings *s = &line_config[p->line_id];
+
+	if (ast_exists_extension(NULL, p->context, s->autodial_ext, 1, p->cid_num))
+	{
+		brcm_stop_dialtone(p);
+		ast_copy_string(p->dtmfbuf, s->autodial_ext, sizeof(p->dtmfbuf));
+		ast_debug(9, "Autodialing extension: %s\n", p->dtmfbuf);
+		brcm_start_calling(p, sub, p->context);
+	}
+	//ast_mutex_unlock(&p->lock);
+	pvt_unlock(p);
+	return 0;
+}
+
+/*
+ * Dialtone expired, play congestion tone
+ * Called on scheduler thread.
+ */
+static int handle_dialtone_timeout(const void *data)
+{
+	ast_debug(9, "Dialtone timeout\n");
+	struct brcm_pvt *p = (struct brcm_pvt *) data;
+
+	pvt_lock(p, "dialtone timeout");
+	//ast_mutex_lock(&p->lock);
+	p->dialtone_timeout_timer_id = -1;
+
+	struct brcm_subchannel *sub = brcm_get_active_subchannel(p);
+	if (sub && sub->channel_state == OFFHOOK) {
+		/* Enter state where nothing else than ONHOOK is accepted and play congestion tone */
+		sub->channel_state = AWAITONHOOK;
+		brcm_signal_congestion(p);
+	}
+
+	//ast_mutex_unlock(&p->lock);
+	pvt_unlock(p);
+	return 0;
+}
+
+/*
+ * Start calling if we have a match in asterisks dialplan.
+ * Called after each new DTMF event, from monitor_events thread,
+ * with the required locks already held.
+ */
+void handle_dtmf_calling(struct brcm_subchannel *sub)
+{
+	struct brcm_pvt *p = sub->parent;
+	int dtmfbuf_len = strlen(p->dtmfbuf);
+	char dtmf_last_char = p->dtmfbuf[(dtmfbuf_len - 1)];
+
+	if (ast_exists_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num) && !ast_matchmore_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num))
+	{
+		//We have a full match in the "direct" context, so have asterisk place a call immediately
+		ast_debug(9, "Direct extension matching %s found\n", p->dtmfbuf);
+		brcm_start_calling(p, sub, p->context_direct);
+	}
+	else if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num) && dtmf_last_char == 0x23 && feature_access_code_match(p->dtmfbuf) != 1) {
+		//We have a match in the "normal" context, and user ended the dialling sequence with a #,
+		//so have asterisk place a call immediately if sequence is not partially matching a feature access code
+		ast_debug(9, "Pound-key pressed during dialling, extension %s found\n", p->dtmfbuf);
+		brcm_start_calling(p, sub, p->context);
+	}
+	else if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num) && !ast_matchmore_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num))
+	{
+		//We have a full match in the "normal" context, so have asterisk place a call immediately,
+		//since no more digits can be added to the number
+		//(this is unlikely to happen since there is probably a "catch-all" extension)
+		ast_debug(9, "Unique extension matching %s found\n", p->dtmfbuf);
+		brcm_start_calling(p, sub, p->context);
+	}
+	else {
+		//No matches. We schedule a (new) interdigit timeout to occur
+		int timeoutmsec = line_config[p->line_id].timeoutmsec;
+		ast_debug(9, "Scheduling new interdigit timeout in %d msec\n", timeoutmsec);
+		p->interdigit_timer_id = ast_sched_add(sched, timeoutmsec, handle_interdigit_timeout, p);
+	}
+}
+
+/*
+ * Perform actions for hook flash.
+ * Preconditions: One subchannel should be in CALLWAITING or ONHOLD,
+ * 		  One subchannel should be in INCALL.
+ * 		  channel locks are held
+ *		  brcm_pvt->lock is held
+ */
+static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer,
+		struct ast_channel *owner, struct ast_channel *peer_owner)
+{
+	struct brcm_pvt *p = sub->parent;
+
+	ast_log(LOG_DTMF, "Hook Flash detected for phone line %d\r\n", sub->parent->line_id);
+
+	if (p->dtmf_first < 0) {
+		/* If current subchannel is in call and peer subchannel is idle, provide dialtone */
+		if (sub->channel_state == INCALL && (sub_peer->channel_state == ONHOOK || sub_peer->channel_state == CALLENDED)) {
+			ast_debug(2, "R while in call and idle peer subchannel\n");
+
+			brcm_cancel_dialing_timeouts(p);
+			brcm_reset_dtmf_buffer(p);
+			p->hf_detected = 0;
+
+			/* Put current call on hold */
+			if (owner) {
+				brcm_mute_connection(sub);
+				sub->channel_state = ONHOLD;
+				ast_queue_hold(owner, NULL);
+			}
+
+			/* Provide new line */
+			brcm_signal_dialtone(p);
+			sub_peer->channel_state = OFFHOOK;
+
+		/* If offhook/dialing/calling and peer subchannel is on hold, switch call */
+		} else if ((sub->channel_state == DIALING ||
+				sub->channel_state == OFFHOOK ||
+				sub->channel_state == AWAITONHOOK ||
+				sub->channel_state == CALLING ||
+				sub->channel_state == RINGBACK)
+				&& sub_peer->channel_state == ONHOLD) {
+
+			ast_debug(2, "R while offhook/dialing and peer subchannel on hold\n");
+
+			brcm_cancel_dialing_timeouts(p);
+			brcm_reset_dtmf_buffer(p);
+			p->hf_detected = 0;
+
+			if (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK) {
+				brcm_stop_dialtone(p);
+			}
+			sub->channel_state = ONHOOK;
+
+			/* Hang up current */
+			if(owner) {
+				ast_queue_control(owner, AST_CONTROL_HANGUP);
+			}
+
+			/* Pick up old */
+			if (peer_owner) {
+				brcm_unmute_connection(sub_peer);
+				ast_queue_unhold(peer_owner);
+				sub_peer->channel_state = INCALL;
+			}
+
+		/* Switch back to old call (remote hung up) */
+		} else if ((sub->channel_state == ONHOOK || sub->channel_state == CALLENDED)
+				&& sub_peer->channel_state == ONHOLD) {
+
+			ast_debug(2, "R when idle and peer subchannel on hold\n");
+
+			brcm_cancel_dialing_timeouts(p);
+			p->hf_detected = 0;
+
+			/* Hang up current */
+			if (owner) {
+				ast_queue_control(owner, AST_CONTROL_HANGUP);
+			}
+
+			/* Pick up old */
+			if (peer_owner) {
+				brcm_unmute_connection(sub_peer);
+				ast_queue_unhold(peer_owner);
+				sub_peer->channel_state = INCALL;
+			}
+		}
+
+		return;
+	}
+
+	switch (p->dtmf_first) {
+		/* Force busy on waiting call or hang up call on hold */
+		case '0':
+			if (sub->channel_state == INCALL && sub_peer->channel_state == CALLWAITING) {
+				ast_debug(2, "Sending busy to waiting call\n");
+
+				/* Immediately send busy next time someone calls us during this call */
+				sub->cw_rejected = 1;
+
+				if (ast_sched_del(sched, sub_peer->cw_timer_id)) {
+					ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n");
+				}
+				sub_peer->cw_timer_id = -1;
+
+				ast_queue_control(peer_owner, AST_CONTROL_BUSY);
+				brcm_busy_all_ringers();
+				ast_indicate(owner, AST_CONTROL_UNHOLD);
+			} else if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) {
+				ast_debug(2, "Hanging up call on hold\n");
+
+				sub_peer = brcm_get_onhold_subchannel(p);
+
+				ast_queue_control(peer_owner, AST_CONTROL_HANGUP);
+				sub_peer->channel_state = CALLENDED;
+				brcm_close_connection(sub_peer);
+			}
+			break;
+
+		/* Hangup current call and answer waiting call */
+		case '1':
+			if (sub->channel_state == INCALL && (sub_peer->channel_state == CALLWAITING || sub_peer->channel_state == ONHOLD)) {
+
+				/* Close connection and hangup active subchannel */
+				if (owner) {
+					ast_queue_control(owner, AST_CONTROL_HANGUP);
+				}
+				sub->channel_state = CALLENDED;
+				brcm_close_connection(sub);
+
+				if (sub_peer->channel_state == CALLWAITING) {
+					ast_log(LOG_WARNING, "R1 call waiting\n");
+					/* Stop call waiting tone on current call */
+					brcm_stop_callwaiting(p);
+
+					if (ast_sched_del(sched, sub_peer->cw_timer_id)) {
+						ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n");
+					}
+					sub_peer->cw_timer_id = -1;
+
+					/* Pick up call waiting */
+					if (!sub_peer->connection_init) {
+						ast_debug(9, "create_connection()\n");
+						brcm_create_connection(sub_peer);
+					}
+					if (peer_owner) {
+						ast_queue_control(peer_owner, AST_CONTROL_ANSWER);
+						sub_peer->channel_state = INCALL;
+					}
+				} else if (sub_peer->channel_state == ONHOLD) {
+					ast_log(LOG_WARNING, "R1 Unholding\n");
+
+					/* Unhold inactive subchannel */
+					if (peer_owner) {
+						brcm_unmute_connection(sub_peer);
+						ast_queue_unhold(peer_owner);
+						sub_peer->channel_state = INCALL;
+					}
+				}
+			}
+			break;
+
+		/* Answer waiting call and put other call on hold (switch calls) */
+		case '2':
+			if (sub->channel_state == INCALL && (sub_peer->channel_state == CALLWAITING || sub_peer->channel_state == ONHOLD)) {
+
+				brcm_mute_connection(sub);
+				if (owner) {
+					ast_queue_hold(owner, NULL);
+				}
+
+				if (sub_peer->channel_state == CALLWAITING) {
+					ast_log(LOG_WARNING, "R2 Call waiting\n");
+
+					/* Stop call waiting tone on current call */
+					brcm_stop_callwaiting(p);
+
+					/* Cancel timer */
+					if (ast_sched_del(sched, sub_peer->cw_timer_id)) {
+						ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n");
+					}
+					sub_peer->cw_timer_id = -1;
+
+					/* Pick up call waiting */
+					if (!sub_peer->connection_init) {
+						ast_debug(9, "create_connection()\n");
+						brcm_create_connection(sub_peer);
+					}
+					if (peer_owner) {
+						ast_queue_control(peer_owner, AST_CONTROL_ANSWER);
+						sub_peer->channel_state = INCALL;
+					}
+				} else if (sub_peer->channel_state == ONHOLD) {
+					ast_log(LOG_WARNING, "R2 on hold\n");
+
+					/* Unhold inactive subchannel */
+					if (peer_owner) {
+						brcm_unmute_connection(sub_peer);
+						ast_queue_unhold(peer_owner);
+						sub_peer->channel_state = INCALL;
+					}
+				}
+
+				sub->channel_state = ONHOLD;
+			}
+			break;
+
+		/* Connect waiting call to existing call to create 3-way */
+		case '3':
+			if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) {
+				ast_debug(2, "DTMF3 after HF\n");
+				brcm_create_conference(p);
+			}
+			break;
+
+		/* Remote transfer held call to active call */
+		case '4':
+			ast_debug(2, "R4 Transfer\n");
+			if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD &&
+				owner && peer_owner) {
+				struct ast_channel *bridged_chan_inactive = ast_channel_bridge_peer(peer_owner);
+				struct ast_channel *bridged_chan_active = ast_channel_bridge_peer(owner);
+				char dest[AST_CHANNEL_NAME * 2] = { '\0', };
+
+				if (bridged_chan_inactive && bridged_chan_active) {
+					// Hold the transfer target by default unless being configured no
+					if (hold_target_before_refer)
+						ast_queue_hold(owner, NULL);
+
+					// Start the transfer by sending REFER to the transferee
+					snprintf(dest, sizeof(dest) - 1, "%s?Replaces=%s",
+							ast_channel_exten(owner), ast_channel_name(bridged_chan_active));
+					ast_debug(1, "Start transfer to [%s] on channel %s\n", dest, ast_channel_name(bridged_chan_active));
+					int res = -1;
+					ast_channel_lock(bridged_chan_inactive);
+					if (!ast_test_flag(ast_channel_flags(bridged_chan_inactive), AST_FLAG_ZOMBIE) &&
+						!ast_check_hangup(bridged_chan_inactive)) {
+						if (ast_channel_tech(bridged_chan_inactive)->transfer) {
+							res = ast_channel_tech(bridged_chan_inactive)->transfer(bridged_chan_inactive, dest);
+							if (res == 0)
+								res = 1;
+						} else
+							res = 0;
+					}
+					ast_channel_unlock(bridged_chan_inactive);
+					if (res < 0) {
+						ast_log(LOG_ERROR, "ast_transfer() failed\n");
+					} else if (res == 0) {
+						ast_log(LOG_ERROR, "ast_transfer() is not supported on the peer channel\n");
+					} else {
+						sub->channel_state = TRANSFERING;
+					}
+				} else {
+					ast_log(LOG_ERROR, "can't get the peer channel\n");
+				}
+			}
+			break;
+
+		default:
+			ast_log(LOG_NOTICE, "Unhandled DTMF %c\n", p->dtmfbuf[0]);
+			break;
+	}
+
+	brcm_reset_dtmf_buffer(p);
+}
+
+static void handle_dtmf(enum LINE_EVENT event,
+		struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer,
+		struct ast_channel *owner, struct ast_channel *peer_owner)
+{
+	struct brcm_pvt *p;
+	const DTMF_CHARNAME_MAP *dtmfMap = dtmf_to_charname;
+	struct timeval tim;
+
+	/* Lookup event to find corresponding DTMF */
+	while (dtmfMap->event != event) {
+		dtmfMap++;
+		if (dtmfMap->event == EVENT_LAST) {
+			/* DTMF not found. Should not be reached. */
+			ast_log(LOG_WARNING, "Failed to handle DTMF. Event not found.\n");
+			return;
+		}
+	}
+
+	char dtmf_button = dtmfMap->c;
+	gettimeofday(&tim, NULL);
+	p = sub->parent;
+
+	if (p->dtmf_first < 0) {
+		p->dtmf_first = dtmf_button;
+		ast_debug(9,"Pressed DTMF %s\n", dtmfMap->name);
+		if (owner && brcm_should_relay_dtmf(sub)) {
+			// INCALL
+			send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_BEGIN);
+		}
+
+		/* Do not send AST_FRAME_DTMF_BEGIN to allow DSP-generated tone to pass through */
+	}
+	else if (p->dtmf_first == dtmf_button) {
+		ast_debug(9,"Depressed DTMF %s\n", dtmfMap->name);
+		//ast_log(LOG_DTMF, "Detected DTMF %c, line %d\n", dtmfMap->c, sub->parent->line_id);
+
+		if (p->hf_detected) {
+			ast_debug(2, "DTMF after HF\n");
+			p->hf_detected = 0;
+			if (sub->channel_state == INCALL &&
+				(brcm_in_callwaiting(p) || brcm_in_onhold(p) || brcm_in_conference(p))) {
+				handle_hookflash(sub, sub_peer, owner, peer_owner);
+			} else {
+				/* HF while not in a call doesn't make sense */
+				ast_debug(2, "DTMF after HF while not in call. \
+						state: %d, \
+						callwaiting: %d, \
+						onhold: %d, \
+						conference: %d\n",
+					sub->channel_state,
+					brcm_in_callwaiting(p),
+					brcm_in_onhold(p),
+					brcm_in_conference(p));
+			}
+		} else {
+			//ast_debug(5,"DTMF %s in state %s.\n", dtmfMap->name, state2str(sub->channel_state));
+			p->dtmfbuf[p->dtmf_len] = dtmf_button;
+			p->dtmf_len++;
+			p->dtmfbuf[p->dtmf_len] = '\0';
+			p->dtmf_first = -1;
+			if (sub->channel_state == OFFHOOK) {
+				sub->channel_state = DIALING;
+				//ast_debug(5,"Set to state %s.\n", state2str(sub->channel_state));
+			}
+			else if (sub->channel_state != INCALL) {
+				struct ast_frame f = { 0, };
+				f.subclass.integer = dtmf_button;
+				f.src = "TELCHAN";
+				f.frametype = AST_FRAME_DTMF_END;
+
+				//ast_debug(5,"DTMF %s not in call.\n", dtmfMap->name);
+
+				if (owner) {
+					//ast_debug(5,"DTMF %s queuing frame.\n", dtmfMap->name);
+					ast_queue_frame(owner, &f);
+				}
+			} else {
+				if (owner) {
+					// INCALL
+					send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_END);
+				}
+			}
+		}
+	}
+	else {
+		p->dtmf_first = -1;
+	}
+}
+
+static void send_outgoing_dtmf(struct ast_channel *owner, char dtmf_button, int frametype) {
+	struct ast_frame fr;
+
+	if (!owner) return;
+
+	if (frametype != AST_FRAME_DTMF_BEGIN &&
+			frametype != AST_FRAME_DTMF_END) {
+		return;
+	}
+
+	if (ast_channel_state(owner) != AST_STATE_UP &&
+			ast_channel_state(owner) != AST_STATE_RING) {
+		return;
+	}
+
+	memset(&fr, 0, sizeof fr);
+	ast_debug(2, "Sending DTMF %c %s\n", dtmf_button, ast_channel_name(owner));
+	fr.src = brcm_tech.type;
+	fr.frametype = frametype;
+	fr.subclass.integer = dtmf_button;
+
+	ast_queue_frame(owner, &fr);
+}
+
+/* Handle audio packets from endptmngr. */
+static void audio_packet_handler(pe_packet_t *p) {
+	struct brcm_subchannel *sub;
+	int rtp_packet_type  = BRCM_UNKNOWN, drop_frame = 0;
+	struct ast_frame fr  = {0};
+	audio_packet_t *ap = (audio_packet_t *)p->data;
+	struct brcm_pvt *pvt;
+
+	fr.src = "TELCHAN";
+	rtp_packet_type = brcm_classify_rtp_packet(ap->rtp[1]);
+	pvt = brcm_get_pvt_from_lineid(iflist, ap->line);
+	sub = brcm_get_active_subchannel(pvt);
+	if (!pvt || !sub) {
+		ast_log(LOG_ERROR, "Failed to find subchannel for %s/%d/%d\n",
+			fr.src, ap->line, ap->cnx_id);
+		endpt_connection(ap->line, ap->cnx_id, "destroy");						// Request line close
+		return;
+	}
+
+	//pvt_lock(sub->parent, "brcm monitor packets");
+	//ast_mutex_lock(&sub->parent->lock);
+	struct ast_channel *owner = NULL;
+	if (sub->owner) {
+		ast_channel_ref(sub->owner);
+		owner = sub->owner;
+	}
+
+	/* We seem to get packets from DSP even if connection is muted (perhaps muting only affects packet callback).
+	 * Drop packets if subchannel is on hold. */
+	/* Handle rtp packet according to classification */
+	if (sub->channel_state != ONHOLD && rtp_packet_type == BRCM_AUDIO &&
+			(ap->rtp[0] & 0x80) && ap->rtp_size) {
+		fr.frametype = AST_FRAME_VOICE;
+		fr.offset = 0;
+		fr.data.ptr = ap->rtp + 12;
+		fr.datalen = ap->rtp_size - 12;
+
+		switch (ap->rtp[1]) {
+		case PCMU:
+			fr.subclass.format = ast_format_ulaw;
+			fr.samples = 160;
+			break;
+		case PCMA:
+			fr.subclass.format = ast_format_alaw;
+			fr.samples = 160;
+			break;
+		case G726:
+			fr.subclass.format = ast_format_g726;
+			fr.samples = 160; //for 20 ms frame size
+			break;
+		case G723:
+			fr.subclass.format = ast_format_g723;
+			fr.samples = 240;
+			break;
+		case G729:
+			fr.subclass.format = ast_format_g729;
+			fr.samples = 80; //for 10 ms frame size
+			break;
+		case G722:
+			fr.subclass.format = ast_format_g722;
+			fr.samples = 160;
+			break;
+		default:
+			ast_log(LOG_WARNING, "Unknown rtp codec id [%d]\n", p->data[1]);
+			return;
+		}
+	} else {
+		//ast_debug(5, "Dropping RTP frame of type %d.\n", rtp_packet_type);
+		drop_frame=1;
+		//pvt_unlock(sub->parent);
+	}
+	//ast_mutex_unlock(&sub->parent->lock);
+	//pvt_unlock(sub->parent);
+
+	if (owner) {
+		if (!drop_frame && (ast_channel_state(owner) == AST_STATE_UP || ast_channel_state(owner) == AST_STATE_RING)) {
+			struct ast_frame *cfr = NULL;
+			ast_queue_frame(owner, &fr);
+			if (cfr) {
+				ast_queue_frame(owner, cfr);
+			}
+		}
+		ast_channel_unref(owner);
+	}
+}
+
+static void brcm_cancel_dialing_timeouts(struct brcm_pvt *p)
+{
+	//If we have interdigit timeout, cancel it
+	if (p->interdigit_timer_id > 0) {
+		p->interdigit_timer_id = ast_sched_del(sched, p->interdigit_timer_id);
+	}
+
+	//If we have a autodial timeout, cancel it
+	if (p->autodial_timer_id > 0) {
+		p->autodial_timer_id = ast_sched_del(sched, p->autodial_timer_id);
+	}
+
+	//If we have a dialtone timeout, cancel it
+	if (p->dialtone_timeout_timer_id > 0) {
+		p->dialtone_timeout_timer_id = ast_sched_del(sched, p->dialtone_timeout_timer_id);
+	}
+}
+
+static int brcm_should_relay_dtmf(const struct brcm_subchannel *sub)
+{
+	if (sub->channel_state == INCALL && sub->parent->hf_detected == 0) {
+		return 1;
+	}
+	return 0;
+}
+
+static int is_sip_account_registered(const char *sip_account)
+{
+        int res = 0;
+        const char *cmd = "sip show registry";
+        char template[] = "/tmp/ast-brcm-XXXXXX";     /* template for temporary file */
+        char line[160];
+        int fd_temp = -1;
+        FILE *fp = NULL;
+
+        if ((fd_temp = mkstemp(template)) < 0) {
+                ast_log(LOG_ERROR, "Failed to create temporary file for command \"%s\": %s\n",
+                                cmd, strerror(errno));
+                return res;
+        }
+
+        if (ast_cli_command(fd_temp, cmd) == 0) {
+                close(fd_temp);
+                fd_temp = -1;
+
+                fp = fopen(template, "r");
+                if (fp) {
+                        while (fgets(line, sizeof(line), fp) != NULL) {
+                                /* The example output of the command is shown below.
+                                 * Some extra spaces are removed since the line is too long.
+                                 * sip0:5060 N 1313@10.0.2. 285 Registered Mon, 17 Jun 2019 17:12:58 */
+                                if (strstr(line, sip_account) == line && strstr(line, "Registered") != NULL) {
+                                        res = 1;
+                                        break;
+                                }
+                        }
+                }
+        } else {
+                ast_log(LOG_ERROR, "Execution CLI \"%s\" failed\n", cmd);
+        }
+
+        if (fd_temp >= 0)
+                close(fd_temp);
+        if (fp)
+                fclose(fp);
+        unlink(template);
+        return res;
+}
+
+static void *brcm_process_event(struct endpt_event *ev) {
+	struct brcm_pvt *p = NULL;
+	struct brcm_subchannel *sub = NULL;
+
+	ast_debug(9, "Event %s detected\n", ev->name);
+	p = brcm_get_pvt_from_lineid(iflist, ev->line);
+	if (!p) {
+		ast_debug(3, "No pvt with the correct line_id %d found!\n", ev->line);
+		return NULL;
+	}
+
+	/* Get locks in correct order */
+	//ast_mutex_lock(&p->lock);
+	pvt_lock(p, "brcm monitor events");
+	sub = brcm_get_active_subchannel(p);
+	struct brcm_subchannel *sub_peer = brcm_subchannel_get_peer(sub);
+	struct ast_channel *owner = NULL;
+	struct ast_channel *peer_owner = NULL;
+	if (sub->owner) {
+		ast_channel_ref(sub->owner);
+		owner = sub->owner;
+	}
+	if (sub_peer->owner) {
+		ast_channel_ref(sub_peer->owner);
+		peer_owner = sub_peer->owner;
+	}
+	pvt_unlock(p);
+	//ast_mutex_unlock(&p->lock);
+
+	if (owner && peer_owner) {
+		if (owner < peer_owner) {
+			ast_channel_lock(owner);
+			ast_channel_lock(peer_owner);
+		}
+		else {
+			ast_channel_lock(peer_owner);
+			ast_channel_lock(owner);
+		}
+	}
+	else if (owner) {
+		ast_channel_lock(owner);
+	}
+	else if (peer_owner) {
+		ast_channel_lock(peer_owner);
+	}
+	pvt_lock(p, "brcm monitor events");
+	//ast_mutex_lock(&p->lock);
+
+	ast_debug(3, "me: got mutex\n");
+	if (sub) {
+
+		switch (ev->event) {
+		case EVENT_OFFHOOK: {
+			ast_debug(9, "EVENT_OFFHOOK detected\n");
+
+			/* Reset the dtmf buffer */
+			memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf));
+			p->dtmf_len          = 0;
+			p->dtmf_first        = -1;
+			p->dtmfbuf[p->dtmf_len] = '\0';
+			sub->channel_state = OFFHOOK;
+
+			if (owner) {
+				if (!sub->connection_init) {
+					ast_debug(9, "create_connection()\n");
+					brcm_stop_dialtone(p);
+					brcm_create_connection(sub);
+				}
+
+				if (sub->cw_timer_id > -1) {
+					/* Picking up during reminder ringing for call waiting */
+					if (ast_sched_del(sched, sub->cw_timer_id) < 0)
+						ast_debug(3, "Error deleting timer\n");
+					sub->cw_timer_id = -1;
+				}
+
+				sub->channel_state = INCALL;
+				ast_queue_control(owner, AST_CONTROL_ANSWER);
+				//TODO Ronny: ast_channel_queue_connected_line_update() ?
+			}
+			else if (sub_peer->channel_state == ONHOLD) {
+
+				/* Picking up during reminder ringing for call on hold */
+				if (ast_sched_del(sched, sub_peer->onhold_hangup_timer_id) < 0)
+					ast_debug(3, "Error deleting timer\n");
+				sub_peer->onhold_hangup_timer_id = -1;
+
+				sub->channel_state = CALLENDED;
+				sub_peer->channel_state = INCALL;
+				ast_queue_control(peer_owner, AST_CONTROL_ANSWER);
+
+				brcm_unmute_connection(sub_peer);
+
+				endpt_connection(sub_peer->parent->line_id, sub_peer->connection_id, "create");
+				ast_queue_unhold(peer_owner);
+			}
+			else if (sub->channel_state == OFFHOOK) {
+				/* EVENT_OFFHOOK changed endpoint state to OFFHOOK, apply dialtone */
+                                ast_debug(9, "Resetting dial tones.\n");
+                                if ( p->context[0] != '\0' && is_sip_account_registered(p->context)) {
+                                    p->dialtone = DIALTONE_ON;
+                                    brcm_signal_dialtone(p);
+                                }
+
+				line_settings *s = &line_config[p->line_id];
+
+				if (strlen(s->autodial_ext)) {
+					/* Schedule autodial timeout if autodial extension is set */
+					p->autodial_timer_id = ast_sched_add(sched, s->autodial_timeoutmsec, handle_autodial_timeout, p);
+				}
+				else {
+					/* No autodial, schedule dialtone timeout */
+					p->dialtone_timeout_timer_id = ast_sched_add(sched, s->dialtone_timeoutmsec, handle_dialtone_timeout, p);
+				}
+			}
+			break;
+		}
+		case EVENT_ONHOOK: {
+			ast_debug(9, "EVENT_ONHOOK detected\n");
+			if (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK) {
+				/* Received EVENT_ONHOOK in state OFFHOOK/AWAITONHOOK, stop dial/congestion tone */
+				brcm_stop_dialtone(p);
+			}
+
+			sub->channel_state = ONHOOK;
+			brcm_cancel_dialing_timeouts(p);
+
+			/* Reset the dtmf buffer */
+			memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf));
+			p->dtmf_len          = 0;
+			p->dtmf_first        = -1;
+			p->dtmfbuf[p->dtmf_len] = '\0';
+
+			if (owner) {
+				ast_queue_control(owner, AST_CONTROL_HANGUP);
+				endpt_connection(sub->parent->line_id, sub->connection_id, "destroy");
+			}
+
+			//TODO: possible bug below - we don't change the channel_state when hanging up
+			if (sub_peer->channel_state == CALLWAITING) {
+				/* Remind user of waiting call */
+				sub_peer->channel_state = RINGING;
+				p->tech->signal_ringing(p); //TODO: This should use CCSS "ringing signal"
+			} else if (sub_peer->channel_state == ONHOLD) {
+				/* Remind user of call on hold */
+				sub_peer->onhold_hangup_timer_id = ast_sched_add(sched, onholdhanguptimeout * 1000, onholdhanguptimeout_cb, sub_peer);
+				ast_debug(2, "Forgotten call on hold for %s!\n", ast_channel_name(peer_owner));
+				endpt_connection(sub_peer->parent->line_id, sub_peer->connection_id, "destroy");
+				p->tech->signal_ringing_callerid_pending(p);
+				p->tech->signal_callerid(sub_peer->owner, sub_peer); //TODO: This should use CCSS "ringing signal"
+				sub->channel_state = RINGING;
+				ast_queue_control(peer_owner, AST_CONTROL_RINGING);
+			} else if (peer_owner && sub_peer->channel_state != TRANSFERING) {
+				/* Hangup peer subchannels in call or on hold */
+				ast_debug(2, "Hanging up call (not transfering)\n");
+				ast_queue_control(peer_owner, AST_CONTROL_HANGUP);
+			}
+			break;
+		}
+		case EVENT_DTMF0:
+		case EVENT_DTMF1:
+		case EVENT_DTMF2:
+		case EVENT_DTMF3:
+		case EVENT_DTMF4:
+		case EVENT_DTMF5:
+		case EVENT_DTMF6:
+		case EVENT_DTMF7:
+		case EVENT_DTMF8:
+		case EVENT_DTMF9:
+		case EVENT_DTMFA:
+		case EVENT_DTMFB:
+		case EVENT_DTMFC:
+		case EVENT_DTMFD:
+		case EVENT_DTMFS:
+		case EVENT_DTMFH:
+			{
+				brcm_cancel_dialing_timeouts(p);
+
+				unsigned int old_state = sub->channel_state;
+				ast_debug(2, "====> GOT DTMF %d\n", ev->event);
+				//ast_debug(5, "State = %s\n", state2str(sub->channel_state));
+				handle_dtmf(ev->event, sub, sub_peer, owner, peer_owner);
+				if (sub->channel_state == DIALING && old_state != sub->channel_state) {
+					/* DTMF event took channel state to DIALING. Stop dial tone. */
+					ast_debug(2, "Dialing. Stop dialtone.\n");
+					brcm_stop_dialtone(p);
+				}
+
+				if (sub->channel_state == DIALING) {
+					ast_debug(2, "Handle DTMF calling\n");
+					handle_dtmf_calling(sub);
+				}
+				break;
+			}
+		case EVENT_FLASH:
+			ast_debug(1, "EVENT_FLASH\n");
+			p->hf_detected = 1;
+
+			/* Schedule hook flash timeout. Until hook flash is handled or timeout expires, no
+			 * dtmf will be relayed to asterisk. */
+			int timeoutmsec = line_config[p->line_id].timeoutmsec;
+			p->interdigit_timer_id = ast_sched_add(sched, timeoutmsec, handle_hookflash_timeout, p);
+
+			handle_hookflash(sub, sub_peer, owner, peer_owner);
+			break;
+		case EVENT_EARLY_OFFHOOK:
+			ast_debug(1, "EVENT_EARLY_OFFHOOK\n");
+			break;
+		case EVENT_EARLY_ONHOOK:
+			ast_debug(1, "EVENT_EARLY_ONHOOK\n");
+			break;
+		default:
+			/* ast_debug(1, "UNKNOWN event %d detected\n", tEventParm.event); */
+			break;
+		}
+	}
+
+	pvt_unlock(p);
+	ast_debug(9, "me: unlocked mutex\n");
+
+	if (owner) {
+		ast_channel_unlock(owner);
+		ast_channel_unref(owner);
+	}
+
+	if (peer_owner) {
+		ast_channel_unlock(peer_owner);
+		ast_channel_unref(peer_owner);
+	}
+
+	return NULL;
+}
+
+/* Load settings for each line */
+static void brcm_initialize_pvt(struct brcm_pvt *p)
+{
+	line_settings *s = &line_config[p->line_id];
+
+	ast_copy_string(p->language, s->language, sizeof(p->language));
+	ast_copy_string(p->context, s->context, sizeof(p->context));
+	ast_copy_string(p->context_direct, s->context_direct, sizeof(p->context_direct));
+	ast_copy_string(p->cid_num, s->cid_num, sizeof(p->cid_num));
+	ast_copy_string(p->cid_name, s->cid_name, sizeof(p->cid_name));
+}
+
+static struct brcm_pvt *brcm_allocate_pvt(void)
+{
+	/* Make a brcm_pvt structure for this interface */
+	struct brcm_pvt *tmp;
+
+	tmp = ast_calloc(1, sizeof(*tmp));
+	if (tmp) {
+		struct brcm_subchannel *sub;
+		int i;
+
+		for (i=0; i<NUM_SUBCHANNELS; i++) {
+			sub = ast_calloc(1, sizeof(*sub));
+			if (sub) {
+				sub->id = i;
+				sub->owner = NULL;
+				sub->connection_id = -1;
+				sub->connection_init = 0;
+				sub->channel_state = ONHOOK;
+				sub->time_stamp = 0;
+				sub->sequence_number = 0;
+				sub->ssrc = 0;
+				sub->codec = -1;
+				sub->parent = tmp;
+				sub->cw_timer_id = -1;
+				sub->r4_hangup_timer_id = -1;
+				sub->onhold_hangup_timer_id = -1;
+				sub->period = 20;												// 20 ms
+				sub->conference_initiator = 0;
+				tmp->sub[i] = sub;
+				ast_debug(2, "subchannel created\n");
+			} else {
+				ast_log(LOG_ERROR, "no subchannel created\n");
+			}
+		}
+		tmp->line_id = -1;
+		tmp->dtmf_len = 0;
+		tmp->dtmf_first = -1;
+		tmp->lastformat = NULL;
+		tmp->lastinput = NULL;
+		memset(tmp->ext, 0, sizeof(tmp->ext));
+		tmp->next = NULL;
+		tmp->dialtone = DIALTONE_UNKNOWN;
+		tmp->interdigit_timer_id = -1;
+		tmp->autodial_timer_id = -1;
+		ast_mutex_init(&tmp->lock);
+		tmp->tech = &fxs_tech;
+	}
+	return tmp;
+}
+
+
+static void brcm_create_pvts(struct brcm_pvt *p, int mode) {
+	int i;
+	struct brcm_pvt *tmp = iflist;
+	struct brcm_pvt *tmp_next;
+
+	for (i=0; i<num_endpoints ; i++) {
+		tmp_next = brcm_allocate_pvt();
+		if (tmp == NULL) {
+			iflist = tmp_next; //First loop round, set iflist to point at first pvt
+			tmp    = tmp_next;
+			tmp->next = NULL;
+		} else {
+			tmp->next = tmp_next;
+			tmp_next->next = NULL;
+			tmp = tmp_next;
+		}
+	}
+}
+
+static void brcm_assign_line_id(struct brcm_pvt *p)
+{
+	struct brcm_pvt *tmp = p;
+	int i;
+	/* Assign line_id's */
+	for (i=0 ; i<num_endpoints ; i++) {
+		tmp->line_id = i;
+		brcm_initialize_pvt(tmp);
+		int j;
+		for (j=0; j<NUM_SUBCHANNELS; j++) {
+			tmp->sub[j]->channel_state = ONHOOK;
+		}
+		tmp = tmp->next;
+	}
+}
+
+
+static int brcm_in_call(const struct brcm_pvt *p)
+{
+	int i;
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		if (p->sub[i]->channel_state == INCALL) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int brcm_in_callwaiting(const struct brcm_pvt *p)
+{
+	int i;
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		if (p->sub[i]->channel_state == CALLWAITING) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int brcm_in_transferring(const struct brcm_pvt *p)
+{
+	int i;
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		if (p->sub[i]->channel_state == TRANSFERING) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int brcm_in_onhold(const struct brcm_pvt *p)
+{
+	int i;
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		if (p->sub[i]->channel_state == ONHOLD) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int brcm_in_conference(const struct brcm_pvt *p)
+{
+	return (p->sub[0]->channel_state == INCALL && 
+		p->sub[1]->channel_state == INCALL) ||
+		p->sub[0]->conference_initiator ||
+		p->sub[1]->conference_initiator;
+}
+
+/*
+ * Return idle subchannel
+ */
+struct brcm_subchannel *brcm_get_idle_subchannel(const struct brcm_pvt *p)
+{
+	int i;
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		if (p->sub[i]->channel_state == ONHOOK || p->sub[i]->channel_state == CALLENDED) {
+			return p->sub[i];
+		}
+	}
+	return NULL;
+}
+
+static struct ast_channel *brcm_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause)
+{
+	struct brcm_pvt *p;
+	struct brcm_subchannel *sub = NULL;
+	struct ast_channel *tmp = NULL;
+	struct ast_str * buf = ast_str_create(256);
+	int line_id = -1;
+
+	ast_format_cap_get_names(cap, &buf);
+	ast_debug(1, "Asked to create a channel with formats: %s\n", ast_str_buffer(buf));
+
+	/* Search for an unowned channel */
+	if (ast_mutex_lock(&iflock)) {
+		ast_log(LOG_ERROR, "Unable to lock interface list???\n");
+		return NULL;
+	}
+
+
+	/* Get line id */
+	line_id = atoi((char*)dest);
+	//ast_debug(1, "brcm_request = %s, line_id=%d, format %x\n", (char*) data, line_id, (unsigned int) format);
+
+	/* Map id to the correct pvt */
+	p = brcm_get_pvt_from_lineid(iflist, line_id);
+
+	/* If the id doesn't exist (p==NULL) use 0 as default */
+	if (!p) {
+		ast_log(LOG_ERROR, "Port id %s not found using default 0 instead.\n", (char*) dest);
+		p = iflist;
+	}
+
+	pvt_lock(p, "brcm request");
+	//ast_mutex_lock(&p->lock);
+
+	sub = brcm_get_idle_subchannel(p);
+
+	/* Check that the request has an allowed format */
+	if (!(ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO))) {
+		struct ast_str *codec_buf = ast_str_alloca(64);
+		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
+		return NULL;
+	} else if (sub) {
+		sub->channel_state = ALLOCATED;
+		sub->connection_id = ast_atomic_fetchadd_int((int *)&current_connection_id, +1);
+		tmp = brcm_new(sub, AST_STATE_DOWN, sub->parent->ext, sub->parent->context, assignedids, requestor ? requestor : NULL, cap);
+	} else {
+		*cause = AST_CAUSE_BUSY;
+	}
+
+	//ast_mutex_unlock(&p->lock);
+	pvt_unlock(p);
+	ast_mutex_unlock(&iflock);
+
+	return tmp;
+}
+
+
+static void brcm_lock_pvts(void)
+{
+	struct brcm_pvt *p = iflist;
+	while(p) {
+		pvt_lock(p, "brcm lock pvts");
+		//ast_mutex_lock(&p->lock);
+		p = brcm_get_next_pvt(p);
+	}
+}
+
+static void brcm_unlock_pvts(void)
+{
+	struct brcm_pvt *p = iflist;
+	while(p) {
+		pvt_unlock(p);
+		//ast_mutex_unlock(&p->lock);
+		p = brcm_get_next_pvt(p);
+	}
+}
+
+static void brcm_show_subchannels(struct ast_cli_args *a, struct brcm_pvt *p)
+{
+	struct brcm_subchannel *sub;
+
+	/* Output status for sub channels */
+	int i;
+	for (i=0; i<NUM_SUBCHANNELS; i++) {
+		sub = p->sub[i];
+		ast_cli(a->fd, "Subchannel: %d\n", sub->id);
+		ast_cli(a->fd, "  Connection id       : %d\n", sub->connection_id);
+
+		ast_cli(a->fd, "  Owner               : %p\n", sub->owner);
+		ast_cli(a->fd, "  Channel state       : %s\n", state2str(sub->channel_state));
+		ast_cli(a->fd, "  Connection init     : %d\n", sub->connection_init);
+		ast_cli(a->fd, "  Codec used          : %s\n", brcm_get_codec_string(sub->codec));
+		ast_cli(a->fd, "  RTP sequence number : %d\n", sub->sequence_number);
+		ast_cli(a->fd, "  RTP SSRC            : %d\n", sub->ssrc);
+		ast_cli(a->fd, "  RTP timestamp       : %d\n", sub->time_stamp);
+		ast_cli(a->fd, "  CW Timer id         : %d\n", sub->cw_timer_id);
+		ast_cli(a->fd, "  CW Rejected         : %d\n", sub->cw_rejected);
+		ast_cli(a->fd, "  R4 Hangup Timer id  : %d\n", sub->r4_hangup_timer_id);
+		ast_cli(a->fd, "  Conference initiator: %d\n", sub->conference_initiator);
+		ast_cli(a->fd, "  Onhold Hangup Timer id: %d\n", sub->onhold_hangup_timer_id);
+	}
+}
+
+static void brcm_show_pvts(struct ast_cli_args *a)
+{
+	struct brcm_pvt *p = iflist;
+	int i = 0;
+
+	while(p) {
+		pvt_lock(p, "brcm show pvts");
+		//ast_mutex_lock(&p->lock);
+		ast_cli(a->fd, "\nPvt nr: %d\n",i);
+		ast_cli(a->fd, "Line id             : %d\n", p->line_id);
+		ast_cli(a->fd, "Pvt next ptr        : 0x%x\n", (unsigned int) p->next);
+		ast_cli(a->fd, "DTMF buffer         : %s\n", p->dtmfbuf);
+		ast_cli(a->fd, "Default context     : %s\n", p->context);
+		ast_cli(a->fd, "Direct context      : %s\n", p->context_direct);
+		line_settings* s = &line_config[p->line_id];
+
+		ast_cli(a->fd, "Ringsignal          : %s\n", s->ringsignal ? "on" : "off");
+		ast_cli(a->fd, "Dialout msecs       : %d\n", s->timeoutmsec);
+		ast_cli(a->fd, "Autodial extension  : %s\n", s->autodial_ext);
+		ast_cli(a->fd, "Autodial msecs      : %d\n", s->autodial_timeoutmsec);
+		ast_cli(a->fd, "Dialt. timeout msecs: %d\n", s->dialtone_timeoutmsec);
+
+		ast_cli(a->fd, "Ast JitterBuf impl  : %s\n", global_jbconf.impl);
+		ast_cli(a->fd, "Ast JitterBuf max   : %ld\n", global_jbconf.max_size);
+		ast_cli(a->fd, "Call waiting        : %s\n", s->callwaiting ? "on" : "off");
+		ast_cli(a->fd, "CLIR                : %s\n", s->clir ? "on" : "off");
+
+		ast_cli(a->fd, "Dialtone            : ");
+		const DIALTONE_MAP *dialtone = dialtone_map;
+		while (dialtone->state != DIALTONE_LAST) {
+			if (dialtone->state == p->dialtone) {
+				break;
+			}
+			dialtone++;
+		}
+		ast_cli(a->fd, "%s\n", dialtone->str);
+
+		/* Print status for subchannels */
+		brcm_show_subchannels(a, p);
+
+		ast_cli(a->fd, "\n");
+
+		i++;
+		pvt_unlock(p);
+		//ast_mutex_unlock(&p->lock);
+		p = brcm_get_next_pvt(p);
+	}
+}
+
+/*! \brief CLI for showing brcm status.
+ * This is a new-style CLI handler so a single function contains
+ * the prototype for the function, the 'generator' to produce multiple
+ * entries in case it is required, and the actual handler for the command.
+ */
+
+static char *brcm_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	char buffer[AST_MAX_EXTENSION];
+
+	if (cmd == CLI_INIT) {
+		e->command = "telephony show status";
+		e->usage =
+			"Usage: telephony show status\n"
+			"       Shows the current chan_brcm status.\n";
+		return NULL;
+	} else if (cmd == CLI_GENERATE)
+		return NULL;
+
+	/* print chan brcm status information */
+	ast_cli(a->fd, "Channel version: %s\n\n", CHANNEL_VERSION);
+	ast_cli(a->fd, "Number of endpoints: %d\n", num_endpoints);
+	ast_cli(a->fd, "FAC list      : %s\n", feature_access_code_string(buffer, AST_MAX_EXTENSION));
+
+	/* print status for individual pvts */
+	brcm_show_pvts(a);
+
+	return CLI_SUCCESS;
+}
+
+/*! \brief CLI for showing brcm dialtone status. */
+static char *brcm_show_dialtone_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	if (cmd == CLI_INIT) {
+		e->command = "brcm show dialtone status";
+		e->usage =
+			"Usage: brcm show dialtone status\n"
+			"       Shows the current chan_brcm dialtone status.\n";
+		return NULL;
+	}
+	else if (cmd == CLI_GENERATE) {
+		return NULL;
+	}
+
+	struct brcm_pvt *p = iflist;
+	int i = 0;
+
+	ast_cli(a->fd, "Pvt nr\tDialtone\n\n");
+	while(p) {
+		const DIALTONE_MAP *dialtone = dialtone_map;
+		while (dialtone->state != DIALTONE_LAST) {
+			if (dialtone->state == p->dialtone) {
+				break;
+			}
+			dialtone++;
+		}
+		ast_cli(a->fd, "%d\t%s\n", i, dialtone->str);
+
+		i++;
+		p = brcm_get_next_pvt(p);
+	}
+
+	return CLI_SUCCESS;
+}
+
+/*! \brief CLI for reloading brcm config. */
+static char *brcm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_config *cfg = NULL;
+	if (cmd == CLI_INIT) {
+		e->command = "telephony reload";
+		e->usage =
+			"Usage: telephony reload\n"
+			"       Reload chan_brcm configuration.\n";
+		return NULL;
+	} else if (cmd == CLI_GENERATE) {
+		return NULL;
+	}
+
+	ast_mutex_lock(&iflock);
+
+	/* Acquire locks for all pvt:s to prevent nasty things from happening */
+	brcm_lock_pvts();
+
+	feature_access_code_clear();
+
+	/* Reload configuration */
+        if (load_common_settings(&cfg)) {
+		brcm_unlock_pvts();
+		ast_mutex_unlock(&iflock);
+		return CLI_FAILURE;
+	}
+
+	load_lines_settings(cfg);
+	struct brcm_pvt *p = iflist;
+	while(p) {
+		brcm_initialize_pvt(p);
+		p = brcm_get_next_pvt(p);
+	}
+
+	brcm_unlock_pvts();
+	ast_mutex_unlock(&iflock);
+
+	ast_verbose("TELCHAN reload done\n");
+
+	return CLI_SUCCESS;
+}
+
+static char *brcm_set_parameters_on_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	int on_off = 0;
+
+	if (cmd == CLI_INIT) {
+		e->command = "brcm set {echocancel|ringsignal} {on|off}";
+		e->usage =
+			"Usage: brcm set {echocancel|ringsignal} {on|off} PvtNr\n"
+			"       echocancel, echocancel mode.\n"
+			"       ringsignal, ring signal mode.\n"
+			"       PvtNr, the Pvt to modify.\n";
+		return NULL;
+	} else if (cmd == CLI_GENERATE) {
+		return NULL;
+	}
+
+	if (a->argc <= 4) {
+		return CLI_SHOWUSAGE; //Too few arguments
+	}
+
+	int pvt_id = atoi(a->argv[4]);
+	if (pvt_id >= num_endpoints || pvt_id < 0) {
+		return CLI_SHOWUSAGE;
+	}
+	line_settings *s = &line_config[pvt_id];
+
+	if (!strcasecmp(a->argv[3], "on")) {
+		on_off = 1;
+	} else {
+		on_off = 0;
+	}
+
+	if (!strcasecmp(a->argv[2], "ringsignal")) {
+		s->ringsignal = on_off;
+	}
+	return CLI_SUCCESS;
+}
+
+static char *brcm_set_parameters_value(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	if (cmd == CLI_INIT) {
+		e->command = "brcm set dialout_msecs";
+		e->usage =
+			"Usage: brcm set dialout_msecs 4000 PvtNr\n"
+			"       dialout_msecs, dialout delay in msecs.\n"
+			"       PvtNr, the Pvt to modify.\n";
+		return NULL;
+	} else if (cmd == CLI_GENERATE)
+		return NULL;
+
+	if (a->argc <= 4)
+		return CLI_SHOWUSAGE;
+
+	int pvt_id = atoi(a->argv[4]);
+    if (pvt_id >= num_endpoints || pvt_id < 0) {
+        return CLI_SHOWUSAGE;
+    }
+    line_settings *s = &line_config[pvt_id];
+
+	s->timeoutmsec = atoi(a->argv[3]);
+
+	return CLI_SUCCESS;
+}
+
+static char *brcm_set_autodial_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct brcm_pvt *p;
+
+	if (cmd == CLI_INIT) {
+		e->command = "brcm set autodial";
+		e->usage =
+			"Usage: brcm set autodial 0 1234\n"
+			"       brcm set autodial 0 \"\"\n"
+			"       autodial, extension to autodial on of hook.\n";
+		return NULL;
+	} else if (cmd == CLI_GENERATE)
+		return NULL;
+
+	if (a->argc <= 4)
+		return CLI_SHOWUSAGE;
+
+//	ast_verbose("%d %s",(a->argv[3][0] -'0'), a->argv[4]);
+
+	p = iflist;
+	while(p) {
+		if (p->line_id == (a->argv[3][0]-'0')) {
+			line_settings *s = &line_config[p->line_id];
+			ast_copy_string(s->autodial_ext, a->argv[4], sizeof(s->autodial_ext));
+			break;
+		}
+		p = brcm_get_next_pvt(p);
+	}
+
+	return CLI_SUCCESS;
+}
+
+
+/*! \brief Channel CLI commands definition */
+static struct ast_cli_entry cli_brcm[] = {
+	AST_CLI_DEFINE(brcm_show_status, "Show chan_brcm status"),
+	AST_CLI_DEFINE(brcm_show_dialtone_status, "Show chan_brcm dialtone status"),
+	AST_CLI_DEFINE(brcm_set_parameters_on_off,  "Set chan_brcm parameters"),
+	AST_CLI_DEFINE(brcm_set_parameters_value,  "Set chan_brcm dialout msecs"),
+	AST_CLI_DEFINE(brcm_set_autodial_extension,  "Set chan_brcm autodial extension"),
+	AST_CLI_DEFINE(brcm_reload, "Reload chan_brcm configuration"),
+};
+
+
+static int unload_module(void)
+{
+	struct brcm_pvt *p, *pl;
+
+	//ast_sched_dump(sched);
+
+	/* First, take us out of the channel loop */
+	if (cur_tech)
+		ast_channel_unregister(cur_tech);
+	if (!ast_mutex_lock(&iflock)) {
+		/* Hangup all interfaces if they have an owner */
+		p = iflist;
+		while(p) {
+			int i;
+			pvt_lock(p, "brcm unload module");
+			//ast_mutex_lock(&p->lock);
+			for (i=0; i<NUM_SUBCHANNELS; i++) {
+				struct ast_channel *owner = p->sub[i]->owner;
+				if (owner) {
+					ast_channel_ref(owner);
+					ast_mutex_unlock(&p->lock);
+					ast_softhangup(owner, AST_SOFTHANGUP_APPUNLOAD);
+					ast_channel_unref(owner);
+					ast_mutex_lock(&p->lock);
+				}
+			}
+			free(registration_change_sub);
+			registration_change_sub = NULL;
+			pvt_unlock(p);
+			//ast_mutex_unlock(&p->lock);
+			p = p->next;
+		}
+		iflist = NULL;
+		ast_mutex_unlock(&iflock);
+	} else {
+		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+		return -1;
+	}
+	if (!ast_mutex_lock(&monlock)) {
+		ast_debug(1, "Stopping threads...\n");
+		if (monitor) {
+			monitor = 0;
+			while (pthread_kill(monitor_thread, SIGURG) == 0)
+				sched_yield();
+			pthread_join(monitor_thread, NULL);
+		}
+		monitor_thread = AST_PTHREADT_STOP;
+
+		if (packets) {
+			packets = 0;
+			while (pthread_kill(packet_thread, SIGURG) == 0)
+				sched_yield();
+			pthread_join(packet_thread, NULL);
+		}
+		packet_thread = AST_PTHREADT_STOP;
+
+		ast_mutex_unlock(&monlock);
+	} else {
+		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+		return -1;
+	}
+	ast_debug(1, "[%d, %d,]\n",monitor, packets);
+
+	if (!ast_mutex_lock(&iflock)) {
+		/* Destroy all the interfaces and free their memory */
+		p = iflist;
+		while(p) {
+			/* Close the socket, assuming it's real */
+			pl = p;
+			p = p->next;
+			/* Free associated memory */
+			ast_free(pl);
+		}
+		iflist = NULL;
+		ast_mutex_unlock(&iflock);
+	} else {
+		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+		return -1;
+	}
+
+	/* Unregister CLI commands */
+	ast_cli_unregister_multiple(cli_brcm, ARRAY_LEN(cli_brcm));
+
+	feature_access_code_clear();
+	ast_sched_context_destroy(sched);
+
+	return 0;
+}
+
+/*
+ * Create a line_settings struct with default values.
+ */
+static line_settings line_settings_create(void)
+{
+	line_settings line_conf = (line_settings){
+		.language = "",
+		.cid_num = "",
+		.cid_name = "",
+		.context_direct = "default-direct",
+		.context = "default",
+		.autodial_ext = "",
+		.ringsignal = 1,
+		.timeoutmsec = 4000,
+		.autodial_timeoutmsec = 60000,
+		.period = 20,
+		.hangup_xfer = 0,
+		.dialtone_timeoutmsec = 20000,
+		.callwaiting = 1,
+		.clir = 0,
+	};
+	return line_conf;
+}
+
+/*
+ * Load config file settings into the specified line_settings struct.
+ * Can be called multiple times in order to load from multiple ast_variables.
+ */
+static void line_settings_load(line_settings *line_config, struct ast_variable *v)
+{
+	while(v) {
+		if (!strcasecmp(v->name, "language")) {
+			ast_copy_string(line_config->language, v->value, sizeof(line_config->language));
+		} else if (!strcasecmp(v->name, "callerid")) {
+			ast_callerid_split(v->value, line_config->cid_name, sizeof(line_config->cid_name), line_config->cid_num, sizeof(line_config->cid_num));
+		} else if (!strcasecmp(v->name, "context")) {
+			ast_copy_string(line_config->context, v->value, sizeof(line_config->context));
+		} else if (!strcasecmp(v->name, "context_direct")) {
+			ast_copy_string(line_config->context_direct, v->value, sizeof(line_config->context_direct));
+		} else if (!strcasecmp(v->name, "autodial")) {
+			ast_copy_string(line_config->autodial_ext, v->value, sizeof(line_config->autodial_ext));
+		} else if (!strcasecmp(v->name, "ringsignal")) {
+			line_config->ringsignal = ast_true(v->value)?1:0;
+		} else if (!strcasecmp(v->name, "dialoutmsec")) {
+			line_config->timeoutmsec = atoi(v->value);
+		} else if (!strcasecmp(v->name, "autodial_timeoutmsec")) {
+			line_config->autodial_timeoutmsec = atoi(v->value);
+		} else if (!strcasecmp(v->name, "dialtone_timeoutmsec")) {
+			line_config->dialtone_timeoutmsec = atoi(v->value);
+		}
+		else if (!strcasecmp(v->name, "hangup_xfer")) {
+			line_config->hangup_xfer = ast_true(v->value)?1:0;
+		}
+		else if (!strcasecmp(v->name, "callwaiting")) {
+			line_config->callwaiting = ast_true(v->value)?1:0;
+		}
+		else if (!strcasecmp(v->name, "clir")) {
+			line_config->clir = ast_true(v->value)?1:0;
+		}
+
+		v = v->next;
+	}
+}
+
+static int load_common_settings(struct ast_config **cfg)
+{
+	struct ast_flags config_flags = { 0 };
+
+	/* Set default values */
+	hold_target_before_refer = 1;
+
+	if ((*cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) {
+		ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", config);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	/* We *must* have a config file otherwise stop immediately */
+	if (!(*cfg)) {
+		ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	/* Load jitterbuffer defaults, Copy the default jb config over global_jbconf */
+	memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+	/* Load global settings */
+	struct ast_variable *v;
+	v = ast_variable_browse(*cfg, "default");
+
+	while(v) {
+		if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
+			ast_debug(2, "Loaded jitterbuffer settings '%s'\n", v->value);
+			v = v->next;
+			continue;
+		}
+
+		if (!strcasecmp(v->name, "cwtimeout")) {
+			cwtimeout = atoi(v->value);
+			if (cwtimeout > 60 || cwtimeout < 0) {
+				cwtimeout = DEFAULT_CALL_WAITING_TIMEOUT;
+				ast_log(LOG_WARNING, "Incorrect cwtimeout '%s', defaulting to '%d'\n", v->value, cwtimeout);
+			}
+		} else if (!strcasecmp(v->name, "r4hanguptimeout")) {
+			r4hanguptimeout = atoi(v->value);
+			if (r4hanguptimeout > 30000 || r4hanguptimeout < 0) {
+				r4hanguptimeout = DEFAULT_R4_HANGUP_TIMEOUT;
+				ast_log(LOG_WARNING, "Incorrect r4hanguptimeout '%s', defaulting to '%d'\n",
+						v->value, r4hanguptimeout);
+			}
+		} else if (!strcasecmp(v->name, "onholdhanguptimeout")) {
+			onholdhanguptimeout = atoi(v->value);
+			if (onholdhanguptimeout > 60 || onholdhanguptimeout < 0) {
+				onholdhanguptimeout = DEFAULT_ONHOLD_HANGUP_TIMEOUT;
+				ast_log(LOG_WARNING, "Incorrect onholdhanguptimeout '%s', defaulting to '%d'\n",
+						v->value, onholdhanguptimeout);
+			}
+		} else if (!strcasecmp(v->name, "hold_target_before_refer")) {
+			if (!strcasecmp(v->value, "no")) {
+				ast_debug(1, "The transfer target will not be put on-hold before sending REFER\n");
+				hold_target_before_refer = 0;
+			}
+		} else if (!strcasecmp(v->name, "featureaccesscodes")) {
+			char *tok;
+
+			if (ast_strlen_zero(v->value)) {
+				ast_debug(1, "No value given for featureaccesscodes on line %d\n", v->lineno);
+			}
+			else {
+				tok = strtok(ast_strdupa(v->value), ",");
+				while (tok) {
+					char *code = ast_strdupa(tok);
+					code = ast_strip(code);
+
+					feature_access_code_add(code);
+
+					tok = strtok(NULL, ",");
+				}
+			}
+		}
+
+		v = v->next;
+	}
+
+	return 0;
+}
+
+// Load settings fore all lines
+static void load_lines_settings(struct ast_config *cfg)
+{
+	struct ast_variable *v;
+
+	int i;
+	for (i = 0; i < num_endpoints; i++) {
+		// Create and init a new settings struct
+		line_config[i] = line_settings_create();
+		// Load default settings
+		v = ast_variable_browse(cfg, "default");
+		line_settings_load(&line_config[i], v);
+		// Load per line specific settings
+		char config_section[64];
+		snprintf(config_section, 64, "telline%d", i);
+		v = ast_variable_browse(cfg, config_section);
+		if (!v) {
+			ast_log(LOG_WARNING, "Unable to load endpoint specific config (missing config section?): %s\n", config_section);
+		}
+		line_settings_load(&line_config[i], v);
+	}
+
+}
+
+enum {
+	NUM_ENDPOINTS,
+	NUM_FXO_ENDPOINTS,
+	NUM_FXS_ENDPOINTS,
+	NUM_DECT_ENDPOINTS,
+	__MAX_ENDPOINTS,
+};
+
+static const struct blobmsg_policy endpt_count_policy[__MAX_ENDPOINTS] = {
+	[NUM_ENDPOINTS] = { .name = "num_endpoints", .type = BLOBMSG_TYPE_INT32 },
+	[NUM_FXO_ENDPOINTS] = { .name = "num_fxo_endpoints", .type = BLOBMSG_TYPE_INT32 },
+	[NUM_FXS_ENDPOINTS] = { .name = "num_fxs_endpoints", .type = BLOBMSG_TYPE_INT32 },
+	[NUM_DECT_ENDPOINTS] = { .name = "num_dect_endpoints", .type = BLOBMSG_TYPE_INT32 },
+};
+
+
+// Reception of event
+// { "ubus.object.add": {"id":123, "path":"foo"} }
+static void ubus_event_new_obj(struct ubus_context *ctx __attribute__((unused)), struct ubus_event_handler *ev __attribute__((unused)), const char *type, struct blob_attr *blob)
+{
+	struct blob_attr *keys[ARRAY_SIZE(new_obj_policy)];
+	const char *objPath;
+	uint32_t objId;
+
+	// Tokenize message key/value paris into an array
+	if(blobmsg_parse(new_obj_policy, ARRAY_SIZE(new_obj_policy),
+			keys, blob_data(blob), blob_len(blob))) {
+		return;
+	}
+
+	if(!type || !keys[OBJ_ID] || !keys[OBJ_PATH]) return;						// Did we get all arguments we need?
+
+	objId = blobmsg_get_u32(keys[OBJ_ID]);
+	objPath = blobmsg_get_string(keys[OBJ_PATH]);
+
+	if(strcmp(type, ubusStrObjAdd) == 0) {										// Object added to global context
+		if(strcmp(objPath, endpt_ubus_path) == 0) endpt_id = objId;				// Endptmngr has started up
+	}
+	else if(strcmp(type, ubusStrObjRm) == 0) {									// Object removed from global context
+		if(strcmp(objPath, endpt_ubus_path) == 0) endpt_id = 0;					// Endptmngr has closed down
+	}
+}
+
+
+static void endpt_get_count_cb(struct ubus_request *req,
+							   int type, struct blob_attr *msg) {
+	struct blob_attr *tb[__MAX_ENDPOINTS];
+
+	blobmsg_parse(endpt_count_policy, __MAX_ENDPOINTS, tb, blob_data(msg), blob_len(msg));
+
+	if (tb[NUM_FXS_ENDPOINTS])
+		num_fxs_endpoints = blobmsg_get_u32(tb[NUM_FXS_ENDPOINTS]);
+	if (tb[NUM_FXO_ENDPOINTS])
+		num_fxo_endpoints = blobmsg_get_u32(tb[NUM_FXO_ENDPOINTS]);
+	if (tb[NUM_DECT_ENDPOINTS])
+		num_dect_endpoints = blobmsg_get_u32(tb[NUM_DECT_ENDPOINTS]);
+	num_endpoints = num_fxs_endpoints + num_dect_endpoints;
+}
+
+static int endpt_get_count(void) {
+	uint32_t local_endpt_id;
+	struct blob_buf bb;
+
+	local_endpt_id = get_ubus_endpt_id();
+	if(!local_endpt_id) return -1;
+
+	memset(&bb, 0, sizeof(bb));
+	blob_buf_init(&bb, 0);
+	blobmsg_add_u8(&bb, "effective", 0);
+
+	return (ubus_invoke(ctx, local_endpt_id, "count", bb.head,
+		endpt_get_count_cb, NULL, 2000) == UBUS_STATUS_OK ? 0 : -1);
+}
+
+// Reception of RPC call
+// ubus call asterisk event '{ "line" : 1, "event" : "EVENT_DTMF0" }'
+static int asterisk_event(struct ubus_context *ctx, struct ubus_object *obj,
+				   struct ubus_request_data *req, const char *method,
+				   struct blob_attr *msg)
+{
+	struct blob_attr *tb[__EVENT_MAX];
+	char *event_str;
+	struct endpt_event *ev;
+	int line;
+
+	blobmsg_parse(asterisk_event_policy, __EVENT_MAX,
+				  tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[EVENT_LINE_ID] || !tb[EVENT_TYPE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	line = blobmsg_get_u32(tb[EVENT_LINE_ID]);
+
+	event_str = blobmsg_get_string(tb[EVENT_TYPE]);
+
+	/* Check if event is valid */
+	for (ev = event_map; ev->event != EVENT_LAST; ev++) {
+		if (strncmp(ev->name, event_str, strlen(ev->name)) == 0)
+			break;
+	}
+	if (ev->event == EVENT_LAST) {
+		printf("Unknown event: %s\n", event_str);
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+	ev->line = line;
+
+	printf("event: %s, line: %d\n", ev->name, ev->line);
+	if(iflist && cur_tech) brcm_process_event(ev);
+
+	return UBUS_STATUS_OK;
+}
+
+static struct ubus_method asterisk_methods[] = {
+	UBUS_METHOD("event", asterisk_event, asterisk_event_policy),
+};
+
+static struct ubus_object_type asterisk_obj_type = UBUS_OBJECT_TYPE("asterisk", asterisk_methods);
+
+static struct ubus_object asterisk_obj = {
+	.name = "asterisk",
+	.type = &asterisk_obj_type,
+	.methods = asterisk_methods,
+	.n_methods = ARRAY_SIZE(asterisk_methods),
+};
+
+/* Process ubus events by ubus stack */
+static void ubus_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event __attribute__((unused))) {
+	ubus_handle_event(ctx);
+}
+
+static int ubus_init(void) {
+	pe_stream_t *ubus_stream;
+
+	base = pe_base_new();
+	if (base < 0)
+		exit_failure("pe_base_new\n");
+	
+	ctx = ubus_connect(NULL);
+	if (!ctx) return -1;
+
+	/* Register event handlers (not calls) for:
+	 * { "ubus.object.add": {"id":123, "path":"foo"} } */
+	memset(&ObjAddListener, 0, sizeof(ObjAddListener));
+	ObjAddListener.cb = ubus_event_new_obj;
+	if(ubus_register_event_handler(ctx, &ObjAddListener,
+			ubusStrObjAdd) != UBUS_STATUS_OK) {
+		printf("Error registering ubus event handler %s", ubusStrObjAdd);
+		return -1;
+	}
+
+	memset(&ObjRmListener, 0, sizeof(ObjRmListener));
+	ObjRmListener.cb = ubus_event_new_obj;
+	if(ubus_register_event_handler(ctx, &ObjRmListener,
+			ubusStrObjRm) != UBUS_STATUS_OK) {
+		printf("Error registering ubus event handler %s", ubusStrObjRm);
+		return -1;
+	}
+
+	/* Lookup path to Endptmngr AFTER registration of ubus object
+	 * event handler above. It's no error if lookup fails. */
+	if(ubus_lookup_id(ctx, endpt_ubus_path,
+			(uint32_t*) &endpt_id) != UBUS_STATUS_OK) {
+		endpt_id = 0;
+	}
+
+	if (ubus_add_object(ctx, &asterisk_obj) != UBUS_STATUS_OK) {
+		printf("Failed to register asterisk object");
+		return -1;
+	}
+
+	/* Listen for data on ubus socket in our main event loop. */
+	ubus_stream = pe_stream_new(ctx->sock.fd);
+	pe_stream_add_handler(ubus_stream, 0, ubus_stream_handler);
+	pe_base_add_stream(base, ubus_stream);
+
+	return 0;
+}
+
+// pthread wrapper for lib picoevent dispatcher
+static void *pe_base_run(void *unused) {
+	int delay;
+
+	for(delay = 0; delay < 5 && (!iflist || !cur_tech); delay++) sleep(1);
+
+	pe_base_dispatch(base);
+	return NULL;
+}
+
+static int load_module(void)
+{
+	struct ast_config *cfg;
+	int result, try;
+
+	if (!(default_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (!(brcm_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+		ao2_ref(default_cap, -1);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	ast_format_cap_append_by_type(brcm_tech.capabilities, AST_MEDIA_TYPE_AUDIO);
+	ast_format_cap_append(default_cap, ast_format_alaw, 0);
+	
+	// Init UBUS and wait for endptmngr to start
+	if(ubus_init()) goto err;
+
+	/* Create audio fifos. Both ends open in R/W mode to become
+	 * independent of daemon startup order and peer restart
+	 * recovery. However, later each end use unidirectional flow. */
+	result = mkfifo(audio_tx_str, 0666);
+	if ((result == -1) && (errno != EEXIST)) exit_failure("Failed to create tx pipe");
+	audio_tx_fd = open(audio_tx_str, O_RDWR|O_LARGEFILE|O_NONBLOCK);
+	if(audio_tx_fd == -1) exit_failure("Failed to open tx pipe");
+	audio_tx_bus = pe_bus_new(audio_tx_fd);
+	if (!audio_tx_bus) exit_failure("Failed to create audio_tx bus");
+	result = mkfifo(audio_rx_str, 0666);
+	if ((result == -1) && (errno != EEXIST)) exit_failure("Failed to create rx pipe");
+	audio_rx_fd = open(audio_rx_str, O_RDWR|O_LARGEFILE|O_NONBLOCK);
+	if(audio_rx_fd == -1) exit_failure("Failed to open rx pipe");
+	audio_rx_stream = pe_stream_new(audio_rx_fd);
+	pe_stream_add_handler(audio_rx_stream, PE_MAX_EVENT_SIZE, audio_rx_stream_handler);
+	pe_base_add_stream(base, audio_rx_stream);
+
+	audio_rx_bus = pe_bus_new(-1);
+	if (!audio_rx_bus) exit_failure("Failed to create audio_rx bus");
+	pe_bus_add_handler(audio_rx_bus, audio_packet_handler);
+
+	/* Run pe_base_dispatch in separate thread. */
+	if (ast_pthread_create_background(&ubus_thread, NULL, pe_base_run, NULL) < 0) {
+		ast_log(LOG_ERROR, "Unable to start ubus thread.\n");
+		goto err;
+	}
+
+	for(try = 0; try < 20 && num_endpoints == -1; try++) {
+		if(endpt_get_count()) {
+			ast_log(LOG_ERROR, "Waiting for endptmngr...\n");
+			sleep(1);
+		}
+	}
+	if(num_endpoints == -1) goto err;
+
+	registration_change_sub = calloc(num_endpoints, sizeof(struct stasis_subscription *));
+
+	/* Setup scheduler thread */
+	if (!(sched = ast_sched_context_create())) {
+		ast_log(LOG_ERROR, "Unable to create scheduler thread/context. Aborting.\n");
+		goto err;
+	}
+
+	if (ast_mutex_lock(&iflock)) {
+		/* It's a little silly to lock it, but we mind as well just to be sure */
+		ast_log(LOG_ERROR, "Unable to lock interface list???\n");
+		goto err;
+	}
+
+	/* Load settings file and read default section */
+	if ((result = load_common_settings(&cfg)) != 0) {
+		goto err;
+	}
+
+	load_lines_settings(cfg);
+	brcm_create_pvts(iflist, 0);
+	brcm_assign_line_id(iflist);
+
+	ast_mutex_unlock(&iflock);
+
+	/* Make sure we can register our channel */
+	cur_tech = (struct ast_channel_tech *) &brcm_tech;
+	if (ast_channel_register(cur_tech)) { 
+		ast_log(LOG_ERROR, "Unable to register channel class 'Brcm'\n");
+		ast_config_destroy(cfg);
+		unload_module();
+		goto err;
+	}
+
+	/* Register all channel CLI functions */
+	ast_cli_register_multiple(cli_brcm, ARRAY_LEN(cli_brcm));
+	ast_config_destroy(cfg);
+
+    if (ast_sched_start_thread(sched)) {
+		ast_sched_context_destroy(sched);
+		sched = NULL;
+		goto err;
+	}
+
+	ast_debug(3, "%s(): done\n", __func__);
+
+	return AST_MODULE_LOAD_SUCCESS;
+
+ err:
+	ao2_ref(default_cap, -1);
+	return AST_MODULE_LOAD_FAILURE;
+}
+
+static int brcm_signal_callwaiting(const struct brcm_pvt *p)
+{
+	endpt_signal(p->line_id, "callwt", "on", NULL);
+	return 0;
+}
+
+static int brcm_stop_callwaiting(const struct brcm_pvt *p)
+{
+	endpt_signal(p->line_id, "callwt", "off", NULL);
+	return 0;
+}
+
+static int brcm_signal_ringing(struct brcm_pvt *p)
+{
+	if (line_config[p->line_id].ringsignal) {
+		endpt_signal(p->line_id, "ringing", "on", NULL);
+	}
+	return 0;
+}
+
+
+static int brcm_stop_ringing(struct brcm_pvt *p)
+{
+	if (line_config[p->line_id].ringsignal) {
+		endpt_signal(p->line_id, "ringing", "off", NULL);
+	}
+
+	return 0;
+}
+
+/* Prepare endpoint for ringing. Caller ID signal pending. */
+static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p)
+{
+	if (line_config[p->line_id].ringsignal) {
+		endpt_signal(p->line_id, "callid_ringing", "on", NULL);
+	}
+
+	return 0;
+}
+
+static int brcm_stop_ringing_callerid_pending(struct brcm_pvt *p)
+{
+	if (line_config[p->line_id].ringsignal) {
+		endpt_signal(p->line_id, "callid_ringing", "off", NULL);
+	}
+
+	return 0;
+}
+
+/*
+ * Send caller id message to endpoint.
+ * MMDDHHMM, number, name
+ * 'O' in number or name => not available
+ * 'P' in number or name => presentation not allowed
+ */
+static int brcm_signal_callerid(struct ast_channel *chan, struct brcm_subchannel *sub)
+{
+	if (line_config[sub->parent->line_id].ringsignal) {
+		CLID_STRING clid_string;
+		struct timeval utc_time;
+		struct ast_tm local_time;
+		char number[CLID_MAX_NUMBER];
+		char name[CLID_MAX_NAME];
+
+		/* Add datetime to caller id string, format: MMDDHHMM */
+		utc_time = ast_tvnow();
+		ast_localtime(&utc_time, &local_time, NULL);
+		sprintf(clid_string.date,
+			"%02d%02d%02d%02d, ",
+			local_time.tm_mon + 1,
+			local_time.tm_mday,
+			local_time.tm_hour,
+			local_time.tm_min);
+
+		/* Get connected line identity if valid and presentation is allowed */
+		if (chan) {
+			if ((ast_party_id_presentation(&ast_channel_connected(chan)->id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+				if (ast_channel_connected(chan)->id.number.valid) {
+					strncpy(number, ast_channel_connected(chan)->id.number.str, CLID_MAX_NAME);
+					number[CLID_MAX_NUMBER - 1] = '\0';
+				} else {
+					strcpy(number, "O\0");
+				}
+
+				if (ast_channel_connected(chan)->id.name.valid) {
+					strncpy(name, ast_channel_connected(chan)->id.name.str, CLID_MAX_NAME);
+					name[CLID_MAX_NAME - 1] = '\0';
+				} else {
+					strcpy(name, "O\0");
+				}
+			} else {
+				/* Number and/or name available but presentation is not allowed */
+				strcpy(number, "P\0");
+				strcpy(name, "P\0");
+			}
+		} else {
+			/* Name and number not available. Will probably not be reached */
+			strcpy(number, "0\0");
+			strcpy(name, "0\0");
+		}
+
+		/* Add number and name to caller id string, format: number,"name" */
+		int str_length = 0;
+		strncpy(&clid_string.number_name[str_length], number, CLID_MAX_NUMBER);
+		str_length = strlen(number);
+		clid_string.number_name[str_length++] = ',';
+		clid_string.number_name[str_length++] = '"';
+		strncpy(&clid_string.number_name[str_length], name, CLID_MAX_NAME);
+		str_length = strlen(clid_string.number_name);
+		clid_string.number_name[str_length++] = '"';
+		clid_string.number_name[str_length++] = '\0';
+
+		ast_debug(2, "CLID string: %s\n", (char *) &clid_string);
+		endpt_signal(sub->parent->line_id, "callid", "on", (char *)&clid_string);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int brcm_create_connection(struct brcm_subchannel *sub) {
+	if (!sub->connection_init) {
+		/* generate random nr for rtp header */
+		sub->ssrc = rand();
+
+		ast_debug(1, "Creating virtual Asterisk connection for pvt line_id=%i connection_id=%d\n", sub->parent->line_id, sub->connection_id);
+		sub->connection_init = 1;
+
+		if(!brcm_in_onhold(sub->parent) && !brcm_in_call(sub->parent)) {		// Is there another connection already?
+			ast_debug(1, "Creating real endpoint connection for pvt line_id=%i\n", sub->parent->line_id);
+			endpt_connection(sub->parent->line_id, sub->connection_id, "create");
+		}
+	}
+
+	return 0;
+}
+
+static int brcm_mute_connection(struct brcm_subchannel *sub)
+{
+	/* Workaround for AA. Unmuting is not working. Throw away packets in packets thread instead */
+	return 0;
+}
+
+static int brcm_unmute_connection(struct brcm_subchannel *sub)
+{
+	/* Workaround for AA. Unmuting is not working. Throw away packets in packets thread instead */
+	return 0;
+}
+
+/* Put all subchannels in conferencing mode */
+static int brcm_create_conference(struct brcm_pvt *p)
+{
+	struct brcm_subchannel *second, *onhold;
+	struct ast_bridge *onholdBridge, *secondBridge;
+	struct ast_channel *chanToKick[1];
+	struct ast_frame astFrame;
+	int res;
+
+	memset(&astFrame, 0, sizeof(astFrame));
+	astFrame.src = "TELCHAN";
+
+	// Second call from initiator.
+	second = brcm_get_active_subchannel(p);
+	if(!second || !second->owner) return -1;
+
+	// Second bridge. Initiator + second remote call.
+	secondBridge = ast_channel_internal_bridge(second->owner);
+	if(!secondBridge) return -1;
+
+	// First call from initiator (is onhold).
+	onhold = brcm_get_onhold_subchannel(p);
+	if(!onhold || !onhold->owner) return -1;
+
+	ast_debug(1, "Starting conference for pvt line_id=%i connection_id=%d\n",
+		onhold->parent->line_id, onhold->connection_id);
+
+	/* First bridge. Initiator + first (active but
+	 * waiting in background) remote call. */
+	onholdBridge = ast_channel_internal_bridge(onhold->owner);
+	if(!onholdBridge) return -1;
+
+	/* Put second initiator call onhold and unhold the first initiator
+	 * call. The other way around would be better, but for some reason
+	 * it doesn't work... If doing so, the second call get one way
+	 * audio only. */
+	second->conference_initiator = 0;
+	second->conference_id = strdup(secondBridge->uniqueid);
+	brcm_mute_connection(second);
+	ast_queue_hold(second->owner, NULL);
+	second->channel_state = ONHOLD;
+	onhold->conference_id = strdup(onholdBridge->uniqueid);
+	onhold->conference_initiator = 1;
+	brcm_unmute_connection(onhold);
+	ast_queue_unhold(second->owner);
+	onhold->channel_state = INCALL;
+	sched_yield();
+
+	// Move second call into first bridge and wait for it to finish.
+	chanToKick[0] = second->owner;
+	res = ast_bridge_merge(onholdBridge, secondBridge, 0, chanToKick, 1);
+	while(ast_bridge_find_by_id(second->conference_id)) sched_yield();
+
+	// SIP calls need unhold sent to the bridge as well.
+	astFrame.frametype = AST_FRAME_CONTROL;
+	astFrame.subclass.integer = AST_CONTROL_UNHOLD;
+	ast_bridge_queue_everyone_else(onholdBridge, NULL, &astFrame);
+
+	return res;
+}
+
+static int brcm_stop_conference(struct brcm_subchannel *p)
+{
+	struct ast_bridge *confBridge;
+
+	if (p->connection_init && p->owner) {
+		ast_debug(1, "Ending conference for pvt line_id=%i connection_id=%d\n",
+			p->parent->line_id, p->connection_id);
+
+		// Force end of the conference if it's still active.
+		confBridge = ast_bridge_find_by_id(p->conference_id);
+		if(!confBridge) return -1;
+
+		if(ao2_ref(confBridge, +1) >= 0 && confBridge->uniqueid &&
+				confBridge->technology) {
+			ast_bridge_destroy(confBridge, AST_CAUSE_NORMAL_CLEARING);
+		}
+		ao2_ref(confBridge, -1);
+	}
+
+	return 0;
+}
+
+static int brcm_close_connection(struct brcm_subchannel *sub) {
+
+	if (sub->connection_init) {
+		if(!brcm_in_onhold(sub->parent) && !brcm_in_call(sub->parent) &&
+			!brcm_in_callwaiting(sub->parent) && !brcm_in_transferring(sub->parent) ) { // Does line have another call?
+			ast_debug(1, "Closing real endpoint connection %d\n", sub->parent->line_id);
+			endpt_connection(sub->parent->line_id, sub->connection_id, "destroy");
+		}
+		sub->connection_init = 0;
+		ast_debug(1, "Virtual Asterisk connection %d/%d destroyed\n", sub->parent->line_id, sub->connection_id);
+	}
+
+	return 0;
+}
+
+
+/* Generate rtp payload, 12 bytes of header and 160 bytes of ulaw payload */
+static void brcm_generate_rtp_packet(struct brcm_subchannel *sub, uint8_t *packet_buf, int type, int marker, int dtmf_timestamp) {
+	unsigned short* packet_buf16 = (unsigned short*)packet_buf;
+	unsigned int*   packet_buf32 = (unsigned int*)packet_buf;
+
+	//Generate the rtp header, packet is zero from the start, that fact is used
+	packet_buf[0] |= 0x80; //Set version 2 of header
+	//Padding 0
+	//Extension 0
+	//CSRC count 0
+	packet_buf[1] = type;
+	packet_buf[1] |= marker?0x80:0x00;
+	packet_buf16[1] = htons(sub->sequence_number++); //Add sequence number
+	if (sub->sequence_number > 0xFFFF) sub->sequence_number=0;
+	packet_buf32[1] = htonl(sub->time_stamp);	//Add timestamp
+	sub->time_stamp += sub->period*8;
+	packet_buf32[2] = sub->ssrc;	//Random SSRC
+}
+
+
+static void brcm_dialtone_set(struct brcm_pvt *p, dialtone_state state)
+{
+	ast_debug(3, "Old dialtone: %s, new dialtone: %s\n",
+			dialtone_map[p->dialtone].str, dialtone_map[state].str);
+
+	if (state != p->dialtone) {
+		ast_debug(2, "Changing dialtone for pvt %d from '%s' to '%s'\n",
+			p->line_id,
+			dialtone_map[p->dialtone].str,
+			dialtone_map[state].str);
+		p->dialtone = state;
+		brcm_signal_dialtone(p);
+	}
+}
+
+static const char *feature_access_code_string(char *buffer, unsigned int buffer_length)
+{
+	struct feature_access_code *current;
+
+	if (AST_LIST_EMPTY(&feature_access_codes)) {
+		strncpy(buffer, "(empty)", buffer_length);
+		return buffer;
+	}
+
+	buffer[0] = '\0';
+	int write_length = 0;
+	AST_LIST_TRAVERSE(&feature_access_codes, current, list) {
+		int rv = snprintf(buffer + write_length, buffer_length - write_length, "%s ", current->code);
+		if (rv <= 0) {
+			break;
+		}
+		write_length += rv;
+	}
+
+	return buffer;
+}
+
+static int feature_access_code_add(const char *code)
+{
+	struct feature_access_code *fac;
+
+	if (ast_strlen_zero(code)) {
+		ast_log(LOG_WARNING, "Zero length FAC\n");
+		return 1;
+	}
+
+	if (!(fac = ast_calloc(1, sizeof(*fac)))) {
+		ast_log(LOG_WARNING, "FAC alloc failed\n");
+		return 1;
+	}
+
+	ast_copy_string(fac->code, code, sizeof(fac->code));
+	ast_log(LOG_DEBUG, "Adding FAC: [%s]\n", fac->code);
+
+	AST_LIST_INSERT_TAIL(&feature_access_codes, fac, list);
+	return 0;
+}
+
+static int feature_access_code_clear(void)
+{
+	struct feature_access_code *fac;
+
+	while ((fac = AST_LIST_REMOVE_HEAD(&feature_access_codes, list))) {
+		ast_free(fac);
+	}
+	return 0;
+}
+
+static int feature_access_code_match(char *sequence)
+{
+	struct feature_access_code *current;
+	int retval = -1;
+
+	AST_LIST_TRAVERSE(&feature_access_codes, current, list) {
+		char *seq = sequence;
+		char *fac = current->code;
+
+		int res = -1;
+		for (; *seq && *fac; seq++, fac++) {
+			if (*fac == '.') {
+				/* Perfect match */
+				return 0;
+			}
+			else if (*seq == *fac) {
+				/* Partial match */
+				res = 1;
+			}
+			else {
+				/* No match */
+				res = -1;
+				break;
+			}
+		}
+
+		if (res == 1 && *seq == *fac) {
+			/* Perfect match */
+			return 0;
+		}
+
+		if (res != -1) {
+			retval = res;
+		}
+	}
+
+	return retval;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Broadcom SLIC channel");
diff --git a/channels/chan_brcm.h b/channels/chan_brcm.h
new file mode 100644
index 0000000000000000000000000000000000000000..70b82aa0307365817afdd2aff6ba84fdf71a514e
--- /dev/null
+++ b/channels/chan_brcm.h
@@ -0,0 +1,238 @@
+/* Inteno AB, Stockholm, Sweden
+ * Channel for Broadcom FXS ports
+ */
+
+#ifndef CHAN_BRCM_H
+#define CHAN_BRCM_H
+
+/* Change this value when needed */
+#define CHANNEL_VERSION "1.2"
+
+#define DEFAULT_CALLER_ID "Unknown"
+#define PHONE_MAX_BUF 480
+
+#define TIMEMSEC 1000
+
+#define PCMU 0
+#define G726 2
+#define G723 4
+#define PCMA 8
+#define G729 18
+#define G722 9
+#define DTMF_PAYLOAD 101
+#define DTMF 128
+#define RTCP 200
+
+#define NOT_INITIALIZED -1
+//#define EPSTATUS_DRIVER_ERROR -1
+#define MAX_NUM_LINEID 30
+#define PACKET_BUFFER_SIZE 1024
+#define NUM_SUBCHANNELS 2
+
+#define BEGIN 0
+#define CONT  1
+#define END   2
+
+enum channel_state {
+    ONHOOK,
+    OFFHOOK,
+    DIALING,
+    CALLING,
+    INCALL,
+    ANSWER,
+	CALLENDED,
+	RINGING,
+	CALLWAITING,
+	ONHOLD,
+	TRANSFERING,
+	RINGBACK,
+	AWAITONHOOK,
+	ALLOCATED,
+};
+
+enum LINE_EVENT {																// Events from low level line (endpoint etc.)
+	EVENT_DTMF0,
+	EVENT_DTMF1,
+	EVENT_DTMF2,
+	EVENT_DTMF3,
+	EVENT_DTMF4,
+	EVENT_DTMF5,
+	EVENT_DTMF6,
+	EVENT_DTMF7,
+	EVENT_DTMF8,
+	EVENT_DTMF9,
+	EVENT_DTMFA,
+	EVENT_DTMFB,
+	EVENT_DTMFC,
+	EVENT_DTMFD,
+	EVENT_DTMFH,
+	EVENT_DTMFS,
+	EVENT_OFFHOOK,
+	EVENT_ONHOOK,
+	EVENT_EARLY_OFFHOOK,
+	EVENT_EARLY_ONHOOK,
+	EVENT_FLASH,
+	EVENT_LAST,
+};
+
+enum endpoint_type {
+	FXS,
+	FXO,
+	DECT,
+};
+
+typedef enum dialtone_state {
+	DIALTONE_OFF = 0,
+	DIALTONE_ON,
+	DIALTONE_CONGESTION,
+	DIALTONE_SPECIAL_CONDITION,
+	DIALTONE_UNKNOWN,
+	DIALTONE_LAST,
+} dialtone_state;
+
+struct brcm_subchannel {
+	int id;
+	struct ast_channel *owner;		/* Channel we belong to, possibly NULL */
+	int connection_id;		/* Current connection id, may be -1 */
+	unsigned int channel_state;	/* Channel states */
+	unsigned int connection_init;	/* State for endpoint id connection initialization */
+	struct ast_frame fr;		/* Frame */
+	unsigned int sequence_number;	/* Endpoint RTP sequence number state */
+	unsigned int time_stamp;	/* Endpoint RTP time stamp state */
+	unsigned int period;		/* Endpoint RTP period */
+	unsigned int ssrc;		/* Endpoint RTP synchronization source */
+	int codec;			/* Used codec */
+	struct brcm_pvt *parent;	/* brcm_line owning this subchannel */
+	int cw_timer_id;			/* Current call waiting timer id, -1 if no active timer */
+	int cw_rejected;            /* True if a previous waiting call has been rejected during current call */
+	int r4_hangup_timer_id;		/* Current R4 hangup timer id, -1 if no active timer */
+	int onhold_hangup_timer_id;	/* Current onhold hangup timer id, -1 if no active timer */
+	int conference_initiator;       /* True if this subchannel is the original leg in a 3-way conference */
+	char *conference_id;            /* uuid of the conference initiated by this subchannel */
+};
+
+
+struct brcm_channel_tech {
+	int (* signal_ringing)(struct brcm_pvt *p);
+	int (* signal_ringing_callerid_pending)(struct brcm_pvt *p);
+	int (* signal_callerid)(struct ast_channel *chan, struct brcm_subchannel *s);
+	int (* stop_ringing)(struct brcm_pvt *p);
+	int (* stop_ringing_callerid_pending)(struct brcm_pvt *p);
+	int (* release)(struct brcm_pvt *p);
+};
+
+
+struct brcm_pvt {
+	ast_mutex_t lock;
+	int fd;							/* Raw file descriptor for this device */
+	int line_id;				/* Maps to the correct port */
+	char dtmfbuf[AST_MAX_EXTENSION];/* DTMF buffer per channel */
+	int dtmf_len;					/* Length of DTMF buffer */
+	int dtmf_first;					/* DTMF control state, button pushes generate 2 events, one on button down and one on button up */
+	struct ast_format_cap * lastformat;            /* Last output format */
+	struct ast_format_cap * lastinput;             /* Last input format */
+	struct brcm_pvt *next;			/* Next channel in list */
+	char offset[AST_FRIENDLY_OFFSET];
+	char buf[PHONE_MAX_BUF];					/* Static buffer for reading frames */
+	char context_direct[AST_MAX_EXTENSION];
+	char context[AST_MAX_EXTENSION];
+	char obuf[PHONE_MAX_BUF * 2];
+	char ext[AST_MAX_EXTENSION];
+	char language[MAX_LANGUAGE];
+	char cid_num[AST_MAX_EXTENSION];
+	char cid_name[AST_MAX_EXTENSION];
+
+	struct brcm_subchannel *sub[NUM_SUBCHANNELS];	/* List of sub-channels, needed for callwaiting and 3-way support */
+	int hf_detected;			/* Hook flash detected */
+	dialtone_state dialtone;		/* Set by manager command */
+	struct brcm_channel_tech *tech;
+
+	int interdigit_timer_id;		/* Id of timer that tracks interdigit timeout */
+	int autodial_timer_id;			/* Id of timer that tracks autodial timeout */
+	int dialtone_timeout_timer_id;	/* Id of timer that tracks dialtone timeout */
+	int onhold_hangup_timer_id;		/* Id of timer that tracks onhold hangup timeout */
+};
+
+enum rtp_type {
+	BRCM_UNKNOWN,
+	BRCM_AUDIO,
+};
+
+
+
+/* Mapping of DTMF to char/name/intval */
+typedef struct DTMF_CHARNAME_MAP
+{
+	enum LINE_EVENT	event;
+	char	name[12];
+	char	c;
+	int		i;
+} DTMF_CHARNAME_MAP;
+
+
+
+
+typedef struct DIALTONE_MAP
+{
+	dialtone_state	state;
+	char		str[11];
+} DIALTONE_MAP;
+
+static const DIALTONE_MAP dialtone_map[] =
+{
+	{DIALTONE_OFF,		"off"},
+	{DIALTONE_ON,		"on"},
+	{DIALTONE_CONGESTION,	"congestion"},
+	{DIALTONE_SPECIAL_CONDITION, "special"},
+	{DIALTONE_UNKNOWN,	"unknown"},
+	{DIALTONE_LAST,		"-"},
+};
+
+/* Struct for individual endpoint settings */
+typedef struct {
+	char language[MAX_LANGUAGE];
+	char cid_num[AST_MAX_EXTENSION];
+	char cid_name[AST_MAX_EXTENSION];
+	char context_direct[AST_MAX_EXTENSION]; //Context that will be checked for exact matches
+	char context[AST_MAX_EXTENSION]; //Default context for dialtone mode
+	char autodial_ext[AST_MAX_EXTENSION];
+	int autodial_timeoutmsec;
+	int ringsignal;
+	int timeoutmsec;
+	int period;
+	int hangup_xfer;
+	int dialtone_timeoutmsec;
+	int callwaiting;
+	int clir;
+} line_settings;
+
+
+/* Caller ID */
+#define CLID_MAX_DATE	10
+#define CLID_MAX_NUMBER	16
+#define CLID_MAX_NAME	16
+typedef struct CLID_STRING
+{
+	char date[CLID_MAX_DATE];
+	char number_name[CLID_MAX_NUMBER + CLID_MAX_NAME + 4]; // 4 = comma, quotation marks and null terminator
+} CLID_STRING;
+
+
+/* Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+	.flags = 0,
+	.max_size = -1,
+	.resync_threshold = -1,
+	.impl = "",
+	.target_extra = -1,
+};
+
+
+#define DEFAULT_CALL_WAITING_TIMEOUT 24 // In seconds, Telia uses 24s
+
+#define DEFAULT_R4_HANGUP_TIMEOUT 5000 // In milliseconds
+#define DEFAULT_ONHOLD_HANGUP_TIMEOUT 20 // In seconds
+
+
+#endif /* CHAN_BRCM_H */
diff --git a/configs/brcm.conf.sample b/configs/brcm.conf.sample
new file mode 100644
index 0000000000000000000000000000000000000000..fb80497c1abb8737671599b1ec13c29e6c6a38de
--- /dev/null
+++ b/configs/brcm.conf.sample
@@ -0,0 +1,50 @@
+;
+; Linux Telephony Interface
+;
+; Configuration file
+;
+[interfaces]
+;
+; Select a mode, either the phone jack provides dialtone, reads digits,
+; then starts PBX with the given extension (dialtone mode), or
+; immediately provides the PBX without reading any digits or providing
+; any dialtone (this is the immediate mode, the default).  Also, you
+; can set the mode to "fxo" if you have a linejack to make it operate
+; properly.
+;
+mode=immediate
+;mode=dialtone
+;mode=fxo
+;
+; You can decide which format to use by default, "g723.1" or "slinear".
+; XXX Be careful, sometimes the card causes kernel panics when running
+; in signed linear mode for some reason... XXX
+;
+;format=slinear
+format=g723.1
+;
+; And set the echo cancellation to "off", "low", "medium", and "high".
+; This is not supported on all phones.
+;
+echocancel=medium
+;
+; You can optionally use VAD/CNG silence supression
+;
+;silencesupression=yes
+;
+; List all devices we can use.  Contexts may also be specified
+;
+;context=local
+;
+; You can set txgain and rxgain for each device in the same way as context.
+; If you want to change default gain value (1.0 =~ 100%) for device, simple
+; add txgain or rxgain line before device line. But rememeber, if you change
+; volume all cards listed below will be affected by these values. You can
+; use float values (1.0, 0.5, 2.0) or percentage values (100%, 150%, 50%).
+;
+;txgain=100%
+;rxgain=1.0
+device => /dev/bcmendpoint0
+
+
+