diff --git a/src/core/cntlr_map_debug.c b/src/core/cntlr_map_debug.c
index 632b41e369fda31dfcf921c8c387de09f2e540a0..f4fc3a9d17cbd8ff524436d6a184181b3c5828ed 100644
--- a/src/core/cntlr_map_debug.c
+++ b/src/core/cntlr_map_debug.c
@@ -411,12 +411,78 @@ int debug_channel_pref_report(void *cntlr, struct cmdu_cstruct *cmdu)
 int debug_channel_sel_response(void *cntlr, struct cmdu_cstruct *cmdu)
 {
 	trace("%s: --->\n", __func__);
+	trace("parsing channel selection response of |%s:" MACFMT "|\n",
+		cmdu->intf_name,
+		MAC2STR(cmdu->origin));
+
+	int i;
+	uint8_t *tlv = NULL;
+
+	for (i = 0; i < cmdu->num_tlvs; i++) {
+		tlv = (uint8_t *) cmdu->tlvs[i];
+		trace("CMDU type: %s\n", map_stringify_tlv_type(*tlv));
+		switch (*tlv) {
+		case MAP_TLV_CHANNEL_SELECTION_RESPONSE:
+			{
+				struct tlv_ch_selection_resp *p =
+					(struct tlv_ch_selection_resp *)tlv;
+
+				trace("\tradio_id: " MACFMT "\n",
+					MAC2STR(p->radio_id));
+				trace("\tresponse_code: %d\n",
+					p->response_code);
+				break;
+			}
+		default:
+			fprintf(stdout, "unknown TLV in CMDU:|%s|",
+				map_stringify_cmdu_type(cmdu->message_type));
+			break;
+		}
+		trace("\n");
+	}
 	return 0;
 }
 
 int debug_oper_channel_report(void *cntlr, struct cmdu_cstruct *cmdu)
 {
 	trace("%s: --->\n", __func__);
+	trace("parsing operating channel report of |%s:" MACFMT "|\n",
+		cmdu->intf_name,
+		MAC2STR(cmdu->origin));
+
+	int i, j;
+	uint8_t *tlv = NULL;
+
+	for (i = 0; i < cmdu->num_tlvs; i++) {
+		tlv = (uint8_t *) cmdu->tlvs[i];
+		trace("CMDU type: %s\n", map_stringify_tlv_type(*tlv));
+		switch (*tlv) {
+		case MAP_TLV_OPERATING_CHANNEL_REPORT:
+			{
+				struct tlv_oper_ch_report *p =
+					(struct tlv_oper_ch_report *)tlv;
+
+				trace("\tradio_id: " MACFMT "\n",
+					MAC2STR(p->radio_id));
+				trace("\tch_preference_op_class_nr: %d\n",
+					p->op_ch_op_class_nr);
+				for (j = 0; j < p->op_ch_op_class_nr; j++) {
+					trace("\t\top_class: %d\n",
+						p->op_ch_op_class[j].op_class);
+					trace("\t\top_channel: %d\n",
+						p->op_ch_op_class[j].channel);
+				}
+				trace("\tcurr_tx_power: %d\n",
+					p->curr_tx_power);
+				break;
+			}
+		default:
+				fprintf(stdout, "unknown TLV in CMDU:|%s|",
+				map_stringify_cmdu_type(cmdu->message_type));
+				break;
+		}
+		trace("\n");
+	}
 	return 0;
 }
 
diff --git a/src/core/cntlr_ubus.c b/src/core/cntlr_ubus.c
index 87a378d5238c93983e9bda710755dc60eb16f0ee..b1706340db6e100e686f15264a540efd63b022c7 100644
--- a/src/core/cntlr_ubus.c
+++ b/src/core/cntlr_ubus.c
@@ -71,6 +71,25 @@ static const struct blobmsg_policy channel_pref_policy_params[__CHANNEL_PREF_POL
 	[CHANNEL_PREF_POLICY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING }
 };
 
