/*
 * cntlr_ubus.c - UBUS interface to Multi-AP Controller's management object.
 *
 * Copyright (C) 2019-2024 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#include "cntlr_ubus.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <cmdu.h>

#include "utils/utils.h"
#include "utils/debug.h"
#include "cntlr.h"
#include "cntlr_commands.h"
#include "cntlr_commands_impl.h"

static int cntlr_ubus_help(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	fprintf(stderr, "%s: blob-len = %zu\n", __func__, blob_len(msg));
	ret = COMMAND(help)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

static int cntlr_ubus_log(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	fprintf(stderr, "%s: blob-len = %zu\n", __func__, blob_len(msg));
	ret = COMMAND(log)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

static int cntlr_ubus_status(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(status)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

static int cntlr_ubus_reconf(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(reconf)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

static int cntlr_ubus_status_full(struct ubus_context *ctx,
				  struct ubus_object *obj,
				  struct ubus_request_data *req,
				  const char *method,
				  struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(status_full)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

static int cntlr_ubus_metrics(struct ubus_context *ctx,
			  struct ubus_object *obj,
			  struct ubus_request_data *req,
			  const char *method,
			  struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(metrics)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

static int cntlr_ubus_timers(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(timers)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

int cntlr_ubus_query_ap_caps(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_ap_caps)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_sta_caps(struct ubus_context *ctx,
			      struct ubus_object *obj,
			      struct ubus_request_data *req,
			      const char *method,
			      struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_sta_caps)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_bsta_caps(struct ubus_context *ctx,
			       struct ubus_object *obj,
			       struct ubus_request_data *req,
			       const char *method,
			       struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_bsta_caps)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_ap_metrics(struct ubus_context *ctx,
				struct ubus_object *obj,
				struct ubus_request_data *req,
				const char *method,
				struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_ap_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_sta_metrics(struct ubus_context *ctx,
				 struct ubus_object *obj,
				 struct ubus_request_data *req,
				 const char *method,
				 struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_sta_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_unassoc_sta_metrics(struct ubus_context *ctx,
					 struct ubus_object *obj,
					 struct ubus_request_data *req,
					 const char *method,
					 struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_unassoc_sta_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_channel_pref(struct ubus_context *ctx,
				  struct ubus_object *obj,
				  struct ubus_request_data *req,
				  const char *method,
				  struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_channel_pref)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_query_beacon_metrics(struct ubus_context *ctx,
				    struct ubus_object *obj,
				    struct ubus_request_data *req,
				    const char *method,
				    struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(query_beacon_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_send_channel_sel(struct ubus_context *ctx,
				struct ubus_object *obj,
				struct ubus_request_data *req,
				const char *method,
				struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(send_channel_sel)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_send_channel_pref(struct ubus_context *ctx,
				struct ubus_object *obj,
				struct ubus_request_data *req,
				const char *method,
				struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(send_channel_pref)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_trigger_channel_clearing(struct ubus_context *ctx,
					struct ubus_object *obj,
					struct ubus_request_data *req,
					const char *method,
					struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(trigger_channel_clearing)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_trigger_acs(struct ubus_context *ctx,
			   struct ubus_object *obj,
			   struct ubus_request_data *req,
			   const char *method,
			   struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(trigger_acs)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_steer(struct ubus_context *ctx,
		     struct ubus_object *obj,
		     struct ubus_request_data *req,
		     const char *method,
		     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(steer)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_steer_op(struct ubus_context *ctx,
			 struct ubus_object *obj,
			 struct ubus_request_data *req,
			 const char *method,
			 struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(steer_op)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

static int cntlr_ubus_dump_assoc_control(struct ubus_context *ctx,
					 struct ubus_object *obj,
					 struct ubus_request_data *req,
					 const char *method,
					 struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_assoc_control)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

int cntlr_ubus_assoc_control(struct ubus_context *ctx,
			     struct ubus_object *obj,
			     struct ubus_request_data *req,
			     const char *method,
			     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(assoc_control)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_steer_backhaul(struct ubus_context *ctx,
			      struct ubus_object *obj,
			      struct ubus_request_data *req,
			      const char *method,
			      struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(steer_backhaul)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_scan(struct ubus_context *ctx,
		    struct ubus_object *obj,
		    struct ubus_request_data *req,
		    const char *method,
		    struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(scan)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_scanresults(struct ubus_context *ctx,
			   struct ubus_object *obj,
			   struct ubus_request_data *req,
			   const char *method,
			   struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(scanresults)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_dump_steer_summary(struct ubus_context *ctx,
				  struct ubus_object *obj,
				  struct ubus_request_data *req,
				  const char *method,
				  struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_steer_summary)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_dump_steer_history(struct ubus_context *ctx,
				  struct ubus_object *obj,
				  struct ubus_request_data *req,
				  const char *method,
				  struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_steer_history)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

#if (EASYMESH_VERSION >= 6)
int cntlr_ubus_dump_mlo_caps(struct ubus_context *ctx,
				  struct ubus_object *obj,
				  struct ubus_request_data *req,
				  const char *method,
				  struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_mlo_caps)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}
#endif

int cntlr_ubus_dump_unassoc_sta_metrics(struct ubus_context *ctx,
					struct ubus_object *obj,
					struct ubus_request_data *req,
					const char *method,
					struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_unassoc_sta_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_dump_beacon_metrics(struct ubus_context *ctx,
				   struct ubus_object *obj,
				   struct ubus_request_data *req,
				   const char *method,
				   struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_beacon_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_dump_policy(struct ubus_context *ctx,
				   struct ubus_object *obj,
				   struct ubus_request_data *req,
				   const char *method,
				   struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_policy)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_cac_start(struct ubus_context *ctx,
			   struct ubus_object *obj,
			   struct ubus_request_data *req,
			   const char *method,
			   struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(cac_start)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_cac_stop(struct ubus_context *ctx,
			struct ubus_object *obj,
			struct ubus_request_data *req,
			const char *method,
			struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(cac_stop)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_send_topology_query(struct ubus_context *ctx,
				   struct ubus_object *obj,
				   struct ubus_request_data *req,
				   const char *method,
				   struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(send_topology_query)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_send_hld(struct ubus_context *ctx,
			struct ubus_object *obj,
			struct ubus_request_data *req,
			const char *method,
			struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(send_hld)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_send_combined_metrics(struct ubus_context *ctx,
				     struct ubus_object *obj,
				     struct ubus_request_data *req,
				     const char *method,
				     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(send_combined_metrics)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
static int cntlr_ubus_dump_dpp_uri(struct ubus_context *ctx,
				   struct ubus_object *obj,
				   struct ubus_request_data *req,
				   const char *method,
				   struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_dpp_uri)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}

int cntlr_ubus_dpp_add_uri(struct ubus_context *ctx,
				     struct ubus_object *obj,
				     struct ubus_request_data *req,
				     const char *method,
				     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dpp_add_uri)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_dpp_del_uri(struct ubus_context *ctx,
				     struct ubus_object *obj,
				     struct ubus_request_data *req,
				     const char *method,
				     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dpp_del_uri)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}


int cntlr_ubus_dpp_advertise_cce(struct ubus_context *ctx,
				     struct ubus_object *obj,
				     struct ubus_request_data *req,
				     const char *method,
				     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(dpp_advertise_cce)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

#ifdef ZEROTOUCH_DPP
int cntlr_ubus_zerotouch_add_key(struct ubus_context *ctx,
				     struct ubus_object *obj,
				     struct ubus_request_data *req,
				     const char *method,
				     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(zerotouch_add_key)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_zerotouch_del_key(struct ubus_context *ctx,
				     struct ubus_object *obj,
				     struct ubus_request_data *req,
				     const char *method,
				     struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(zerotouch_del_key)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}
#endif
#endif
#endif

#ifdef EASYMESH_VENDOR_EXT
int cntlr_ubus_disassociate_sta(struct ubus_context *ctx,
				  struct ubus_object *obj,
				  struct ubus_request_data *req,
				  const char *method,
				  struct blob_attr *msg)
{
	//TRACE_ENTER();
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(disassociate_sta)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

int cntlr_ubus_reset_agent(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(reset_agent)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;

}

int cntlr_ubus_reload_wifi(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb;
	int ret;

	memset(&bb, 0, sizeof(bb));
	blob_buf_init(&bb, 0);

	ret = COMMAND(reload_wifi)(c, msg, &bb);
	if (ret) {
		if (ret == -EINVAL)
			ret = UBUS_STATUS_INVALID_ARGUMENT;

		goto out;
	}

	ubus_send_reply(ctx, req, bb.head);
out:
	blob_buf_free(&bb);
	return ret;
}

#endif

static int cntlr_ubus_dump_topology(struct ubus_context *ctx,
				    struct ubus_object *obj,
				    struct ubus_request_data *req,
				    const char *method,
				    struct blob_attr *msg)
{
	struct controller *c = container_of(obj, struct controller, obj);
	struct blob_buf bb = {0};
	int ret;

	blob_buf_init(&bb, 0);

	ret = COMMAND(dump_topology)(c, msg, &bb);

	ubus_send_reply(ctx, req, bb.head);
	blob_buf_free(&bb);

	return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}


#if 0
static int attach_ubus_method(const char *cmdname, struct ubus_method *out)
{
	struct ubus_method_handlers {
		const char *name;
		ubus_handler_t handler;
	} m[] = {
		{ "help", cntlr_ubus_help },
		{ "status", cntlr_ubus_status },
		{ "status_full", cntlr_ubus_status_full },
		{ "metrics", cntlr_ubus_metrics },
		{ "timers", cntlr_ubus_timers },
		{ "query_ap_caps", cntlr_ubus_query_ap_caps },
		{ "query_sta_caps", cntlr_ubus_query_sta_caps },
		{ "query_bsta_caps", cntlr_ubus_query_bsta_caps },
		{ "query_ap_metrics", cntlr_ubus_query_ap_metrics },
		{ "query_sta_metrics", cntlr_ubus_query_sta_metrics },
		{ "query_unassoc_sta_metrics", cntlr_ubus_query_unassoc_sta_metrics },
		{ "query_channel_pref", cntlr_ubus_query_channel_pref },
		{ "query_beacon_metrics", cntlr_ubus_query_beacon_metrics },
		{ "send_channel_sel", cntlr_ubus_send_channel_sel },
		{ "trigger_channel_clearing", cntlr_ubus_trigger_channel_clearing },
		{ "steer", cntlr_ubus_steer },
		{ "steer_op", cntlr_ubus_steer_op },
		{ "assoc_control", cntlr_ubus_assoc_control },
		{ "steer_backhaul", cntlr_ubus_steer_backhaul },
		//{ "set_policy", cntlr_ubus_set_policy },
		{ "scan", cntlr_ubus_scan },
		{ "scanresults", cntlr_ubus_scanresults },
		{ "cac_start", cntlr_ubus_cac_start },
		{ "cac_stop", cntlr_ubus_cac_stop },
		{ "send_topology_query", cntlr_ubus_send_topology_query },
		{ "send_hld", cntlr_ubus_send_hld },
		{ "send_combined_metrics", cntlr_ubus_send_combined_metrics },
		{ "dump_unassoc_sta_metrics", cntlr_ubus_dump_unassoc_sta_metrics },
		{ "dump_beacon_metrics", cntlr_ubus_dump_beacon_metrics },
		{ "dump_steer_summary", cntlr_ubus_dump_steer_summary },
		{ "dump_steer_history", cntlr_ubus_dump_steer_history },
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
		{ "dpp_add_uri", cntlr_ubus_dpp_add_uri },
		{ "dpp_del_uri", cntlr_ubus_dpp_del_uri },
#endif
#endif
	};

	for (int i = 0; i < ARRAY_SIZE(m); i++) {
		if (!strncmp(m[i].name, cmdname, strlen(m[i].name))) {
			out->handler= m[i].handler;
			dbg("%s: attached COMMAND (%s) --> UBUS method (%s)\n",
			    __func__, cmdname, m[i].name);
			return 0;
		}
	}

	return -1;
}
#endif

static int create_ubus_method(struct ubus_method *m)
{
	struct blobmsg_policy *pol = NULL;
	size_t n_pol = 0;
	int ret;

	dbg("%s: name = %s\n", __func__, m->name);
	ret = controller_command_get_policy(m->name, (void **)&pol, &n_pol);
	if (ret) {
		warn("%s: Failed for cmd-name = %s\n", __func__, m->name);
		return -1;
	}

	m->n_policy = n_pol;
	m->policy = pol;
	for (int i = 0; i < m->n_policy; i++) {
		dbg("%s: cmdname = %s: attr[%d] = '%s', type = %d\n",
			__func__, m->name, i, m->policy[i].name, m->policy[i].type);
	}

	return 0;
}

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;
	int ret;
	int i = 0;
	struct ubus_method_handlers {
		const char *name;
		ubus_handler_t handler;
	} methods[] = {
		{ "help", cntlr_ubus_help },
		{ "log", cntlr_ubus_log },
		{ "status", cntlr_ubus_status },
		{ "status_full", cntlr_ubus_status_full },
		{ "reconf", cntlr_ubus_reconf },
		{ "metrics", cntlr_ubus_metrics },
		{ "timers", cntlr_ubus_timers },
		{ "query_ap_caps", cntlr_ubus_query_ap_caps },
		{ "query_sta_caps", cntlr_ubus_query_sta_caps },
		{ "query_bsta_caps", cntlr_ubus_query_bsta_caps },
		{ "query_ap_metrics", cntlr_ubus_query_ap_metrics },
		{ "query_sta_metrics", cntlr_ubus_query_sta_metrics },
		{ "query_unassoc_sta_metrics", cntlr_ubus_query_unassoc_sta_metrics },
		{ "query_channel_pref", cntlr_ubus_query_channel_pref },
		{ "query_beacon_metrics", cntlr_ubus_query_beacon_metrics },
		{ "send_channel_sel", cntlr_ubus_send_channel_sel },
		{ "send_channel_pref", cntlr_ubus_send_channel_pref },
		{ "trigger_channel_clearing", cntlr_ubus_trigger_channel_clearing },
		{ "trigger_acs", cntlr_ubus_trigger_acs },
		{ "steer", cntlr_ubus_steer },
		{ "steer_op", cntlr_ubus_steer_op },
		{ "dump_assoc_control", cntlr_ubus_dump_assoc_control },
		{ "assoc_control", cntlr_ubus_assoc_control },
		{ "steer_backhaul", cntlr_ubus_steer_backhaul },
		//{ "set_policy", cntlr_ubus_set_policy },
		{ "scan", cntlr_ubus_scan },
		{ "scanresults", cntlr_ubus_scanresults },
		{ "cac_start", cntlr_ubus_cac_start },
		{ "cac_stop", cntlr_ubus_cac_stop },
		{ "send_topology_query", cntlr_ubus_send_topology_query },
		{ "send_hld", cntlr_ubus_send_hld },
		{ "send_combined_metrics", cntlr_ubus_send_combined_metrics },
		{ "dump_unassoc_sta_metrics", cntlr_ubus_dump_unassoc_sta_metrics },
		{ "dump_beacon_metrics", cntlr_ubus_dump_beacon_metrics },
		{ "dump_policy", cntlr_ubus_dump_policy },
		{ "dump_steer_summary", cntlr_ubus_dump_steer_summary },
		{ "dump_steer_history", cntlr_ubus_dump_steer_history },
#if (EASYMESH_VERSION >= 6)
		{ "dump_mlo_caps", cntlr_ubus_dump_mlo_caps },
#endif
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
		{ "dump_dpp_uri", cntlr_ubus_dump_dpp_uri },
		{ "dpp_add_uri", cntlr_ubus_dpp_add_uri },
		{ "dpp_del_uri", cntlr_ubus_dpp_del_uri },
		{ "dpp_advertise_cce", cntlr_ubus_dpp_advertise_cce },
#ifdef ZEROTOUCH_DPP
		{ "zerotouch_add_key", cntlr_ubus_zerotouch_add_key },
		{ "zerotouch_del_key", cntlr_ubus_zerotouch_del_key },
#endif
#endif
#ifdef EASYMESH_VENDOR_EXT
		{ "disassociate_sta", cntlr_ubus_disassociate_sta },
		{ "reset_agent", cntlr_ubus_reset_agent },
		{ "reload_wifi", cntlr_ubus_reload_wifi },
#endif
#endif
		{ "dump_topology", cntlr_ubus_dump_topology },
	};
	int num_methods = ARRAY_SIZE(methods);
	//struct controller_command *cmd;


	obj = &c->obj;
	memset(obj, 0, sizeof(*obj));

	obj_type = calloc(1, sizeof(struct ubus_object_type));
	if (!obj_type)
		return -1;

	obj_methods = calloc(num_methods, sizeof(struct ubus_method));
	if (!obj_methods) {
		free(obj_type);
		return -1;
	}

	obj->name = objname;
	for (i = 0; i < num_methods; i++) {
		struct ubus_method m = {0};

		m.name = strdup(methods[i].name);
		ret = create_ubus_method(&m);
		if (ret) {
			warn("%s: %d: Failed to create UBUS-method '%s'\n",
			    __func__, __LINE__, m.name);
			return -1;
		}

		/* attach ubus handler */
		m.handler= methods[i].handler;
		memcpy(&obj_methods[i], &m, sizeof(struct ubus_method));
	}

