diff --git a/src/Makefile b/src/Makefile
index 62eb124a706410bbae4c0512f27902cf8469c8f5..c9ec84289663280d8ec10bea721cf5981d835f20 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -45,17 +45,20 @@ LIBS += -rdynamic -ldl
 
 .PHONY: all clean tests
 
-all: i1905d libi1905.so extmods tests
+all: libmidgen.so i1905d libi1905.so extmods tests
 
 %.o: %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 i1905d: $(OBJS)
-	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+	$(CC) $(LDFLAGS) -L. -o $@ $^ $(LIBS) -lmidgen
 
 libi1905.so: $(LIBOBJS)
 	$(CC) -shared -Wl,-soname,libi1905.so $^ -o $@ $(LIBS)
 
+libmidgen.so: midgen.o
+	$(CC) -shared -Wl,-soname,libmidgen.so $^ -o $@ -pthread
+
 extmods:
 	@echo "$(extmod_subdirs)"
 	for i in $(extmod_subdirs); do [ -d $$i ] && $(MAKE) -C $$i all; done
diff --git a/src/cmdu.c b/src/cmdu.c
index 5cb9a0235318ba72fe7daf28e88c568ebf53c9d1..9f35afa11ea3486b40dd3230b8f29a040fcc3438 100644
--- a/src/cmdu.c
+++ b/src/cmdu.c
@@ -70,25 +70,6 @@ uint16_t tlv_total_length(struct tlv *t)
 	return tlv_length(t) + 3;
 }
 