+enum {
+	CHANNEL_SEL_POLICY_AGENT,
+	CHANNEL_SEL_POLICY_RADIO_ID,
+	CHANNEL_SEL_POLICY_CLASS_ID,
+	CHANNEL_SEL_POLICY_CHANNEL,
+	CHANNEL_SEL_POLICY_PREF,
+	CHANNEL_SEL_POLICY_TRANSMIT_POWER,
+	__CHANNEL_SEL_POLICY_MAX,
+};
+
+static const struct blobmsg_policy channel_select_policy_params[__CHANNEL_SEL_POLICY_MAX] = {
+	[CHANNEL_SEL_POLICY_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING },
+	[CHANNEL_SEL_POLICY_RADIO_ID] = { .name = "radio_id", .type = BLOBMSG_TYPE_STRING },
+	[CHANNEL_SEL_POLICY_CLASS_ID] = { .name = "class_id", .type = BLOBMSG_TYPE_INT32 },
+	[CHANNEL_SEL_POLICY_CHANNEL] = { .name = "channel", .type = BLOBMSG_TYPE_ARRAY },
+	[CHANNEL_SEL_POLICY_PREF] = { .name = "preference", .type = BLOBMSG_TYPE_INT32 },
+	[CHANNEL_SEL_POLICY_TRANSMIT_POWER] = { .name = "transmit_power", .type = BLOBMSG_TYPE_INT32 }
+};
+
 enum {
 	CFG_POLICY_AGENT,
 	CFG_POLICY_RADIO,
@@ -411,6 +430,151 @@ static int cntlr_channel_pref(struct ubus_context *ctx, struct ubus_object *obj,
 	return 0;
 }
 
+static int cntlr_channel_select(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CHANNEL_SEL_POLICY_MAX];
+	struct controller *c = container_of(obj, struct controller, obj);
+	struct tlv_channel_pref *p = NULL;
+	struct tlv_tx_power_limit *p1 = NULL;
+	uint32_t count = 0, tlv_index = 0, k = 0, j = 0;
+	char agent[18] = {0};
+	char radio_id[18] = {0};
+	uint32_t radio_present = 0;
+	uint32_t class_id = -1;
+	uint32_t channel_nr = 0, pref = -1, transmit_power = -1;
+	uint32_t channel[20];
+	struct cmdu_cstruct *cmdu_data;
+	struct blob_attr *cur;
+	int rem, l = 0;
+
+	cmdu_data = (struct cmdu_cstruct *)calloc(1,
+		sizeof(struct cmdu_cstruct));
+
+	if (!cmdu_data) {
+		fprintf(stderr, "failed to malloc cmdu\n");
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	blobmsg_parse(channel_select_policy_params, __CHANNEL_SEL_POLICY_MAX,
+			tb, blob_data(msg), blob_len(msg));
+
+	if (tb[CHANNEL_SEL_POLICY_AGENT]) {
+		strncpy(agent, blobmsg_data(tb[CHANNEL_SEL_POLICY_AGENT]),
+			sizeof(agent) - 1);
+		if (!hwaddr_aton(agent, cmdu_data->origin))
+			goto fail_cmdu;
+	}
+
+	// TODO: ff:ff:ff:ff:ff:ff = send to all agents
+	if (tb[CHANNEL_SEL_POLICY_RADIO_ID]) {
+		strncpy(radio_id, blobmsg_data(tb[CHANNEL_SEL_POLICY_RADIO_ID]),
+			sizeof(radio_id) - 1);
+		radio_present = 1;
+	}
+
+	if (tb[CHANNEL_SEL_POLICY_CLASS_ID])
+		class_id = (int) blobmsg_get_u32(tb[CHANNEL_SEL_POLICY_CLASS_ID]);
+
+	if (tb[CHANNEL_SEL_POLICY_CHANNEL]) {
+		channel_nr = blobmsg_check_array(tb[CHANNEL_SEL_POLICY_CHANNEL],
+				BLOBMSG_TYPE_INT32);
+		l = 0;
+		blobmsg_for_each_attr(cur, tb[CHANNEL_SEL_POLICY_CHANNEL], rem)
+			channel[l++] = blobmsg_get_u32(cur);
+	}
+
+	if (tb[CHANNEL_SEL_POLICY_PREF])
+		pref = (int) blobmsg_get_u32(tb[CHANNEL_SEL_POLICY_PREF]);
+
+	if (tb[CHANNEL_SEL_POLICY_TRANSMIT_POWER])
+		transmit_power = (int) blobmsg_get_u32
+			(tb[CHANNEL_SEL_POLICY_TRANSMIT_POWER]);
+
+	cmdu_data->message_type = CMDU_CHANNEL_SELECTION_REQ;
+
+	if (radio_present == 1 && class_id != -1 && pref != -1) {
+		p = calloc(1, sizeof(struct tlv_channel_pref));
+		if (!p) {
+			fprintf(stderr, "failed to malloc cmdu\n");
+			goto fail_cmdu;
+		}
+
+		p->tlv_type = MAP_TLV_CHANNEL_PREFERENCE;
+
+		hwaddr_aton(radio_id, p->radio_id);
+		p->ch_preference_op_class_nr = 1;
+
+		if (p->ch_preference_op_class_nr > 0)
+			p->op_class = calloc(p->ch_preference_op_class_nr,
+				sizeof(*p->op_class));
+
+		if (!p->op_class) {
+			fprintf(stderr, "|%s:%d| out of memory!\n", __func__, __LINE__);
+			p->ch_preference_op_class_nr = 0;
+			goto fail_p;
+		}
+
+		for (j = 0; j < p->ch_preference_op_class_nr; j++) {
+			p->op_class[j].op_class = (uint8_t)class_id;
+			p->op_class[j].channel_list = NULL;
+			if (channel_nr != 0)
+				p->op_class[j].channel_nr = channel_nr;
+
+			if (p->op_class[j].channel_nr > 0) {
+
+				p->op_class[j].channel_list =
+						calloc(p->op_class[j].channel_nr,
+						sizeof(uint8_t));
+
+				if (!p->op_class[j].channel_list) {
+					fprintf(stderr, "|%s:%d| out of memory!\n",
+							__func__, __LINE__);
+					p->op_class[j].channel_nr = 0;
+					goto fail_p;
+				}
+				for (k = 0; k < p->op_class[j].channel_nr; k++)
+					p->op_class[j].channel_list[k] = (uint8_t) channel[k];
+			}
+			p->op_class[j].preference = (uint8_t) pref;
+			p->op_class[j].preference_reason = 0x00;
+		}
+		count++;
+	}
+
+	if (radio_present == 1 && transmit_power != -1) {
+		p1 = calloc(1, sizeof(struct tlv_tx_power_limit));
+		if (!p1) {
+			fprintf(stderr, "failed to malloc cmdu\n");
+			goto fail_p;
+		}
+
+		p1->tlv_type = MAP_TLV_TRANSMIT_POWER_LIMIT;
+		hwaddr_aton(radio_id, p1->radio_id);
+		p1->tx_power_limit = (uint8_t)transmit_power;
+		count++;
+	}
+
+	cmdu_data->num_tlvs = count;
+	if (count)
+		cmdu_data->tlvs = (uint8_t **)calloc(cmdu_data->num_tlvs,
+				sizeof(uint8_t *));
+	if (cmdu_data->tlvs && p != NULL)
+		cmdu_data->tlvs[tlv_index++] = (uint8_t *)p;
+
+	if (cmdu_data->tlvs && p1 != NULL)
+		cmdu_data->tlvs[tlv_index++] = (uint8_t *)p1;
+
+	send_cmdu(c, cmdu_data);
+	map_free_cmdu(cmdu_data);
+	return 0;
+fail_p:
+	map_free_tlv_cstruct((uint8_t *) p);
+fail_cmdu:
+	map_free_cmdu(cmdu_data);
+	return UBUS_STATUS_UNKNOWN_ERROR;
+}
 
 static int cntlr_sta_caps(struct ubus_context *ctx, struct ubus_object *obj,
 			struct ubus_request_data *req, const char *method,
@@ -980,14 +1144,15 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 	struct ubus_object *obj;
 	struct ubus_object_type *obj_type;
 	struct ubus_method *obj_methods;
-	struct ubus_method m[6] = {
+	struct ubus_method m[7] = {
 		UBUS_METHOD_NOARG("status", cntlr_status),
 		UBUS_METHOD("ap_caps", cntlr_ap_caps, ap_caps_policy_params),
 		UBUS_METHOD("sta_caps", cntlr_sta_caps, sta_caps_policy_params),
 		UBUS_METHOD("channels", cntlr_channel_pref,
 				channel_pref_policy_params),
 		UBUS_METHOD("bk_steer", cntlr_bk_steer, bk_steer_policy_params),
-		UBUS_METHOD("agent_policy", cntlr_ap_policy_config, ap_policy_config_params)
+		UBUS_METHOD("agent_policy", cntlr_ap_policy_config, ap_policy_config_params),
+		UBUS_METHOD("channel", cntlr_channel_select, channel_select_policy_params)
 		/*
 		UBUS_METHOD("teardown_ap", cntlr_teardown_ap,
 				config_policy_params),