#if 0
	controller_for_each_command(cmd) {
		struct ubus_method m = {0};

		ret = create_ubus_method(cmd->command, &m);
		if (ret) {
			dbg("%s:%d: Failed to create UBUS-method for cmd = %s\n",
			    __func__, __LINE__, cmd->command);
			return -1;
		}

		attach_ubus_method(cmd->command, &m);
		memcpy(&obj_methods[i++], &m, sizeof(struct ubus_method));
		dbg("%s: i = %d, ubus-method-name = %s\n", __func__, i, m.name);
	}
#endif

	obj->methods = obj_methods;
	obj->n_methods = num_methods;

	obj_type->name = obj->name;
	obj_type->n_methods = obj->n_methods;
	obj_type->methods = obj->methods;
	obj->type = obj_type;

	ret = ubus_add_object(c->ubus_ctx, obj);
	if (ret) {
		err("Failed to add '%s' err = %s\n",
				objname, ubus_strerror(ret));
		free(obj_methods);
		free(obj_type);
		return ret;
	}

	info("Published '%s' object\n", objname);
	return 0;
}

void cntlr_remove_object(struct controller *c)
{
	if (c->ubus_ctx && c->obj.id != OBJECT_INVALID) {
		struct ubus_object *obj;

		obj = &c->obj;
		for (int i = 0; i < obj->n_methods; i++) {
			struct ubus_method *m = (struct ubus_method *) &obj->methods[i];
			struct blobmsg_policy *p;

			free((void *)m->name);

			if (!m->n_policy)
				continue;

			for (int j = 0; j < m->n_policy; j++) {
				p = (struct blobmsg_policy *) m->policy + j;

				free((void *)p->name);

			}
			free((void *)m->policy);

		}

		ubus_remove_object(c->ubus_ctx, obj);
		free(obj->type);
		free((void *)obj->methods);
	}
}