-
-uint16_t g_mid;
-
-uint16_t cmdu_init_mid(void)
-{
-	uint8_t b[2];
-
-	get_random_bytes(2, b);
-	g_mid = *(uint16_t *)b;
-
-	return (g_mid == 0) ? g_mid = 1 : g_mid;
-}
-
-
-uint16_t cmdu_get_next_mid(void)
-{
-	return (g_mid % 0xffff == 0) ? g_mid += 2: g_mid++;
-}
-
 int is_cmdu_type_valid(uint16_t type)
 {
 	return (type >= CMDU_TYPE_TOPOLOGY_DISCOVERY &&
diff --git a/src/cmdu.h b/src/cmdu.h
index 5a6a0e8d577f886940f1f43d080d3bf0a5e4b8fd..5730f35d65ad02de799491da183fc48ff0655fc3 100644
--- a/src/cmdu.h
+++ b/src/cmdu.h
@@ -23,13 +23,13 @@
 #include "bufutil.h"
 #include "hlist.h"
 
-
+/** struct cmdu_header - IEEE1905 CMDU header */
 struct cmdu_header {
 	uint8_t version;
-	uint8_t rsvd;
+	uint8_t rsvd;		/**< reserved */
 	uint16_t type;
-	uint16_t mid;
-	uint8_t fid;
+	uint16_t mid;		/**< message id */
+	uint8_t fid;		/**< fragment id */
 	uint8_t flag;
 } __attribute__ ((packed));
 
@@ -57,7 +57,21 @@ struct cmdu_frag {
 	uint16_t len;
 	struct list_head list;
 };
-
+/**
+ * @struct cmdu_buff
+ * @brief control structure for a CMDU buffer
+ *
+ * This control structure abstracts out a CMDU frame. In addition to holding
+ * pointer to the actual CMDU frame, it holds meta information about the CMDU,
+ * viz. for a receive CMDU which interface it arrived on, sender's macaddress
+ * etc.
+ *
+ * APIs are provided for working with cmdu_buff. Function for iterating through
+ * TLVs in a CMDU, adding TLV, getting size of CMDU frame etc.
+ *
+ * On the transmit and receive path, it is possible to achieve zero-memcpy for
+ * CMDU manipulation including inserting/extracting the ethernet frame header.
+ */
 struct cmdu_buff {
 	uint8_t *head;
 	uint8_t *data;
@@ -74,10 +88,14 @@ struct cmdu_buff {
 	struct list_head list;
 };
 
+/**
+ * @enum tlv_presence
+ * @brief defines the policy for occurrences of a TLV in a CMDU frame.
+ */
 enum tlv_presence {
 	TLV_PRESENT_UNDEFINED,
-	TLV_PRESENT_ONE,
-	TLV_PRESENT_MORE,
+	TLV_PRESENT_ONE,	/**< only one tlv of this type present */
+	TLV_PRESENT_MORE,	/**< one or more tlv of the same type present */
 	TLV_PRESENT_NUM,
 };
 
@@ -88,14 +106,23 @@ struct tlv_policy {
 	enum tlv_presence present;
 };
 
+/**
+ * @struct tlv
+ * @brief defines a IEEE1905 TLV
+ */
 struct tlv {
 	uint8_t type;
 	uint16_t len;
 	uint8_t data[];
 } __attribute__ ((packed));
 
-
-
+/**
+ * Allocates a TLV
+ * @param[in] datalen length of tlv data in bytes
+ * @param[out] band  band struct wifi_band type
+ *
+ * @return newly allocated tlv on success, or NULL if failed.
+ */
 struct tlv *tlv_alloc(uint16_t datalen);
 void tlv_zero(struct tlv *t);
 int tlv_ok(struct tlv *t, int rem);
diff --git a/src/cmdu_output.c b/src/cmdu_output.c
index 56c4c66298c510f25d53b4ed1f73ad70a982e3bd..c7ebfb39d28b200ba3895c859a3fbafb50121189 100644
--- a/src/cmdu_output.c
+++ b/src/cmdu_output.c
@@ -39,6 +39,7 @@
 #include <easy/easy.h>
 
 
+#include "debug.h"
 #include "worker.h"
 #include "bufutil.h"
 #include "util.h"
@@ -761,7 +762,7 @@ int i1905_send_ap_autoconfig_search(struct i1905_private *priv, uint8_t freqband
 
 
 	if (freqband > IEEE80211_FREQUENCY_BAND_60_GHZ) {
-		fprintf(stderr, "Invalid band %u in ap-autoconfig\n", freqband);
+		err("Invalid band %u in ap-autoconfig\n", freqband);
 		return -1;
 	}
 
@@ -774,7 +775,7 @@ int i1905_send_ap_autoconfig_search(struct i1905_private *priv, uint8_t freqband
 
 		frm = cmdu_alloc_simple(CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH, &mid);
 		if (!frm) {
-			fprintf(stderr, "%s: -ENOMEM\n", __func__);
+			err("%s: -ENOMEM\n", __func__);
 			return -1;
 		}
 
@@ -793,7 +794,7 @@ int i1905_send_ap_autoconfig_search(struct i1905_private *priv, uint8_t freqband
 
 		ret = cmdu_put_tlv(frm, t);
 		if (ret) {
-			fprintf(stderr, "%s: error: cmdu_put_tlv()\n", __func__);
+			dbg("%s: error: cmdu_put_tlv()\n", __func__);
 			cmdu_free(frm);
 			return -1;
 		}
@@ -809,7 +810,7 @@ int i1905_send_ap_autoconfig_search(struct i1905_private *priv, uint8_t freqband
 		t->data[0] = 0x00;
 		ret = cmdu_put_tlv(frm, t);
 		if (ret) {
-			fprintf(stderr, "%s: error: cmdu_put_tlv()\n", __func__);
+			dbg("%s: error: cmdu_put_tlv()\n", __func__);
 			cmdu_free(frm);
 			return -1;
 		}
@@ -826,18 +827,18 @@ int i1905_send_ap_autoconfig_search(struct i1905_private *priv, uint8_t freqband
 
 		ret = cmdu_put_tlv(frm, t);
 		if (ret) {
-			fprintf(stderr, "%s: error: cmdu_put_tlv()\n", __func__);
+			dbg("%s: error: cmdu_put_tlv()\n", __func__);
 			cmdu_free(frm);
 			return -1;
 		}
 
 		cmdu_put_eom(frm);
 
-		fprintf(stderr, "%s:%d: ifname = %s\n", __func__, __LINE__, ifs->ifname);
+		loud("%s: ifname = %s\n", __func__, ifs->ifname);
 		ret = i1905_send_cmdu(ifs->priv, MCAST_1905, ifs->aladdr,
 				      ETHERTYPE_1905, frm);
 		if (ret) {
-			fprintf(stderr, "Error sending AP_AUTOCONFIG_SEARCH\n");
+			dbg("Error sending AP_AUTOCONFIG_SEARCH\n");
 		}
 
 		cmdu_free(frm);
@@ -867,7 +868,7 @@ int i1905_send_ap_autoconfig_renew(struct i1905_private *priv, uint8_t freqband)
 
 
 	if (freqband > IEEE80211_FREQUENCY_BAND_60_GHZ) {
-		fprintf(stderr, "Invalid band %u in ap-autoconf renew\n", freqband);
+		err("Invalid band %u in ap-autoconf renew\n", freqband);
 		return -EINVAL;
 	}
 
@@ -883,7 +884,7 @@ int i1905_send_ap_autoconfig_renew(struct i1905_private *priv, uint8_t freqband)
 
 		frm = cmdu_alloc_simple(CMDU_TYPE_AP_AUTOCONFIGURATION_RENEW, &mid);
 		if (!frm) {
-			fprintf(stderr, "%s: -ENOMEM\n", __func__);
+			err("%s: -ENOMEM\n", __func__);
 			return -1;
 		}
 
diff --git a/src/midgen.c b/src/midgen.c
new file mode 100644
index 0000000000000000000000000000000000000000..b5c31710d141e21458bd2fa969768ae3023dbb11
--- /dev/null
+++ b/src/midgen.c
@@ -0,0 +1,61 @@
+/*
+ * midgen.c - 1905 CMDU mid generator
+ *
+ * Copyright (C) 2021 IOPSYS Software Solutions AB. All rights reserved.
+ *
+ * See LICENSE file for license related information.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <pthread.h>
+
+uint16_t g_mid;
+pthread_mutex_t g_mid_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void get_randombytes(uint8_t *buf, int num)
+{
+	unsigned int seed = (unsigned int)time(NULL);
+	int i;
+
+	srand(seed);
+	for (i = 0; i < num; i++)
+		buf[i] = rand_r(&seed) & 0xff;
+}
+
+uint16_t cmdu_init_mid(void)
+{
+	uint8_t b[2];
+	uint16_t mid;
+
+	get_randombytes(b, 2);
+	pthread_mutex_lock(&g_mid_lock);
+	g_mid = *(uint16_t *)b;
+	if (g_mid == 0)
+		g_mid = 1;
+
+	mid = g_mid;
+	pthread_mutex_unlock(&g_mid_lock);
+
+	return mid;
+}
+
+uint16_t cmdu_get_next_mid(void)
+{
+	uint16_t mid;
+
+	pthread_mutex_lock(&g_mid_lock);
+	g_mid++;
+	if (g_mid == 0xffff)
+		g_mid++;
+	mid = g_mid;
+	pthread_mutex_unlock(&g_mid_lock);
+
+	return mid;
+}
+
+void __attribute__ ((constructor)) libmidgen_init(void)
+{
+	cmdu_init_mid();
+}