/* Do not use for events' reporting. Only for debug purpose */
void cntlr_notify_event_raw(struct controller *c, void *ev_type, void *ev_data)
{
	struct blob_buf b;

	memset(&b, 0, sizeof(struct blob_buf));
	blob_buf_init(&b, 0);
	if (ev_data)
		blobmsg_add_json_from_string(&b, (char *)ev_data);

	ubus_send_event(c->ubus_ctx, (char *)ev_type, b.head);
	blob_buf_free(&b);
}

/* { "map.controller": {"event":"<ev_type>" [,"data":{<ev_data>}]} } */
void cntlr_notify_event(struct controller *c, void *ev_type, void *ev_data)
{
    struct blob_buf b;
    void *t;

    memset(&b, 0, sizeof(struct blob_buf));
    blob_buf_init(&b, 0);

    if (ev_type)
        blobmsg_add_string(&b, "event", ev_type);

    if (ev_data) {
        t = blobmsg_open_table(&b, "data");
        blobmsg_add_json_from_string(&b, (char *)ev_data);
        blobmsg_close_table(&b, t);
    }

    ubus_send_event(c->ubus_ctx, "map.controller", b.head);
    blob_buf_free(&b);
}

int ubus_call_object(struct controller *c, uint32_t obj,
		     const char *method,
		     void (*response_cb)(struct ubus_request *, int, struct blob_attr *),
		     void *priv)
{
	struct blob_buf bb = {};
	int ret;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(c->ubus_ctx, obj, method, bb.head,
				response_cb, priv, 2 * 1000);
	if (ret) {
		err("Failed to get '%s' (ret = %d)\n", method, ret);
		blob_buf_free(&bb);
		return -1;
	}

	blob_buf_free(&bb);
	return 0;
}


struct buildcmdu_ctx {
	struct cmdu_buff *buff;
	int status;
};

static void ieee1905_ubus_buildcmdu_cb(struct ubus_request *req,
			  int type, struct blob_attr *msg)
{
	struct blob_attr *tb[2];
	static const struct blobmsg_policy ev_attr[2] = {
		[0] = { .name = "type", .type = BLOBMSG_TYPE_INT32 },
		[1] = { .name = "data", .type = BLOBMSG_TYPE_STRING }
	};
	uint16_t cmdu_type = 0, mid = 0;
	char *data;
	uint8_t origin[6] = { 0 };
	uint8_t *tlv;
	uint32_t b_len;
	struct buildcmdu_ctx *ctx = req->priv;
	struct cmdu_buff **buff = NULL;

	if (!ctx) {
		err("%s: No priv\n", __func__);
		return;
	}

	buff = &ctx->buff;

	if (!msg) {
		err("%s: Message NULL\n", __func__);
		ctx->status = -1;
		return;
	}


	blobmsg_parse(ev_attr, 2, tb, blob_data(msg), blob_len(msg));

	if (!tb[0] || !tb[1])
		return;

	cmdu_type = blobmsg_get_u32(tb[0]);
	data = blobmsg_get_string(tb[1]);
	if (!data) {
		err("%s: No data\n", __func__);
		ctx->status = -1;
		return;
	}

	dbg("%s: type = %u data = %s\n", __func__, cmdu_type, data);

	b_len = (strlen(data)/2) - 3; /* strip EOM */

	tlv = (uint8_t *) calloc(1, b_len);
	if (!tlv) {
		err("%s: No memory\n", __func__);
		ctx->status = -1;
		return;
	}

	strtob(data, b_len, tlv);

	*buff = cmdu_alloc_custom(cmdu_type, &mid, NULL, origin,
			tlv, b_len);
	free(tlv);

	if (!*buff) {
		err("%s: Couldn't allocate cmdu buff\n", __func__);
		ctx->status = -1;
		return;
	}

	ctx->status = 0;
}

struct cmdu_buff *ieee1905_ubus_buildcmdu(struct ubus_context *ubus_ctx,
					  uint16_t msg_type)
{
	trace("%s: --->\n", __func__);

	struct blob_buf b = { 0 };
	int ret = 0;
	uint32_t id;
	struct buildcmdu_ctx ctx = {
		.buff = NULL,
		.status = -1,
	};

	blob_buf_init(&b, 0);

	blobmsg_add_u32(&b, "type", (uint32_t)msg_type);

	if (ubus_lookup_id(ubus_ctx, "ieee1905", &id)) {
		dbg("%s:not present ieee1905", __func__);
		goto out;
	}

	ret = ubus_invoke(ubus_ctx, id, "buildcmdu",
			b.head, ieee1905_ubus_buildcmdu_cb,
			&ctx, 2000);

	if (ctx.status)
		ret = ctx.status;

	if (ret) {
		warn("%s: ubus call failed for |ieee1905 buildcmdu|", __func__);
		goto out;
	}
out:
	blob_buf_free(&b);
	return ctx.buff;
}

int cntlr_wait_for_object_timeout(struct controller *c, void *object,
				  uint32_t tmo_msecs, void *res)
{
	uint32_t obj;

	//TODO: handle tmo_msecs
	// -1 = forever

	for (;;) {
		int ret;

		ret = ubus_lookup_id(c->ubus_ctx, (char *)object, &obj);
		if (!ret) {
			*((uint32_t *)res) = obj;
			return 0;
		}

		trace("%s not up yet, sleeping for 2s!\n", (char *)object);
		sleep(1);
	}
}

static void ieee1905_cb_get_almac(struct ubus_request *req, int type,
				  struct blob_attr *msg)
{
	uint8_t *macaddr = (uint8_t *)req->priv;
	struct blob_attr *tb[1];
	static const struct blobmsg_policy ieee_attrs[1] = {
		[0] = { .name = "ieee1905id", .type = BLOBMSG_TYPE_STRING },
	};

	blobmsg_parse(ieee_attrs, 1, tb, blob_data(msg), blob_len(msg));

	if (tb[0]) {
		uint8_t almacaddr[6] = {0};
		char *mac;

		mac = blobmsg_get_string(tb[0]);
		if (hwaddr_aton(mac, almacaddr)) {
			memcpy(macaddr, almacaddr, 6);
			dbg("almacaddr = " MACFMT "\n", MAC2STR(macaddr));
		}
	}
}

int cntlr_get_ieee1905_almac(struct controller *c, uint8_t *almacaddr)
{
	uint32_t obj;
	int ret;

	if (!almacaddr)
		return -1;

	memset(almacaddr, 0, 6);
	ret = cntlr_wait_for_object_timeout(c, "ieee1905", -1, &obj);
	if (!ret)
		ret = ubus_call_object(c, obj, "info", ieee1905_cb_get_almac, almacaddr);

	return ret;
}
