Merge "[[rdkb][tops] add config file for ccn34 project]"
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c
index 2e91034..4f0f145 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7981.c
@@ -436,6 +436,39 @@
 	PIN_FIELD_BASE(39, 39, 3, 0x40, 0x10, 1, 1),
 };
 
+static const unsigned int mt7981_pull_type[] = {
+	MTK_PULL_PUPD_R1R0_TYPE,/*0*/ MTK_PULL_PUPD_R1R0_TYPE,/*1*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*2*/ MTK_PULL_PUPD_R1R0_TYPE,/*3*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*4*/ MTK_PULL_PUPD_R1R0_TYPE,/*5*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*6*/ MTK_PULL_PUPD_R1R0_TYPE,/*7*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*8*/ MTK_PULL_PUPD_R1R0_TYPE,/*9*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*10*/ MTK_PULL_PUPD_R1R0_TYPE,/*11*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*12*/ MTK_PULL_PUPD_R1R0_TYPE,/*13*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*14*/ MTK_PULL_PUPD_R1R0_TYPE,/*15*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*16*/ MTK_PULL_PUPD_R1R0_TYPE,/*17*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*18*/ MTK_PULL_PUPD_R1R0_TYPE,/*19*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*20*/ MTK_PULL_PUPD_R1R0_TYPE,/*21*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*22*/ MTK_PULL_PUPD_R1R0_TYPE,/*23*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*24*/ MTK_PULL_PUPD_R1R0_TYPE,/*25*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*26*/ MTK_PULL_PUPD_R1R0_TYPE,/*27*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*28*/ MTK_PULL_PUPD_R1R0_TYPE,/*29*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*30*/ MTK_PULL_PUPD_R1R0_TYPE,/*31*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*32*/ MTK_PULL_PUPD_R1R0_TYPE,/*33*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*34*/ MTK_PULL_PUPD_R1R0_TYPE,/*35*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*36*/ MTK_PULL_PUPD_R1R0_TYPE,/*37*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*38*/ MTK_PULL_PUPD_R1R0_TYPE,/*39*/
+	MTK_PULL_PU_PD_TYPE,/*40*/ MTK_PULL_PU_PD_TYPE,/*41*/
+	MTK_PULL_PU_PD_TYPE,/*42*/ MTK_PULL_PU_PD_TYPE,/*43*/
+	MTK_PULL_PU_PD_TYPE,/*44*/ MTK_PULL_PU_PD_TYPE,/*45*/
+	MTK_PULL_PU_PD_TYPE,/*46*/ MTK_PULL_PU_PD_TYPE,/*47*/
+	MTK_PULL_PU_PD_TYPE,/*48*/ MTK_PULL_PU_PD_TYPE,/*49*/
+	MTK_PULL_PU_PD_TYPE,/*50*/ MTK_PULL_PU_PD_TYPE,/*51*/
+	MTK_PULL_PU_PD_TYPE,/*52*/ MTK_PULL_PU_PD_TYPE,/*53*/
+	MTK_PULL_PU_PD_TYPE,/*54*/ MTK_PULL_PU_PD_TYPE,/*55*/
+	MTK_PULL_PU_PD_TYPE,/*56*/
+};
+
+
 static const struct mtk_pin_reg_calc mt7981_reg_cals[] = {
 	[PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7981_pin_mode_range),
 	[PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7981_pin_dir_range),
@@ -964,6 +997,9 @@
 	.bias_disable_get = mtk_pinconf_bias_disable_get,
 	.bias_set = mtk_pinconf_bias_set,
 	.bias_get = mtk_pinconf_bias_get,
+	.pull_type = mt7981_pull_type,
+	.bias_set_combo = mtk_pinconf_bias_set_combo,
+	.bias_get_combo = mtk_pinconf_bias_get_combo,
 	.drive_set = mtk_pinconf_drive_set_rev1,
 	.drive_get = mtk_pinconf_drive_get_rev1,
 	.adv_pull_get = mtk_pinconf_adv_pull_get,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c
index b0f4c95..215642a 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7986.c
@@ -633,6 +633,60 @@
 	PIN_FIELD_BASE(68, 68, 4, 0x60, 0x10, 4, 1),
 };
 
+static const unsigned int mt7986_pull_type[] = {
+	MTK_PULL_PUPD_R1R0_TYPE,/*0*/ MTK_PULL_PUPD_R1R0_TYPE,/*1*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*2*/ MTK_PULL_PUPD_R1R0_TYPE,/*3*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*4*/ MTK_PULL_PUPD_R1R0_TYPE,/*5*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*6*/ MTK_PULL_PUPD_R1R0_TYPE,/*7*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*8*/ MTK_PULL_PUPD_R1R0_TYPE,/*9*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*10*/ MTK_PULL_PUPD_R1R0_TYPE,/*11*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*12*/ MTK_PULL_PUPD_R1R0_TYPE,/*13*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*14*/ MTK_PULL_PUPD_R1R0_TYPE,/*15*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*16*/ MTK_PULL_PUPD_R1R0_TYPE,/*17*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*18*/ MTK_PULL_PUPD_R1R0_TYPE,/*19*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*20*/ MTK_PULL_PUPD_R1R0_TYPE,/*21*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*22*/ MTK_PULL_PUPD_R1R0_TYPE,/*23*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*24*/ MTK_PULL_PUPD_R1R0_TYPE,/*25*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*26*/ MTK_PULL_PUPD_R1R0_TYPE,/*27*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*28*/ MTK_PULL_PUPD_R1R0_TYPE,/*29*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*30*/ MTK_PULL_PUPD_R1R0_TYPE,/*31*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*32*/ MTK_PULL_PUPD_R1R0_TYPE,/*33*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*34*/ MTK_PULL_PUPD_R1R0_TYPE,/*35*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*36*/ MTK_PULL_PUPD_R1R0_TYPE,/*37*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*38*/ MTK_PULL_PUPD_R1R0_TYPE,/*39*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*40*/ MTK_PULL_PUPD_R1R0_TYPE,/*41*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*42*/ MTK_PULL_PUPD_R1R0_TYPE,/*43*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*44*/ MTK_PULL_PUPD_R1R0_TYPE,/*45*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*46*/ MTK_PULL_PUPD_R1R0_TYPE,/*47*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*48*/ MTK_PULL_PUPD_R1R0_TYPE,/*49*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*50*/ MTK_PULL_PUPD_R1R0_TYPE,/*51*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*52*/ MTK_PULL_PUPD_R1R0_TYPE,/*53*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*54*/ MTK_PULL_PUPD_R1R0_TYPE,/*55*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*56*/ MTK_PULL_PUPD_R1R0_TYPE,/*57*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*58*/ MTK_PULL_PUPD_R1R0_TYPE,/*59*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*60*/ MTK_PULL_PUPD_R1R0_TYPE,/*61*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*62*/ MTK_PULL_PUPD_R1R0_TYPE,/*63*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*64*/ MTK_PULL_PUPD_R1R0_TYPE,/*65*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*66*/ MTK_PULL_PUPD_R1R0_TYPE,/*67*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*68*/ MTK_PULL_PU_PD_TYPE,/*69*/
+	MTK_PULL_PU_PD_TYPE,/*70*/ MTK_PULL_PU_PD_TYPE,/*71*/
+	MTK_PULL_PU_PD_TYPE,/*72*/ MTK_PULL_PU_PD_TYPE,/*73*/
+	MTK_PULL_PU_PD_TYPE,/*74*/ MTK_PULL_PU_PD_TYPE,/*75*/
+	MTK_PULL_PU_PD_TYPE,/*76*/ MTK_PULL_PU_PD_TYPE,/*77*/
+	MTK_PULL_PU_PD_TYPE,/*78*/ MTK_PULL_PU_PD_TYPE,/*79*/
+	MTK_PULL_PU_PD_TYPE,/*80*/ MTK_PULL_PU_PD_TYPE,/*81*/
+	MTK_PULL_PU_PD_TYPE,/*82*/ MTK_PULL_PU_PD_TYPE,/*83*/
+	MTK_PULL_PU_PD_TYPE,/*84*/ MTK_PULL_PU_PD_TYPE,/*85*/
+	MTK_PULL_PU_PD_TYPE,/*86*/ MTK_PULL_PU_PD_TYPE,/*87*/
+	MTK_PULL_PU_PD_TYPE,/*88*/ MTK_PULL_PU_PD_TYPE,/*89*/
+	MTK_PULL_PU_PD_TYPE,/*90*/ MTK_PULL_PU_PD_TYPE,/*91*/
+	MTK_PULL_PU_PD_TYPE,/*92*/ MTK_PULL_PU_PD_TYPE,/*93*/
+	MTK_PULL_PU_PD_TYPE,/*94*/ MTK_PULL_PU_PD_TYPE,/*95*/
+	MTK_PULL_PU_PD_TYPE,/*96*/ MTK_PULL_PU_PD_TYPE,/*97*/
+	MTK_PULL_PU_PD_TYPE,/*98*/ MTK_PULL_PU_PD_TYPE,/*99*/
+	MTK_PULL_PU_PD_TYPE,/*100*/
+};
+
 static const struct mtk_pin_reg_calc mt7986_reg_cals[] = {
 	[PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7986_pin_mode_range),
 	[PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7986_pin_dir_range),
@@ -1065,6 +1119,9 @@
 	.bias_disable_get = mtk_pinconf_bias_disable_get,
 	.bias_set = mtk_pinconf_bias_set,
 	.bias_get = mtk_pinconf_bias_get,
+	.pull_type = mt7986_pull_type,
+	.bias_set_combo = mtk_pinconf_bias_set_combo,
+	.bias_get_combo = mtk_pinconf_bias_get_combo,
 	.drive_set = mtk_pinconf_drive_set_rev1,
 	.drive_get = mtk_pinconf_drive_get_rev1,
 	.adv_pull_get = mtk_pinconf_adv_pull_get,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c
index 49c0be1..2fe1746 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/files-5.4/drivers/pinctrl/mediatek/pinctrl-mt7988.c
@@ -596,6 +596,51 @@
 	PIN_FIELD_BASE(83, 83, 1, 0x90, 0x10, 15, 1),
 };
 
+static const unsigned int mt7988_pull_type[] = {
+	MTK_PULL_PUPD_R1R0_TYPE,/*0*/ MTK_PULL_PUPD_R1R0_TYPE,/*1*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*2*/ MTK_PULL_PUPD_R1R0_TYPE,/*3*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*4*/ MTK_PULL_PUPD_R1R0_TYPE,/*5*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*6*/ MTK_PULL_PU_PD_TYPE,    /*7*/
+	MTK_PULL_PU_PD_TYPE,    /*8*/ MTK_PULL_PU_PD_TYPE,    /*9*/
+	MTK_PULL_PU_PD_TYPE,    /*10*/ MTK_PULL_PUPD_R1R0_TYPE,/*11*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*12*/ MTK_PULL_PU_PD_TYPE,    /*13*/
+	MTK_PULL_PU_PD_TYPE,    /*14*/ MTK_PULL_PD_TYPE,       /*15*/
+	MTK_PULL_PD_TYPE,       /*16*/ MTK_PULL_PD_TYPE,       /*17*/
+	MTK_PULL_PD_TYPE,       /*18*/ MTK_PULL_PUPD_R1R0_TYPE,/*19*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*20*/ MTK_PULL_PUPD_R1R0_TYPE,/*21*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*22*/ MTK_PULL_PUPD_R1R0_TYPE,/*23*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*24*/ MTK_PULL_PUPD_R1R0_TYPE,/*25*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*26*/ MTK_PULL_PUPD_R1R0_TYPE,/*27*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*28*/ MTK_PULL_PUPD_R1R0_TYPE,/*29*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*30*/ MTK_PULL_PUPD_R1R0_TYPE,/*31*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*32*/ MTK_PULL_PUPD_R1R0_TYPE,/*33*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*34*/ MTK_PULL_PUPD_R1R0_TYPE,/*35*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*36*/ MTK_PULL_PUPD_R1R0_TYPE,/*37*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*38*/ MTK_PULL_PUPD_R1R0_TYPE,/*39*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*40*/ MTK_PULL_PUPD_R1R0_TYPE,/*41*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*42*/ MTK_PULL_PUPD_R1R0_TYPE,/*43*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*44*/ MTK_PULL_PUPD_R1R0_TYPE,/*45*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*46*/ MTK_PULL_PUPD_R1R0_TYPE,/*47*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*48*/ MTK_PULL_PUPD_R1R0_TYPE,/*49*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*50*/ MTK_PULL_PUPD_R1R0_TYPE,/*51*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*52*/ MTK_PULL_PUPD_R1R0_TYPE,/*53*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*54*/ MTK_PULL_PUPD_R1R0_TYPE,/*55*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*56*/ MTK_PULL_PUPD_R1R0_TYPE,/*57*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*58*/ MTK_PULL_PUPD_R1R0_TYPE,/*59*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*60*/ MTK_PULL_PUPD_R1R0_TYPE,/*61*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*62*/ MTK_PULL_PU_PD_TYPE,    /*63*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*64*/ MTK_PULL_PUPD_R1R0_TYPE,/*65*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*66*/ MTK_PULL_PUPD_R1R0_TYPE,/*67*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*68*/ MTK_PULL_PUPD_R1R0_TYPE,/*69*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*70*/ MTK_PULL_PD_TYPE,       /*71*/
+	MTK_PULL_PD_TYPE,       /*72*/ MTK_PULL_PUPD_R1R0_TYPE,/*73*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*74*/ MTK_PULL_PU_PD_TYPE,    /*75*/
+	MTK_PULL_PU_PD_TYPE,    /*76*/ MTK_PULL_PU_PD_TYPE,    /*77*/
+	MTK_PULL_PU_PD_TYPE,    /*78*/ MTK_PULL_PU_PD_TYPE,    /*79*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*80*/ MTK_PULL_PUPD_R1R0_TYPE,/*81*/
+	MTK_PULL_PUPD_R1R0_TYPE,/*82*/ MTK_PULL_PUPD_R1R0_TYPE,/*83*/
+};
+
 static const struct mtk_pin_reg_calc mt7988_reg_cals[] = {
 	[PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7988_pin_mode_range),
 	[PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7988_pin_dir_range),
@@ -1433,6 +1478,9 @@
 	.bias_disable_get = mtk_pinconf_bias_disable_get,
 	.bias_set = mtk_pinconf_bias_set,
 	.bias_get = mtk_pinconf_bias_get,
+	.pull_type = mt7988_pull_type,
+	.bias_set_combo = mtk_pinconf_bias_set_combo,
+	.bias_get_combo = mtk_pinconf_bias_get_combo,
 	.drive_set = mtk_pinconf_drive_set_rev1,
 	.drive_get = mtk_pinconf_drive_get_rev1,
 	.adv_pull_get = mtk_pinconf_adv_pull_get,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
index d661a18..fb27f0d 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/mt7988.cfg
@@ -7,6 +7,7 @@
 CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_GANG=y
 # CONFIG_AQUANTIA_PHY_FW_DOWNLOAD_SINGLE is not set
 CONFIG_AQUANTIA_PHY_FW_FILE="Rhe-05.06-Candidate9-AQR_Mediatek_23B_P5_ID45824_LCLVER1.cld"
+CONFIG_AQUANTIA_PHY_MIB=y
 CONFIG_ARCH_CLOCKSOURCE_DATA=y
 CONFIG_ARCH_DMA_ADDR_T_64BIT=y
 CONFIG_ARCH_KEEP_MEMBLOCK=y
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1000-backport-pinctrl-pinconf-setting-combo.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1000-backport-pinctrl-pinconf-setting-combo.patch
new file mode 100644
index 0000000..43ca0c5
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-1000-backport-pinctrl-pinconf-setting-combo.patch
@@ -0,0 +1,632 @@
+--- a/drivers/pinctrl/mediatek/pinctrl-moore.c
++++ b/drivers/pinctrl/mediatek/pinctrl-moore.c
+@@ -99,14 +99,22 @@ static int mtk_pinconf_get(struct pinctr
+ {
+ 	struct mtk_pinctrl *hw = pinctrl_dev_get_drvdata(pctldev);
+ 	u32 param = pinconf_to_config_param(*config);
+-	int val, val2, err, reg, ret = 1;
++	int val, val2, err, pullup, reg, ret = 1;
+ 	const struct mtk_pin_desc *desc;
+ 
+ 	desc = (const struct mtk_pin_desc *)&hw->soc->pins[pin];
++	if (!desc->name)
++		return -ENOTSUPP;
+ 
+ 	switch (param) {
+ 	case PIN_CONFIG_BIAS_DISABLE:
+-		if (hw->soc->bias_disable_get) {
++		if (hw->soc->bias_get_combo) {
++			err = hw->soc->bias_get_combo(hw, desc, &pullup, &ret);
++			if (err)
++				return err;
++			if (ret != MTK_PUPD_SET_R1R0_00 && ret != MTK_DISABLE)
++				return -EINVAL;
++		} else if (hw->soc->bias_disable_get) {
+ 			err = hw->soc->bias_disable_get(hw, desc, &ret);
+ 			if (err)
+ 				return err;
+@@ -115,7 +123,15 @@ static int mtk_pinconf_get(struct pinctr
+ 		}
+ 		break;
+ 	case PIN_CONFIG_BIAS_PULL_UP:
+-		if (hw->soc->bias_get) {
++		if (hw->soc->bias_get_combo) {
++			err = hw->soc->bias_get_combo(hw, desc, &pullup, &ret);
++			if (err)
++				return err;
++			if (ret == MTK_PUPD_SET_R1R0_00 || ret == MTK_DISABLE)
++				return -EINVAL;
++			if (!pullup)
++				return -EINVAL;
++		} else if (hw->soc->bias_get) {
+ 			err = hw->soc->bias_get(hw, desc, 1, &ret);
+ 			if (err)
+ 				return err;
+@@ -124,7 +140,15 @@ static int mtk_pinconf_get(struct pinctr
+ 		}
+ 		break;
+ 	case PIN_CONFIG_BIAS_PULL_DOWN:
+-		if (hw->soc->bias_get) {
++		if (hw->soc->bias_get_combo) {
++			err = hw->soc->bias_get_combo(hw, desc, &pullup, &ret);
++			if (err)
++				return err;
++			if (ret == MTK_PUPD_SET_R1R0_00 || ret == MTK_DISABLE)
++				return -EINVAL;
++			if (pullup)
++				return -EINVAL;
++		} else if (hw->soc->bias_get) {
+ 			err = hw->soc->bias_get(hw, desc, 0, &ret);
+ 			if (err)
+ 				return err;
+@@ -218,14 +242,19 @@ static int mtk_pinconf_set(struct pinctr
+ 	int cfg, err = 0;
+ 
+ 	desc = (const struct mtk_pin_desc *)&hw->soc->pins[pin];
++	if (!desc->name)
++		return -ENOTSUPP;
+ 
+ 	for (cfg = 0; cfg < num_configs; cfg++) {
+ 		param = pinconf_to_config_param(configs[cfg]);
+ 		arg = pinconf_to_config_argument(configs[cfg]);
+-
+ 		switch (param) {
+ 		case PIN_CONFIG_BIAS_DISABLE:
+-			if (hw->soc->bias_disable_set) {
++			if (hw->soc->bias_set_combo) {
++				err = hw->soc->bias_set_combo(hw, desc, 0, MTK_DISABLE);
++				if (err)
++					return err;
++			} else if (hw->soc->bias_disable_set) {
+ 				err = hw->soc->bias_disable_set(hw, desc);
+ 				if (err)
+ 					return err;
+@@ -234,7 +263,11 @@ static int mtk_pinconf_set(struct pinctr
+ 			}
+ 			break;
+ 		case PIN_CONFIG_BIAS_PULL_UP:
+-			if (hw->soc->bias_set) {
++			if (hw->soc->bias_set_combo) {
++				err = hw->soc->bias_set_combo(hw, desc, 1, arg);
++				if (err)
++					return err;
++			} else if (hw->soc->bias_set) {
+ 				err = hw->soc->bias_set(hw, desc, 1);
+ 				if (err)
+ 					return err;
+@@ -243,7 +276,11 @@ static int mtk_pinconf_set(struct pinctr
+ 			}
+ 			break;
+ 		case PIN_CONFIG_BIAS_PULL_DOWN:
+-			if (hw->soc->bias_set) {
++			if (hw->soc->bias_set_combo) {
++				err = hw->soc->bias_set_combo(hw, desc, 0, arg);
++				if (err)
++					return err;
++			} else if (hw->soc->bias_set) {
+ 				err = hw->soc->bias_set(hw, desc, 0);
+ 				if (err)
+ 					return err;
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
+@@ -506,6 +506,404 @@ int mtk_pinconf_bias_get_rev1(struct mtk
+ 	return 0;
+ }
+ 
++/* Combo for the following pull register type:
++ * 1. PU + PD
++ * 2. PULLSEL + PULLEN
++ * 3. PUPD + R0 + R1
++ */
++static int mtk_pinconf_bias_set_pu_pd(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 pullup, u32 arg)
++{
++	int err, pu, pd;
++
++	if (arg == MTK_DISABLE) {
++		pu = 0;
++		pd = 0;
++	} else if ((arg == MTK_ENABLE) && pullup) {
++		pu = 1;
++		pd = 0;
++	} else if ((arg == MTK_ENABLE) && !pullup) {
++		pu = 0;
++		pd = 1;
++	} else {
++		err = -EINVAL;
++		goto out;
++	}
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PU, pu);
++	if (err)
++		goto out;
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PD, pd);
++
++out:
++	return err;
++}
++
++static int mtk_pinconf_bias_set_pullsel_pullen(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 pullup, u32 arg)
++{
++	int err, enable;
++
++	if (arg == MTK_DISABLE)
++		enable = 0;
++	else if (arg == MTK_ENABLE)
++		enable = 1;
++	else {
++		err = -EINVAL;
++		goto out;
++	}
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PULLEN, enable);
++	if (err)
++		goto out;
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PULLSEL, pullup);
++
++out:
++	return err;
++}
++
++static int mtk_pinconf_bias_set_pupd_r1_r0(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 pullup, u32 arg)
++{
++	int err, r0, r1;
++
++	if ((arg == MTK_DISABLE) || (arg == MTK_PUPD_SET_R1R0_00)) {
++		pullup = 0;
++		r0 = 0;
++		r1 = 0;
++	} else if (arg == MTK_PUPD_SET_R1R0_01) {
++		r0 = 1;
++		r1 = 0;
++	} else if (arg == MTK_PUPD_SET_R1R0_10) {
++		r0 = 0;
++		r1 = 1;
++	} else if (arg == MTK_PUPD_SET_R1R0_11) {
++		r0 = 1;
++		r1 = 1;
++	} else {
++		err = -EINVAL;
++		goto out;
++	}
++
++	/* MTK HW PUPD bit: 1 for pull-down, 0 for pull-up */
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PUPD, !pullup);
++	if (err)
++		goto out;
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_R0, r0);
++	if (err)
++		goto out;
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_R1, r1);
++
++out:
++	return err;
++}
++
++static int mtk_hw_pin_rsel_lookup(struct mtk_pinctrl *hw,
++				  const struct mtk_pin_desc *desc,
++				  u32 pullup, u32 arg, u32 *rsel_val)
++{
++	const struct mtk_pin_rsel *rsel;
++	int check;
++	bool found = false;
++
++	rsel = hw->soc->pin_rsel;
++
++	for (check = 0; check <= hw->soc->npin_rsel - 1; check++) {
++		if (desc->number >= rsel[check].s_pin &&
++		    desc->number <= rsel[check].e_pin) {
++			if (pullup) {
++				if (rsel[check].up_rsel == arg) {
++					found = true;
++					*rsel_val = rsel[check].rsel_index;
++					break;
++				}
++			} else {
++				if (rsel[check].down_rsel == arg) {
++					found = true;
++					*rsel_val = rsel[check].rsel_index;
++					break;
++				}
++			}
++		}
++	}
++
++	if (!found) {
++		dev_err(hw->dev, "Not support rsel value %d Ohm for pin = %d (%s)\n",
++			arg, desc->number, desc->name);
++		return -ENOTSUPP;
++	}
++
++	return 0;
++}
++
++static int mtk_pinconf_bias_set_rsel(struct mtk_pinctrl *hw,
++				     const struct mtk_pin_desc *desc,
++				     u32 pullup, u32 arg)
++{
++	int err, rsel_val;
++
++	if (!pullup && arg == MTK_DISABLE)
++		return 0;
++
++	if (hw->rsel_si_unit) {
++		/* find pin rsel_index from pin_rsel array*/
++		err = mtk_hw_pin_rsel_lookup(hw, desc, pullup, arg, &rsel_val);
++		if (err)
++			goto out;
++	} else {
++		if (arg < MTK_PULL_SET_RSEL_000 ||
++		    arg > MTK_PULL_SET_RSEL_111) {
++			err = -EINVAL;
++			goto out;
++		}
++
++		rsel_val = arg - MTK_PULL_SET_RSEL_000;
++	}
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_RSEL, rsel_val);
++	if (err)
++		goto out;
++
++	err = mtk_pinconf_bias_set_pu_pd(hw, desc, pullup, MTK_ENABLE);
++
++out:
++	return err;
++}
++
++int mtk_pinconf_bias_set_combo(struct mtk_pinctrl *hw,
++			       const struct mtk_pin_desc *desc,
++			       u32 pullup, u32 arg)
++{
++	int err = -ENOTSUPP;
++	u32 try_all_type;
++
++	if (hw->soc->pull_type)
++		try_all_type = hw->soc->pull_type[desc->number];
++	else
++		try_all_type = MTK_PULL_TYPE_MASK;
++
++	if (try_all_type & MTK_PULL_RSEL_TYPE) {
++		err = mtk_pinconf_bias_set_rsel(hw, desc, pullup, arg);
++		if (!err)
++			return err;
++	}
++
++	if (try_all_type & MTK_PULL_PU_PD_TYPE) {
++		err = mtk_pinconf_bias_set_pu_pd(hw, desc, pullup, arg);
++		if (!err)
++			return err;
++	}
++
++	if (try_all_type & MTK_PULL_PULLSEL_TYPE) {
++		err = mtk_pinconf_bias_set_pullsel_pullen(hw, desc,
++							  pullup, arg);
++		if (!err)
++			return err;
++	}
++
++	if (try_all_type & MTK_PULL_PUPD_R1R0_TYPE)
++		err = mtk_pinconf_bias_set_pupd_r1_r0(hw, desc, pullup, arg);
++
++	if (err)
++		dev_err(hw->dev, "Invalid pull argument\n");
++
++	return err;
++}
++EXPORT_SYMBOL_GPL(mtk_pinconf_bias_set_combo);
++
++static int mtk_rsel_get_si_unit(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 pullup, u32 rsel_val, u32 *si_unit)
++{
++	const struct mtk_pin_rsel *rsel;
++	int check;
++
++	rsel = hw->soc->pin_rsel;
++
++	for (check = 0; check <= hw->soc->npin_rsel - 1; check++) {
++		if (desc->number >= rsel[check].s_pin &&
++		    desc->number <= rsel[check].e_pin) {
++			if (rsel_val == rsel[check].rsel_index) {
++				if (pullup)
++					*si_unit = rsel[check].up_rsel;
++				else
++					*si_unit = rsel[check].down_rsel;
++				break;
++			}
++		}
++	}
++
++	return 0;
++}
++
++static int mtk_pinconf_bias_get_rsel(struct mtk_pinctrl *hw,
++				     const struct mtk_pin_desc *desc,
++				     u32 *pullup, u32 *enable)
++{
++	int pu, pd, rsel, err;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_RSEL, &rsel);
++	if (err)
++		goto out;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PU, &pu);
++	if (err)
++		goto out;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PD, &pd);
++	if (err)
++		goto out;
++
++	if (pu == 0 && pd == 0) {
++		*pullup = 0;
++		*enable = MTK_DISABLE;
++	} else if (pu == 1 && pd == 0) {
++		*pullup = 1;
++		if (hw->rsel_si_unit)
++			mtk_rsel_get_si_unit(hw, desc, *pullup, rsel, enable);
++		else
++			*enable = rsel + MTK_PULL_SET_RSEL_000;
++	} else if (pu == 0 && pd == 1) {
++		*pullup = 0;
++		if (hw->rsel_si_unit)
++			mtk_rsel_get_si_unit(hw, desc, *pullup, rsel, enable);
++		else
++			*enable = rsel + MTK_PULL_SET_RSEL_000;
++	} else {
++		err = -EINVAL;
++		goto out;
++	}
++
++out:
++	return err;
++}
++
++static int mtk_pinconf_bias_get_pu_pd(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 *pullup, u32 *enable)
++{
++	int err, pu, pd;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PU, &pu);
++	if (err)
++		goto out;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PD, &pd);
++	if (err)
++		goto out;
++
++	if (pu == 0 && pd == 0) {
++		*pullup = 0;
++		*enable = MTK_DISABLE;
++	} else if (pu == 1 && pd == 0) {
++		*pullup = 1;
++		*enable = MTK_ENABLE;
++	} else if (pu == 0 && pd == 1) {
++		*pullup = 0;
++		*enable = MTK_ENABLE;
++	} else
++		err = -EINVAL;
++
++out:
++	return err;
++}
++
++static int mtk_pinconf_bias_get_pullsel_pullen(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 *pullup, u32 *enable)
++{
++	int err;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PULLSEL, pullup);
++	if (err)
++		goto out;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PULLEN, enable);
++
++out:
++	return err;
++}
++
++static int mtk_pinconf_bias_get_pupd_r1_r0(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 *pullup, u32 *enable)
++{
++	int err, r0, r1;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PUPD, pullup);
++	if (err)
++		goto out;
++	/* MTK HW PUPD bit: 1 for pull-down, 0 for pull-up */
++	*pullup = !(*pullup);
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_R0, &r0);
++	if (err)
++		goto out;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_R1, &r1);
++	if (err)
++		goto out;
++
++	if ((r1 == 0) && (r0 == 0))
++		*enable = MTK_PUPD_SET_R1R0_00;
++	else if ((r1 == 0) && (r0 == 1))
++		*enable = MTK_PUPD_SET_R1R0_01;
++	else if ((r1 == 1) && (r0 == 0))
++		*enable = MTK_PUPD_SET_R1R0_10;
++	else if ((r1 == 1) && (r0 == 1))
++		*enable = MTK_PUPD_SET_R1R0_11;
++	else
++		err = -EINVAL;
++
++out:
++	return err;
++}
++
++int mtk_pinconf_bias_get_combo(struct mtk_pinctrl *hw,
++			      const struct mtk_pin_desc *desc,
++			      u32 *pullup, u32 *enable)
++{
++	int err = -ENOTSUPP;
++	u32 try_all_type;
++
++	if (hw->soc->pull_type)
++		try_all_type = hw->soc->pull_type[desc->number];
++	else
++		try_all_type = MTK_PULL_TYPE_MASK;
++
++	if (try_all_type & MTK_PULL_RSEL_TYPE) {
++		err = mtk_pinconf_bias_get_rsel(hw, desc, pullup, enable);
++		if (!err)
++			return err;
++	}
++
++	if (try_all_type & MTK_PULL_PU_PD_TYPE) {
++		err = mtk_pinconf_bias_get_pu_pd(hw, desc, pullup, enable);
++		if (!err)
++			return err;
++	}
++
++	if (try_all_type & MTK_PULL_PULLSEL_TYPE) {
++		err = mtk_pinconf_bias_get_pullsel_pullen(hw, desc,
++							  pullup, enable);
++		if (!err)
++			return err;
++	}
++
++	if (try_all_type & MTK_PULL_PUPD_R1R0_TYPE)
++		err = mtk_pinconf_bias_get_pupd_r1_r0(hw, desc, pullup, enable);
++
++	return err;
++}
++EXPORT_SYMBOL_GPL(mtk_pinconf_bias_get_combo);
++
+ /* Revision 0 */
+ int mtk_pinconf_drive_set(struct mtk_pinctrl *hw,
+ 			  const struct mtk_pin_desc *desc, u32 arg)
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
+@@ -17,6 +17,34 @@
+ #define MTK_ENABLE     1
+ #define MTK_PULLDOWN   0
+ #define MTK_PULLUP     1
++#define MTK_PULL_PU_PD_TYPE		BIT(0)
++#define MTK_PULL_PULLSEL_TYPE		BIT(1)
++#define MTK_PULL_PUPD_R1R0_TYPE 	BIT(2)
++/* MTK_PULL_RSEL_TYPE can select resistance and can be
++ * turned on/off itself. But it can't be selected pull up/down
++ */
++#define MTK_PULL_RSEL_TYPE		BIT(3)
++/* MTK_PULL_PU_PD_RSEL_TYPE is a type which is controlled by
++ * MTK_PULL_PU_PD_TYPE and MTK_PULL_RSEL_TYPE.
++ */
++#define MTK_PULL_PU_PD_RSEL_TYPE	(MTK_PULL_PU_PD_TYPE \
++					| MTK_PULL_RSEL_TYPE)
++#define MTK_PULL_TYPE_MASK	(MTK_PULL_PU_PD_TYPE |\
++				 MTK_PULL_PULLSEL_TYPE |\
++				 MTK_PULL_PUPD_R1R0_TYPE |\
++				 MTK_PULL_RSEL_TYPE)
++#define MTK_PUPD_SET_R1R0_00 100
++#define MTK_PUPD_SET_R1R0_01 101
++#define MTK_PUPD_SET_R1R0_10 102
++#define MTK_PUPD_SET_R1R0_11 103
++#define MTK_PULL_SET_RSEL_000  200
++#define MTK_PULL_SET_RSEL_001  201
++#define MTK_PULL_SET_RSEL_010  202
++#define MTK_PULL_SET_RSEL_011  203
++#define MTK_PULL_SET_RSEL_100  204
++#define MTK_PULL_SET_RSEL_101  205
++#define MTK_PULL_SET_RSEL_110  206
++#define MTK_PULL_SET_RSEL_111  207
+ 
+ #define EINT_NA	U16_MAX
+ #define NO_EINT_SUPPORT	EINT_NA
+@@ -66,6 +94,8 @@ enum {
+ 	PINCTRL_PIN_REG_DRV_EN,
+ 	PINCTRL_PIN_REG_DRV_E0,
+ 	PINCTRL_PIN_REG_DRV_E1,
++	PINCTRL_PIN_REG_DRV_ADV,
++	PINCTRL_PIN_REG_RSEL,
+ 	PINCTRL_PIN_REG_MAX,
+ };
+ 
+@@ -101,6 +131,22 @@ struct mtk_pin_field {
+ 	u8  next;
+ };
+ 
++/**
++ * struct mtk_pin_rsel - the structure that provides bias resistance selection.
++ * @s_pin:		the start pin within the rsel range
++ * @e_pin:		the end pin within the rsel range
++ * @rsel_index:	the rsel bias resistance index
++ * @up_rsel:	the pullup rsel bias resistance value
++ * @down_rsel:	the pulldown rsel bias resistance value
++ */
++struct mtk_pin_rsel {
++	u16 s_pin;
++	u16 e_pin;
++	u16 rsel_index;
++	u32 up_rsel;
++	u32 down_rsel;
++};
++
+ /* struct mtk_pin_field_calc - the structure that holds the range providing
+  *			       the guide used to look up the relevant field
+  * @s_pin:		the start pin within the range
+@@ -205,6 +251,9 @@ struct mtk_pin_soc {
+ 	bool				ies_present;
+ 	const char * const		*base_names;
+ 	unsigned int			nbase_names;
++    	const unsigned int		*pull_type;
++	const struct mtk_pin_rsel	*pin_rsel;
++	unsigned int			npin_rsel;
+ 
+ 	/* Specific pinconfig operations */
+ 	int (*bias_disable_set)(struct mtk_pinctrl *hw,
+@@ -215,7 +264,10 @@ struct mtk_pin_soc {
+ 			const struct mtk_pin_desc *desc, bool pullup);
+ 	int (*bias_get)(struct mtk_pinctrl *hw,
+ 			const struct mtk_pin_desc *desc, bool pullup, int *res);
+-
++	int (*bias_set_combo)(struct mtk_pinctrl *hw,
++			      const struct mtk_pin_desc *desc, u32 pullup, u32 arg);
++	int (*bias_get_combo)(struct mtk_pinctrl *hw,
++			      const struct mtk_pin_desc *desc, u32 *pullup, u32 *arg);
+ 	int (*drive_set)(struct mtk_pinctrl *hw,
+ 			 const struct mtk_pin_desc *desc, u32 arg);
+ 	int (*drive_get)(struct mtk_pinctrl *hw,
+@@ -246,6 +298,10 @@ struct mtk_pinctrl {
+ 	struct mtk_eint			*eint;
+ 	struct mtk_pinctrl_group	*groups;
+ 	const char          **grp_names;
++	/* lock pin's register resource to avoid multiple threads issue*/
++	spinlock_t lock;
++	/* identify rsel setting by si unit or rsel define in dts node */
++	bool rsel_si_unit;
+ };
+ 
+ void mtk_rmw(struct mtk_pinctrl *pctl, u8 i, u32 reg, u32 mask, u32 set);
+@@ -282,7 +338,12 @@ int mtk_pinconf_drive_set(struct mtk_pin
+ 			  const struct mtk_pin_desc *desc, u32 arg);
+ int mtk_pinconf_drive_get(struct mtk_pinctrl *hw,
+ 			  const struct mtk_pin_desc *desc, int *val);
+-
++int mtk_pinconf_bias_set_combo(struct mtk_pinctrl *hw,
++			       const struct mtk_pin_desc *desc,
++			       u32 pullup, u32 enable);
++int mtk_pinconf_bias_get_combo(struct mtk_pinctrl *hw,
++			       const struct mtk_pin_desc *desc,
++			       u32 *pullup, u32 *enable);
+ int mtk_pinconf_drive_set_rev1(struct mtk_pinctrl *hw,
+ 			       const struct mtk_pin_desc *desc, u32 arg);
+ int mtk_pinconf_drive_get_rev1(struct mtk_pinctrl *hw,
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2023-pinctrl-add-mt7988-pd-pulltype-support.patch b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2023-pinctrl-add-mt7988-pd-pulltype-support.patch
new file mode 100644
index 0000000..6ef9357
--- /dev/null
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/999-2023-pinctrl-add-mt7988-pd-pulltype-support.patch
@@ -0,0 +1,99 @@
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
+@@ -541,6 +541,30 @@ out:
+ 	return err;
+ }
+ 
++static int mtk_pinconf_bias_set_pd(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 pullup, u32 arg)
++{
++    int err, pd;
++
++	if (arg == MTK_DISABLE)
++		pd = 0;
++	else if ((arg == MTK_ENABLE) && pullup)
++		pd = 0;
++	else if ((arg == MTK_ENABLE) && !pullup)
++		pd = 1;
++	else {
++		err = -EINVAL;
++		goto out;
++	}
++
++	err = mtk_hw_set_value(hw, desc, PINCTRL_PIN_REG_PD, pd);
++
++out:
++	return err;
++
++}
++
+ static int mtk_pinconf_bias_set_pullsel_pullen(struct mtk_pinctrl *hw,
+ 				const struct mtk_pin_desc *desc,
+ 				u32 pullup, u32 arg)
+@@ -695,6 +719,12 @@ int mtk_pinconf_bias_set_combo(struct mt
+ 			return err;
+ 	}
+ 
++	if (try_all_type & MTK_PULL_PD_TYPE) {
++		err = mtk_pinconf_bias_set_pd(hw, desc, pullup, arg);
++		if (!err)
++			return err;
++    }
++
+ 	if (try_all_type & MTK_PULL_PU_PD_TYPE) {
+ 		err = mtk_pinconf_bias_set_pu_pd(hw, desc, pullup, arg);
+ 		if (!err)
+@@ -815,6 +845,29 @@ out:
+ 	return err;
+ }
+ 
++static int mtk_pinconf_bias_get_pd(struct mtk_pinctrl *hw,
++				const struct mtk_pin_desc *desc,
++				u32 *pullup, u32 *enable)
++{
++	int err, pd;
++
++	err = mtk_hw_get_value(hw, desc, PINCTRL_PIN_REG_PD, &pd);
++	if (err)
++		goto out;
++
++	if (pd == 0) {
++		*pullup = 0;
++		*enable = MTK_DISABLE;
++	} else if (pd == 1) {
++		*pullup = 0;
++		*enable = MTK_ENABLE;
++	} else
++		err = -EINVAL;
++
++out:
++	return err;
++}
++
+ static int mtk_pinconf_bias_get_pullsel_pullen(struct mtk_pinctrl *hw,
+ 				const struct mtk_pin_desc *desc,
+ 				u32 *pullup, u32 *enable)
+@@ -883,6 +936,12 @@ int mtk_pinconf_bias_get_combo(struct mt
+ 		if (!err)
+ 			return err;
+ 	}
++
++	if (try_all_type & MTK_PULL_PD_TYPE) {
++		err = mtk_pinconf_bias_get_pd(hw, desc, pullup, enable);
++		if (!err)
++			return err;
++	}
+ 
+ 	if (try_all_type & MTK_PULL_PU_PD_TYPE) {
+ 		err = mtk_pinconf_bias_get_pu_pd(hw, desc, pullup, enable);
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.h
+@@ -24,6 +24,7 @@
+  * turned on/off itself. But it can't be selected pull up/down
+  */
+ #define MTK_PULL_RSEL_TYPE		BIT(3)
++#define MTK_PULL_PD_TYPE        BIT(4)
+ /* MTK_PULL_PU_PD_RSEL_TYPE is a type which is controlled by
+  * MTK_PULL_PU_PD_TYPE and MTK_PULL_RSEL_TYPE.
+  */
diff --git a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
index 3fce397..1fe0212 100644
--- a/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
+++ b/recipes-kernel/linux/linux-mediatek-5.4/mediatek/patches-5.4/patches-5.4.inc
@@ -35,6 +35,7 @@
     file://1010-pcie-mediatek-fix-clearing-interrupt-status.patch \
     file://1020-spi-nor-w25q512jv.patch \
     file://1021-ubnt-ledbar-driver.patch \
+    file://999-1000-backport-pinctrl-pinconf-setting-combo.patch \
     file://999-1600-mdiobus-add-c45.patch \
     file://999-1700-macsec-revert-async-support.patch \
     file://999-1701-add-default-setting-to-dsa-unused-port.patch \
@@ -58,6 +59,7 @@
     file://999-2020-pinctrl-add-mt7986-driver.patch \
     file://999-2021-pinctrl-enable-mt7988-pinctrl-config.patch \
     file://999-2022-pinctrl-add-mt7981-driver.patch \
+    file://999-2023-pinctrl-add-mt7988-pd-pulltype-support.patch \
     file://999-2040-powerdomain-add-mt7988-support.patch \
     file://999-2050-watchdog-add-mt7986-assert.patch \
     file://999-2100-mt7986-trng-add-rng-support.patch \
diff --git a/recipes-wifi/hostapd/files/002-rdkb-add-ucode-support.patch b/recipes-wifi/hostapd/files/002-rdkb-add-ucode-support.patch
new file mode 100644
index 0000000..16e099a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/002-rdkb-add-ucode-support.patch
@@ -0,0 +1,21 @@
+--- a/hostapd/Makefile	2023-08-14 14:07:55.808647300 +0800
++++ b/hostapd/Makefile	2023-08-14 14:09:14.222019605 +0800
+@@ -182,6 +182,7 @@
+ OBJS += ../src/utils/ucode.o
+ OBJS += ../src/ap/ucode.o
+ NEED_ULOOP:=y
++LIBS += -lblobmsg_json -lucode
+ endif
+ 
+ ifdef NEED_ULOOP
+
+--- a/wpa_supplicant/Makefile	2023-08-14 17:51:44.351944100 +0800
++++ b/wpa_supplicant/Makefile	2023-08-14 17:54:54.232111091 +0800
+@@ -206,6 +206,7 @@
+ OBJS += ../src/utils/ucode.o
+ OBJS += ucode.o
+ NEED_ULOOP:=y
++LIBS += -lblobmsg_json -lucode
+ endif
+ 
+ ifdef NEED_ULOOP
\ No newline at end of file
diff --git a/recipes-wifi/hostapd/files/common.uc b/recipes-wifi/hostapd/files/common.uc
new file mode 100644
index 0000000..9ece3b1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/common.uc
@@ -0,0 +1,168 @@
+import * as nl80211 from "nl80211";
+import * as rtnl from "rtnl";
+import { readfile } from "fs";
+
+const iftypes = {
+	ap: nl80211.const.NL80211_IFTYPE_AP,
+	mesh: nl80211.const.NL80211_IFTYPE_MESH_POINT,
+	sta: nl80211.const.NL80211_IFTYPE_STATION,
+	adhoc: nl80211.const.NL80211_IFTYPE_ADHOC,
+	monitor: nl80211.const.NL80211_IFTYPE_MONITOR,
+};
+
+function wdev_remove(name)
+{
+	nl80211.request(nl80211.const.NL80211_CMD_DEL_INTERFACE, 0, { dev: name });
+}
+
+function __phy_is_fullmac(phyidx)
+{
+	let data = nl80211.request(nl80211.const.NL80211_CMD_GET_WIPHY, 0, { wiphy: phyidx });
+
+	return !data.software_iftypes.ap_vlan;
+}
+
+function phy_is_fullmac(phy)
+{
+	let phyidx = int(trim(readfile(`/sys/class/ieee80211/${phy}/index`)));
+
+	return __phy_is_fullmac(phyidx);
+}
+
+function find_reusable_wdev(phyidx)
+{
+	if (!__phy_is_fullmac(phyidx))
+		return null;
+
+	let data = nl80211.request(
+		nl80211.const.NL80211_CMD_GET_INTERFACE,
+		nl80211.const.NLM_F_DUMP,
+		{ wiphy: phyidx });
+	for (let res in data)
+		if (trim(readfile(`/sys/class/net/${res.ifname}/operstate`)) == "down")
+			return res.ifname;
+	return null;
+}
+
+function wdev_create(phy, name, data)
+{
+	let phyidx = int(readfile(`/sys/class/ieee80211/${phy}/index`));
+
+	wdev_remove(name);
+
+	if (!iftypes[data.mode])
+		return `Invalid mode: ${data.mode}`;
+
+	let req = {
+		wiphy: phyidx,
+		ifname: name,
+		iftype: iftypes[data.mode],
+	};
+
+	if (data["4addr"])
+		req["4addr"] = data["4addr"];
+	if (data.macaddr)
+		req.mac = data.macaddr;
+
+	nl80211.error();
+
+	let reuse_ifname = find_reusable_wdev(phyidx);
+	if (reuse_ifname &&
+	    (reuse_ifname == name ||
+	     rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false))
+		nl80211.request(
+			nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
+				wiphy: phyidx,
+				dev: name,
+				iftype: iftypes[data.mode],
+			});
+	else
+		nl80211.request(
+			nl80211.const.NL80211_CMD_NEW_INTERFACE,
+			nl80211.const.NLM_F_CREATE,
+			req);
+
+	let error = nl80211.error();
+	if (error)
+		return error;
+
+	if (data.powersave != null) {
+		nl80211.request(nl80211.const.NL80211_CMD_SET_POWER_SAVE, 0,
+			{ dev: name, ps_state: data.powersave ? 1 : 0});
+	}
+
+	return null;
+}
+
+const vlist_proto = {
+	update: function(values, arg) {
+		let data = this.data;
+		let cb = this.cb;
+		let seq = { };
+		let new_data = {};
+		let old_data = {};
+
+		this.data = new_data;
+
+		if (type(values) == "object") {
+			for (let key in values) {
+				old_data[key] = data[key];
+				new_data[key] = values[key];
+				delete data[key];
+			}
+		} else {
+			for (let val in values) {
+				let cur_key = val[0];
+				let cur_obj = val[1];
+
+				old_data[cur_key] = data[cur_key];
+				new_data[cur_key] = val[1];
+				delete data[cur_key];
+			}
+		}
+
+		for (let key in data) {
+			cb(null, data[key], arg);
+			delete data[key];
+		}
+		for (let key in new_data)
+			cb(new_data[key], old_data[key], arg);
+	}
+};
+
+function is_equal(val1, val2) {
+	let t1 = type(val1);
+
+	if (t1 != type(val2))
+		return false;
+
+	if (t1 == "array") {
+		if (length(val1) != length(val2))
+			return false;
+
+		for (let i = 0; i < length(val1); i++)
+			if (!is_equal(val1[i], val2[i]))
+				return false;
+
+		return true;
+	} else if (t1 == "object") {
+		for (let key in val1)
+			if (!is_equal(val1[key], val2[key]))
+				return false;
+		for (let key in val2)
+			if (!val1[key])
+				return false;
+		return true;
+	} else {
+		return val1 == val2;
+	}
+}
+
+function vlist_new(cb) {
+	return proto({
+			cb: cb,
+			data: {}
+		}, vlist_proto);
+}
+
+export { wdev_remove, wdev_create, is_equal, vlist_new, phy_is_fullmac };
diff --git a/recipes-wifi/hostapd/files/hostapd.uc b/recipes-wifi/hostapd/files/hostapd.uc
new file mode 100644
index 0000000..b52732a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/hostapd.uc
@@ -0,0 +1,402 @@
+let libubus = require("ubus");
+import { open, readfile } from "fs";
+import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac } from "common";
+
+let ubus = libubus.connect();
+
+hostapd.data.config = {};
+
+hostapd.data.file_fields = {
+	vlan_file: true,
+	wpa_psk_file: true,
+	accept_mac_file: true,
+	deny_mac_file: true,
+	eap_user_file: true,
+	ca_cert: true,
+	server_cert: true,
+	server_cert2: true,
+	private_key: true,
+	private_key2: true,
+	dh_file: true,
+	eap_sim_db: true,
+};
+
+function iface_remove(cfg)
+{
+	if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
+		return;
+
+	hostapd.remove_iface(cfg.bss[0].ifname);
+	for (let bss in cfg.bss)
+		wdev_remove(bss.ifname);
+}
+
+function iface_gen_config(phy, config)
+{
+	let str = `data:
+${join("\n", config.radio.data)}
+channel=${config.radio.channel}
+`;
+
+	for (let i = 0; i < length(config.bss); i++) {
+		let bss = config.bss[i];
+		let type = i > 0 ? "bss" : "interface";
+
+		str += `
+${type}=${bss.ifname}
+${join("\n", bss.data)}
+`;
+	}
+
+	return str;
+}
+
+function iface_restart(phy, config, old_config)
+{
+	iface_remove(old_config);
+	iface_remove(config);
+
+	if (!config.bss || !config.bss[0]) {
+		hostapd.printf(`No bss for phy ${phy}`);
+		return;
+	}
+
+	let bss = config.bss[0];
+	let err = wdev_create(phy, bss.ifname, { mode: "ap" });
+	if (err)
+		hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
+	let config_inline = iface_gen_config(phy, config);
+	if (hostapd.add_iface(`bss_config=${bss.ifname}:${config_inline}`) < 0) {
+		hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
+		return;
+	}
+}
+
+function array_to_obj(arr, key, start)
+{
+	let obj = {};
+
+	start ??= 0;
+	for (let i = start; i < length(arr); i++) {
+		let cur = arr[i];
+		obj[cur[key]] = cur;
+	}
+
+	return obj;
+}
+
+function find_array_idx(arr, key, val)
+{
+	for (let i = 0; i < length(arr); i++)
+		if (arr[i][key] == val)
+			return i;
+
+	return -1;
+}
+
+function bss_reload_psk(bss, config, old_config)
+{
+	if (is_equal(old_config.hash.wpa_psk_file, config.hash.wpa_psk_file))
+		return;
+
+	old_config.hash.wpa_psk_file = config.hash.wpa_psk_file;
+	if (!is_equal(old_config, config))
+		return;
+
+	let ret = bss.ctrl("RELOAD_WPA_PSK");
+	ret ??= "failed";
+
+	hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
+}
+
+function iface_reload_config(phy, config, old_config)
+{
+	if (!old_config || !is_equal(old_config.radio, config.radio))
+		return false;
+
+	if (is_equal(old_config.bss, config.bss))
+		return true;
+
+	if (!old_config.bss || !old_config.bss[0])
+		return false;
+
+	if (config.bss[0].ifname != old_config.bss[0].ifname)
+		return false;
+
+	let iface = hostapd.interfaces[config.bss[0].ifname];
+	if (!iface)
+		return false;
+
+	let config_inline = iface_gen_config(phy, config);
+
+	bss_reload_psk(iface.bss[0], config.bss[0], old_config.bss[0]);
+	if (!is_equal(config.bss[0], old_config.bss[0])) {
+		if (phy_is_fullmac(phy))
+			return false;
+
+		hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
+		if (iface.bss[0].set_config(config_inline, 0) < 0) {
+			hostapd.printf(`Failed to set config`);
+			return false;
+		}
+	}
+
+	let bss_list = array_to_obj(iface.bss, "name", 1);
+	let new_cfg = array_to_obj(config.bss, "ifname", 1);
+	let old_cfg = array_to_obj(old_config.bss, "ifname", 1);
+
+	for (let name in old_cfg) {
+		let bss = bss_list[name];
+		if (!bss) {
+			hostapd.printf(`bss '${name}' not found`);
+			return false;
+		}
+
+		if (!new_cfg[name]) {
+			hostapd.printf(`Remove bss '${name}' on phy '${phy}'`);
+			bss.delete();
+			wdev_remove(name);
+			continue;
+		}
+
+		let new_cfg_data = new_cfg[name];
+		delete new_cfg[name];
+
+		if (is_equal(old_cfg[name], new_cfg_data))
+			continue;
+
+		hostapd.printf(`Reload config for bss '${name}' on phy '${phy}'`);
+		let idx = find_array_idx(config.bss, "ifname", name);
+		if (idx < 0) {
+			hostapd.printf(`bss index not found`);
+			return false;
+		}
+
+		if (bss.set_config(config_inline, idx) < 0) {
+			hostapd.printf(`Failed to set config`);
+			return false;
+		}
+	}
+
+	for (let name in new_cfg) {
+		hostapd.printf(`Add bss '${name}' on phy '${phy}'`);
+
+		let idx = find_array_idx(config.bss, "ifname", name);
+		if (idx < 0) {
+			hostapd.printf(`bss index not found`);
+			return false;
+		}
+
+		if (iface.add_bss(config_inline, idx) < 0) {
+			hostapd.printf(`Failed to add bss`);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+function iface_set_config(phy, config)
+{
+	let old_config = hostapd.data.config[phy];
+
+	hostapd.data.config[phy] = config;
+
+	if (!config)
+		return iface_remove(old_config);
+
+	let ret = iface_reload_config(phy, config, old_config);
+	if (ret) {
+		hostapd.printf(`Reloaded settings for phy ${phy}`);
+		return 0;
+	}
+
+	hostapd.printf(`Restart interface for phy ${phy}`);
+	return iface_restart(phy, config, old_config);
+}
+
+function config_add_bss(config, name)
+{
+	let bss = {
+		ifname: name,
+		data: [],
+		hash: {}
+	};
+
+	push(config.bss, bss);
+
+	return bss;
+}
+
+function iface_load_config(filename)
+{
+	let f = open(filename, "r");
+	if (!f)
+		return null;
+
+	let config = {
+		radio: {
+			data: []
+		},
+		bss: [],
+		orig_file: filename,
+	};
+
+	let bss;
+	let line;
+	while ((line = trim(f.read("line"))) != null) {
+		let val = split(line, "=", 2);
+		if (!val[0])
+			continue;
+
+		if (val[0] == "interface") {
+			bss = config_add_bss(config, val[1]);
+			break;
+		}
+
+		if (val[0] == "channel") {
+			config.radio.channel = val[1];
+			continue;
+		}
+
+		push(config.radio.data, line);
+	}
+
+	while ((line = trim(f.read("line"))) != null) {
+		let val = split(line, "=", 2);
+		if (!val[0])
+			continue;
+
+		if (val[0] == "bss") {
+			bss = config_add_bss(config, val[1]);
+			continue;
+		}
+
+		if (hostapd.data.file_fields[val[0]])
+			bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
+
+		push(bss.data, line);
+	}
+	f.close();
+
+	return config;
+}
+
+
+
+let main_obj = {
+	reload: {
+		args: {
+			phy: "",
+		},
+		call: function(req) {
+			try {
+				let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
+				for (let phy_name in phy_list) {
+					let phy = hostapd.data.config[phy_name];
+					let config = iface_load_config(phy.orig_file);
+					iface_set_config(phy_name, config);
+				}
+			} catch(e) {
+				hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
+				return libubus.STATUS_INVALID_ARGUMENT;
+			}
+
+			return 0;
+		}
+	},
+	config_set: {
+		args: {
+			phy: "",
+			config: "",
+			prev_config: "",
+		},
+		call: function(req) {
+			let phy = req.args.phy;
+			let file = req.args.config;
+			let prev_file = req.args.prev_config;
+
+			if (!phy)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			try {
+				if (prev_file && !hostapd.data.config[phy]) {
+					let config = iface_load_config(prev_file);
+					if (config)
+						config.radio.data = [];
+					hostapd.data.config[phy] = config;
+				}
+
+				let config = iface_load_config(file);
+
+				hostapd.printf(`Set new config for phy ${phy}: ${file}`);
+				iface_set_config(phy, config);
+			} catch(e) {
+				hostapd.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
+				return libubus.STATUS_INVALID_ARGUMENT;
+			}
+
+			return {
+				pid: hostapd.getpid()
+			};
+		}
+	},
+	config_add: {
+		args: {
+			iface: "",
+			config: "",
+		},
+		call: function(req) {
+			if (!req.args.iface || !req.args.config)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			if (hostapd.add_iface(`bss_config=${req.args.iface}:${req.args.config}`) < 0)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			return {
+				pid: hostapd.getpid()
+			};
+		}
+	},
+	config_remove: {
+		args: {
+			iface: ""
+		},
+		call: function(req) {
+			if (!req.args.iface)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			hostapd.remove_iface(req.args.iface);
+			return 0;
+		}
+	},
+};
+
+hostapd.data.ubus = ubus;
+hostapd.data.obj = ubus.publish("hostapd", main_obj);
+
+function bss_event(type, name, data) {
+	let ubus = hostapd.data.ubus;
+
+	data ??= {};
+	data.name = name;
+	hostapd.data.obj.notify(`bss.${type}`, data, null, null, null, -1);
+	ubus.call("service", "event", { type: `hostapd.${name}.${type}`, data: {} });
+}
+
+return {
+	shutdown: function() {
+		for (let phy in hostapd.data.config)
+			iface_set_config(phy, null);
+		hostapd.ubus.disconnect();
+	},
+	bss_add: function(name, obj) {
+		bss_event("add", name);
+	},
+	bss_reload: function(name, obj, reconf) {
+		bss_event("reload", name, { reconf: reconf != 0 });
+	},
+	bss_remove: function(name, obj) {
+		bss_event("remove", name);
+	}
+};
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch b/recipes-wifi/hostapd/files/patches-2.10.3/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
similarity index 66%
rename from recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch
rename to recipes-wifi/hostapd/files/patches-2.10.3/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
index 7196118..7b0435a 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
@@ -1,23 +1,32 @@
-From ce585467d784d1015b5a40ec09895d0949690b04 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 3 May 2023 14:55:18 +0800
-Subject: [PATCH 29/32] hostapd: mtk: Fix CCA issue
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 7 Aug 2023 21:55:57 +0200
+Subject: [PATCH] BSS coloring: fix CCA with multiple BSS
 
-When receiving CCA-related nl80211 commands, hostapd used to work on
-struct wpa_driver_nl80211_data, whose ctx always points to
-hostpad_iface->bss[0]. However, CCA commands are sent on a per-BSS based.
-This patch makes hostapd handle CCA-related commands on a per-BSS based.
+Pass bss->ctx instead of drv->ctx in order to avoid multiple reports for
+the first bss. The first report would otherwise clear hapd->cca_color and
+subsequent reports would cause the iface bss color to be set to 0.
+In order to avoid any issues with cancellations, only overwrite the color
+based on hapd->cca_color if it was actually set.
 
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+Fixes: 33c4dd26cd11 ("BSS coloring: Handle the collision and CCA events coming from the kernel")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
 ---
- src/drivers/driver_nl80211_event.c | 24 ++++++++++++------------
- 1 file changed, 12 insertions(+), 12 deletions(-)
 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 3d0d9b7bb..ab988fad7 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2260,7 +2260,8 @@ void wpa_supplicant_event(void *ctx, enu
+ 	case EVENT_CCA_NOTIFY:
+ 		wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ 			   hapd->conf->iface);
+-		hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
++		if (hapd->cca_color)
++			hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+ 		hostapd_cleanup_cca_params(hapd);
+ 		break;
+ #endif /* CONFIG_IEEE80211AX */
 --- a/src/drivers/driver_nl80211_event.c
 +++ b/src/drivers/driver_nl80211_event.c
-@@ -3662,7 +3662,7 @@ static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv,
+@@ -3653,7 +3653,7 @@ static void nl80211_assoc_comeback(struc
  
  #ifdef CONFIG_IEEE80211AX
  
@@ -26,7 +35,7 @@
  					 struct nlattr *tb[])
  {
  	union wpa_event_data data;
-@@ -3676,37 +3676,37 @@ static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
+@@ -3667,37 +3667,37 @@ static void nl80211_obss_color_collision
  
  	wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
  		   (long long unsigned int) data.bss_color_collision.bitmap);
@@ -71,7 +80,7 @@
  }
  
  #endif /* CONFIG_IEEE80211AX */
-@@ -3968,16 +3968,16 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+@@ -3957,16 +3957,16 @@ static void do_process_drv_event(struct
  		break;
  #ifdef CONFIG_IEEE80211AX
  	case NL80211_CMD_OBSS_COLOR_COLLISION:
@@ -92,6 +101,3 @@
  		break;
  #endif /* CONFIG_IEEE80211AX */
  	default:
--- 
-2.39.2
-
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch b/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch
index f7e797a..8ebbed0 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/200-multicall.patch
@@ -189,7 +189,7 @@
  {
  	struct hostapd_data *hapd = ctx;
  #ifndef CONFIG_NO_STDOUT_DEBUG
-@@ -2271,7 +2271,7 @@ void wpa_supplicant_event(void *ctx, enu
+@@ -2272,7 +2272,7 @@ void wpa_supplicant_event(void *ctx, enu
  }
  
  
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch
index f393634..f420c18 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/600-ubus_support.patch
@@ -1,11 +1,12 @@
 --- a/hostapd/Makefile
 +++ b/hostapd/Makefile
-@@ -166,6 +166,11 @@ OBJS += ../src/common/hw_features_common
+@@ -166,6 +166,12 @@ OBJS += ../src/common/hw_features_common
  
  OBJS += ../src/eapol_auth/eapol_auth_sm.o
  
 +ifdef CONFIG_UBUS
 +CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/utils/uloop.o
 +OBJS += ../src/ap/ubus.o
 +LIBS += -lubox -lubus
 +endif
@@ -22,15 +23,6 @@
  
  #define OCE_STA_CFON_ENABLED(hapd) \
  	((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -92,7 +93,7 @@ struct hapd_interfaces {
- #ifdef CONFIG_CTRL_IFACE_UDP
-        unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
- #endif /* CONFIG_CTRL_IFACE_UDP */
--
-+	struct ubus_object ubus;
- };
- 
- enum hostapd_chan_status {
 @@ -184,6 +185,7 @@ struct hostapd_data {
  	struct hostapd_iface *iface;
  	struct hostapd_config *iconf;
@@ -330,20 +322,21 @@
  
 --- a/wpa_supplicant/Makefile
 +++ b/wpa_supplicant/Makefile
-@@ -194,6 +194,12 @@ ifdef CONFIG_EAPOL_TEST
+@@ -194,6 +194,13 @@ ifdef CONFIG_EAPOL_TEST
  CFLAGS += -Werror -DEAPOL_TEST
  endif
  
 +ifdef CONFIG_UBUS
 +CFLAGS += -DUBUS_SUPPORT
 +OBJS += ubus.o
++OBJS += ../src/utils/uloop.o
 +LIBS += -lubox -lubus
 +endif
 +
  ifdef CONFIG_CODE_COVERAGE
  CFLAGS += -O0 -fprofile-arcs -ftest-coverage
  LIBS += -lgcov
-@@ -989,6 +995,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
+@@ -989,6 +996,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
  CFLAGS += -DCONFIG_CTRL_IFACE_MIB
  endif
  OBJS += ../src/ap/ctrl_iface_ap.o
@@ -432,24 +425,6 @@
  	if (wpa_s->conf->wps_cred_processing == 1)
  		return 0;
  
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -991,6 +991,7 @@ int main(int argc, char *argv[])
- 	}
- 
- 	hostapd_global_ctrl_iface_init(&interfaces);
-+	hostapd_ubus_add(&interfaces);
- 
- 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
- 		wpa_printf(MSG_ERROR, "Failed to start eloop");
-@@ -1000,6 +1001,7 @@ int main(int argc, char *argv[])
- 	ret = 0;
- 
-  out:
-+	hostapd_ubus_free(&interfaces);
- 	hostapd_global_ctrl_iface_deinit(&interfaces);
- 	/* Deinitialize all interfaces */
- 	for (i = 0; i < interfaces.count; i++) {
 --- a/wpa_supplicant/main.c
 +++ b/wpa_supplicant/main.c
 @@ -204,7 +204,7 @@ int main(int argc, char *argv[])
@@ -623,3 +598,151 @@
  	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
  		    pos, end - pos);
  }
+--- a/src/utils/eloop.c
++++ b/src/utils/eloop.c
+@@ -77,6 +77,9 @@ struct eloop_sock_table {
+ struct eloop_data {
+ 	int max_sock;
+ 
++	eloop_timeout_poll_handler timeout_poll_cb;
++	eloop_poll_handler poll_cb;
++
+ 	size_t count; /* sum of all table counts */
+ #ifdef CONFIG_ELOOP_POLL
+ 	size_t max_pollfd_map; /* number of pollfds_map currently allocated */
+@@ -1121,6 +1124,12 @@ void eloop_run(void)
+ 				os_reltime_sub(&timeout->time, &now, &tv);
+ 			else
+ 				tv.sec = tv.usec = 0;
++		}
++
++		if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
++			timeout = (void *)1;
++
++		if (timeout) {
+ #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+ 			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+ #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+@@ -1190,7 +1199,8 @@ void eloop_run(void)
+ 		eloop.exceptions.changed = 0;
+ 
+ 		eloop_process_pending_signals();
+-
++		if (eloop.poll_cb)
++			eloop.poll_cb();
+ 
+ 		/* check if some registered timeouts have occurred */
+ 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+@@ -1252,6 +1262,14 @@ out:
+ 	return;
+ }
+ 
++int eloop_register_cb(eloop_poll_handler poll_cb,
++		      eloop_timeout_poll_handler timeout_cb)
++{
++	eloop.poll_cb = poll_cb;
++	eloop.timeout_poll_cb = timeout_cb;
++
++	return 0;
++}
+ 
+ void eloop_terminate(void)
+ {
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(vo
+  */
+ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+ 
++typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
++typedef void (*eloop_poll_handler)(void);
++
+ /**
+  * eloop_init() - Initialize global event loop data
+  * Returns: 0 on success, -1 on failure
+@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int
+  */
+ int eloop_init(void);
+ 
++int eloop_register_cb(eloop_poll_handler poll_cb,
++		      eloop_timeout_poll_handler timeout_cb);
++
+ /**
+  * eloop_register_read_sock - Register handler for read events
+  * @sock: File descriptor number for the socket
+@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop
+  */
+ int eloop_sock_requeue(void);
+ 
++void eloop_add_uloop(void);
++
+ /**
+  * eloop_run - Start the event loop
+  *
+--- /dev/null
++++ b/src/utils/uloop.c
+@@ -0,0 +1,64 @@
++#include <libubox/uloop.h>
++#include "includes.h"
++#include "common.h"
++#include "eloop.h"
++
++static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
++{
++}
++
++static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
++{
++	unsigned int changed = events ^ fd->flags;
++
++	if (changed & ULOOP_READ) {
++		if (events & ULOOP_READ)
++			eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
++		else
++			eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
++	}
++
++	if (changed & ULOOP_WRITE) {
++		if (events & ULOOP_WRITE)
++			eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
++		else
++			eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
++	}
++}
++
++static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
++{
++	struct os_reltime tv_uloop;
++	int timeout_ms = uloop_get_next_timeout();
++
++	if (timeout_ms < 0)
++		return false;
++
++	tv_uloop.sec = timeout_ms / 1000;
++	tv_uloop.usec = (timeout_ms % 1000) * 1000;
++
++	if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
++		*tv = tv_uloop;
++		return true;
++	}
++
++	return false;
++}
++
++static void uloop_poll_handler(void)
++{
++	uloop_run_timeout(0);
++}
++
++void eloop_add_uloop(void)
++{
++	static bool init_done = false;
++
++	if (!init_done) {
++		uloop_init();
++		uloop_fd_set_cb = eloop_uloop_fd_cb;
++		init_done = true;
++	}
++
++	eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
++}
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/601-ucode_support.patch b/recipes-wifi/hostapd/files/patches-2.10.3/601-ucode_support.patch
new file mode 100644
index 0000000..ff1a9dd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/601-ucode_support.patch
@@ -0,0 +1,234 @@
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -168,9 +168,21 @@ OBJS += ../src/eapol_auth/eapol_auth_sm.
+ 
+ ifdef CONFIG_UBUS
+ CFLAGS += -DUBUS_SUPPORT
+-OBJS += ../src/utils/uloop.o
+ OBJS += ../src/ap/ubus.o
+-LIBS += -lubox -lubus
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ../src/ap/ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
+ endif
+ 
+ ifdef CONFIG_CODE_COVERAGE
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -991,6 +991,7 @@ int main(int argc, char *argv[])
+ 	}
+ 
+ 	hostapd_global_ctrl_iface_init(&interfaces);
++	hostapd_ucode_init(&interfaces);
+ 
+ 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
+ 		wpa_printf(MSG_ERROR, "Failed to start eloop");
+@@ -1000,6 +1001,7 @@ int main(int argc, char *argv[])
+ 	ret = 0;
+ 
+  out:
++	hostapd_ucode_free();
+ 	hostapd_global_ctrl_iface_deinit(&interfaces);
+ 	/* Deinitialize all interfaces */
+ 	for (i = 0; i < interfaces.count; i++) {
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -19,6 +19,7 @@
+ #include "ap_config.h"
+ #include "drivers/driver.h"
+ #include "ubus.h"
++#include "ucode.h"
+ 
+ #define OCE_STA_CFON_ENABLED(hapd) \
+ 	((hapd->conf->oce & OCE_STA_CFON) && \
+@@ -51,6 +52,10 @@ struct hapd_interfaces {
+ 	struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ 	int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ 	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
++	int (*ctrl_iface_recv)(struct hostapd_data *hapd,
++			       char *buf, char *reply, int reply_size,
++			       struct sockaddr_storage *from,
++			       socklen_t fromlen);
+ 	int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ 				  int (*cb)(struct hostapd_iface *iface,
+ 					    void *ctx), void *ctx);
+@@ -186,6 +191,7 @@ struct hostapd_data {
+ 	struct hostapd_config *iconf;
+ 	struct hostapd_bss_config *conf;
+ 	struct hostapd_ubus_bss ubus;
++	struct hostapd_ucode_bss ucode;
+ 	int interface_added; /* virtual interface added for this BSS */
+ 	unsigned int started:1;
+ 	unsigned int disabled:1;
+@@ -506,6 +512,7 @@ struct hostapd_sta_info {
+  */
+ struct hostapd_iface {
+ 	struct hapd_interfaces *interfaces;
++	struct hostapd_ucode_iface ucode;
+ 	void *owner;
+ 	char *config_fname;
+ 	struct hostapd_config *conf;
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -276,6 +276,8 @@ int hostapd_reload_config(struct hostapd
+ 	size_t j;
+ 	int i;
+ 
++	hostapd_ucode_reload_bss(hapd, reconf);
++
+ 	if (iface->config_fname == NULL) {
+ 		/* Only in-memory config in use - assume it has been updated */
+ 		hostapd_clear_old(iface);
+@@ -455,6 +457,7 @@ void hostapd_free_hapd_data(struct hosta
+ 	hapd->beacon_set_done = 0;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
++	hostapd_ucode_free_bss(hapd);
+ 	hostapd_ubus_free_bss(hapd);
+ 	accounting_deinit(hapd);
+ 	hostapd_deinit_wpa(hapd);
+@@ -619,6 +622,7 @@ void hostapd_cleanup_iface_partial(struc
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++	hostapd_ucode_free_iface(iface);
+ 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ 	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ 			     NULL);
+@@ -1209,6 +1213,7 @@ static int hostapd_start_beacon(struct h
+ 		hapd->driver->set_operstate(hapd->drv_priv, 1);
+ 
+ 	hostapd_ubus_add_bss(hapd);
++	hostapd_ucode_add_bss(hapd);
+ 
+ 	return 0;
+ }
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -197,8 +197,20 @@ endif
+ ifdef CONFIG_UBUS
+ CFLAGS += -DUBUS_SUPPORT
+ OBJS += ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
+ OBJS += ../src/utils/uloop.o
+-LIBS += -lubox -lubus
++LIBS += -lubox
+ endif
+ 
+ ifdef CONFIG_CODE_COVERAGE
+@@ -999,6 +1011,9 @@ OBJS += ../src/ap/ctrl_iface_ap.o
+ ifdef CONFIG_UBUS
+ OBJS += ../src/ap/ubus.o
+ endif
++ifdef CONFIG_UCODE
++OBJS += ../src/ap/ucode.o
++endif
+ endif
+ 
+ CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -7636,6 +7636,7 @@ struct wpa_supplicant * wpa_supplicant_a
+ #endif /* CONFIG_P2P */
+ 
+ 	wpas_ubus_add_bss(wpa_s);
++	wpas_ucode_add_bss(wpa_s);
+ 
+ 	return wpa_s;
+ }
+@@ -7663,6 +7664,7 @@ int wpa_supplicant_remove_iface(struct w
+ 	struct wpa_supplicant *parent = wpa_s->parent;
+ #endif /* CONFIG_MESH */
+ 
++	wpas_ucode_free_bss(wpa_s);
+ 	wpas_ubus_free_bss(wpa_s);
+ 
+ 	/* Remove interface from the global list of interfaces */
+@@ -7973,6 +7975,7 @@ struct wpa_global * wpa_supplicant_init(
+ 
+ 	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ 			       wpas_periodic, global, NULL);
++	wpas_ucode_init(global);
+ 
+ 	return global;
+ }
+@@ -8011,12 +8014,8 @@ int wpa_supplicant_run(struct wpa_global
+ 	eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+ 	eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+ 
+-	wpas_ubus_add(global);
+-
+ 	eloop_run();
+ 
+-	wpas_ubus_free(global);
+-
+ 	return 0;
+ }
+ 
+@@ -8049,6 +8048,8 @@ void wpa_supplicant_deinit(struct wpa_gl
+ 
+ 	wpas_notify_supplicant_deinitialized(global);
+ 
++	wpas_ucode_free();
++
+ 	eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ 	eap_server_unregister_methods();
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -22,6 +22,7 @@
+ #include "wmm_ac.h"
+ #include "pasn/pasn_common.h"
+ #include "ubus.h"
++#include "ucode.h"
+ 
+ extern const char *const wpa_supplicant_version;
+ extern const char *const wpa_supplicant_license;
+@@ -659,6 +660,7 @@ struct wpa_supplicant {
+ 	unsigned char perm_addr[ETH_ALEN];
+ 	char ifname[100];
+ 	struct wpas_ubus_bss ubus;
++	struct wpas_ucode_bss ucode;
+ #ifdef CONFIG_MATCH_IFACE
+ 	int matched;
+ #endif /* CONFIG_MATCH_IFACE */
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4921,6 +4921,7 @@ try_again:
+ 		return -1;
+ 	}
+ 
++	interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+ 
+ 	return 0;
+@@ -5022,6 +5023,7 @@ fail:
+ 	os_free(fname);
+ 
+ 	interface->global_ctrl_sock = s;
++	interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ 	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+ 				 interface, NULL);
+ 
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/700-wifi-reload.patch b/recipes-wifi/hostapd/files/patches-2.10.3/700-wifi-reload.patch
index 0c76276..c0e7e4d 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/700-wifi-reload.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/700-wifi-reload.patch
@@ -40,6 +40,15 @@
  	int rts_threshold;
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
+@@ -127,7 +127,7 @@ void hostapd_reconfig_encryption(struct
+ }
+ 
+ 
+-static void hostapd_reload_bss(struct hostapd_data *hapd)
++void hostapd_reload_bss(struct hostapd_data *hapd)
+ {
+ 	struct hostapd_ssid *ssid;
+ 
 @@ -255,6 +255,10 @@ static int hostapd_iface_conf_changed(st
  {
  	size_t i;
@@ -60,7 +69,7 @@
  {
  	struct hapd_interfaces *interfaces = iface->interfaces;
  	struct hostapd_data *hapd = iface->bss[0];
-@@ -296,6 +300,9 @@ int hostapd_reload_config(struct hostapd
+@@ -298,6 +302,9 @@ int hostapd_reload_config(struct hostapd
  		char *fname;
  		int res;
  
@@ -70,7 +79,7 @@
  		hostapd_clear_old(iface);
  
  		wpa_printf(MSG_DEBUG,
-@@ -322,6 +329,24 @@ int hostapd_reload_config(struct hostapd
+@@ -324,6 +331,24 @@ int hostapd_reload_config(struct hostapd
  			wpa_printf(MSG_ERROR,
  				   "Failed to enable interface on config reload");
  		return res;
@@ -95,7 +104,7 @@
  	}
  	iface->conf = newconf;
  
-@@ -338,6 +363,12 @@ int hostapd_reload_config(struct hostapd
+@@ -340,6 +365,12 @@ int hostapd_reload_config(struct hostapd
  
  	for (j = 0; j < iface->num_bss; j++) {
  		hapd = iface->bss[j];
@@ -108,7 +117,17 @@
  		if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
  		    os_strcmp(hapd->conf->config_id,
  			      newconf->bss[j]->config_id) != 0)
-@@ -2700,6 +2731,10 @@ hostapd_alloc_bss_data(struct hostapd_if
+@@ -1236,8 +1267,7 @@ static int hostapd_start_beacon(struct h
+  * initialized. Most of the modules that are initialized here will be
+  * deinitialized in hostapd_cleanup().
+  */
+-static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+-			     bool start_beacon)
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ {
+ 	struct hostapd_bss_config *conf = hapd->conf;
+ 	u8 ssid[SSID_MAX_LEN + 1];
+@@ -2705,6 +2735,10 @@ hostapd_alloc_bss_data(struct hostapd_if
  	hapd->iconf = conf;
  	hapd->conf = bss;
  	hapd->iface = hapd_iface;
@@ -119,9 +138,18 @@
  	if (conf)
  		hapd->driver = conf->driver;
  	hapd->ctrl_sock = -1;
+@@ -2723,7 +2757,7 @@ hostapd_alloc_bss_data(struct hostapd_if
+ }
+ 
+ 
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ 	if (!hapd)
+ 		return;
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -47,7 +47,7 @@ struct mesh_conf;
+@@ -48,7 +48,7 @@ struct mesh_conf;
  struct hostapd_iface;
  
  struct hapd_interfaces {
@@ -130,23 +158,33 @@
  	struct hostapd_config * (*config_read_cb)(const char *config_fname);
  	int (*ctrl_iface_init)(struct hostapd_data *hapd);
  	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-@@ -186,6 +186,7 @@ struct hostapd_data {
- 	struct hostapd_config *iconf;
+@@ -192,6 +192,7 @@ struct hostapd_data {
  	struct hostapd_bss_config *conf;
  	struct hostapd_ubus_bss ubus;
+ 	struct hostapd_ucode_bss ucode;
 +	char *config_id;
  	int interface_added; /* virtual interface added for this BSS */
  	unsigned int started:1;
  	unsigned int disabled:1;
-@@ -689,7 +690,7 @@ struct hostapd_iface {
+@@ -696,7 +697,9 @@ struct hostapd_iface {
  int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
  			       int (*cb)(struct hostapd_iface *iface,
  					 void *ctx), void *ctx);
 -int hostapd_reload_config(struct hostapd_iface *iface);
 +int hostapd_reload_config(struct hostapd_iface *iface, int reconf);
++void hostapd_reload_bss(struct hostapd_data *hapd);
++void hostapd_bss_deinit(struct hostapd_data *hapd);
  void hostapd_reconfig_encryption(struct hostapd_data *hapd);
  struct hostapd_data *
  hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+@@ -713,6 +716,7 @@ struct hostapd_iface * hostapd_init(stru
+ struct hostapd_iface *
+ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ 			   const char *config_fname, int debug);
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 			   int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
 --- a/src/drivers/driver_nl80211.c
 +++ b/src/drivers/driver_nl80211.c
 @@ -5322,6 +5322,9 @@ static int wpa_driver_nl80211_set_ap(voi
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/701-reload_config_inline.patch b/recipes-wifi/hostapd/files/patches-2.10.3/701-reload_config_inline.patch
new file mode 100644
index 0000000..071281e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/701-reload_config_inline.patch
@@ -0,0 +1,33 @@
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4814,7 +4814,12 @@ struct hostapd_config * hostapd_config_r
+ 	int errors = 0;
+ 	size_t i;
+ 
+-	f = fopen(fname, "r");
++	if (!strncmp(fname, "data:", 5)) {
++		f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
++		fname = "<inline>";
++	} else {
++		f = fopen(fname, "r");
++	}
+ 	if (f == NULL) {
+ 		wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+ 			   "for reading.", fname);
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(cons
+ 	while (cred_tail && cred_tail->next)
+ 		cred_tail = cred_tail->next;
+ 
++	if (!strncmp(name, "data:", 5)) {
++		f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
++		name = "<inline>";
++	} else {
++		f = fopen(name, "r");
++	}
+ 	wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+-	f = fopen(name, "r");
+ 	if (f == NULL) {
+ 		wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ 			   "error: %s", name, strerror(errno));
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch b/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch
index 0bb00f9..5f40aab 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/720-iface_max_num_sta.patch
@@ -17,7 +17,7 @@
  	} else if (os_strcmp(buf, "extended_key_id") == 0) {
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -734,6 +734,7 @@ void hostapd_cleanup_cs_params(struct ho
+@@ -744,6 +744,7 @@ void hostapd_cleanup_cs_params(struct ho
  void hostapd_periodic_iface(struct hostapd_iface *iface);
  int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
  void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch b/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
index 124f5ea..3e282b4 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
@@ -32,7 +32,7 @@
  		os_free(bss->dump_msk_file);
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -1534,6 +1534,7 @@ static int hostapd_setup_bss(struct host
+@@ -1538,6 +1538,7 @@ int hostapd_setup_bss(struct hostapd_dat
  		wpa_printf(MSG_ERROR, "GAS server initialization failed");
  		return -1;
  	}
@@ -40,7 +40,7 @@
  
  	if (conf->qos_map_set_len &&
  	    hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1541,7 +1542,6 @@ static int hostapd_setup_bss(struct host
+@@ -1545,7 +1546,6 @@ int hostapd_setup_bss(struct hostapd_dat
  		wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
  		return -1;
  	}
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch b/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch
index dad7afd..d60764a 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/761-shared_das_port.patch
@@ -10,7 +10,7 @@
  	unsigned int time_window;
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -1471,6 +1471,7 @@ static int hostapd_setup_bss(struct host
+@@ -1475,6 +1475,7 @@ int hostapd_setup_bss(struct hostapd_dat
  
  			os_memset(&das_conf, 0, sizeof(das_conf));
  			das_conf.port = conf->radius_das_port;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/770-radius_server.patch b/recipes-wifi/hostapd/files/patches-2.10.3/770-radius_server.patch
new file mode 100644
index 0000000..6fca86d
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/770-radius_server.patch
@@ -0,0 +1,154 @@
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -63,6 +63,10 @@ endif
+ OBJS += main.o
+ OBJS += config_file.o
+ 
++ifdef CONFIG_RADIUS_SERVER
++OBJS += radius.o
++endif
++
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/drv_callbacks.o
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -42,6 +42,7 @@ static struct hapd_global global;
+ static int daemonize = 0;
+ static char *pid_file = NULL;
+ 
++extern int radius_main(int argc, char **argv);
+ 
+ #ifndef CONFIG_NO_HOSTAPD_LOGGER
+ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+@@ -755,6 +756,11 @@ int main(int argc, char *argv[])
+ 	if (os_program_init())
+ 		return -1;
+ 
++#ifdef RADIUS_SERVER
++	if (strstr(argv[0], "radius"))
++		return radius_main(argc, argv);
++#endif
++
+ 	os_memset(&interfaces, 0, sizeof(interfaces));
+ 	interfaces.reload_config = hostapd_reload_config;
+ 	interfaces.config_read_cb = hostapd_config_read;
+--- a/src/radius/radius_server.c
++++ b/src/radius/radius_server.c
+@@ -63,6 +63,12 @@ struct radius_server_counters {
+ 	u32 unknown_acct_types;
+ };
+ 
++struct radius_accept_attr {
++	u8 type;
++	u16 len;
++	void *data;
++};
++
+ /**
+  * struct radius_session - Internal RADIUS server data for a session
+  */
+@@ -90,7 +96,7 @@ struct radius_session {
+ 	unsigned int macacl:1;
+ 	unsigned int t_c_filtering:1;
+ 
+-	struct hostapd_radius_attr *accept_attr;
++	struct radius_accept_attr *accept_attr;
+ 
+ 	u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
+ };
+@@ -394,6 +400,7 @@ static void radius_server_session_free(s
+ 	radius_msg_free(sess->last_reply);
+ 	os_free(sess->username);
+ 	os_free(sess->nas_ip);
++	os_free(sess->accept_attr);
+ 	os_free(sess);
+ 	data->num_sess--;
+ }
+@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius
+ }
+ #endif /* CONFIG_ERP */
+ 
++static struct radius_accept_attr *
++radius_server_copy_attr(const struct hostapd_radius_attr *data)
++{
++	const struct hostapd_radius_attr *attr;
++	struct radius_accept_attr *attr_new;
++	size_t data_size = 0;
++	void *data_buf;
++	int n_attr = 1;
++
++	for (attr = data; attr; attr = attr->next) {
++		n_attr++;
++		data_size += wpabuf_len(attr->val);
++	}
++
++	attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
++	if (!attr_new)
++		return NULL;
++
++	data_buf = &attr_new[n_attr];
++	for (n_attr = 0, attr = data; attr; attr = attr->next) {
++		struct radius_accept_attr *cur = &attr_new[n_attr++];
++
++		cur->type = attr->type;
++		cur->len = wpabuf_len(attr->val);
++		cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
++		data_buf += cur->len;
++	}
++
++	return attr_new;
++}
+ 
+ static struct radius_session *
+ radius_server_get_new_session(struct radius_server_data *data,
+@@ -607,7 +644,7 @@ radius_server_get_new_session(struct rad
+ 		eap_user_free(tmp);
+ 		return NULL;
+ 	}
+-	sess->accept_attr = tmp->accept_attr;
++	sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
+ 	sess->macacl = tmp->macacl;
+ 	eap_user_free(tmp);
+ 
+@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct rad
+ 	}
+ 
+ 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+-		struct hostapd_radius_attr *attr;
+-		for (attr = sess->accept_attr; attr; attr = attr->next) {
+-			if (!radius_msg_add_attr(msg, attr->type,
+-						 wpabuf_head(attr->val),
+-						 wpabuf_len(attr->val))) {
++		struct radius_accept_attr *attr;
++		for (attr = sess->accept_attr; attr->data; attr++) {
++			if (!radius_msg_add_attr(msg, attr->type, attr->data,
++						 attr->len)) {
+ 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ 				radius_msg_free(msg);
+ 				return NULL;
+@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_serve
+ 	}
+ 
+ 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+-		struct hostapd_radius_attr *attr;
+-		for (attr = sess->accept_attr; attr; attr = attr->next) {
+-			if (!radius_msg_add_attr(msg, attr->type,
+-						 wpabuf_head(attr->val),
+-						 wpabuf_len(attr->val))) {
++		struct radius_accept_attr *attr;
++		for (attr = sess->accept_attr; attr->data; attr++) {
++			if (!radius_msg_add_attr(msg, attr->type, attr->data,
++						 attr->len)) {
+ 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ 				radius_msg_free(msg);
+ 				return NULL;
+@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(vo
+ 	ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+ 				 phase2, user);
+ 	if (ret == 0 && user) {
+-		sess->accept_attr = user->accept_attr;
++		sess->accept_attr = radius_server_copy_attr(user->accept_attr);
+ 		sess->remediation = user->remediation;
+ 		sess->macacl = user->macacl;
+ 		sess->t_c_timestamp = user->t_c_timestamp;
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch
index 4e51a1c..4fa2dfb 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch
@@ -1,8 +1,8 @@
-From df9a616286f2d33e5c580517238b93ee22359f95 Mon Sep 17 00:00:00 2001
+From bfca3c43597bf827e4d5faec91ac8f420d33c921 Mon Sep 17 00:00:00 2001
 From: "howard.hsu" <howard-yh.hsu@mediatek.com>
 Date: Wed, 19 Jan 2022 19:18:07 +0800
-Subject: [PATCH 01/32] hostapd: mtk: Add neighbor report and BSS Termination
- for MBO certification
+Subject: [PATCH] hostapd: mtk: Add neighbor report and BSS Termination for MBO
+ certification
 
 1. Add hostapd_neighbor_count() and hostapd_neighbor_insert_buffer ()
 The first function can count the number of neighbor report in neighbore report
@@ -13,12 +13,11 @@
 3. Support including neignbor report elements in BTM response
 4. Support configuring BSS Termination TSF by using hostapd_cli command
 5. Disable interface if BSS Termination TSF is set
-6. Add set_send_disassoc_frame_timer() to send disassociate frame
-Function set_disassoc_timer() may fail if key was deleted first. This new
-function will not ask to delete key as set_disassoc_timer() did.
-7. Support including neighbor report elements in BTM request
-8. Add hostapd_neighbor_set_own_report_pref()
-9. Add hostapd_neighbor_set_pref_by_non_pref_chan()
+6. Support including neighbor report elements in BTM request
+7. Add hostapd_neighbor_set_own_report_pref()
+8. Add hostapd_neighbor_set_pref_by_non_pref_chan()
+
+Revert set_send_disassoc_frame_timer
 ---
  hostapd/ctrl_iface.c   |   5 ++
  src/ap/ap_config.c     |   1 +
@@ -28,11 +27,11 @@
  src/ap/gas_serv.h      |   2 +
  src/ap/neighbor_db.c   | 119 +++++++++++++++++++++++++++++++++++++++++
  src/ap/neighbor_db.h   |   9 ++++
- src/ap/wnm_ap.c        |  72 +++++++++++++++++++++++--
- 9 files changed, 252 insertions(+), 5 deletions(-)
+ src/ap/wnm_ap.c        |  42 ++++++++++++++-
+ 9 files changed, 223 insertions(+), 4 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 55711ab10..fab1287cd 100644
+index 55711ab..fab1287 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
 @@ -1347,6 +1347,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
@@ -48,7 +47,7 @@
  		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
  		if (ret)
 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 41c70708d..7d0de122d 100644
+index 41c7070..7d0de12 100644
 --- a/src/ap/ap_config.c
 +++ b/src/ap/ap_config.c
 @@ -171,6 +171,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
@@ -60,7 +59,7 @@
  
  
 diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 02fd4940c..dc6025279 100644
+index 02fd494..dc60252 100644
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
 @@ -558,6 +558,7 @@ struct hostapd_bss_config {
@@ -72,7 +71,7 @@
  	/* IEEE 802.11u - Interworking */
  	int interworking;
 diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 50a4dc4d6..cf1cff447 100644
+index 50a4dc4..cf1cff4 100644
 --- a/src/ap/ctrl_iface_ap.c
 +++ b/src/ap/ctrl_iface_ap.c
 @@ -1280,6 +1280,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
@@ -115,7 +114,7 @@
  	if (pos) {
  		unsigned int mbo_reason, cell_pref, reassoc_delay;
 diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
-index 4642e4927..cce6df41c 100644
+index 4642e49..cce6df4 100644
 --- a/src/ap/gas_serv.c
 +++ b/src/ap/gas_serv.c
 @@ -19,6 +19,7 @@
@@ -183,7 +182,7 @@
  #ifdef CONFIG_FILS
  		if (info_id == ANQP_FILS_REALM_INFO &&
 diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
-index 7646a98a4..ce492b53f 100644
+index 7646a98..ce492b5 100644
 --- a/src/ap/gas_serv.h
 +++ b/src/ap/gas_serv.h
 @@ -40,6 +40,8 @@
@@ -196,7 +195,7 @@
   * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
   * optimized bitmap.
 diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
-index 5b276e8da..1c14b3201 100644
+index 5b276e8..1c14b32 100644
 --- a/src/ap/neighbor_db.c
 +++ b/src/ap/neighbor_db.c
 @@ -89,6 +89,38 @@ int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
@@ -330,7 +329,7 @@
 +}
 +#endif
 diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
-index 992671b62..a1ddc075b 100644
+index 992671b..a1ddc07 100644
 --- a/src/ap/neighbor_db.h
 +++ b/src/ap/neighbor_db.h
 @@ -24,4 +24,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
@@ -348,7 +347,7 @@
 +#endif
  #endif /* NEIGHBOR_DB_H */
 diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index ba1dd2ed1..32ccf14ae 100644
+index ba1dd2e..939d447 100644
 --- a/src/ap/wnm_ap.c
 +++ b/src/ap/wnm_ap.c
 @@ -20,6 +20,7 @@
@@ -402,38 +401,10 @@
  	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
  	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
  		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
-@@ -890,6 +911,50 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+@@ -890,6 +911,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
  }
  
  
-+static void set_send_disassoc_frame_timer(struct hostapd_data *hapd, struct sta_info *sta,
-+			       int disassoc_timer)
-+{
-+	int timeout, beacon_int;
-+
-+	/*
-+	 * Prevent STA from reconnecting using cached PMKSA to force
-+	 * full authentication with the authentication server (which may
-+	 * decide to reject the connection),
-+	 */
-+	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
-+
-+	beacon_int = hapd->iconf->beacon_int;
-+	if (beacon_int < 1)
-+		beacon_int = 100; /* best guess */
-+	/* Calculate timeout in ms based on beacon_int in TU */
-+	timeout = disassoc_timer * beacon_int * 128 / 125;
-+	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
-+		   " set to %d ms", MAC2STR(sta->addr), timeout);
-+
-+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
-+
-+	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
-+	if (sta)
-+		ap_sta_disassociate(hapd, sta, reason);
-+}
-+
-+
 +void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
 +{
 +	struct hostapd_data *hapd = eloop_ctx;
@@ -453,16 +424,7 @@
  int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
  				   struct sta_info *sta, const char *url,
  				   int disassoc_timer)
-@@ -934,7 +999,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
- 	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	if (disassoc_timer) {
- 		/* send disassociation frame after time-out */
--		set_disassoc_timer(hapd, sta, disassoc_timer);
-+		set_send_disassoc_frame_timer(hapd, sta, disassoc_timer);
- 	}
- 
- 	return 0;
-@@ -979,6 +1044,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+@@ -979,6 +1016,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
  	    bss_term_dur) {
  		os_memcpy(pos, bss_term_dur, 12);
  		pos += 12;
@@ -471,5 +433,5 @@
  
  	if (url) {
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch
similarity index 89%
rename from recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch
rename to recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch
index af79b24..dbd7163 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch
@@ -1,4 +1,4 @@
-From ff273381d463d82898df70cb7b43341405e3d943 Mon Sep 17 00:00:00 2001
+From 67e2363c4875dd918418dd84b43f86041db690c7 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Fri, 12 May 2023 05:21:28 +0800
 Subject: [PATCH] hostapd: mtk: Avoid setting beacon after wpa_supplicant stop
@@ -19,7 +19,7 @@
  6 files changed, 22 insertions(+), 6 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 9989a04..4952bef 100644
+index e575c37d1..0e352c5c7 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
 @@ -194,11 +194,15 @@ static int hostapd_ctrl_iface_update(struct hostapd_data *hapd, char *txt)
@@ -39,7 +39,7 @@
  
  	iface->interfaces->config_read_cb = config_read_cb;
 diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index f26e525..1aaeaa8 100644
+index f26e5254c..1aaeaa8e4 100644
 --- a/src/ap/beacon.c
 +++ b/src/ap/beacon.c
 @@ -2246,7 +2246,8 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
@@ -69,7 +69,7 @@
  			ret = -1;
  	}
 diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
-index 725d3cd..f2a1a10 100644
+index 725d3cd34..78fd9d8ec 100644
 --- a/src/ap/bss_load.c
 +++ b/src/ap/bss_load.c
 @@ -49,6 +49,9 @@ static void update_channel_utilization(void *eloop_data, void *user_data)
@@ -104,7 +104,7 @@
  			       NULL);
  }
 diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index aab8a46..d52188b 100644
+index aab8a4665..d52188bb7 100644
 --- a/src/ap/ctrl_iface_ap.c
 +++ b/src/ap/ctrl_iface_ap.c
 @@ -1028,8 +1028,10 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
@@ -120,18 +120,18 @@
  	return 0;
  }
 diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index abadd1a..afe0611 100644
+index 28e549b61..be75613c8 100644
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -508,6 +508,7 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+@@ -510,6 +510,7 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
  	}
  	hapd->started = 0;
  	hapd->beacon_set_done = 0;
 +	hapd->stopped_by_supplicant = 0;
  
  	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
- 	hostapd_ubus_free_bss(hapd);
-@@ -1316,6 +1317,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ 	hostapd_ucode_free_bss(hapd);
+@@ -1320,6 +1321,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
  		return -1;
  	}
  	hapd->started = 1;
@@ -139,7 +139,7 @@
  
  	if (!first || first == -1) {
  		u8 *addr = hapd->own_addr;
-@@ -4285,7 +4287,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+@@ -4289,7 +4291,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
  {
  	struct os_reltime now;
  
@@ -149,10 +149,10 @@
  
  	if (os_get_reltime(&now))
 diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 072205c..4f1f988 100644
+index 5bd1537fe..435dbd027 100644
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -191,6 +191,7 @@ struct hostapd_data {
+@@ -197,6 +197,7 @@ struct hostapd_data {
  	unsigned int started:1;
  	unsigned int disabled:1;
  	unsigned int reenable_beacon:1;
@@ -161,5 +161,5 @@
  	u8 own_addr[ETH_ALEN];
  	u8 mld_addr[ETH_ALEN];
 -- 
-2.25.1
+2.39.2
 
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch
new file mode 100644
index 0000000..dd121ca
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch
@@ -0,0 +1,64 @@
+From 76e54b095fdd8ab65a562c28fba330afcfb519ec Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:44:15 +0800
+Subject: [PATCH] hostapd: mtk: Fix background channel overlapping operating
+ channel issue
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 2f5c86e..c9a9c6c 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -807,6 +807,20 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+ }
+ 
+ 
++static void dfs_check_background_overlapped(struct hostapd_iface *iface)
++{
++	int width = hostapd_get_oper_chwidth(iface->conf);
++
++	if (!dfs_use_radar_background(iface))
++		return;
++
++	if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
++					width, iface->radar_background.centr_freq_seg0_idx,
++					iface->radar_background.centr_freq_seg1_idx))
++		iface->radar_background.channel = -1;
++}
++
++
+ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ 				     int start_chan_idx, int n_chans)
+ {
+@@ -1127,6 +1141,8 @@ static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+ 						  &oper_centr_freq_seg1_idx,
+ 						  &channel_type);
+ 	if (!channel ||
++	    channel->chan == iface->conf->channel ||
++	    channel->chan == iface->radar_background.channel ||
+ 	    hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ 				  channel->freq, channel->chan,
+ 				  iface->conf->ieee80211n,
+@@ -1361,6 +1377,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+ 	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ 					     oper_centr_freq_seg1_idx);
+ 	err = 0;
++	dfs_check_background_overlapped(iface);
+ 
+ 	hostapd_setup_interface_complete(iface, err);
+ 	return err;
+@@ -1488,6 +1505,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ 			hostapd_set_oper_centr_freq_seg1_idx(
+ 				iface->conf, oper_centr_freq_seg1_idx);
+ 
++			dfs_check_background_overlapped(iface);
+ 			hostapd_disable_iface(iface);
+ 			hostapd_enable_iface(iface);
+ 			return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
new file mode 100644
index 0000000..7cd34cd
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
@@ -0,0 +1,30 @@
+From 1671a37b5990929bd11823158d496e7877d83d92 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Wed, 2 Aug 2023 18:33:31 +0800
+Subject: [PATCH 38/38] hostapd: mtk: Fix 11vmbss aid using wrong pool
+
+Fix 11vmbss aid using wrong pool.
+All STAs use the aid pool in transmitted bss.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index d36798e..f20073c 100755
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3307,6 +3307,9 @@ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ 	int i, j = 32, aid;
+ 
++	if (hapd->iconf->mbssid)
++		hapd = hostapd_mbssid_get_tx_bss(hapd);
++
+ 	/* get a unique AID */
+ 	if (sta->aid > 0) {
+ 		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
new file mode 100644
index 0000000..9218f9b
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
@@ -0,0 +1,34 @@
+From ea3e20d6cc8d11750e509a701131297da81ef35d Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Mon, 7 Aug 2023 15:27:27 +0800
+Subject: [PATCH 39/39] hostapd: mtk: Fix rnr ie length when no need to report
+ bss
+
+Fix rnr ie length when no need to report bss. If we don't have content in
+TBTT then don't change the length of the ie (*size_offset).
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index f20073c..ef520c8 100755
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7494,8 +7494,10 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ 		}
+ 
+ 		start = i;
+-		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+-		*size_offset = (eid - size_offset) - 1;
++		if (tbtt_count != 0) {
++			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
++			*size_offset = (eid - size_offset) - 1;
++		}
+ 	}
+ 
+ 	if (tbtt_count == 0)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..0a27496
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From 2ea36366ea036e3063ff553a2939c9cac17c6ac8 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 8 Aug 2023 19:21:41 +0800
+Subject: [PATCH] hostapd: mtk: add back ht vht cap missing field before dfs
+ channel fallback
+
+hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
+on the bandwidth of switched channel.
+For example, vht bw 160 support field would be cleared if we switch to
+non bw 160 channel.
+This design works fine with NON-DFS channel switch.
+However, for those DFS channels who require CAC, channel switch command
+calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
+This is simply restarting the interface not CHANNEL SWITCHING, so
+hostapd will not receive any ch_switch event from kernel.
+Therefore, the cleared field in vht_capab will not be set back to 1,
+even if we channel switch to dfs channel bw 160.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index c2e0b13..4b7ebc5 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4161,6 +4161,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++	if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++	    freq_params->bandwidth > 20)
++		iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++	if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
++	    freq_params->bandwidth == 160)
++		iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
index 78dae59..89585bc 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/patches.inc
@@ -17,6 +17,7 @@
     file://150-add-NULL-checks-encountered-during-tests-hwsim.patch \
     file://160-dpp_pkex-EC-point-mul-w-value-prime.patch \
     file://170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch \
+    file://180-BSS-coloring-fix-CCA-with-multiple-BSS.patch \
     file://200-multicall.patch \
     file://300-noscan.patch \
     file://301-mesh-noscan.patch \
@@ -46,8 +47,10 @@
     file://590-rrm-wnm-statistics.patch \
     file://599-wpa_supplicant-fix-warnings.patch \
     file://600-ubus_support.patch \
+    file://601-ucode_support.patch \
     file://610-hostapd_cli_ujail_permission.patch \
     file://700-wifi-reload.patch \
+    file://701-reload_config_inline.patch \
     file://710-vlan_no_bridge.patch \
     file://711-wds_bridge_force.patch \
     file://720-iface_max_num_sta.patch \
@@ -57,6 +60,7 @@
     file://751-qos_map_ignore_when_unsupported.patch \
     file://760-dynamic_own_ip.patch \
     file://761-shared_das_port.patch \
+    file://770-radius_server.patch \
     file://990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch \
     file://mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch \
     file://mtk-0002-hostapd-mtk-print-sae-groups-by-hostapd-ctrl.patch \
@@ -83,17 +87,20 @@
     file://mtk-0023-hostapd-mtk-Add-vendor-for-CAPI-certification-comman.patch \
     file://mtk-0024-hostapd-mtk-Air-Monitor-support-in-hostapd-by-vendor.patch \
     file://mtk-0025-hostapd-mtk-Fix-scan-result-updating-issue.patch \
-    file://mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch \
+    file://mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch \
     file://mtk-0027-hostapd-mtk-Fix-setting-wrong-seg0-index-for-5G-cent.patch \
     file://mtk-0028-hostapd-mtk-Add-muru-user-number-debug-command.patch \
-    file://mtk-0029-hostapd-mtk-Fix-CCA-issue.patch \
     file://mtk-0030-hostapd-mtk-Fix-unexpected-AP-beacon-state-transitio.patch \
     file://mtk-0031-hostapd-mtk-add-connac3-PHY-MURU-manual-mode-config-.patch \
     file://mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch \
+    file://mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch \
     file://mtk-0034-hostapd-mtk-Fix-hostapd_dfs_start_cac-log.patch \
     file://mtk-0035-hostapd-mtk-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
     file://mtk-0036-hostapd-mtk-Update-parameter_set_count-in-MU-EDCA-IE.patch \
     file://mtk-0037-hostapd-mtk-Add-extension-IE-list-for-non-inherit-IE.patch \
+    file://mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch \
+    file://mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch \
+    file://mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch \
     file://mtk-1000-hostapd-mtk-update-eht-operation-element.patch \
     file://mtk-1001-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch \
     "
diff --git a/recipes-wifi/hostapd/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/hostapd/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch
index f51b42f..b3f4138 100644
--- a/recipes-wifi/hostapd/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch
+++ b/recipes-wifi/hostapd/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch
@@ -1,7 +1,7 @@
-From e063e89785d74506976e34a0a3cbb14836d6b8e3 Mon Sep 17 00:00:00 2001
+From 53a7b7af61b75ea0e3f9d12d3d6302cf40941a8a Mon Sep 17 00:00:00 2001
 From: TomLiu <tomml.liu@mediatek.com>
 Date: Tue, 9 Aug 2022 10:23:44 -0700
-Subject: [PATCH 06/35] hostapd: mtk: Add hostapd MU SET/GET control
+Subject: [PATCH 06/37] hostapd: mtk: Add hostapd MU SET/GET control
 
 ---
  hostapd/config_file.c             |   9 +++
@@ -156,14 +156,14 @@
  	{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
  	  "report a scanned DPP URI from a QR Code" },
 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 344585a..d4fc191 100644
+index 344585a..1b0e990 100644
 --- a/src/ap/ap_config.c
 +++ b/src/ap/ap_config.c
 @@ -280,6 +280,7 @@ struct hostapd_config * hostapd_config_defaults(void)
  	conf->he_6ghz_max_ampdu_len_exp = 7;
  	conf->he_6ghz_rx_ant_pat = 1;
  	conf->he_6ghz_tx_ant_pat = 1;
-+	conf->mu_onoff = 13;
++	conf->mu_onoff = 0xf;
  #endif /* CONFIG_IEEE80211AX */
  
  	/* The third octet of the country string uses an ASCII space character
@@ -446,5 +446,5 @@
  			}
  
 -- 
-2.18.0
+2.39.0
 
diff --git a/recipes-wifi/hostapd/files/patches/mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch b/recipes-wifi/hostapd/files/patches/mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
new file mode 100644
index 0000000..aff2234
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches/mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
@@ -0,0 +1,30 @@
+From 845f192fe55b4774425752ecba8cb8f5c4e24c69 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Wed, 2 Aug 2023 18:33:31 +0800
+Subject: [PATCH 37/38] hostapd: mtk: Fix 11vmbss aid using wrong pool
+
+Fix 11vmbss aid using wrong pool.
+All STAs use the aid pool in transmitted bss.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index f866b8a..b73b821 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4002,6 +4002,9 @@ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ 	int i, j = 32, aid;
+ 
++	if (hapd->iconf->mbssid)
++		hapd = hostapd_mbssid_get_tx_bss(hapd);
++
+ 	/* get a unique AID */
+ 	if (sta->aid > 0) {
+ 		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches/mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/hostapd/files/patches/mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..ec82302
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches/mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From 87ed81b81030cb5567c04227d0fe65c6a15148f3 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 8 Aug 2023 19:21:41 +0800
+Subject: [PATCH] hostapd: mtk: add back ht vht cap missing field before dfs
+ channel fallback
+
+hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
+on the bandwidth of switched channel.
+For example, vht bw 160 support field would be cleared if we switch to
+non bw 160 channel.
+This design works fine with NON-DFS channel switch.
+However, for those DFS channels who require CAC, channel switch command
+calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
+This is simply restarting the interface not CHANNEL SWITCHING, so
+hostapd will not receive any ch_switch event from kernel.
+Therefore, the cleared field in vht_capab will not be set back to 1,
+even if we channel switch to dfs channel bw 160.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 707e7d5..5d17cf8 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3888,6 +3888,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++	if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++	    freq_params->bandwidth > 20)
++		iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++	if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
++	    freq_params->bandwidth == 160)
++		iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches/mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch b/recipes-wifi/hostapd/files/patches/mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
new file mode 100644
index 0000000..d6fa8b6
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches/mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
@@ -0,0 +1,34 @@
+From 70c8f9355c4df3c083965ce652d33e01f8fe7e7c Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Mon, 7 Aug 2023 15:27:27 +0800
+Subject: [PATCH 38/38] hostapd: mtk: Fix rnr ie length when no need to report
+ bss
+
+Fix rnr ie length when no need to report bss. If we don't have content in
+TBTT then don't change the length of the ie (*size_offset).
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index b73b821..ff39fad 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7530,8 +7530,10 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ 		}
+ 
+ 		start = i;
+-		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+-		*size_offset = (eid - size_offset) - 1;
++		if (tbtt_count != 0) {
++			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
++			*size_offset = (eid - size_offset) - 1;
++		}
+ 	}
+ 
+ 	if (tbtt_count == 0)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/hostapd/files/patches/patches.inc b/recipes-wifi/hostapd/files/patches/patches.inc
index 3e8cc14..f783c48 100644
--- a/recipes-wifi/hostapd/files/patches/patches.inc
+++ b/recipes-wifi/hostapd/files/patches/patches.inc
@@ -99,4 +99,7 @@
     file://mtk-0035-hostapd-mtk-add-extension-IE-list-for-non-inherit-IE.patch \
     file://mtk-0036-hostapd-mtk-Fix-background-channel-overlapping-opera.patch \
     file://mtk-0036-hostapd-mtk-Fix-wpa_supplicant-configuration-parsing.patch \
+    file://mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch \
+    file://mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch \
+    file://mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch \
     "
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/hostapd/radius.c b/recipes-wifi/hostapd/files/src-2.10.3/hostapd/radius.c
new file mode 100644
index 0000000..362a22c
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/hostapd/radius.c
@@ -0,0 +1,715 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+
+#include "ap/ap_config.h"
+#include "eap_server/eap.h"
+#include "radius/radius.h"
+#include "radius/radius_server.h"
+#include "eap_register.h"
+
+#include <libubox/blobmsg_json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/avl.h>
+#include <libubox/avl-cmp.h>
+#include <libubox/kvlist.h>
+
+#include <sys/stat.h>
+#include <fnmatch.h>
+
+#define VENDOR_ID_WISPR 14122
+#define VENDOR_ATTR_SIZE 6
+
+struct radius_parse_attr_data {
+	unsigned int vendor;
+	u8 type;
+	int size;
+	char format;
+	const char *data;
+};
+
+struct radius_parse_attr_state {
+	struct hostapd_radius_attr *prev;
+	struct hostapd_radius_attr *attr;
+	struct wpabuf *buf;
+	void *attrdata;
+};
+
+struct radius_user_state {
+	struct avl_node node;
+	struct eap_user data;
+};
+
+struct radius_user_data {
+	struct kvlist users;
+	struct avl_tree user_state;
+	struct blob_attr *wildcard;
+};
+
+struct radius_state {
+	struct radius_server_data *radius;
+	struct eap_config eap;
+
+	struct radius_user_data phase1, phase2;
+	const char *user_file;
+	time_t user_file_ts;
+
+	int n_attrs;
+	struct hostapd_radius_attr *attrs;
+};
+
+struct radius_config {
+	struct tls_connection_params tls;
+	struct radius_server_conf radius;
+};
+
+enum {
+	USER_ATTR_PASSWORD,
+	USER_ATTR_HASH,
+	USER_ATTR_SALT,
+	USER_ATTR_METHODS,
+	USER_ATTR_RADIUS,
+	USER_ATTR_VLAN,
+	USER_ATTR_MAX_RATE_UP,
+	USER_ATTR_MAX_RATE_DOWN,
+	__USER_ATTR_MAX
+};
+
+static void radius_tls_event(void *ctx, enum tls_event ev,
+			      union tls_event_data *data)
+{
+	switch (ev) {
+	case TLS_CERT_CHAIN_SUCCESS:
+		wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
+		break;
+	case TLS_CERT_CHAIN_FAILURE:
+		wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+			   data->cert_fail.reason,
+			   data->cert_fail.depth,
+			   data->cert_fail.subject,
+			   data->cert_fail.reason_txt);
+		break;
+	case TLS_PEER_CERTIFICATE:
+		wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
+			   data->peer_cert.depth,
+			   data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+			   data->peer_cert.subject);
+		break;
+	case TLS_ALERT:
+		if (data->alert.is_local)
+			wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
+				   data->alert.description);
+		else
+			wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
+				   data->alert.description);
+		break;
+	case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+		/* Not applicable to TLS server */
+		break;
+	}
+}
+
+static void radius_userdata_init(struct radius_user_data *u)
+{
+	kvlist_init(&u->users, kvlist_blob_len);
+	avl_init(&u->user_state, avl_strcmp, false, NULL);
+}
+
+static void radius_userdata_free(struct radius_user_data *u)
+{
+	struct radius_user_state *s, *tmp;
+
+	kvlist_free(&u->users);
+	free(u->wildcard);
+	u->wildcard = NULL;
+	avl_remove_all_elements(&u->user_state, s, node, tmp)
+		free(s);
+}
+
+static void
+radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
+{
+	enum {
+		USERSTATE_USERS,
+		USERSTATE_WILDCARD,
+		__USERSTATE_MAX,
+	};
+	static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
+		[USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
+		[USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
+	};
+	struct blob_attr *tb[__USERSTATE_MAX], *cur;
+	int rem;
+
+	if (!data)
+		return;
+
+	blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
+
+	blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
+		kvlist_set(&u->users, blobmsg_name(cur), cur);
+
+	if (tb[USERSTATE_WILDCARD])
+		u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
+}
+
+static void
+load_userfile(struct radius_state *s)
+{
+	enum {
+		USERDATA_PHASE1,
+		USERDATA_PHASE2,
+		__USERDATA_MAX
+	};
+	static const struct blobmsg_policy policy[__USERDATA_MAX] = {
+		[USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
+		[USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
+	};
+	struct blob_attr *tb[__USERDATA_MAX], *cur;
+	static struct blob_buf b;
+	struct stat st;
+	int rem;
+
+	if (stat(s->user_file, &st))
+		return;
+
+	if (s->user_file_ts == st.st_mtime)
+		return;
+
+	s->user_file_ts = st.st_mtime;
+	radius_userdata_free(&s->phase1);
+	radius_userdata_free(&s->phase2);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_json_from_file(&b, s->user_file);
+	blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
+	radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
+	radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
+
+	blob_buf_free(&b);
+}
+
+static struct blob_attr *
+radius_user_get(struct radius_user_data *s, const char *name)
+{
+	struct blob_attr *cur;
+	int rem;
+
+	cur = kvlist_get(&s->users, name);
+	if (cur)
+		return cur;
+
+	blobmsg_for_each_attr(cur, s->wildcard, rem) {
+		static const struct blobmsg_policy policy = {
+			"name", BLOBMSG_TYPE_STRING
+		};
+		struct blob_attr *pattern;
+
+		if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
+			continue;
+
+		blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
+		if (!name)
+			continue;
+
+		if (!fnmatch(blobmsg_get_string(pattern), name, 0))
+			return cur;
+	}
+
+	return NULL;
+}
+
+static struct radius_parse_attr_data *
+radius_parse_attr(struct blob_attr *attr)
+{
+	static const struct blobmsg_policy policy[4] = {
+		{ .type = BLOBMSG_TYPE_INT32 },
+		{ .type = BLOBMSG_TYPE_INT32 },
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+	};
+	static struct radius_parse_attr_data data;
+	struct blob_attr *tb[4];
+	const char *format;
+
+	blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
+
+	if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
+		return NULL;
+
+	format = blobmsg_get_string(tb[2]);
+	if (strlen(format) != 1)
+		return NULL;
+
+	data.vendor = blobmsg_get_u32(tb[0]);
+	data.type = blobmsg_get_u32(tb[1]);
+	data.format = format[0];
+	data.data = blobmsg_get_string(tb[3]);
+	data.size = strlen(data.data);
+
+	switch (data.format) {
+	case 's':
+		break;
+	case 'x':
+		if (data.size & 1)
+			return NULL;
+		data.size /= 2;
+		break;
+	case 'd':
+		data.size = 4;
+		break;
+	default:
+		return NULL;
+	}
+
+	return &data;
+}
+
+static void
+radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
+{
+	struct blob_attr *data = tb[USER_ATTR_RADIUS];
+	struct blob_attr *cur;
+	int rem;
+
+	blobmsg_for_each_attr(cur, data, rem) {
+		struct radius_parse_attr_data *data;
+		size_t prev = *attr_size;
+
+		data = radius_parse_attr(cur);
+		if (!data)
+			continue;
+
+		*attr_size += data->size;
+		if (data->vendor)
+			*attr_size += VENDOR_ATTR_SIZE;
+
+		(*n_attr)++;
+	}
+
+	*n_attr += !!tb[USER_ATTR_VLAN] * 3 +
+		   !!tb[USER_ATTR_MAX_RATE_UP] +
+		   !!tb[USER_ATTR_MAX_RATE_DOWN];
+	*attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
+		      !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
+		      !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
+}
+
+static void *
+radius_add_attr(struct radius_parse_attr_state *state,
+		u32 vendor, u8 type, u8 len)
+{
+	struct hostapd_radius_attr *attr;
+	struct wpabuf *buf;
+	void *val;
+
+	val = state->attrdata;
+
+	buf = state->buf++;
+	buf->buf = val;
+
+	attr = state->attr++;
+	attr->val = buf;
+	attr->type = type;
+
+	if (state->prev)
+		state->prev->next = attr;
+	state->prev = attr;
+
+	if (vendor) {
+		u8 *vendor_hdr = val + 4;
+
+		WPA_PUT_BE32(val, vendor);
+		vendor_hdr[0] = type;
+		vendor_hdr[1] = len + 2;
+
+		len += VENDOR_ATTR_SIZE;
+		val += VENDOR_ATTR_SIZE;
+		attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
+	}
+
+	buf->size = buf->used = len;
+	state->attrdata += len;
+
+	return val;
+}
+
+static void
+radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
+{
+	struct blob_attr *data = tb[USER_ATTR_RADIUS];
+	struct hostapd_radius_attr *prev = NULL;
+	struct blob_attr *cur;
+	int len, rem;
+	void *val;
+
+	if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
+		char buf[5];
+
+		val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
+		WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
+
+		val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
+		WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
+
+		len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
+		val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
+		memcpy(val, buf, len);
+	}
+
+	if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
+		val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
+		WPA_PUT_BE32(val, blobmsg_get_u32(cur));
+	}
+
+	if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
+		val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
+		WPA_PUT_BE32(val, blobmsg_get_u32(cur));
+	}
+
+	blobmsg_for_each_attr(cur, data, rem) {
+		struct radius_parse_attr_data *data;
+		void *val;
+		int size;
+
+		data = radius_parse_attr(cur);
+		if (!data)
+			continue;
+
+		val = radius_add_attr(state, data->vendor, data->type, data->size);
+		switch (data->format) {
+		case 's':
+			memcpy(val, data->data, data->size);
+			break;
+		case 'x':
+			hexstr2bin(data->data, val, data->size);
+			break;
+		case 'd':
+			WPA_PUT_BE32(val, atoi(data->data));
+			break;
+		}
+	}
+}
+
+static void
+radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
+{
+	struct blob_attr *cur;
+	int rem, n = 0;
+
+	if (!data)
+		return;
+
+	blobmsg_for_each_attr(cur, data, rem) {
+		const char *method;
+
+		if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
+			continue;
+
+		if (n == EAP_MAX_METHODS)
+			break;
+
+		method = blobmsg_get_string(cur);
+		eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
+		if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
+		    eap->methods[n].method == EAP_TYPE_NONE) {
+			if (!strcmp(method, "TTLS-PAP")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
+				continue;
+			}
+			if (!strcmp(method, "TTLS-CHAP")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+				continue;
+			}
+			if (!strcmp(method, "TTLS-MSCHAP")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+				continue;
+			}
+			if (!strcmp(method, "TTLS-MSCHAPV2")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+				continue;
+			}
+		}
+		n++;
+	}
+}
+
+static struct eap_user *
+radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
+		      const char *id)
+{
+	static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
+		[USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
+		[USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
+		[USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
+		[USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
+		[USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
+		[USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
+		[USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
+		[USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
+	};
+	struct blob_attr *tb[__USER_ATTR_MAX], *cur;
+	char *password_buf, *salt_buf, *name_buf;
+	struct radius_parse_attr_state astate = {};
+	struct hostapd_radius_attr *attr;
+	struct radius_user_state *state;
+	int pw_len = 0, salt_len = 0;
+	struct eap_user *eap;
+	struct wpabuf *val;
+	size_t attrsize = 0;
+	void *attrdata;
+	int n_attr = 0;
+
+	state = avl_find_element(&u->user_state, id, state, node);
+	if (state)
+		return &state->data;
+
+	blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
+
+	if ((cur = tb[USER_ATTR_SALT]) != NULL)
+		salt_len = strlen(blobmsg_get_string(cur)) / 2;
+	if ((cur = tb[USER_ATTR_HASH]) != NULL)
+		pw_len = strlen(blobmsg_get_string(cur)) / 2;
+	else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
+		pw_len = blobmsg_len(cur) - 1;
+	radius_count_attrs(tb, &n_attr, &attrsize);
+
+	state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
+			 &password_buf, pw_len,
+			 &salt_buf, salt_len,
+			 &astate.attr, n_attr * sizeof(*astate.attr),
+			 &astate.buf, n_attr * sizeof(*astate.buf),
+			 &astate.attrdata, attrsize);
+	eap = &state->data;
+	eap->salt = salt_len ? salt_buf : NULL;
+	eap->salt_len = salt_len;
+	eap->password = pw_len ? password_buf : NULL;
+	eap->password_len = pw_len;
+	eap->force_version = -1;
+
+	if ((cur = tb[USER_ATTR_SALT]) != NULL)
+		hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
+	if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
+		memcpy(password_buf, blobmsg_get_string(cur), pw_len);
+	else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
+		hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
+		eap->password_hash = 1;
+	}
+	radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
+
+	if (n_attr > 0) {
+		cur = tb[USER_ATTR_RADIUS];
+		eap->accept_attr = astate.attr;
+		radius_parse_attrs(tb, &astate);
+	}
+
+	state->node.key = strcpy(name_buf, id);
+	avl_insert(&u->user_state, &state->node);
+
+	return &state->data;
+
+free:
+	free(state);
+	return NULL;
+}
+
+static int radius_get_eap_user(void *ctx, const u8 *identity,
+			       size_t identity_len, int phase2,
+			       struct eap_user *user)
+{
+	struct radius_state *s = ctx;
+	struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
+	struct blob_attr *entry;
+	struct eap_user *data;
+	char *id;
+
+	if (identity_len > 512)
+		return -1;
+
+	load_userfile(s);
+
+	id = alloca(identity_len + 1);
+	memcpy(id, identity, identity_len);
+	id[identity_len] = 0;
+
+	entry = radius_user_get(u, id);
+	if (!entry)
+		return -1;
+
+	if (!user)
+		return 0;
+
+	data = radius_user_get_state(u, entry, id);
+	if (!data)
+		return -1;
+
+	*user = *data;
+	if (user->password_len > 0)
+		user->password = os_memdup(user->password, user->password_len);
+	if (user->salt_len > 0)
+		user->salt = os_memdup(user->salt, user->salt_len);
+	user->phase2 = phase2;
+
+	return 0;
+}
+
+static int radius_setup(struct radius_state *s, struct radius_config *c)
+{
+	struct eap_config *eap = &s->eap;
+	struct tls_config conf = {
+		.event_cb = radius_tls_event,
+		.tls_flags = TLS_CONN_DISABLE_TLSv1_3,
+		.cb_ctx = s,
+	};
+
+	eap->eap_server = 1;
+	eap->max_auth_rounds = 100;
+	eap->max_auth_rounds_short = 50;
+	eap->ssl_ctx = tls_init(&conf);
+	if (!eap->ssl_ctx) {
+		wpa_printf(MSG_INFO, "TLS init failed\n");
+		return 1;
+	}
+
+	if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
+		wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
+		return 1;
+	}
+
+	c->radius.eap_cfg = eap;
+	c->radius.conf_ctx = s;
+	c->radius.get_eap_user = radius_get_eap_user;
+	s->radius = radius_server_init(&c->radius);
+	if (!s->radius) {
+		wpa_printf(MSG_INFO, "failed to initialize radius server\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+static int radius_init(struct radius_state *s)
+{
+	memset(s, 0, sizeof(*s));
+	radius_userdata_init(&s->phase1);
+	radius_userdata_init(&s->phase2);
+}
+
+static void radius_deinit(struct radius_state *s)
+{
+	if (s->radius)
+		radius_server_deinit(s->radius);
+
+	if (s->eap.ssl_ctx)
+		tls_deinit(s->eap.ssl_ctx);
+
+	radius_userdata_free(&s->phase1);
+	radius_userdata_free(&s->phase2);
+}
+
+static int usage(const char *progname)
+{
+	fprintf(stderr, "Usage: %s <options>\n",
+		progname);
+}
+
+int radius_main(int argc, char **argv)
+{
+	static struct radius_state state = {};
+	static struct radius_config config = {};
+	const char *progname = argv[0];
+	int ret = 0;
+	int ch;
+
+	wpa_debug_setup_stdout();
+	wpa_debug_level = 0;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return 1;
+	}
+
+	eap_server_register_methods();
+	radius_init(&state);
+
+	while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
+		switch (ch) {
+		case '6':
+			config.radius.ipv6 = 1;
+			break;
+		case 'C':
+			config.tls.ca_cert = optarg;
+			break;
+		case 'c':
+			if (config.tls.client_cert2)
+				return usage(progname);
+
+			if (config.tls.client_cert)
+				config.tls.client_cert2 = optarg;
+			else
+				config.tls.client_cert = optarg;
+			break;
+		case 'd':
+			config.tls.dh_file = optarg;
+			break;
+		case 'i':
+			state.eap.server_id = optarg;
+			state.eap.server_id_len = strlen(optarg);
+			break;
+		case 'k':
+			if (config.tls.private_key2)
+				return usage(progname);
+
+			if (config.tls.private_key)
+				config.tls.private_key2 = optarg;
+			else
+				config.tls.private_key = optarg;
+			break;
+		case 'K':
+			if (config.tls.private_key_passwd2)
+				return usage(progname);
+
+			if (config.tls.private_key_passwd)
+				config.tls.private_key_passwd2 = optarg;
+			else
+				config.tls.private_key_passwd = optarg;
+			break;
+		case 'p':
+			config.radius.auth_port = atoi(optarg);
+			break;
+		case 'P':
+			config.radius.acct_port = atoi(optarg);
+			break;
+		case 's':
+			config.radius.client_file = optarg;
+			break;
+		case 'u':
+			state.user_file = optarg;
+			break;
+		default:
+			return usage(progname);
+		}
+	}
+
+	if (!config.tls.client_cert || !config.tls.private_key ||
+	    !config.radius.client_file || !state.eap.server_id ||
+	    !state.user_file) {
+		wpa_printf(MSG_INFO, "missing options\n");
+		goto out;
+	}
+
+	ret = radius_setup(&state, &config);
+	if (ret)
+		goto out;
+
+	load_userfile(&state);
+	eloop_run();
+
+out:
+	radius_deinit(&state);
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c
new file mode 100644
index 0000000..a609eb1
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.c
@@ -0,0 +1,2002 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/wpabuf.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "hostapd.h"
+#include "neighbor_db.h"
+#include "wps_hostapd.h"
+#include "sta_info.h"
+#include "ubus.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "rrm.h"
+#include "wnm_ap.h"
+#include "taxonomy.h"
+#include "airtime_policy.h"
+#include "hw_features.h"
+
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static int ctx_ref;
+
+static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct hostapd_data, ubus.obj);
+}
+
+struct ubus_banned_client {
+	struct avl_node avl;
+	u8 addr[ETH_ALEN];
+};
+
+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
+{
+	if (ubus_reconnect(ctx, NULL)) {
+		eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+		return;
+	}
+
+	ubus_add_uloop(ctx);
+}
+
+static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
+{
+	uloop_fd_delete(&ctx->sock);
+	eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+}
+
+static bool hostapd_ubus_init(void)
+{
+	if (ctx)
+		return true;
+
+	eloop_add_uloop();
+	ctx = ubus_connect(NULL);
+	if (!ctx)
+		return false;
+
+	ctx->connection_lost = hostapd_ubus_connection_lost;
+	ubus_add_uloop(ctx);
+
+	return true;
+}
+
+static void hostapd_ubus_ref_inc(void)
+{
+	ctx_ref++;
+}
+
+static void hostapd_ubus_ref_dec(void)
+{
+	ctx_ref--;
+	if (!ctx)
+		return;
+
+	if (ctx_ref)
+		return;
+
+	uloop_fd_delete(&ctx->sock);
+	ubus_free(ctx);
+	ctx = NULL;
+}
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+	if (!hostapd_ubus_init())
+		return;
+}
+
+void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+	if (!ctx)
+		return;
+}
+
+static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
+{
+	char *event_type;
+
+	if (!ctx || !obj)
+		return;
+
+	if (asprintf(&event_type, "bss.%s", event) < 0)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "name", bssname);
+	ubus_notify(ctx, obj, event_type, b.head, -1);
+	free(event_type);
+}
+
+static void
+hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
+{
+	struct ubus_banned_client *ban = eloop_data;
+	struct hostapd_data *hapd = user_ctx;
+
+	avl_delete(&hapd->ubus.banned, &ban->avl);
+	free(ban);
+}
+
+static void
+hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
+{
+	struct ubus_banned_client *ban;
+
+	if (time < 0)
+		time = 0;
+
+	ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+	if (!ban) {
+		if (!time)
+			return;
+
+		ban = os_zalloc(sizeof(*ban));
+		memcpy(ban->addr, addr, sizeof(ban->addr));
+		ban->avl.key = ban->addr;
+		avl_insert(&hapd->ubus.banned, &ban->avl);
+	} else {
+		eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
+		if (!time) {
+			hostapd_bss_del_ban(ban, hapd);
+			return;
+		}
+	}
+
+	eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
+}
+
+static int
+hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	return hostapd_reload_config(hapd->iface, 1);
+}
+
+
+static void
+hostapd_parse_vht_map_blobmsg(uint16_t map)
+{
+	char label[4];
+	int16_t val;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		snprintf(label, 4, "%dss", i + 1);
+
+		val = (map & (BIT(1) | BIT(0))) + 7;
+		blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
+		map = map >> 2;
+	}
+}
+
+static void
+hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
+{
+	void *supported_mcs;
+	void *map;
+	int i;
+
+	static const struct {
+		const char *name;
+		uint32_t flag;
+	} vht_capas[] = {
+		{ "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
+		{ "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
+		blobmsg_add_u8(&b, vht_capas[i].name,
+				!!(vhtc->vht_capabilities_info & vht_capas[i].flag));
+
+	supported_mcs = blobmsg_open_table(&b, "mcs_map");
+
+	/* RX map */
+	map = blobmsg_open_table(&b, "rx");
+	hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
+	blobmsg_close_table(&b, map);
+
+	/* TX map */
+	map = blobmsg_open_table(&b, "tx");
+	hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
+	blobmsg_close_table(&b, map);
+
+	blobmsg_close_table(&b, supported_mcs);
+}
+
+static void
+hostapd_parse_capab_blobmsg(struct sta_info *sta)
+{
+	void *r, *v;
+
+	v = blobmsg_open_table(&b, "capabilities");
+
+	if (sta->vht_capabilities) {
+		r = blobmsg_open_table(&b, "vht");
+		hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
+		blobmsg_close_table(&b, r);
+	}
+
+	/* ToDo: Add HT / HE capability parsing */
+
+	blobmsg_close_table(&b, v);
+}
+
+static int
+hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct hostap_sta_driver_data sta_driver_data;
+	struct sta_info *sta;
+	void *list, *c;
+	char mac_buf[20];
+	static const struct {
+		const char *name;
+		uint32_t flag;
+	} sta_flags[] = {
+		{ "auth", WLAN_STA_AUTH },
+		{ "assoc", WLAN_STA_ASSOC },
+		{ "authorized", WLAN_STA_AUTHORIZED },
+		{ "preauth", WLAN_STA_PREAUTH },
+		{ "wds", WLAN_STA_WDS },
+		{ "wmm", WLAN_STA_WMM },
+		{ "ht", WLAN_STA_HT },
+		{ "vht", WLAN_STA_VHT },
+		{ "he", WLAN_STA_HE },
+		{ "wps", WLAN_STA_WPS },
+		{ "mfp", WLAN_STA_MFP },
+	};
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	list = blobmsg_open_table(&b, "clients");
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		void *r;
+		int i;
+
+		sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
+		c = blobmsg_open_table(&b, mac_buf);
+		for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
+			blobmsg_add_u8(&b, sta_flags[i].name,
+				       !!(sta->flags & sta_flags[i].flag));
+
+#ifdef CONFIG_MBO
+		blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
+#endif
+
+		r = blobmsg_open_array(&b, "rrm");
+		for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
+			blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
+		blobmsg_close_array(&b, r);
+
+		r = blobmsg_open_array(&b, "extended_capabilities");
+		/* Check if client advertises extended capabilities */
+		if (sta->ext_capability && sta->ext_capability[0] > 0) {
+			for (i = 0; i < sta->ext_capability[0]; i++) {
+				blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
+			}
+		}
+		blobmsg_close_array(&b, r);
+
+		blobmsg_add_u32(&b, "aid", sta->aid);
+#ifdef CONFIG_TAXONOMY
+		r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
+		if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
+			blobmsg_add_string_buffer(&b);
+#endif
+
+		/* Driver information */
+		if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
+			r = blobmsg_open_table(&b, "bytes");
+			blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
+			blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "airtime");
+			blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
+			blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "packets");
+			blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
+			blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "rate");
+			/* Rate in kbits */
+			blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
+			blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
+			blobmsg_close_table(&b, r);
+			blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
+		}
+
+		hostapd_parse_capab_blobmsg(sta);
+
+		blobmsg_close_table(&b, c);
+	}
+	blobmsg_close_array(&b, list);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
+	blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *req, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
+	struct os_reltime now;
+	char ssid[SSID_MAX_LEN + 1];
+	char phy_name[17];
+	size_t ssid_len = SSID_MAX_LEN;
+	u8 channel = 0, op_class = 0;
+
+	if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
+		ssid_len = hapd->conf->ssid.ssid_len;
+	
+	ieee80211_freq_to_channel_ext(hapd->iface->freq,
+				      hapd->iconf->secondary_channel,
+				      hostapd_get_oper_chwidth(hapd->iconf),
+				      &op_class, &channel);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
+	blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
+
+	memset(ssid, 0, SSID_MAX_LEN + 1);
+	memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
+	blobmsg_add_string(&b, "ssid", ssid);
+
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	blobmsg_add_u32(&b, "channel", channel);
+	blobmsg_add_u32(&b, "op_class", op_class);
+	blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
+#ifdef CONFIG_IEEE80211AX
+	blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
+					 hapd->iface->conf->he_op.he_bss_color);
+#else
+	blobmsg_add_u32(&b, "bss_color", -1);
+#endif
+
+	snprintf(phy_name, 17, "%s", hapd->iface->phy);
+	blobmsg_add_string(&b, "phy", phy_name);
+
+	/* RRM */
+	rrm_table = blobmsg_open_table(&b, "rrm");
+	blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
+	blobmsg_close_table(&b, rrm_table);
+
+	/* WNM */
+	wnm_table = blobmsg_open_table(&b, "wnm");
+	blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
+	blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
+	blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
+	blobmsg_close_table(&b, wnm_table);
+
+	/* Airtime */
+	airtime_table = blobmsg_open_table(&b, "airtime");
+	blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
+	blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
+	blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
+	blobmsg_close_table(&b, airtime_table);
+
+	/* DFS */
+	dfs_table = blobmsg_open_table(&b, "dfs");
+	blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
+	blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
+	os_reltime_age(&hapd->iface->dfs_cac_start, &now);
+	blobmsg_add_u32(&b, "cac_seconds_left",
+			hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
+	blobmsg_close_table(&b, dfs_table);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+enum {
+	NOTIFY_RESPONSE,
+	__NOTIFY_MAX
+};
+
+static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
+	[NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__NOTIFY_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct wpabuf *elems;
+	const char *pos;
+	size_t len;
+
+	blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
+		      blob_data(msg), blob_len(msg));
+
+	if (!tb[NOTIFY_RESPONSE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	DEL_CLIENT_ADDR,
+	DEL_CLIENT_REASON,
+	DEL_CLIENT_DEAUTH,
+	DEL_CLIENT_BAN_TIME,
+	__DEL_CLIENT_MAX
+};
+
+static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
+	[DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
+	[DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
+	[DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__DEL_CLIENT_MAX];
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct sta_info *sta;
+	bool deauth = false;
+	int reason;
+	u8 addr[ETH_ALEN];
+
+	blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[DEL_CLIENT_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[DEL_CLIENT_REASON])
+		reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
+
+	if (tb[DEL_CLIENT_DEAUTH])
+		deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		if (deauth) {
+			hostapd_drv_sta_deauth(hapd, addr, reason);
+			ap_sta_deauthenticate(hapd, sta, reason);
+		} else {
+			hostapd_drv_sta_disassoc(hapd, addr, reason);
+			ap_sta_disassociate(hapd, sta, reason);
+		}
+	}
+
+	if (tb[DEL_CLIENT_BAN_TIME])
+		hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
+
+	return 0;
+}
+
+static void
+blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
+{
+	char *s;
+
+	s = blobmsg_alloc_string_buffer(buf, name, 20);
+	sprintf(s, MACSTR, MAC2STR(addr));
+	blobmsg_add_string_buffer(buf);
+}
+
+static int
+hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct ubus_banned_client *ban;
+	void *c;
+
+	blob_buf_init(&b, 0);
+	c = blobmsg_open_array(&b, "clients");
+	avl_for_each_element(&hapd->ubus.banned, ban, avl)
+		blobmsg_add_macaddr(&b, NULL, ban->addr);
+	blobmsg_close_array(&b, c);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+#ifdef CONFIG_WPS
+static int
+hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = hostapd_wps_button_pushed(hapd, NULL);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+
+static const char * pbc_status_enum_str(enum pbc_status status)
+{
+	switch (status) {
+	case WPS_PBC_STATUS_DISABLE:
+		return "Disabled";
+	case WPS_PBC_STATUS_ACTIVE:
+		return "Active";
+	case WPS_PBC_STATUS_TIMEOUT:
+		return "Timed-out";
+	case WPS_PBC_STATUS_OVERLAP:
+		return "Overlap";
+	default:
+		return "Unknown";
+	}
+}
+
+static int
+hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	blob_buf_init(&b, 0);
+
+	blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
+	blobmsg_add_string(&b, "last_wps_result",
+			   (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
+			    "Success":
+			    (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
+			     "Failed" : "None")));
+
+	/* If status == Failure - Add possible Reasons */
+	if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
+	   hapd->wps_stats.failure_reason > 0)
+		blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
+
+	if (hapd->wps_stats.status)
+		blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = hostapd_wps_cancel(hapd);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+static int
+hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = ieee802_11_set_beacon(hapd);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+enum {
+	CONFIG_IFACE,
+	CONFIG_FILE,
+	__CONFIG_MAX
+};
+
+enum {
+	CSA_FREQ,
+	CSA_BCN_COUNT,
+	CSA_CENTER_FREQ1,
+	CSA_CENTER_FREQ2,
+	CSA_BANDWIDTH,
+	CSA_SEC_CHANNEL_OFFSET,
+	CSA_HT,
+	CSA_VHT,
+	CSA_HE,
+	CSA_BLOCK_TX,
+	CSA_FORCE,
+	__CSA_MAX
+};
+
+static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
+	[CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
+	[CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
+	[CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
+	[CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
+	[CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
+	[CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
+	[CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
+	[CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
+	[CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
+	[CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
+	[CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
+};
+
+
+static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_iface *iface = eloop_data;
+	struct hostapd_freq_params *freq_params = user_ctx;
+
+	hostapd_switch_channel_fallback(iface, freq_params);
+}
+
+#ifdef NEED_AP_MLME
+static int
+hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CSA_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_config *iconf = hapd->iface->conf;
+	struct hostapd_freq_params *freq_params;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	struct csa_settings css = {
+		.freq_params = {
+			.ht_enabled = iconf->ieee80211n,
+			.vht_enabled = iconf->ieee80211ac,
+			.he_enabled = iconf->ieee80211ax,
+			.sec_channel_offset = iconf->secondary_channel,
+		}
+	};
+	u8 chwidth = hostapd_get_oper_chwidth(iconf);
+	u8 seg0 = 0, seg1 = 0;
+	int ret = UBUS_STATUS_OK;
+	int i;
+
+	blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CSA_FREQ])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	switch (iconf->vht_oper_chwidth) {
+	case CHANWIDTH_USE_HT:
+		if (iconf->secondary_channel)
+			css.freq_params.bandwidth = 40;
+		else
+			css.freq_params.bandwidth = 20;
+		break;
+	case CHANWIDTH_160MHZ:
+		css.freq_params.bandwidth = 160;
+		break;
+	default:
+		css.freq_params.bandwidth = 80;
+		break;
+	}
+
+	css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
+
+#define SET_CSA_SETTING(name, field, type) \
+	do { \
+		if (tb[name]) \
+			css.field = blobmsg_get_ ## type(tb[name]); \
+	} while(0)
+
+	SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
+	SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
+	SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
+	SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
+	SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
+	SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
+	SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
+	SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
+	SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
+
+	css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
+	if (!css.freq_params.channel)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	switch (css.freq_params.bandwidth) {
+	case 160:
+		chwidth = CHANWIDTH_160MHZ;
+		break;
+	case 80:
+		chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
+		break;
+	default:
+		chwidth = CHANWIDTH_USE_HT;
+		break;
+	}
+
+	hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
+				css.freq_params.freq,
+				css.freq_params.channel, iconf->enable_edmg,
+				iconf->edmg_channel,
+				css.freq_params.ht_enabled,
+				css.freq_params.vht_enabled,
+				css.freq_params.he_enabled,
+				css.freq_params.eht_enabled,
+				css.freq_params.sec_channel_offset,
+				chwidth, seg0, seg1,
+				iconf->vht_capab,
+				mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				NULL,
+				mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+				NULL);
+
+	for (i = 0; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+
+		if (hostapd_switch_channel(bss, &css) != 0)
+			ret = UBUS_STATUS_NOT_SUPPORTED;
+	}
+
+	if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
+		return ret;
+
+	freq_params = malloc(sizeof(*freq_params));
+	memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
+	eloop_register_timeout(0, 1, switch_chan_fallback_cb,
+			       hapd->iface, freq_params);
+
+	return 0;
+#undef SET_CSA_SETTING
+}
+#endif
+
+enum {
+	VENDOR_ELEMENTS,
+	__VENDOR_ELEMENTS_MAX
+};
+
+static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
+	/* vendor elements are provided as hex-string */
+	[VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_bss_config *bss = hapd->conf;
+	struct wpabuf *elems;
+	const char *pos;
+	size_t len;
+
+	blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
+		      blob_data(msg), blob_len(msg));
+
+	if (!tb[VENDOR_ELEMENTS])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
+	len = os_strlen(pos);
+	if (len & 0x01)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+	len /= 2;
+	if (len == 0) {
+		wpabuf_free(bss->vendor_elements);
+		bss->vendor_elements = NULL;
+		return 0;
+	}
+
+	elems = wpabuf_alloc(len);
+	if (elems == NULL)
+		return 1;
+
+	if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+		wpabuf_free(elems);
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	wpabuf_free(bss->vendor_elements);
+	bss->vendor_elements = elems;
+
+	/* update beacons if vendor elements were set successfully */
+	if (ieee802_11_update_beacons(hapd->iface) != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+	return UBUS_STATUS_OK;
+}
+
+static void
+hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
+{
+	const u8 *data;
+	char *str;
+	int len;
+
+	blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
+
+	str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
+	memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
+	str[nr->ssid.ssid_len] = 0;
+	blobmsg_add_string_buffer(&b);
+
+	len = wpabuf_len(nr->nr);
+	str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
+	wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
+	blobmsg_add_string_buffer(&b);
+}
+
+enum {
+	BSS_MGMT_EN_NEIGHBOR,
+	BSS_MGMT_EN_BEACON,
+	BSS_MGMT_EN_LINK_MEASUREMENT,
+#ifdef CONFIG_WNM_AP
+	BSS_MGMT_EN_BSS_TRANSITION,
+#endif
+	__BSS_MGMT_EN_MAX
+};
+
+static bool
+__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+	uint32_t flags;
+
+	switch (flag) {
+	case BSS_MGMT_EN_NEIGHBOR:
+		if (bss->radio_measurements[0] &
+		    WLAN_RRM_CAPS_NEIGHBOR_REPORT)
+			return false;
+
+		bss->radio_measurements[0] |=
+			WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+		hostapd_neighbor_set_own_report(hapd);
+		return true;
+	case BSS_MGMT_EN_BEACON:
+		flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+			WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+			WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+		if (bss->radio_measurements[0] & flags == flags)
+			return false;
+
+		bss->radio_measurements[0] |= (u8) flags;
+		return true;
+	case BSS_MGMT_EN_LINK_MEASUREMENT:
+		flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+		if (bss->radio_measurements[0] & flags == flags)
+			return false;
+
+		bss->radio_measurements[0] |= (u8) flags;
+		return true;
+#ifdef CONFIG_WNM_AP
+	case BSS_MGMT_EN_BSS_TRANSITION:
+		if (bss->bss_transition)
+			return false;
+
+		bss->bss_transition = 1;
+		return true;
+#endif
+	}
+}
+
+static void
+__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
+{
+	bool update = false;
+	int i;
+
+	for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
+		if (!(flags & (1 << i)))
+			continue;
+
+		update |= __hostapd_bss_mgmt_enable_f(hapd, i);
+	}
+
+	if (update)
+		ieee802_11_update_beacons(hapd->iface);
+}
+
+
+static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
+	[BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
+	[BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
+	[BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
+#ifdef CONFIG_WNM_AP
+	[BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
+#endif
+};
+
+static int
+hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct blob_attr *tb[__BSS_MGMT_EN_MAX];
+	struct blob_attr *cur;
+	uint32_t flags = 0;
+	int i;
+	bool neigh = false, beacon = false;
+
+	blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
+
+	for (i = 0; i < ARRAY_SIZE(tb); i++) {
+		if (!tb[i] || !blobmsg_get_bool(tb[i]))
+			continue;
+
+		flags |= (1 << i);
+	}
+
+	__hostapd_bss_mgmt_enable(hapd, flags);
+
+	return 0;
+}
+
+
+static void
+hostapd_rrm_nr_enable(struct hostapd_data *hapd)
+{
+	__hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
+}
+
+static int
+hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *req, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_neighbor_entry *nr;
+	void *c;
+
+	hostapd_rrm_nr_enable(hapd);
+
+	nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
+	if (!nr)
+		return UBUS_STATUS_NOT_FOUND;
+
+	blob_buf_init(&b, 0);
+
+	c = blobmsg_open_array(&b, "value");
+	hostapd_rrm_print_nr(nr);
+	blobmsg_close_array(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_neighbor_entry *nr;
+	void *c;
+
+	hostapd_rrm_nr_enable(hapd);
+	blob_buf_init(&b, 0);
+
+	c = blobmsg_open_array(&b, "list");
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+		void *cur;
+
+		if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+			continue;
+
+		cur = blobmsg_open_array(&b, NULL);
+		hostapd_rrm_print_nr(nr);
+		blobmsg_close_array(&b, cur);
+	}
+	blobmsg_close_array(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+enum {
+	NR_SET_LIST,
+	__NR_SET_LIST_MAX
+};
+
+static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
+	[NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
+};
+
+
+static void
+hostapd_rrm_nr_clear(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr;
+
+restart:
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+		if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+			continue;
+
+		hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
+		goto restart;
+	}
+}
+
+static int
+hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	static const struct blobmsg_policy nr_e_policy[] = {
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+	};
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct blob_attr *tb_l[__NR_SET_LIST_MAX];
+	struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
+	struct blob_attr *cur;
+	int rem;
+
+	hostapd_rrm_nr_enable(hapd);
+
+	blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
+	if (!tb_l[NR_SET_LIST])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	hostapd_rrm_nr_clear(hapd);
+	blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
+		struct wpa_ssid_value ssid;
+		struct wpabuf *data;
+		u8 bssid[ETH_ALEN];
+		char *s, *nr_s;
+
+		blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
+		if (!tb[0] || !tb[1] || !tb[2])
+			goto invalid;
+
+		/* Neighbor Report binary */
+		nr_s = blobmsg_get_string(tb[2]);
+		data = wpabuf_parse_bin(nr_s);
+		if (!data)
+			goto invalid;
+
+		/* BSSID */
+		s = blobmsg_get_string(tb[0]);
+		if (strlen(s) == 0) {
+			/* Copy BSSID from neighbor report */
+			if (hwaddr_compact_aton(nr_s, bssid))
+				goto invalid;
+		} else if (hwaddr_aton(s, bssid)) {
+			goto invalid;
+		}
+
+		/* SSID */
+		s = blobmsg_get_string(tb[1]);
+		if (strlen(s) == 0) {
+			/* Copy SSID from hostapd BSS conf */
+			memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
+		} else {
+			ssid.ssid_len = strlen(s);
+			if (ssid.ssid_len > sizeof(ssid.ssid))
+				goto invalid;
+
+			memcpy(&ssid, s, ssid.ssid_len);
+		}
+
+		hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
+		wpabuf_free(data);
+		continue;
+
+invalid:
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	return 0;
+}
+
+enum {
+	BEACON_REQ_ADDR,
+	BEACON_REQ_MODE,
+	BEACON_REQ_OP_CLASS,
+	BEACON_REQ_CHANNEL,
+	BEACON_REQ_DURATION,
+	BEACON_REQ_BSSID,
+	BEACON_REQ_SSID,
+	__BEACON_REQ_MAX,
+};
+
+static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
+	[BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
+	[BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *ureq, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BEACON_REQ_MAX];
+	struct blob_attr *cur;
+	struct wpabuf *req;
+	u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+	u8 addr[ETH_ALEN];
+	int mode, rem, ret;
+	int buf_len = 13;
+
+	blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
+	    !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BEACON_REQ_SSID])
+		buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
+
+	mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
+	if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BEACON_REQ_BSSID] &&
+	    hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	req = wpabuf_alloc(buf_len);
+	if (!req)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	/* 1: regulatory class */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
+
+	/* 2: channel number */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
+
+	/* 3-4: randomization interval */
+	wpabuf_put_le16(req, 0);
+
+	/* 5-6: duration */
+	wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
+
+	/* 7: mode */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
+
+	/* 8-13: BSSID */
+	wpabuf_put_data(req, bssid, ETH_ALEN);
+
+	if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
+		wpabuf_put_u8(req, WLAN_EID_SSID);
+		wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
+		wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
+	}
+
+	ret = hostapd_send_beacon_req(hapd, addr, 0, req);
+	if (ret < 0)
+		return -ret;
+
+	return 0;
+}
+
+enum {
+	LM_REQ_ADDR,
+	LM_REQ_TX_POWER_USED,
+	LM_REQ_TX_POWER_MAX,
+	__LM_REQ_MAX,
+};
+
+static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
+	[LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
+	[LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *ureq, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__LM_REQ_MAX];
+	struct wpabuf *buf;
+	u8 addr[ETH_ALEN];
+	int ret;
+	int8_t txp_used, txp_max;
+
+	txp_used = 0;
+	txp_max = 0;
+
+	blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[LM_REQ_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[LM_REQ_TX_POWER_USED])
+		txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
+
+	if (tb[LM_REQ_TX_POWER_MAX])
+		txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
+
+	if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	buf = wpabuf_alloc(5);
+	if (!buf)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, 1);
+	/* TX-Power used */
+	wpabuf_put_u8(buf, txp_used);
+	/* Max TX Power */
+	wpabuf_put_u8(buf, txp_max);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+	if (ret < 0)
+		return -ret;
+
+	return 0;
+}
+
+
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
+	const u8 *pos, *end;
+	u8 token;
+
+	end = data + len;
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+
+	if (end - pos < 8)
+		return;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", mgmt->sa);
+	blobmsg_add_u16(&b, "dialog-token", token);
+	blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
+	blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
+	blobmsg_add_u16(&b, "rcpi", pos[6]);
+	blobmsg_add_u16(&b, "rsni", pos[7]);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
+}
+
+
+#ifdef CONFIG_WNM_AP
+
+static int
+hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
+		    u16 disassoc_timer, u8 validity_period, u8 dialog_token,
+		    struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
+{
+	struct blob_attr *cur;
+	struct sta_info *sta;
+	int nr_len = 0;
+	int rem;
+	u8 *nr = NULL;
+	u8 req_mode = 0;
+	u8 mbo[10];
+	size_t mbo_len = 0;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return UBUS_STATUS_NOT_FOUND;
+
+	if (neighbors) {
+		u8 *nr_cur;
+
+		if (blobmsg_check_array(neighbors,
+					BLOBMSG_TYPE_STRING) < 0)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+		blobmsg_for_each_attr(cur, neighbors, rem) {
+			int len = strlen(blobmsg_get_string(cur));
+
+			if (len % 2)
+				return UBUS_STATUS_INVALID_ARGUMENT;
+
+			nr_len += (len / 2) + 2;
+		}
+
+		if (nr_len) {
+			nr = os_zalloc(nr_len);
+			if (!nr)
+				return UBUS_STATUS_UNKNOWN_ERROR;
+		}
+
+		nr_cur = nr;
+		blobmsg_for_each_attr(cur, neighbors, rem) {
+			int len = strlen(blobmsg_get_string(cur)) / 2;
+
+			*nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
+			*nr_cur++ = (u8) len;
+			if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
+				free(nr);
+				return UBUS_STATUS_INVALID_ARGUMENT;
+			}
+
+			nr_cur += len;
+		}
+	}
+
+	if (nr)
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+
+	if (abridged)
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+
+	if (disassoc_imminent)
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+	u8 *mbo_pos = mbo;
+
+	if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+	*mbo_pos++ = 1;
+	*mbo_pos++ = mbo_reason;
+	*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+	*mbo_pos++ = 1;
+	*mbo_pos++ = cell_pref;
+
+	if (reassoc_delay) {
+		*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+		*mbo_pos++ = 2;
+		WPA_PUT_LE16(mbo_pos, reassoc_delay);
+		mbo_pos += 2;
+	}
+
+	mbo_len = mbo_pos - mbo;
+#endif
+
+	if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
+				dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	return 0;
+}
+
+enum {
+	BSS_TR_ADDR,
+	BSS_TR_DA_IMMINENT,
+	BSS_TR_DA_TIMER,
+	BSS_TR_VALID_PERIOD,
+	BSS_TR_NEIGHBORS,
+	BSS_TR_ABRIDGED,
+	BSS_TR_DIALOG_TOKEN,
+#ifdef CONFIG_MBO
+	BSS_TR_MBO_REASON,
+	BSS_TR_CELL_PREF,
+	BSS_TR_REASSOC_DELAY,
+#endif
+	__BSS_TR_DISASSOC_MAX
+};
+
+static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
+	[BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
+	[BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
+#ifdef CONFIG_MBO
+	[BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
+#endif
+};
+
+static int
+hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
+			       struct ubus_request_data *ureq, const char *method,
+			       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
+	struct sta_info *sta;
+	u32 da_timer = 0;
+	u32 valid_period = 0;
+	u8 addr[ETH_ALEN];
+	u32 dialog_token = 1;
+	bool abridged;
+	bool da_imminent;
+	u8 mbo_reason;
+	u8 cell_pref;
+	u8 reassoc_delay;
+
+	blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BSS_TR_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BSS_TR_DA_TIMER])
+		da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
+
+	if (tb[BSS_TR_VALID_PERIOD])
+		valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
+
+	if (tb[BSS_TR_DIALOG_TOKEN])
+		dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
+
+	da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
+	abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
+
+#ifdef CONFIG_MBO
+	if (tb[BSS_TR_MBO_REASON])
+		mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
+
+	if (tb[BSS_TR_CELL_PREF])
+		cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
+
+	if (tb[BSS_TR_REASSOC_DELAY])
+		reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
+#endif
+
+	return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
+				   dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
+}
+#endif
+
+#ifdef CONFIG_AIRTIME_POLICY
+enum {
+	UPDATE_AIRTIME_STA,
+	UPDATE_AIRTIME_WEIGHT,
+	__UPDATE_AIRTIME_MAX,
+};
+
+
+static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
+	[UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
+	[UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
+			   struct ubus_request_data *ureq, const char *method,
+			   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
+	struct sta_info *sta = NULL;
+	u8 addr[ETH_ALEN];
+	int weight;
+
+	blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[UPDATE_AIRTIME_WEIGHT])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
+
+	if (!tb[UPDATE_AIRTIME_STA]) {
+		if (!weight)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+		hapd->conf->airtime_weight = weight;
+		return 0;
+	}
+
+	if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return UBUS_STATUS_NOT_FOUND;
+
+	sta->dyn_airtime_weight = weight;
+	airtime_policy_new_sta(hapd, sta);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_TAXONOMY
+static const struct blobmsg_policy addr_policy[] = {
+	{ "address", BLOBMSG_TYPE_STRING }
+};
+
+static bool
+hostapd_add_b64_data(const char *name, const struct wpabuf *buf)
+{
+	char *str;
+
+	if (!buf)
+		return false;
+
+	str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf)));
+	b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf)));
+	blobmsg_add_string_buffer(&b);
+
+	return true;
+}
+
+static int
+hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb;
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+
+	blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
+
+	if (!tb || hwaddr_aton(blobmsg_data(tb), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy))
+		return UBUS_STATUS_NOT_FOUND;
+
+	blob_buf_init(&b, 0);
+	hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy);
+	hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+#endif
+
+
+static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
+	UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
+#ifdef CONFIG_TAXONOMY
+	UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy),
+#endif
+	UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
+	UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
+#ifdef CONFIG_AIRTIME_POLICY
+	UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
+#endif
+	UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
+#ifdef CONFIG_WPS
+	UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
+	UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
+	UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
+#endif
+	UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
+	UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
+#ifdef NEED_AP_MLME
+	UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
+#endif
+	UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
+	UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
+	UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
+	UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
+	UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
+	UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
+	UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
+	UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
+#ifdef CONFIG_WNM_AP
+	UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
+#endif
+};
+
+static struct ubus_object_type bss_object_type =
+	UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
+
+static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
+{
+	return memcmp(k1, k2, ETH_ALEN);
+}
+
+void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+	struct ubus_object *obj = &hapd->ubus.obj;
+	char *name;
+	int ret;
+
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED)
+		return;
+#endif
+
+	if (!hostapd_ubus_init())
+		return;
+
+	if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
+		return;
+
+	avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
+	obj->name = name;
+	obj->type = &bss_object_type;
+	obj->methods = bss_object_type.methods;
+	obj->n_methods = bss_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	hostapd_ubus_ref_inc();
+}
+
+void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+	struct ubus_object *obj = &hapd->ubus.obj;
+	char *name = (char *) obj->name;
+
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED)
+		return;
+#endif
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		hostapd_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+static void
+hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+			 const char *action)
+{
+	struct vlan_description *desc = &vlan->vlan_desc;
+	void *c;
+	int i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "ifname", vlan->ifname);
+	blobmsg_add_string(&b, "bridge", vlan->bridge);
+	blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
+
+	if (desc->notempty) {
+		blobmsg_add_u32(&b, "untagged", desc->untagged);
+		c = blobmsg_open_array(&b, "tagged");
+		for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
+			blobmsg_add_u32(&b, "", desc->tagged[i]);
+		blobmsg_close_array(&b, c);
+	}
+
+	ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
+}
+
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
+}
+
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
+}
+
+struct ubus_event_req {
+	struct ubus_notify_request nreq;
+	int resp;
+};
+
+static void
+ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
+{
+	struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
+
+	ureq->resp = ret;
+}
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+	struct ubus_banned_client *ban;
+	const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
+		[HOSTAPD_UBUS_PROBE_REQ] = "probe",
+		[HOSTAPD_UBUS_AUTH_REQ] = "auth",
+		[HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
+	};
+	const char *type = "mgmt";
+	struct ubus_event_req ureq = {};
+	const u8 *addr;
+
+	if (req->mgmt_frame)
+		addr = req->mgmt_frame->sa;
+	else
+		addr = req->addr;
+
+	ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+	if (ban)
+		return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return WLAN_STATUS_SUCCESS;
+
+	if (req->type < ARRAY_SIZE(types))
+		type = types[req->type];
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	if (req->mgmt_frame)
+		blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
+	if (req->ssi_signal)
+		blobmsg_add_u32(&b, "signal", req->ssi_signal);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+
+	if (req->elems) {
+		if(req->elems->ht_capabilities)
+		{
+			struct ieee80211_ht_capabilities *ht_capabilities;
+			void *ht_cap, *ht_cap_mcs_set, *mcs_set;
+
+
+			ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
+			ht_cap = blobmsg_open_table(&b, "ht_capabilities");
+			blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
+			ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
+			blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
+			blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
+			blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
+			blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
+			mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
+			for (int i = 0; i < 16; i++) {
+				blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
+			}
+			blobmsg_close_array(&b, mcs_set);
+			blobmsg_close_table(&b, ht_cap_mcs_set);
+			blobmsg_close_table(&b, ht_cap);
+		}
+		if(req->elems->vht_capabilities)
+		{
+			struct ieee80211_vht_capabilities *vht_capabilities;
+			void *vht_cap, *vht_cap_mcs_set;
+
+			vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
+			vht_cap = blobmsg_open_table(&b, "vht_capabilities");
+			blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
+			vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
+			blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
+			blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
+			blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
+			blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
+			blobmsg_close_table(&b, vht_cap_mcs_set);
+			blobmsg_close_table(&b, vht_cap);
+		}
+	}
+
+	if (!hapd->ubus.notify_response) {
+		ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
+		return WLAN_STATUS_SUCCESS;
+
+	ureq.nreq.status_cb = ubus_event_cb;
+	ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+	if (ureq.resp)
+		return ureq.resp;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+
+	ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+}
+
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+				    const char *auth_alg)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", sta->addr);
+	if (auth_alg)
+		blobmsg_add_string(&b, "auth-alg", auth_alg);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
+}
+
+void hostapd_ubus_notify_beacon_report(
+	struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
+	struct rrm_measurement_beacon_report *rep, size_t len)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr || !rep)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u16(&b, "op-class", rep->op_class);
+	blobmsg_add_u16(&b, "channel", rep->channel);
+	blobmsg_add_u64(&b, "start-time", rep->start_time);
+	blobmsg_add_u16(&b, "duration", rep->duration);
+	blobmsg_add_u16(&b, "report-info", rep->report_info);
+	blobmsg_add_u16(&b, "rcpi", rep->rcpi);
+	blobmsg_add_u16(&b, "rsni", rep->rsni);
+	blobmsg_add_macaddr(&b, "bssid", rep->bssid);
+	blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
+	blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
+	blobmsg_add_u16(&b, "rep-mode", rep_mode);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
+}
+
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+					int chan_width, int cf1, int cf2)
+{
+	struct hostapd_data *hapd;
+	int i;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u16(&b, "frequency", frequency);
+	blobmsg_add_u16(&b, "width", chan_width);
+	blobmsg_add_u16(&b, "center1", cf1);
+	blobmsg_add_u16(&b, "center2", cf2);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		hapd = iface->bss[i];
+		ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
+	}
+}
+
+#ifdef CONFIG_WNM_AP
+static void hostapd_ubus_notify_bss_transition_add_candidate_list(
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+	char *cl_str;
+	int i;
+
+	if (candidate_list_len == 0)
+		return;
+
+	cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
+	for (i = 0; i < candidate_list_len; i++)
+		snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
+	blobmsg_add_string_buffer(&b);
+
+}
+#endif
+
+void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+	u16 i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u8(&b, "dialog-token", dialog_token);
+	blobmsg_add_u8(&b, "status-code", status_code);
+	blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
+	if (target_bssid)
+		blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
+	
+	hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
+#endif
+}
+
+int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+	struct ubus_event_req ureq = {};
+	char *cl_str;
+	u16 i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return 0;
+
+	if (!addr)
+		return 0;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u8(&b, "dialog-token", dialog_token);
+	blobmsg_add_u8(&b, "reason", reason);
+	hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+	if (!hapd->ubus.notify_response) {
+		ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
+		return 0;
+	}
+
+	if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
+		return 0;
+
+	ureq.nreq.status_cb = ubus_event_cb;
+	ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+	return ureq.resp;
+#endif
+}
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h
new file mode 100644
index 0000000..b0f7c44
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ubus.h
@@ -0,0 +1,154 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef __HOSTAPD_UBUS_H
+#define __HOSTAPD_UBUS_H
+
+enum hostapd_ubus_event_type {
+	HOSTAPD_UBUS_PROBE_REQ,
+	HOSTAPD_UBUS_AUTH_REQ,
+	HOSTAPD_UBUS_ASSOC_REQ,
+	HOSTAPD_UBUS_TYPE_MAX
+};
+
+struct hostapd_ubus_request {
+	enum hostapd_ubus_event_type type;
+	const struct ieee80211_mgmt *mgmt_frame;
+	const struct ieee802_11_elems *elems;
+	int ssi_signal; /* dBm */
+	const u8 *addr;
+};
+
+struct hostapd_iface;
+struct hostapd_data;
+struct hapd_interfaces;
+struct rrm_measurement_beacon_report;
+
+#ifdef UBUS_SUPPORT
+
+#include <libubox/avl.h>
+#include <libubus.h>
+
+struct hostapd_ubus_bss {
+	struct ubus_object obj;
+	struct avl_tree banned;
+	int notify_response;
+};
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface);
+void hostapd_ubus_free_iface(struct hostapd_iface *iface);
+void hostapd_ubus_add_bss(struct hostapd_data *hapd);
+void hostapd_ubus_free_bss(struct hostapd_data *hapd);
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
+void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+				       const u8 *addr, u8 token, u8 rep_mode,
+				       struct rrm_measurement_beacon_report *rep,
+				       size_t len);
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+					int chan_width, int cf1, int cf2);
+
+void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_add(struct hapd_interfaces *interfaces);
+void hostapd_ubus_free(struct hapd_interfaces *interfaces);
+int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+				    const char *auth_alg);
+
+#else
+
+struct hostapd_ubus_bss {};
+
+static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+	return 0;
+}
+
+static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+}
+
+static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
+{
+}
+
+static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+						     const u8 *addr, u8 token,
+						     u8 rep_mode,
+						     struct rrm_measurement_beacon_report *rep,
+						     size_t len)
+{
+}
+static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+						      int chan_width, int cf1, int cf2)
+{
+}
+
+static inline void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+}
+
+static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+	return 0;
+}
+
+static inline void
+hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+			       const char *auth_alg)
+{
+}
+
+#endif
+
+#endif
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c
new file mode 100644
index 0000000..137cb81
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.c
@@ -0,0 +1,394 @@
+#include <sys/un.h>
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include <libubox/uloop.h>
+
+static uc_resource_type_t *global_type, *bss_type, *iface_type;
+static struct hapd_interfaces *interfaces;
+static uc_value_t *global, *bss_registry, *iface_registry;
+static uc_vm_t *vm;
+
+static uc_value_t *
+hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
+{
+	uc_value_t *val;
+
+	if (hapd->ucode.idx)
+		return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
+
+	val = uc_resource_new(bss_type, hapd);
+	wpa_ucode_registry_add(bss_registry, val, &hapd->ucode.idx);
+
+	return val;
+}
+
+static uc_value_t *
+hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
+{
+	uc_value_t *val;
+
+	if (hapd->ucode.idx)
+		return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
+
+	val = uc_resource_new(iface_type, hapd);
+	wpa_ucode_registry_add(iface_registry, val, &hapd->ucode.idx);
+
+	return val;
+}
+
+static void
+hostapd_ucode_update_bss_list(struct hostapd_iface *iface)
+{
+	uc_value_t *ifval, *list;
+	int i;
+
+	list = ucv_array_new(vm);
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+		uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
+		uc_value_t *proto = ucv_prototype_get(val);
+
+		ucv_object_add(proto, "name", ucv_get(ucv_string_new(hapd->conf->iface)));
+		ucv_object_add(proto, "index", ucv_int64_new(i));
+		ucv_array_set(list, i, ucv_get(val));
+	}
+
+	ifval = hostapd_ucode_iface_get_uval(iface);
+	ucv_object_add(ucv_prototype_get(ifval), "bss", ucv_get(list));
+}
+
+static void
+hostapd_ucode_update_interfaces(void)
+{
+	uc_value_t *ifs = ucv_object_new(vm);
+	int i;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+
+		ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
+		hostapd_ucode_update_bss_list(iface);
+	}
+
+	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+	ucv_gc(vm);
+}
+
+static uc_value_t *
+uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *iface = uc_fn_arg(0);
+	int ret;
+
+	if (ucv_type(iface) != UC_STRING)
+		return ucv_int64_new(-1);
+
+	ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
+	hostapd_ucode_update_interfaces();
+
+	return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *iface = uc_fn_arg(0);
+
+	if (ucv_type(iface) != UC_STRING)
+		return NULL;
+
+	hostapd_remove_iface(interfaces, ucv_string_get(iface));
+	hostapd_ucode_update_interfaces();
+
+	return NULL;
+}
+
+static uc_value_t *
+uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+	struct hostapd_bss_config *old_bss;
+	struct hostapd_iface *iface;
+	struct hostapd_config *conf;
+	uc_value_t *file = uc_fn_arg(0);
+	uc_value_t *index = uc_fn_arg(1);
+	unsigned int i, idx = 0;
+	int ret = -1;
+
+	if (!hapd || ucv_type(file) != UC_STRING)
+		goto out;
+
+	if (ucv_type(index) == UC_INTEGER)
+		idx = ucv_int64_get(index);
+
+	iface = hapd->iface;
+	conf = interfaces->config_read_cb(ucv_string_get(file));
+	if (!conf || idx > conf->num_bss || !conf->bss[idx])
+		goto out;
+
+	hostapd_bss_deinit_no_free(hapd);
+	hostapd_drv_stop_ap(hapd);
+	hostapd_free_hapd_data(hapd);
+
+	old_bss = hapd->conf;
+	for (i = 0; i < iface->conf->num_bss; i++)
+		if (iface->conf->bss[i] == hapd->conf)
+			iface->conf->bss[i] = conf->bss[idx];
+	hapd->conf = conf->bss[idx];
+	conf->bss[idx] = old_bss;
+	hostapd_config_free(conf);
+
+	hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
+
+	ret = 0;
+
+out:
+	return ucv_int64_new(ret);
+}
+
+static void
+hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
+			      struct hostapd_bss_config *conf)
+{
+	int i;
+
+	for (i = 0; i < iconf->num_bss; i++)
+		if (iconf->bss[i] == conf)
+			break;
+
+	if (i == iconf->num_bss)
+		return;
+
+	for (i++; i < iconf->num_bss; i++)
+		iconf->bss[i - 1] = iconf->bss[i];
+	iconf->num_bss--;
+}
+
+
+static uc_value_t *
+uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+	struct hostapd_iface *iface;
+	int i, idx;
+
+	if (!hapd || hapd == hapd->iface->bss[0])
+		return NULL;
+
+	iface = hapd->iface;
+	for (idx = 0; idx < iface->num_bss; idx++)
+		if (iface->bss[idx] == hapd)
+			break;
+
+	if (idx == iface->num_bss)
+		return NULL;
+
+	for (i = idx + 1; i < iface->num_bss; i++)
+		iface->bss[i - 1] = iface->bss[i];
+	iface->num_bss--;
+
+	hostapd_drv_stop_ap(hapd);
+	hostapd_bss_deinit(hapd);
+	hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
+	hostapd_config_free_bss(hapd->conf);
+	os_free(hapd);
+
+	hostapd_ucode_update_bss_list(iface);
+	ucv_gc(vm);
+
+	return NULL;
+}
+
+static uc_value_t *
+uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+	struct hostapd_bss_config *bss;
+	struct hostapd_config *conf;
+	struct hostapd_data *hapd;
+	uc_value_t *file = uc_fn_arg(0);
+	uc_value_t *index = uc_fn_arg(1);
+	unsigned int idx = 0;
+	uc_value_t *ret = NULL;
+
+	if (!iface || ucv_type(file) != UC_STRING)
+		goto out;
+
+	if (ucv_type(index) == UC_INTEGER)
+		idx = ucv_int64_get(index);
+
+	conf = interfaces->config_read_cb(ucv_string_get(file));
+	if (!conf || idx > conf->num_bss || !conf->bss[idx])
+		goto out;
+
+	bss = conf->bss[idx];
+	hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+	if (!hapd)
+		goto out;
+
+	hapd->driver = iface->bss[0]->driver;
+	hapd->drv_priv = iface->bss[0]->drv_priv;
+	if (interfaces->ctrl_iface_init &&
+	    interfaces->ctrl_iface_init(hapd) < 0)
+		goto free_hapd;
+
+	if (iface->state == HAPD_IFACE_ENABLED &&
+	    hostapd_setup_bss(hapd, -1, true))
+		goto deinit_ctrl;
+
+	iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+				      sizeof(*iface->bss));
+	iface->bss[iface->num_bss++] = hapd;
+
+	iface->conf->bss = os_realloc_array(iface->conf->bss,
+					    iface->conf->num_bss + 1,
+					    sizeof(*iface->conf->bss));
+	iface->conf->bss[iface->conf->num_bss] = bss;
+	conf->bss[idx] = NULL;
+	ret = hostapd_ucode_bss_get_uval(hapd);
+	hostapd_ucode_update_bss_list(iface);
+	goto out;
+
+deinit_ctrl:
+	if (interfaces->ctrl_iface_deinit)
+		interfaces->ctrl_iface_deinit(hapd);
+free_hapd:
+	hostapd_free_hapd_data(hapd);
+	os_free(hapd);
+out:
+	hostapd_config_free(conf);
+	return ret;
+}
+
+static uc_value_t *
+uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+	uc_value_t *arg = uc_fn_arg(0);
+	struct sockaddr_storage from = {};
+	static char reply[4096];
+	int reply_len;
+
+	if (!hapd || !interfaces->ctrl_iface_recv ||
+	    ucv_type(arg) != UC_STRING)
+		return NULL;
+
+	reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
+						reply, sizeof(reply),
+						&from, sizeof(from));
+	if (reply_len < 0)
+		return NULL;
+
+	if (reply_len && reply[reply_len - 1] == '\n')
+		reply_len--;
+
+	return ucv_string_new_length(reply, reply_len);
+}
+
+int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+{
+	static const uc_function_list_t global_fns[] = {
+		{ "printf",	uc_wpa_printf },
+		{ "getpid", uc_wpa_getpid },
+		{ "sha1", uc_wpa_sha1 },
+		{ "add_iface", uc_hostapd_add_iface },
+		{ "remove_iface", uc_hostapd_remove_iface },
+	};
+	static const uc_function_list_t bss_fns[] = {
+		{ "ctrl", uc_hostapd_bss_ctrl },
+		{ "set_config", uc_hostapd_bss_set_config },
+		{ "delete", uc_hostapd_bss_delete },
+	};
+	static const uc_function_list_t iface_fns[] = {
+		{ "add_bss", uc_hostapd_iface_add_bss }
+	};
+	uc_value_t *data, *proto;
+
+	interfaces = ifaces;
+	vm = wpa_ucode_create_vm();
+
+	global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
+	bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
+	iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
+
+	bss_registry = ucv_array_new(vm);
+	uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
+
+	iface_registry = ucv_array_new(vm);
+	uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
+
+	global = wpa_ucode_global_init("hostapd", global_type);
+
+	if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
+		goto free_vm;
+	ucv_gc(vm);
+
+	return 0;
+
+free_vm:
+	wpa_ucode_free_vm();
+	return -1;
+}
+
+void hostapd_ucode_free(void)
+{
+	if (wpa_ucode_call_prepare("shutdown") == 0)
+		ucv_put(wpa_ucode_call(0));
+	wpa_ucode_free_vm();
+}
+
+void hostapd_ucode_free_iface(struct hostapd_iface *iface)
+{
+	wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
+}
+
+void hostapd_ucode_add_bss(struct hostapd_data *hapd)
+{
+	uc_value_t *val;
+
+	if (wpa_ucode_call_prepare("bss_add"))
+		return;
+
+	val = hostapd_ucode_bss_get_uval(hapd);
+	uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
+	uc_value_push(ucv_get(val));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
+
+void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf)
+{
+	uc_value_t *val;
+
+	if (wpa_ucode_call_prepare("bss_reload"))
+		return;
+
+	val = hostapd_ucode_bss_get_uval(hapd);
+	uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
+	uc_value_push(ucv_get(val));
+	uc_value_push(ucv_int64_new(reconf));
+	ucv_put(wpa_ucode_call(3));
+	ucv_gc(vm);
+}
+
+void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+{
+	uc_value_t *val;
+
+	val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
+	if (!val)
+		return;
+
+	hapd->ucode.idx = 0;
+	if (wpa_ucode_call_prepare("bss_remove"))
+		return;
+
+	uc_value_push(ucv_string_new(hapd->conf->iface));
+	uc_value_push(ucv_get(val));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.h b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.h
new file mode 100644
index 0000000..dbc49e6
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/ap/ucode.h
@@ -0,0 +1,54 @@
+#ifndef __HOSTAPD_AP_UCODE_H
+#define __HOSTAPD_AP_UCODE_H
+
+#include "utils/ucode.h"
+
+struct hostapd_data;
+
+struct hostapd_ucode_bss {
+#ifdef UCODE_SUPPORT
+	int idx;
+#endif
+};
+
+struct hostapd_ucode_iface {
+#ifdef UCODE_SUPPORT
+	int idx;
+#endif
+};
+
+#ifdef UCODE_SUPPORT
+
+int hostapd_ucode_init(struct hapd_interfaces *ifaces);
+
+void hostapd_ucode_free(void);
+void hostapd_ucode_free_iface(struct hostapd_iface *iface);
+void hostapd_ucode_add_bss(struct hostapd_data *hapd);
+void hostapd_ucode_free_bss(struct hostapd_data *hapd);
+void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf);
+
+#else
+
+static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+{
+	return -EINVAL;
+}
+static inline void hostapd_ucode_free(void)
+{
+}
+static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface)
+{
+}
+static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf)
+{
+}
+static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd)
+{
+}
+static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+{
+}
+
+#endif
+
+#endif
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/utils/build_features.h b/recipes-wifi/hostapd/files/src-2.10.3/src/utils/build_features.h
new file mode 100644
index 0000000..553769e
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/utils/build_features.h
@@ -0,0 +1,65 @@
+#ifndef BUILD_FEATURES_H
+#define BUILD_FEATURES_H
+
+static inline int has_feature(const char *feat)
+{
+#if defined(IEEE8021X_EAPOL) || (defined(HOSTAPD) && !defined(CONFIG_NO_RADIUS))
+	if (!strcmp(feat, "eap"))
+		return 1;
+#endif
+#ifdef CONFIG_IEEE80211AC
+	if (!strcmp(feat, "11ac"))
+		return 1;
+#endif
+#ifdef CONFIG_IEEE80211AX
+	if (!strcmp(feat, "11ax"))
+		return 1;
+#endif
+#ifdef CONFIG_IEEE80211R
+	if (!strcmp(feat, "11r"))
+		return 1;
+#endif
+#ifdef CONFIG_ACS
+	if (!strcmp(feat, "acs"))
+		return 1;
+#endif
+#ifdef CONFIG_SAE
+	if (!strcmp(feat, "sae"))
+		return 1;
+#endif
+#ifdef CONFIG_OWE
+	if (!strcmp(feat, "owe"))
+		return 1;
+#endif
+#ifdef CONFIG_SUITEB192
+	if (!strcmp(feat, "suiteb192"))
+		return 1;
+#endif
+#ifdef CONFIG_WEP
+	if (!strcmp(feat, "wep"))
+		return 1;
+#endif
+#ifdef CONFIG_HS20
+	if (!strcmp(feat, "hs20"))
+		return 1;
+#endif
+#ifdef CONFIG_WPS
+	if (!strcmp(feat, "wps"))
+		return 1;
+#endif
+#ifdef CONFIG_FILS
+	if (!strcmp(feat, "fils"))
+		return 1;
+#endif
+#ifdef CONFIG_OCV
+	if (!strcmp(feat, "ocv"))
+		return 1;
+#endif
+#ifdef CONFIG_MESH
+	if (!strcmp(feat, "mesh"))
+		return 1;
+#endif
+	return 0;
+}
+
+#endif /* BUILD_FEATURES_H */
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/utils/ucode.c b/recipes-wifi/hostapd/files/src-2.10.3/src/utils/ucode.c
new file mode 100644
index 0000000..a92913a
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/utils/ucode.c
@@ -0,0 +1,251 @@
+#include <unistd.h>
+#include "ucode.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include <libubox/uloop.h>
+#include <ucode/compiler.h>
+
+static uc_value_t *registry;
+static uc_vm_t vm;
+static struct uloop_timeout gc_timer;
+
+static void uc_gc_timer(struct uloop_timeout *timeout)
+{
+	ucv_gc(&vm);
+}
+
+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *level = uc_fn_arg(0);
+	uc_value_t *ret, **args;
+	uc_cfn_ptr_t _sprintf;
+	int l = MSG_INFO;
+	int i, start = 0;
+
+	_sprintf = uc_stdlib_function("sprintf");
+	if (!sprintf)
+		return NULL;
+
+	if (ucv_type(level) == UC_INTEGER) {
+		l = ucv_int64_get(level);
+		start++;
+	}
+
+	if (nargs <= start)
+		return NULL;
+
+	ret = _sprintf(vm, nargs - start);
+	if (ucv_type(ret) != UC_STRING)
+		return NULL;
+
+	wpa_printf(l, "%s", ucv_string_get(ret));
+	ucv_put(ret);
+
+	return NULL;
+}
+
+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
+{
+	return ucv_int64_new(getpid());
+}
+
+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
+{
+	u8 hash[SHA1_MAC_LEN];
+	char hash_hex[2 * ARRAY_SIZE(hash) + 1];
+	uc_value_t *val;
+	size_t *lens;
+	const u8 **args;
+	int i;
+
+	if (!nargs)
+		return NULL;
+
+	args = alloca(nargs * sizeof(*args));
+	lens = alloca(nargs * sizeof(*lens));
+	for (i = 0; i < nargs; i++) {
+		val = uc_fn_arg(i);
+		if (ucv_type(val) != UC_STRING)
+			return NULL;
+
+		args[i] = ucv_string_get(val);
+		lens[i] = ucv_string_length(val);
+	}
+
+	if (sha1_vector(nargs, args, lens, hash))
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(hash); i++)
+		sprintf(hash_hex + 2 * i, "%02x", hash[i]);
+
+	return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
+}
+
+uc_vm_t *wpa_ucode_create_vm(void)
+{
+	static uc_parse_config_t config = {
+		.strict_declarations = true,
+		.lstrip_blocks = true,
+		.trim_blocks = true,
+		.raw_mode = true
+	};
+
+	uc_search_path_init(&config.module_search_path);
+	uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
+	uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
+
+	uc_vm_init(&vm, &config);
+
+	uc_stdlib_load(uc_vm_scope_get(&vm));
+	eloop_add_uloop();
+	gc_timer.cb = uc_gc_timer;
+
+	return &vm;
+}
+
+int wpa_ucode_run(const char *script)
+{
+	uc_source_t *source;
+	uc_program_t *prog;
+	uc_value_t *ops;
+	char *err;
+	int ret;
+
+	source = uc_source_new_file(script);
+	if (!source)
+		return -1;
+
+	prog = uc_compile(vm.config, source, &err);
+	uc_source_put(source);
+	if (!prog) {
+		wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
+		return -1;
+	}
+
+	ret = uc_vm_execute(&vm, prog, &ops);
+	uc_program_put(prog);
+	if (ret || !ops)
+		return -1;
+
+	registry = ucv_array_new(&vm);
+	uc_vm_registry_set(&vm, "hostap.registry", registry);
+	ucv_array_set(registry, 0, ucv_get(ops));
+
+	return 0;
+}
+
+int wpa_ucode_call_prepare(const char *fname)
+{
+	uc_value_t *obj, *func;
+
+	if (!registry)
+		return -1;
+
+	obj = ucv_array_get(registry, 0);
+	if (!obj)
+		return -1;
+
+	func = ucv_object_get(obj, fname, NULL);
+	if (!ucv_is_callable(func))
+		return -1;
+
+	uc_vm_stack_push(&vm, ucv_get(obj));
+	uc_vm_stack_push(&vm, ucv_get(func));
+
+	return 0;
+}
+
+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
+{
+	uc_value_t *global = uc_resource_new(global_type, NULL);
+	uc_value_t *proto;
+
+	uc_vm_registry_set(&vm, "hostap.global", global);
+	proto = ucv_prototype_get(global);
+	ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
+
+#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
+	ADD_CONST(MSG_EXCESSIVE);
+	ADD_CONST(MSG_MSGDUMP);
+	ADD_CONST(MSG_DEBUG);
+	ADD_CONST(MSG_INFO);
+	ADD_CONST(MSG_WARNING);
+	ADD_CONST(MSG_ERROR);
+#undef ADD_CONST
+
+	ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
+
+	return global;
+}
+
+static uc_value_t *wpa_ucode_prototype_clone(uc_value_t *uval)
+{
+	uc_value_t *proto, *proto_new;
+
+	proto = ucv_prototype_get(uval);
+	proto_new = ucv_object_new(&vm);
+
+	ucv_object_foreach(proto, key, val)
+		ucv_object_add(proto_new, key, ucv_get(val));
+	ucv_prototype_set(uval, ucv_get(proto));
+
+	return proto;
+}
+
+void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx)
+{
+	uc_value_t *data;
+	int i = 0;
+
+	while (ucv_array_get(reg, i))
+		i++;
+
+	ucv_array_set(reg, i, ucv_get(val));
+
+	data = ucv_object_new(&vm);
+	ucv_object_add(wpa_ucode_prototype_clone(val), "data", ucv_get(data));
+
+	*idx = i + 1;
+}
+
+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
+{
+	if (!idx)
+		return NULL;
+
+	return ucv_array_get(reg, idx - 1);
+}
+
+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
+{
+	uc_value_t *val = wpa_ucode_registry_get(reg, idx);
+
+	if (val)
+		ucv_array_set(reg, idx - 1, NULL);
+
+	return val;
+}
+
+
+uc_value_t *wpa_ucode_call(size_t nargs)
+{
+	if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
+		return NULL;
+
+	if (!gc_timer.pending)
+		uloop_timeout_set(&gc_timer, 10);
+
+	return uc_vm_stack_pop(&vm);
+}
+
+void wpa_ucode_free_vm(void)
+{
+	if (!vm.config)
+		return;
+
+	uc_search_path_free(&vm.config->module_search_path);
+	uc_vm_free(&vm);
+	registry = NULL;
+	vm = (uc_vm_t){};
+}
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/src/utils/ucode.h b/recipes-wifi/hostapd/files/src-2.10.3/src/utils/ucode.h
new file mode 100644
index 0000000..4caf8ad
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/src/utils/ucode.h
@@ -0,0 +1,28 @@
+#ifndef __HOSTAPD_UTILS_UCODE_H
+#define __HOSTAPD_UTILS_UCODE_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include <ucode/lib.h>
+#include <ucode/vm.h>
+
+#define HOSTAPD_UC_PATH	"/usr/share/hostap/"
+
+extern uc_value_t *uc_registry;
+uc_vm_t *wpa_ucode_create_vm(void);
+int wpa_ucode_run(const char *script);
+int wpa_ucode_call_prepare(const char *fname);
+uc_value_t *wpa_ucode_call(size_t nargs);
+void wpa_ucode_free_vm(void);
+
+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type);
+
+void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx);
+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx);
+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx);
+
+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
+
+#endif
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ubus.c b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ubus.c
new file mode 100644
index 0000000..1c477f0
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ubus.c
@@ -0,0 +1,280 @@
+/*
+ * wpa_supplicant / ubus support
+ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/wpabuf.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "ubus.h"
+
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static int ctx_ref;
+
+static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct wpa_global, ubus_global);
+}
+
+static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct wpa_supplicant, ubus.obj);
+}
+
+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
+{
+	if (ubus_reconnect(ctx, NULL)) {
+		eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+		return;
+	}
+
+	ubus_add_uloop(ctx);
+}
+
+static void wpas_ubus_connection_lost(struct ubus_context *ctx)
+{
+	uloop_fd_delete(&ctx->sock);
+	eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+}
+
+static bool wpas_ubus_init(void)
+{
+	if (ctx)
+		return true;
+
+	eloop_add_uloop();
+	ctx = ubus_connect(NULL);
+	if (!ctx)
+		return false;
+
+	ctx->connection_lost = wpas_ubus_connection_lost;
+	ubus_add_uloop(ctx);
+
+	return true;
+}
+
+static void wpas_ubus_ref_inc(void)
+{
+	ctx_ref++;
+}
+
+static void wpas_ubus_ref_dec(void)
+{
+	ctx_ref--;
+	if (!ctx)
+		return;
+
+	if (ctx_ref)
+		return;
+
+	uloop_fd_delete(&ctx->sock);
+	ubus_free(ctx);
+	ctx = NULL;
+}
+
+static int
+wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u8(&b, "ht_supported", ht_supported(wpa_s->hw.modes));
+	blobmsg_add_u8(&b, "vht_supported", vht_supported(wpa_s->hw.modes));
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg)
+{
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	if (wpa_supplicant_reload_configuration(wpa_s))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	else
+		return 0;
+}
+
+#ifdef CONFIG_WPS
+enum {
+	WPS_START_MULTI_AP,
+	__WPS_START_MAX
+};
+
+static const struct blobmsg_policy wps_start_policy[] = {
+	[WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
+};
+
+static int
+wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+	struct blob_attr *tb[__WPS_START_MAX], *cur;
+	int multi_ap = 0;
+
+	blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
+
+	if (tb[WPS_START_MULTI_AP])
+		multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
+
+	rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+static int
+wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	rc = wpas_wps_cancel(wpa_s);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+#endif
+
+static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", wpas_bss_reload),
+	UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
+#ifdef CONFIG_WPS
+	UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
+	UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
+#endif
+};
+
+static struct ubus_object_type bss_object_type =
+	UBUS_OBJECT_TYPE("wpas_bss", bss_methods);
+
+void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
+{
+	struct ubus_object *obj = &wpa_s->ubus.obj;
+	char *name;
+	int ret;
+
+	if (!wpas_ubus_init())
+		return;
+
+	if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
+		return;
+
+	obj->name = name;
+	obj->type = &bss_object_type;
+	obj->methods = bss_object_type.methods;
+	obj->n_methods = bss_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	wpas_ubus_ref_inc();
+}
+
+void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
+{
+	struct ubus_object *obj = &wpa_s->ubus.obj;
+	char *name = (char *) obj->name;
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		wpas_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+#ifdef CONFIG_WPS
+void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
+{
+	u16 auth_type;
+	char *ifname, *encryption, *ssid, *key;
+	size_t ifname_len;
+
+	if (!cred)
+		return;
+
+	auth_type = cred->auth_type;
+
+	if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))
+		auth_type = WPS_AUTH_WPA2PSK;
+
+	if (auth_type != WPS_AUTH_OPEN &&
+	    auth_type != WPS_AUTH_WPAPSK &&
+	    auth_type != WPS_AUTH_WPA2PSK) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
+			   "unsupported authentication type 0x%x",
+			   auth_type);
+		return;
+	}
+
+	if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
+		if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
+			wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
+				   "invalid Network Key length %lu",
+				   (unsigned long) cred->key_len);
+			return;
+		}
+	}
+
+	blob_buf_init(&b, 0);
+
+	ifname_len = strlen(wpa_s->ifname);
+	ifname = blobmsg_alloc_string_buffer(&b, "ifname", ifname_len + 1);
+	memcpy(ifname, wpa_s->ifname, ifname_len + 1);
+	ifname[ifname_len] = '\0';
+	blobmsg_add_string_buffer(&b);
+
+	switch (auth_type) {
+		case WPS_AUTH_WPA2PSK:
+			encryption = "psk2";
+			break;
+		case WPS_AUTH_WPAPSK:
+			encryption = "psk";
+			break;
+		default:
+			encryption = "none";
+			break;
+	}
+
+	blobmsg_add_string(&b, "encryption", encryption);
+
+	ssid = blobmsg_alloc_string_buffer(&b, "ssid", cred->ssid_len + 1);
+	memcpy(ssid, cred->ssid, cred->ssid_len);
+	ssid[cred->ssid_len] = '\0';
+	blobmsg_add_string_buffer(&b);
+
+	if (cred->key_len > 0) {
+		key = blobmsg_alloc_string_buffer(&b, "key", cred->key_len + 1);
+		memcpy(key, cred->key, cred->key_len);
+		key[cred->key_len] = '\0';
+		blobmsg_add_string_buffer(&b);
+	}
+
+//	ubus_notify(ctx, &wpa_s->ubus.obj, "wps_credentials", b.head, -1);
+	ubus_send_event(ctx, "wps_credentials", b.head);
+}
+#endif /* CONFIG_WPS */
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ubus.h b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ubus.h
new file mode 100644
index 0000000..f6681cb
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ubus.h
@@ -0,0 +1,55 @@
+/*
+ * wpa_supplicant / ubus support
+ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef __WPAS_UBUS_H
+#define __WPAS_UBUS_H
+
+struct wpa_supplicant;
+struct wpa_global;
+
+#include "wps_supplicant.h"
+
+#ifdef UBUS_SUPPORT
+#include <libubus.h>
+
+struct wpas_ubus_bss {
+	struct ubus_object obj;
+};
+
+void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
+void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
+
+#ifdef CONFIG_WPS
+void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
+#endif
+
+#else
+struct wpas_ubus_bss {};
+
+static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
+{
+}
+
+static inline void wpas_ubus_add(struct wpa_global *global)
+{
+}
+
+static inline void wpas_ubus_free(struct wpa_global *global)
+{
+}
+#endif
+
+#endif
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ucode.c b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ucode.c
new file mode 100644
index 0000000..eec3980
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ucode.c
@@ -0,0 +1,177 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "ucode.h"
+
+static struct wpa_global *wpa_global;
+static uc_resource_type_t *global_type, *iface_type;
+static uc_value_t *global, *iface_registry;
+static uc_vm_t *vm;
+
+static uc_value_t *
+wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
+{
+	uc_value_t *val;
+
+	if (wpa_s->ucode.idx)
+		return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+
+	val = uc_resource_new(iface_type, wpa_s);
+	wpa_ucode_registry_add(iface_registry, val, &wpa_s->ucode.idx);
+
+	return val;
+}
+
+static void
+wpas_ucode_update_interfaces(void)
+{
+	uc_value_t *ifs = ucv_object_new(vm);
+	struct wpa_supplicant *wpa_s;
+	int i;
+
+	for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
+		ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
+
+	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+	ucv_gc(vm);
+}
+
+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
+{
+	uc_value_t *val;
+
+	if (wpa_ucode_call_prepare("iface_add"))
+		return;
+
+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+	uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
+
+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
+{
+	uc_value_t *val;
+
+	val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
+	if (!val)
+		return;
+
+	wpa_s->ucode.idx = 0;
+	if (wpa_ucode_call_prepare("iface_remove"))
+		return;
+
+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+	uc_value_push(ucv_get(val));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
+
+static const char *obj_stringval(uc_value_t *obj, const char *name)
+{
+	uc_value_t *val = ucv_object_get(obj, name, NULL);
+
+	return ucv_string_get(val);
+}
+
+static uc_value_t *
+uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *info = uc_fn_arg(0);
+	uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
+	uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
+	uc_value_t *config = ucv_object_get(info, "config", NULL);
+	uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
+	uc_value_t *hapd_ctrl = ucv_object_get(info, "hostapd_ctrl", NULL);
+	struct wpa_interface iface;
+	int ret = -1;
+
+	if (ucv_type(info) != UC_OBJECT)
+		goto out;
+
+	iface = (struct wpa_interface){
+		.driver = "nl80211",
+		.ifname = ucv_string_get(ifname),
+		.bridge_ifname = ucv_string_get(bridge),
+		.confname = ucv_string_get(config),
+		.ctrl_interface = ucv_string_get(ctrl),
+		.hostapd_ctrl = ucv_string_get(hapd_ctrl),
+	};
+
+	if (!iface.ifname || !iface.confname)
+		goto out;
+
+	ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
+	wpas_ucode_update_interfaces();
+
+out:
+	return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
+{
+	struct wpa_supplicant *wpa_s = NULL;
+	uc_value_t *ifname_arg = uc_fn_arg(0);
+	const char *ifname = ucv_string_get(ifname_arg);
+	int ret = -1;
+
+	if (!ifname)
+		goto out;
+
+	for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
+		if (!strcmp(wpa_s->ifname, ifname))
+			break;
+
+	if (!wpa_s)
+		goto out;
+
+	ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
+	wpas_ucode_update_interfaces();
+
+out:
+	return ucv_int64_new(ret);
+}
+
+int wpas_ucode_init(struct wpa_global *gl)
+{
+	static const uc_function_list_t global_fns[] = {
+		{ "printf",	uc_wpa_printf },
+		{ "getpid", uc_wpa_getpid },
+		{ "add_iface", uc_wpas_add_iface },
+		{ "remove_iface", uc_wpas_remove_iface },
+	};
+	static const uc_function_list_t iface_fns[] = {
+	};
+	uc_value_t *data, *proto;
+
+	wpa_global = gl;
+	vm = wpa_ucode_create_vm();
+
+	global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
+	iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
+
+	iface_registry = ucv_array_new(vm);
+	uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
+
+	global = wpa_ucode_global_init("wpas", global_type);
+
+	if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
+		goto free_vm;
+
+	ucv_gc(vm);
+	return 0;
+
+free_vm:
+	wpa_ucode_free_vm();
+	return -1;
+}
+
+void wpas_ucode_free(void)
+{
+	if (wpa_ucode_call_prepare("shutdown") == 0)
+		ucv_put(wpa_ucode_call(0));
+	wpa_ucode_free_vm();
+}
diff --git a/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ucode.h b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ucode.h
new file mode 100644
index 0000000..fcd2313
--- /dev/null
+++ b/recipes-wifi/hostapd/files/src-2.10.3/wpa_supplicant/ucode.h
@@ -0,0 +1,38 @@
+#ifndef __WPAS_UCODE_H
+#define __WPAS_UCODE_H
+
+#include "utils/ucode.h"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+struct wpas_ucode_bss {
+#ifdef UCODE_SUPPORT
+	unsigned int idx;
+#endif
+};
+
+#ifdef UCODE_SUPPORT
+int wpas_ucode_init(struct wpa_global *gl);
+void wpas_ucode_free(void);
+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
+#else
+static inline int wpas_ucode_init(struct wpa_global *gl)
+{
+	return -EINVAL;
+}
+static inline void wpas_ucode_free(void)
+{
+}
+static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif
+
+#endif
diff --git a/recipes-wifi/hostapd/files/wdev.uc b/recipes-wifi/hostapd/files/wdev.uc
new file mode 100644
index 0000000..5b32142
--- /dev/null
+++ b/recipes-wifi/hostapd/files/wdev.uc
@@ -0,0 +1,156 @@
+#!/usr/bin/env ucode
+'use strict';
+import { vlist_new, is_equal, wdev_create, wdev_remove } from "/usr/share/hostap/common.uc";
+import { readfile, writefile, basename, readlink, glob } from "fs";
+
+let keep_devices = {};
+let phy = shift(ARGV);
+let new_config = shift(ARGV);
+const mesh_params = [
+	"mesh_retry_timeout", "mesh_confirm_timeout", "mesh_holding_timeout", "mesh_max_peer_links",
+	"mesh_max_retries", "mesh_ttl", "mesh_element_ttl", "mesh_hwmp_max_preq_retries",
+	"mesh_path_refresh_time", "mesh_min_discovery_timeout", "mesh_hwmp_active_path_timeout",
+	"mesh_hwmp_preq_min_interval", "mesh_hwmp_net_diameter_traversal_time", "mesh_hwmp_rootmode",
+	"mesh_hwmp_rann_interval", "mesh_gate_announcements", "mesh_sync_offset_max_neighor",
+	"mesh_rssi_threshold", "mesh_hwmp_active_path_to_root_timeout", "mesh_hwmp_root_interval",
+	"mesh_hwmp_confirmation_interval", "mesh_awake_window", "mesh_plink_timeout",
+	"mesh_auto_open_plinks", "mesh_fwding", "mesh_power_mode"
+];
+
+function iface_stop(wdev)
+{
+	if (keep_devices[wdev.ifname])
+		return;
+
+	wdev_remove(wdev.ifname);
+}
+
+function iface_start(wdev)
+{
+	let ifname = wdev.ifname;
+
+	if (readfile(`/sys/class/net/${ifname}/ifindex`)) {
+		system([ "ip", "link", "set", "dev", ifname, "down" ]);
+		wdev_remove(ifname);
+	}
+	wdev_create(phy, ifname, wdev);
+	system([ "ip", "link", "set", "dev", ifname, "up" ]);
+	if (wdev.freq)
+		system(`iw dev ${ifname} set freq ${wdev.freq} ${wdev.htmode}`);
+	if (wdev.mode == "adhoc") {
+		let cmd = ["iw", "dev", ifname, "ibss", "join", wdev.ssid, wdev.freq, wdev.htmode, "fixed-freq" ];
+		if (wdev.bssid)
+			push(cmd, wdev.bssid);
+		for (let key in [ "beacon-interval", "basic-rates", "mcast-rate", "keys" ])
+			if (wdev[key])
+				push(cmd, key, wdev[key]);
+		system(cmd);
+	} else if (wdev.mode == "mesh") {
+		let cmd = [ "iw", "dev", ifname, "mesh", "join", wdev.ssid, "freq", wdev.freq, wdev.htmode ];
+		for (let key in [ "beacon-interval", "mcast-rate" ])
+			if (wdev[key])
+				push(cmd, key, wdev[key]);
+		system(cmd);
+
+		cmd = ["iw", "dev", ifname, "set", "mesh_param" ];
+		let len = length(cmd);
+
+		for (let param in mesh_params)
+			if (wdev[param])
+				push(cmd, param, wdev[param]);
+
+		if (len == length(cmd))
+			return;
+
+		system(cmd);
+	}
+
+}
+
+function iface_cb(new_if, old_if)
+{
+	if (old_if && new_if && is_equal(old_if, new_if))
+		return;
+
+	if (old_if)
+		iface_stop(old_if);
+	if (new_if)
+		iface_start(new_if);
+}
+
+function drop_inactive(config)
+{
+	for (let key in config) {
+		if (!readfile(`/sys/class/net/${key}/ifindex`))
+			delete config[key];
+	}
+}
+
+function add_ifname(config)
+{
+	for (let key in config)
+		config[key].ifname = key;
+}
+
+function delete_ifname(config)
+{
+	for (let key in config)
+		delete config[key].ifname;
+}
+
+function add_existing(phy, config)
+{
+	let wdevs = glob(`/sys/class/ieee80211/${phy}/device/net/*`);
+	wdevs = map(wdevs, (arg) => basename(arg));
+	for (let wdev in wdevs) {
+		if (config[wdev])
+			continue;
+
+		if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != phy)
+			continue;
+
+		if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down")
+			config[wdev] = {};
+	}
+}
+
+
+let statefile = `/var/run/wdev-${phy}.json`;
+
+for (let dev in ARGV)
+	keep_devices[dev] = true;
+
+if (!phy || !new_config) {
+	warn(`Usage: ${basename(sourcepath())} <phy> <config> [<device]...]\n`);
+	exit(1);
+}
+
+if (!readfile(`/sys/class/ieee80211/${phy}/index`)) {
+	warn(`PHY ${phy} does not exist\n`);
+	exit(1);
+}
+
+new_config = json(new_config);
+if (!new_config) {
+	warn("Invalid configuration\n");
+	exit(1);
+}
+
+let old_config = readfile(statefile);
+if (old_config)
+	old_config = json(old_config);
+
+let config = vlist_new(iface_cb);
+if (type(old_config) == "object")
+	config.data = old_config;
+
+add_existing(phy, config.data);
+add_ifname(config.data);
+drop_inactive(config.data);
+
+add_ifname(new_config);
+config.update(new_config);
+
+drop_inactive(config.data);
+delete_ifname(config.data);
+writefile(statefile, sprintf("%J", config.data));
diff --git a/recipes-wifi/hostapd/hostapd_2.10.3.bb b/recipes-wifi/hostapd/hostapd_2.10.3.bb
index 906d89a..cc9809a 100644
--- a/recipes-wifi/hostapd/hostapd_2.10.3.bb
+++ b/recipes-wifi/hostapd/hostapd_2.10.3.bb
@@ -4,10 +4,10 @@
 LICENSE = "BSD-3-Clause"
 LIC_FILES_CHKSUM = "file://hostapd/README;md5=c905478466c90f1cefc0df987c40e172"
 
-DEPENDS = "libnl openssl ubus"
+DEPENDS = "libnl openssl ubus ucode"
 DEPENDS_append = " ${@bb.utils.contains('DISTRO_FEATURES', 'telemetry2_0', 'telemetry', '', d)}"
 LDFLAGS_append = " ${@bb.utils.contains('DISTRO_FEATURES', 'telemetry2_0', ' -ltelemetry_msgsender ', '', d)}"
-RDEPENDS_${PN} += "gawk"
+RDEPENDS_${PN} += "gawk ucode"
 
 FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
 FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-${PV}:"
@@ -25,8 +25,11 @@
     file://hostapd-init.sh \
     file://mac80211-EHT.sh \
     file://init-uci-config.service \
-    file://src \
-    file://001-rdkb-remove-ubus-support.patch;apply=no \
+    file://hostapd.uc \
+    file://common.uc \
+    file://wdev.uc \
+    file://src-${PV} \
+    file://002-rdkb-add-ucode-support.patch;apply=no \
 "
 require files/patches-${PV}/patches.inc
 
@@ -39,13 +42,13 @@
 SYSTEMD_AUTO_ENABLE_${PN} = "enable"
 SYSTEMD_SERVICE_${PN} = "hostapd.service"
 SYSTEMD_SERVICE_${PN} += " init-uci-config.service"
-
+INSANE_SKIP_${PN} += "file-rdeps"
 do_unpack_append() {
     bb.build.exec_func('do_copy_openwrt_src', d)
 }
 
 do_copy_openwrt_src() {
-    cp -Rfp ${WORKDIR}/src/* ${S}/
+    cp -Rfp ${WORKDIR}/src-${PV}/* ${S}/
 }
 
 do_configure_append() {
@@ -69,12 +72,13 @@
     echo "CONFIG_FILS=y" >> ${B}/.config
     echo "CONFIG_IEEE80211BE=y" >> ${B}/.config
     echo "CONFIG_TESTING_OPTIONS=y" >> ${B}/.config
+    echo "CONFIG_UCODE=y" >> ${B}/.config
 }
 
 do_filogic_patches() {
     cd ${S}
         if [ ! -e patch_applied ]; then
-            patch -p1 < ${WORKDIR}/001-rdkb-remove-ubus-support.patch
+            patch -p1 < ${WORKDIR}/002-rdkb-add-ucode-support.patch
             touch patch_applied
         fi
 }
@@ -88,7 +92,7 @@
 }
 
 do_install() {
-         install -d ${D}${sbindir} ${D}${sysconfdir} ${D}${systemd_unitdir}/system/ ${D}${base_libdir}/rdk
+         install -d ${D}${sbindir} ${D}${sysconfdir} ${D}${systemd_unitdir}/system/ ${D}${base_libdir}/rdk ${D}${datadir}/hostap
          install -m 0755 ${B}/hostapd ${D}${sbindir}
          install -m 0755 ${B}/hostapd_cli ${D}${sbindir}
          install -m 0644 ${WORKDIR}/hostapd-2G-EHT.conf ${D}${sysconfdir}/hostapd-2G.conf
@@ -100,6 +104,9 @@
          install -m 0755 ${WORKDIR}/hostapd-init.sh ${D}${base_libdir}/rdk
          install -m 0644 ${WORKDIR}/init-uci-config.service ${D}${systemd_unitdir}/system
          install -m 0755 ${WORKDIR}/mac80211-EHT.sh ${D}${sbindir}/mac80211.sh
+         install -m 0755 ${WORKDIR}/hostapd.uc ${D}${datadir}/hostap
+         install -m 0755 ${WORKDIR}/wdev.uc ${D}${datadir}/hostap
+         install -m 0755 ${WORKDIR}/common.uc ${D}${datadir}/hostap
 }
 
 FILES_${PN} += " \
@@ -111,4 +118,7 @@
                 ${sysconfdir}/hostapd-5G-7916.conf \
                 ${base_libdir}/rdk/hostapd-init.sh \
                 ${systemd_unitdir}/system/init-uci-config.service \
+                ${datadir}/hostap/hostapd.uc \
+                ${datadir}/hostap/wdev.uc \
+                ${datadir}/hostap/common.uc \
 "
diff --git a/recipes-wifi/json-c/json-c/CVE-2020-12762.patch b/recipes-wifi/json-c/json-c/CVE-2020-12762.patch
new file mode 100644
index 0000000..50674f0
--- /dev/null
+++ b/recipes-wifi/json-c/json-c/CVE-2020-12762.patch
@@ -0,0 +1,231 @@
+From 865b5a65199973bb63dff8e47a2f57e04fec9736 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= <besser82@fedoraproject.org>
+Date: Thu, 14 May 2020 12:32:30 +0200
+Subject: [PATCH] Fix CVE-2020-12762.
+
+This commit is a squashed backport of the following commits
+on the master branch:
+
+  * 099016b7e8d70a6d5dd814e788bba08d33d48426
+  * 77d935b7ae7871a1940cd827e850e6063044ec45
+  * d07b91014986900a3a75f306d302e13e005e9d67
+  * 519dfe1591d85432986f9762d41d1a883198c157
+  * a59d5acfab4485d5133114df61785b1fc633e0c6
+---
+CVE: CVE-2020-12762
+Upstream-Status: Backport [https://github.com/json-c/json-c/commit/865b5a65199973bb63dff8e47a2f57e04fec9736]
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+
+ arraylist.c          |  3 +++
+ linkhash.c           | 21 ++++++++++++++-------
+ printbuf.c           | 38 ++++++++++++++++++++++++++------------
+ tests/test4.c        | 30 +++++++++++++++++++++++++++++-
+ tests/test4.expected |  1 +
+ 5 files changed, 73 insertions(+), 20 deletions(-)
+
+diff --git a/arraylist.c b/arraylist.c
+index ddeb8d4eb4..e737052e32 100644
+--- a/arraylist.c
++++ b/arraylist.c
+@@ -135,6 +135,9 @@ array_list_del_idx( struct array_list *arr, size_t idx, size_t count )
+ {
+ 	size_t i, stop;
+ 
++	/* Avoid overflow in calculation with large indices. */
++	if (idx > SIZE_T_MAX - count)
++		return -1;
+ 	stop = idx + count;
+ 	if ( idx >= arr->length || stop > arr->length ) return -1;
+ 	for ( i = idx; i < stop; ++i ) {
+diff --git a/linkhash.c b/linkhash.c
+index 5497061a8a..6435a154ac 100644
+--- a/linkhash.c
++++ b/linkhash.c
+@@ -12,12 +12,13 @@
+ 
+ #include "config.h"
+ 
+-#include <stdio.h>
+-#include <string.h>
+-#include <stdlib.h>
++#include <assert.h>
++#include <limits.h>
+ #include <stdarg.h>
+ #include <stddef.h>
+-#include <limits.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
+ 
+ #ifdef HAVE_ENDIAN_H
+ # include <endian.h>    /* attempt to define endianness */
+@@ -28,8 +29,8 @@
+ # include <windows.h>   /* Get InterlockedCompareExchange */
+ #endif
+ 
+-#include "random_seed.h"
+ #include "linkhash.h"
++#include "random_seed.h"
+ 
+ /* hash functions */
+ static unsigned long lh_char_hash(const void *k);
+@@ -498,7 +499,9 @@ struct lh_table* lh_table_new(int size,
+ 	int i;
+ 	struct lh_table *t;
+ 
+-	t = (struct lh_table*)calloc(1, sizeof(struct lh_table));
++	/* Allocate space for elements to avoid divisions by zero. */
++	assert(size > 0);
++	t = (struct lh_table *)calloc(1, sizeof(struct lh_table));
+ 	if (!t)
+ 		return NULL;
+ 
+@@ -577,8 +580,12 @@ int lh_table_insert_w_hash(struct lh_table *t, const void *k, const void *v, con
+ 	unsigned long n;
+ 
+ 	if (t->count >= t->size * LH_LOAD_FACTOR)
+-		if (lh_table_resize(t, t->size * 2) != 0)
++	{
++		/* Avoid signed integer overflow with large tables. */
++		int new_size = (t->size > INT_MAX / 2) ? INT_MAX : (t->size * 2);
++		if (t->size == INT_MAX || lh_table_resize(t, new_size) != 0)
+ 			return -1;
++	}
+ 
+ 	n = h % t->size;
+ 
+diff --git a/printbuf.c b/printbuf.c
+index 6c77b5defd..6fc56de455 100644
+--- a/printbuf.c
++++ b/printbuf.c
+@@ -15,6 +15,7 @@
+ 
+ #include "config.h"
+ 
++#include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -64,10 +65,16 @@ static int printbuf_extend(struct printbuf *p, int min_size)
+ 
+ 	if (p->size >= min_size)
+ 		return 0;
+-
+-	new_size = p->size * 2;
+-	if (new_size < min_size + 8)
+-		new_size =  min_size + 8;
++	/* Prevent signed integer overflows with large buffers. */
++	if (min_size > INT_MAX - 8)
++		return -1;
++	if (p->size > INT_MAX / 2)
++		new_size = min_size + 8;
++	else {
++		new_size = p->size * 2;
++		if (new_size < min_size + 8)
++			new_size = min_size + 8;
++	}
+ #ifdef PRINTBUF_DEBUG
+ 	MC_DEBUG("printbuf_memappend: realloc "
+ 	  "bpos=%d min_size=%d old_size=%d new_size=%d\n",
+@@ -82,14 +89,18 @@ static int printbuf_extend(struct printbuf *p, int min_size)
+ 
+ int printbuf_memappend(struct printbuf *p, const char *buf, int size)
+ {
+-  if (p->size <= p->bpos + size + 1) {
+-    if (printbuf_extend(p, p->bpos + size + 1) < 0)
+-      return -1;
+-  }
+-  memcpy(p->buf + p->bpos, buf, size);
+-  p->bpos += size;
+-  p->buf[p->bpos]= '\0';
+-  return size;
++	/* Prevent signed integer overflows with large buffers. */
++	if (size > INT_MAX - p->bpos - 1)
++		return -1;
++	if (p->size <= p->bpos + size + 1)
++	{
++		if (printbuf_extend(p, p->bpos + size + 1) < 0)
++			return -1;
++	}
++	memcpy(p->buf + p->bpos, buf, size);
++	p->bpos += size;
++	p->buf[p->bpos] = '\0';
++	return size;
+ }
+ 
+ int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len)
+@@ -98,6 +109,9 @@ int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len)
+ 
+ 	if (offset == -1)
+ 		offset = pb->bpos;
++	/* Prevent signed integer overflows with large buffers. */
++	if (len > INT_MAX - offset)
++		return -1;
+ 	size_needed = offset + len;
+ 	if (pb->size < size_needed)
+ 	{
+diff --git a/tests/test4.c b/tests/test4.c
+index fc8b79dbf4..82d3f494de 100644
+--- a/tests/test4.c
++++ b/tests/test4.c
+@@ -2,9 +2,11 @@
+  * gcc -o utf8 utf8.c -I/home/y/include -L./.libs -ljson
+  */
+ 
++#include "config.h"
++#include <assert.h>
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <string.h>
+-#include "config.h"
+ 
+ #include "json_inttypes.h"
+ #include "json_object.h"
+@@ -24,6 +26,29 @@ void print_hex(const char* s)
+ 	putchar('\n');
+ }
+ 
++static void test_lot_of_adds(void);
++static void test_lot_of_adds()
++{
++	int ii;
++	char key[50];
++	json_object *jobj = json_object_new_object();
++	assert(jobj != NULL);
++	for (ii = 0; ii < 500; ii++)
++	{
++		snprintf(key, sizeof(key), "k%d", ii);
++		json_object *iobj = json_object_new_int(ii);
++		assert(iobj != NULL);
++		if (json_object_object_add(jobj, key, iobj))
++		{
++			fprintf(stderr, "FAILED to add object #%d\n", ii);
++			abort();
++		}
++	}
++	printf("%s\n", json_object_to_json_string(jobj));
++	assert(json_object_object_length(jobj) == 500);
++	json_object_put(jobj);
++}
++
+ int main(void)
+ {
+ 	const char *input = "\"\\ud840\\udd26,\\ud840\\udd27,\\ud800\\udd26,\\ud800\\udd27\"";
+@@ -49,5 +74,8 @@ int main(void)
+ 		retval = 1;
+ 	}
+ 	json_object_put(parse_result);
++
++	test_lot_of_adds();
++
+ 	return retval;
+ }
+diff --git a/tests/test4.expected b/tests/test4.expected
+index 68d4336d90..cb2744012b 100644
+--- a/tests/test4.expected
++++ b/tests/test4.expected
+@@ -1,3 +1,4 @@
+ input: "\ud840\udd26,\ud840\udd27,\ud800\udd26,\ud800\udd27"
+ JSON parse result is correct: 𠄦,𠄧,𐄦,𐄧
+ PASS
++{ "k0": 0, "k1": 1, "k2": 2, "k3": 3, "k4": 4, "k5": 5, "k6": 6, "k7": 7, "k8": 8, "k9": 9, "k10": 10, "k11": 11, "k12": 12, "k13": 13, "k14": 14, "k15": 15, "k16": 16, "k17": 17, "k18": 18, "k19": 19, "k20": 20, "k21": 21, "k22": 22, "k23": 23, "k24": 24, "k25": 25, "k26": 26, "k27": 27, "k28": 28, "k29": 29, "k30": 30, "k31": 31, "k32": 32, "k33": 33, "k34": 34, "k35": 35, "k36": 36, "k37": 37, "k38": 38, "k39": 39, "k40": 40, "k41": 41, "k42": 42, "k43": 43, "k44": 44, "k45": 45, "k46": 46, "k47": 47, "k48": 48, "k49": 49, "k50": 50, "k51": 51, "k52": 52, "k53": 53, "k54": 54, "k55": 55, "k56": 56, "k57": 57, "k58": 58, "k59": 59, "k60": 60, "k61": 61, "k62": 62, "k63": 63, "k64": 64, "k65": 65, "k66": 66, "k67": 67, "k68": 68, "k69": 69, "k70": 70, "k71": 71, "k72": 72, "k73": 73, "k74": 74, "k75": 75, "k76": 76, "k77": 77, "k78": 78, "k79": 79, "k80": 80, "k81": 81, "k82": 82, "k83": 83, "k84": 84, "k85": 85, "k86": 86, "k87": 87, "k88": 88, "k89": 89, "k90": 90, "k91": 91, "k92": 92, "k93": 93, "k94": 94, "k95": 95, "k96": 96, "k97": 97, "k98": 98, "k99": 99, "k100": 100, "k101": 101, "k102": 102, "k103": 103, "k104": 104, "k105": 105, "k106": 106, "k107": 107, "k108": 108, "k109": 109, "k110": 110, "k111": 111, "k112": 112, "k113": 113, "k114": 114, "k115": 115, "k116": 116, "k117": 117, "k118": 118, "k119": 119, "k120": 120, "k121": 121, "k122": 122, "k123": 123, "k124": 124, "k125": 125, "k126": 126, "k127": 127, "k128": 128, "k129": 129, "k130": 130, "k131": 131, "k132": 132, "k133": 133, "k134": 134, "k135": 135, "k136": 136, "k137": 137, "k138": 138, "k139": 139, "k140": 140, "k141": 141, "k142": 142, "k143": 143, "k144": 144, "k145": 145, "k146": 146, "k147": 147, "k148": 148, "k149": 149, "k150": 150, "k151": 151, "k152": 152, "k153": 153, "k154": 154, "k155": 155, "k156": 156, "k157": 157, "k158": 158, "k159": 159, "k160": 160, "k161": 161, "k162": 162, "k163": 163, "k164": 164, "k165": 165, "k166": 166, "k167": 167, "k168": 168, "k169": 169, "k170": 170, "k171": 171, "k172": 172, "k173": 173, "k174": 174, "k175": 175, "k176": 176, "k177": 177, "k178": 178, "k179": 179, "k180": 180, "k181": 181, "k182": 182, "k183": 183, "k184": 184, "k185": 185, "k186": 186, "k187": 187, "k188": 188, "k189": 189, "k190": 190, "k191": 191, "k192": 192, "k193": 193, "k194": 194, "k195": 195, "k196": 196, "k197": 197, "k198": 198, "k199": 199, "k200": 200, "k201": 201, "k202": 202, "k203": 203, "k204": 204, "k205": 205, "k206": 206, "k207": 207, "k208": 208, "k209": 209, "k210": 210, "k211": 211, "k212": 212, "k213": 213, "k214": 214, "k215": 215, "k216": 216, "k217": 217, "k218": 218, "k219": 219, "k220": 220, "k221": 221, "k222": 222, "k223": 223, "k224": 224, "k225": 225, "k226": 226, "k227": 227, "k228": 228, "k229": 229, "k230": 230, "k231": 231, "k232": 232, "k233": 233, "k234": 234, "k235": 235, "k236": 236, "k237": 237, "k238": 238, "k239": 239, "k240": 240, "k241": 241, "k242": 242, "k243": 243, "k244": 244, "k245": 245, "k246": 246, "k247": 247, "k248": 248, "k249": 249, "k250": 250, "k251": 251, "k252": 252, "k253": 253, "k254": 254, "k255": 255, "k256": 256, "k257": 257, "k258": 258, "k259": 259, "k260": 260, "k261": 261, "k262": 262, "k263": 263, "k264": 264, "k265": 265, "k266": 266, "k267": 267, "k268": 268, "k269": 269, "k270": 270, "k271": 271, "k272": 272, "k273": 273, "k274": 274, "k275": 275, "k276": 276, "k277": 277, "k278": 278, "k279": 279, "k280": 280, "k281": 281, "k282": 282, "k283": 283, "k284": 284, "k285": 285, "k286": 286, "k287": 287, "k288": 288, "k289": 289, "k290": 290, "k291": 291, "k292": 292, "k293": 293, "k294": 294, "k295": 295, "k296": 296, "k297": 297, "k298": 298, "k299": 299, "k300": 300, "k301": 301, "k302": 302, "k303": 303, "k304": 304, "k305": 305, "k306": 306, "k307": 307, "k308": 308, "k309": 309, "k310": 310, "k311": 311, "k312": 312, "k313": 313, "k314": 314, "k315": 315, "k316": 316, "k317": 317, "k318": 318, "k319": 319, "k320": 320, "k321": 321, "k322": 322, "k323": 323, "k324": 324, "k325": 325, "k326": 326, "k327": 327, "k328": 328, "k329": 329, "k330": 330, "k331": 331, "k332": 332, "k333": 333, "k334": 334, "k335": 335, "k336": 336, "k337": 337, "k338": 338, "k339": 339, "k340": 340, "k341": 341, "k342": 342, "k343": 343, "k344": 344, "k345": 345, "k346": 346, "k347": 347, "k348": 348, "k349": 349, "k350": 350, "k351": 351, "k352": 352, "k353": 353, "k354": 354, "k355": 355, "k356": 356, "k357": 357, "k358": 358, "k359": 359, "k360": 360, "k361": 361, "k362": 362, "k363": 363, "k364": 364, "k365": 365, "k366": 366, "k367": 367, "k368": 368, "k369": 369, "k370": 370, "k371": 371, "k372": 372, "k373": 373, "k374": 374, "k375": 375, "k376": 376, "k377": 377, "k378": 378, "k379": 379, "k380": 380, "k381": 381, "k382": 382, "k383": 383, "k384": 384, "k385": 385, "k386": 386, "k387": 387, "k388": 388, "k389": 389, "k390": 390, "k391": 391, "k392": 392, "k393": 393, "k394": 394, "k395": 395, "k396": 396, "k397": 397, "k398": 398, "k399": 399, "k400": 400, "k401": 401, "k402": 402, "k403": 403, "k404": 404, "k405": 405, "k406": 406, "k407": 407, "k408": 408, "k409": 409, "k410": 410, "k411": 411, "k412": 412, "k413": 413, "k414": 414, "k415": 415, "k416": 416, "k417": 417, "k418": 418, "k419": 419, "k420": 420, "k421": 421, "k422": 422, "k423": 423, "k424": 424, "k425": 425, "k426": 426, "k427": 427, "k428": 428, "k429": 429, "k430": 430, "k431": 431, "k432": 432, "k433": 433, "k434": 434, "k435": 435, "k436": 436, "k437": 437, "k438": 438, "k439": 439, "k440": 440, "k441": 441, "k442": 442, "k443": 443, "k444": 444, "k445": 445, "k446": 446, "k447": 447, "k448": 448, "k449": 449, "k450": 450, "k451": 451, "k452": 452, "k453": 453, "k454": 454, "k455": 455, "k456": 456, "k457": 457, "k458": 458, "k459": 459, "k460": 460, "k461": 461, "k462": 462, "k463": 463, "k464": 464, "k465": 465, "k466": 466, "k467": 467, "k468": 468, "k469": 469, "k470": 470, "k471": 471, "k472": 472, "k473": 473, "k474": 474, "k475": 475, "k476": 476, "k477": 477, "k478": 478, "k479": 479, "k480": 480, "k481": 481, "k482": 482, "k483": 483, "k484": 484, "k485": 485, "k486": 486, "k487": 487, "k488": 488, "k489": 489, "k490": 490, "k491": 491, "k492": 492, "k493": 493, "k494": 494, "k495": 495, "k496": 496, "k497": 497, "k498": 498, "k499": 499 }
diff --git a/recipes-wifi/json-c/json-c/add-disable-werror-option-to-configure.patch b/recipes-wifi/json-c/json-c/add-disable-werror-option-to-configure.patch
new file mode 100644
index 0000000..0c20c84
--- /dev/null
+++ b/recipes-wifi/json-c/json-c/add-disable-werror-option-to-configure.patch
@@ -0,0 +1,45 @@
+json-c: Backport --disable-werror patch to allow compilation under icecc
+
+icecc preprocesses source files locally before shipping them off to be compiled
+on remote hosts. This preprocessing removes comments, including /* fallthough */
+comments in switch statements that normally prevent an implicit-fallthrough
+warning, see https://github.com/icecc/icecream/issues/419
+
+Rather than turning off -Werror, the upstream project has implemented a
+configure option, --disable-werror, in response to Ross's
+https://github.com/json-c/json-c/issues/489
+
+This patch from
+https://github.com/json-c/json-c/commit/21c886534f8927fdc0fb5f8647394f3e0e0874b8
+
+Upstream-Status: Backport [Not yet released]
+Signed-off-by: Douglas Royds <douglas.royds@taitradio.com>
+
+From 21c886534f8927fdc0fb5f8647394f3e0e0874b8 Mon Sep 17 00:00:00 2001
+From: Pierce Lopez <pierce.lopez@gmail.com>
+Date: Sun, 9 Jun 2019 10:52:08 -0400
+Subject: [PATCH] build: add --disable-werror option to configure
+
+to omit -Werror compiler option
+---
+ configure.ac | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 272ea6af9c..798fd5b747 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -165,7 +165,12 @@ AS_IF([test "x$enable_Bsymbolic" = "xcheck"],
+ AS_IF([test "x$enable_Bsymbolic" = "xyes"], [JSON_BSYMBOLIC_LDFLAGS=-Wl[,]-Bsymbolic-functions])
+ AC_SUBST(JSON_BSYMBOLIC_LDFLAGS)
+ 
+-AX_APPEND_COMPILE_FLAGS([-Wall -Werror -Wcast-qual -Wno-error=deprecated-declarations])
++AC_ARG_ENABLE([werror],
++    AS_HELP_STRING([--disable-werror], [avoid treating compiler warnings as fatal errors]))
++
++AS_IF([test "x$enable_werror" != "xno"], [AX_APPEND_COMPILE_FLAGS([-Werror])])
++
++AX_APPEND_COMPILE_FLAGS([-Wall -Wcast-qual -Wno-error=deprecated-declarations])
+ AX_APPEND_COMPILE_FLAGS([-Wextra -Wwrite-string -Wno-unused-parameter])
+ AX_APPEND_COMPILE_FLAGS([-D_GNU_SOURCE])
+ 
diff --git a/recipes-wifi/json-c/json-c_0.15.bb b/recipes-wifi/json-c/json-c_0.15.bb
new file mode 100644
index 0000000..04a7892
--- /dev/null
+++ b/recipes-wifi/json-c/json-c_0.15.bb
@@ -0,0 +1,21 @@
+SUMMARY = "C bindings for apps which will manipulate JSON data"
+DESCRIPTION = "JSON-C implements a reference counting object model that allows you to easily construct JSON objects in C."
+HOMEPAGE = "https://github.com/json-c/json-c/wiki"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://COPYING;md5=de54b60fbbc35123ba193fea8ee216f2"
+
+SRC_URI = "https://s3.amazonaws.com/json-c_releases/releases/${BP}.tar.gz \
+           "
+SRC_URI[md5sum] = "55f395745ee1cb3a4a39b41636087501"
+SRC_URI[sha256sum] = "b8d80a1ddb718b3ba7492916237bbf86609e9709fb007e7f7d4322f02341a4c6"
+
+UPSTREAM_CHECK_URI = "https://github.com/${BPN}/${BPN}/releases"
+UPSTREAM_CHECK_REGEX = "json-c-(?P<pver>\d+(\.\d+)+)-\d+"
+
+RPROVIDES_${PN} = "libjson"
+
+inherit cmake
+
+EXTRA_OECONF = "--disable-werror"
+
+BBCLASSEXTEND = "native nativesdk"
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0018-cfg80211-mtk-fix-early-return-in-cfg80211_stop_backg.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0018-cfg80211-mtk-fix-early-return-in-cfg80211_stop_backg.patch
new file mode 100644
index 0000000..d57273b
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0018-cfg80211-mtk-fix-early-return-in-cfg80211_stop_backg.patch
@@ -0,0 +1,29 @@
+From 435bf8765d44b694b86a400fb128fcf6d72b8aa6 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 27 Jul 2023 10:25:59 +0800
+Subject: [PATCH 18/19] cfg80211: mtk: fix early return in
+ cfg80211_stop_background_radar_detection
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/wireless/mlme.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
+index 57b9bde..c2cf8aa 100644
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1145,9 +1145,9 @@ void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
+ 		return;
+ 
+ 	rdev_set_radar_background(rdev, NULL);
+-	rdev->background_radar_wdev = NULL; /* Release offchain ownership */
+ 
+ 	__cfg80211_background_cac_event(rdev, wdev,
+ 					&rdev->background_radar_chandef,
+ 					NL80211_RADAR_CAC_ABORTED);
++	rdev->background_radar_wdev = NULL; /* Release offchain ownership */
+ }
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0019-cfg80211-mtk-add-background-radar-stop-when-backgrou.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0019-cfg80211-mtk-add-background-radar-stop-when-backgrou.patch
new file mode 100644
index 0000000..66d4d10
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0019-cfg80211-mtk-add-background-radar-stop-when-backgrou.patch
@@ -0,0 +1,29 @@
+From 818c8f1d1a9ff9e5423ed0927b052692c282fdf4 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Thu, 27 Jul 2023 10:27:04 +0800
+Subject: [PATCH 19/19] cfg80211: mtk: add background radar stop when
+ background channel is overlapped with operating channel
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/wireless/nl80211.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
+index 53f32c6..ab61f8f 100644
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -9852,6 +9852,10 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
+ 		wdev->cac_started = true;
+ 		wdev->cac_start_time = jiffies;
+ 		wdev->cac_time_ms = cac_time_ms;
++		if (rdev->background_cac_started &&
++		    cfg80211_is_sub_chan(&chandef, rdev->background_radar_chandef.chan, false)) {
++			cfg80211_stop_background_radar_detection(rdev->background_radar_wdev);
++		}
+ 	}
+ unlock:
+ 	wiphy_unlock(wiphy);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0020-mac80211-mtk-avoid-kernel-warning-of-check_flush_dep.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0020-mac80211-mtk-avoid-kernel-warning-of-check_flush_dep.patch
new file mode 100644
index 0000000..cc72cf9
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0020-mac80211-mtk-avoid-kernel-warning-of-check_flush_dep.patch
@@ -0,0 +1,26 @@
+From 6e6ceea486d4bd12a2d4118905d8fc0ba5eec6f6 Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Thu, 3 Aug 2023 07:17:44 +0800
+Subject: [PATCH 1/7] mac80211: mtk: avoid kernel warning of
+ check_flush_dependency
+
+---
+ net/mac80211/main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index 06d111d..8965fff 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -1276,7 +1276,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+ 		hw->queues = IEEE80211_MAX_QUEUES;
+ 
+ 	local->workqueue =
+-		alloc_ordered_workqueue("%s", 0, wiphy_name(local->hw.wiphy));
++		alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, wiphy_name(local->hw.wiphy));
+ 	if (!local->workqueue) {
+ 		result = -ENOMEM;
+ 		goto fail_workqueue;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0021-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0021-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch
new file mode 100644
index 0000000..d9fcacf
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/mtk-0021-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch
@@ -0,0 +1,40 @@
+From d5104c3407f3f1d476d072a8242ba40cc96ae566 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 7 Aug 2023 19:00:53 +0800
+Subject: [PATCH] mac80211: mtk: avoid calling switch_vif_chanctx when
+ use_chanctx is false
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/chan.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index dbc34fb..f9d1a8a 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -1280,13 +1280,15 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
+ 	list_del(&link->reserved_chanctx_list);
+ 	link->reserved_chanctx = NULL;
+ 
+-	err = drv_switch_vif_chanctx(local, vif_chsw, 1,
+-				     CHANCTX_SWMODE_REASSIGN_VIF);
+-	if (err) {
+-		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+-			ieee80211_free_chanctx(local, new_ctx);
++	if (local->use_chanctx) {
++		err = drv_switch_vif_chanctx(local, vif_chsw, 1,
++					     CHANCTX_SWMODE_REASSIGN_VIF);
++		if (err) {
++			if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
++				ieee80211_free_chanctx(local, new_ctx);
+ 
+-		goto out;
++			goto out;
++		}
+ 	}
+ 
+ 	list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
index b559a0a..d392140 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
@@ -69,6 +69,10 @@
     file://mtk-0015-mac80211-support-configurable-addba-resp-time.patch \
     file://mtk-0016-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch \
     file://mtk-0017-nl80211-mtk-Mark-DFS-channel-as-available-for-CSA.patch \
+    file://mtk-0018-cfg80211-mtk-fix-early-return-in-cfg80211_stop_backg.patch \
+    file://mtk-0019-cfg80211-mtk-add-background-radar-stop-when-backgrou.patch \
+    file://mtk-0020-mac80211-mtk-avoid-kernel-warning-of-check_flush_dep.patch \
+    file://mtk-0021-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch \
     file://mtk-1000-mac80211-mtk-add-EHT-BA1024-support.patch \
     file://mtk-1001-mac80211-mtk-add-rate-duration-for-EHT-rate.patch \
     file://mtk-1002-mac80211-mtk-add-send-bar-action-when-recieve-addba-.patch \
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0023-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0023-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch
new file mode 100644
index 0000000..a8a8ded
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0023-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch
@@ -0,0 +1,40 @@
+From 5098ea28917b237691a174020836c5910c1be625 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 7 Aug 2023 19:00:53 +0800
+Subject: [PATCH] mac80211: mtk: avoid calling switch_vif_chanctx when
+ use_chanctx is false
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ net/mac80211/chan.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
+index 63e15f5..0e4ea5d 100644
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -1160,13 +1160,15 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
+ 	list_del(&sdata->reserved_chanctx_list);
+ 	sdata->reserved_chanctx = NULL;
+ 
+-	err = drv_switch_vif_chanctx(local, vif_chsw, 1,
+-				     CHANCTX_SWMODE_REASSIGN_VIF);
+-	if (err) {
+-		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+-			ieee80211_free_chanctx(local, new_ctx);
++	if (local->use_chanctx) {
++		err = drv_switch_vif_chanctx(local, vif_chsw, 1,
++					     CHANCTX_SWMODE_REASSIGN_VIF);
++		if (err) {
++			if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
++				ieee80211_free_chanctx(local, new_ctx);
+ 
+-		goto out;
++			goto out;
++		}
+ 	}
+ 
+ 	list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc b/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc
index d6d111d..6295da6 100644
--- a/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc
@@ -78,6 +78,7 @@
     file://mtk-0020-cfg80211-mtk-fix-early-return-in-cfg80211_stop_backg.patch \
     file://mtk-0021-cfg80211-mtk-add-background-radar-stop-when-backgrou.patch \
     file://mtk-0022-mac80211-mtk-avoid-kernel-warning-of-check_flush_dep.patch \
+    file://mtk-0023-mac80211-mtk-avoid-calling-switch_vif_chanctx-when-u.patch \
     file://mtk-9900-mac80211-mtk-mask-kernel-version-limitation-and-fill.patch \
     file://mtk-9901-mac80211-mtk-add-fill-receive-path-ops-to-get-wed-id.patch \
     file://mtk-9902-mac80211-mtk-add-support-for-letting-drivers-registe.patch \
diff --git a/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-fix-tx-emission-issue-during-bootup.patch b/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-fix-tx-emission-issue-during-bootup.patch
new file mode 100644
index 0000000..322bb3a
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches/0008-wifi-mt76-mt7915-fix-tx-emission-issue-during-bootup.patch
@@ -0,0 +1,30 @@
+From 56916977daae3317294a905950cdc604d048bddb Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 2 Aug 2023 11:26:14 +0800
+Subject: [PATCH] wifi: mt76: mt7915: fix tx emission issue during bootup after
+ applying pre-cal
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Reviewed-by: Shayne Chen <shayne.chen@mediatek.com>
+Reviewed-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
+---
+ mt7915/mcu.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index a34b75d..502610f 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -2737,7 +2737,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
+ 
+ 	if (dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+ 		req.switch_reason = CH_SWITCH_NORMAL;
+-	else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
++	else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL ||
++		 phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
+ 		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+ 	else if (cfg80211_chandef_valid(chandef) &&
+ 		 !cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+-- 
+2.18.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-fix-txpower-issues.patch b/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-fix-txpower-issues.patch
index 814af74..e62d287 100644
--- a/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-fix-txpower-issues.patch
+++ b/recipes-wifi/linux-mt76/files/patches/0009-wifi-mt76-mt7915-fix-txpower-issues.patch
@@ -1,4 +1,4 @@
-From 2016d1f146684a17c813c957b5c8bd51bd25b47f Mon Sep 17 00:00:00 2001
+From a769a5232abe1ca70f376ef90efc227c9fd56901 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Sat, 29 Jul 2023 04:53:47 +0800
 Subject: [PATCH] wifi: mt76: mt7915: fix txpower issues
@@ -8,15 +8,15 @@
  mac80211.c       |  2 +-
  mt7915/debugfs.c | 48 ++++++++++++++++++++++++++----------------------
  mt7915/init.c    |  2 +-
- mt7915/main.c    |  3 ++-
+ mt7915/main.c    |  1 +
  mt7915/mcu.h     |  2 +-
- 6 files changed, 33 insertions(+), 28 deletions(-)
+ 6 files changed, 32 insertions(+), 27 deletions(-)
 
 diff --git a/eeprom.c b/eeprom.c
-index ea54b7a..f9f048c 100644
+index 2558788..750e031 100644
 --- a/eeprom.c
 +++ b/eeprom.c
-@@ -283,7 +283,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+@@ -338,7 +338,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
  	u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
  	char band;
  	size_t len;
@@ -25,7 +25,7 @@
  	s8 txs_delta;
  
  	if (!mcs_rates)
-@@ -321,7 +321,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
+@@ -376,7 +376,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
  	if (!np)
  		return target_power;
  
@@ -145,19 +145,10 @@
  	int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band);
  	struct mt76_power_limits limits;
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 86f794f..d7dfae8 100644
+index 3381325..a90ac45 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
-@@ -479,7 +479,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
- 
- 	mutex_lock(&dev->mt76.mutex);
- 
--	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
-+	if (changed & (IEEE80211_CONF_CHANGE_MONITOR | IEEE80211_CONF_CHANGE_CHANNEL)) {
- 		bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
- 		bool band = phy->mt76->band_idx;
- 
-@@ -1062,6 +1062,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+@@ -1063,6 +1063,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
  	mt76_set_stream_caps(phy->mt76, true);
  	mt7915_set_stream_vht_txbf_caps(phy);
  	mt7915_set_stream_he_caps(phy);
diff --git a/recipes-wifi/linux-mt76/files/patches/1003-wifi-mt76-mt7915-add-support-for-muru_onoff-via.patch b/recipes-wifi/linux-mt76/files/patches/1003-wifi-mt76-mt7915-add-support-for-muru_onoff-via.patch
index e2f17a1..3473003 100644
--- a/recipes-wifi/linux-mt76/files/patches/1003-wifi-mt76-mt7915-add-support-for-muru_onoff-via.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1003-wifi-mt76-mt7915-add-support-for-muru_onoff-via.patch
@@ -1,31 +1,17 @@
-From dd220c1cfc8ee158895cfa31901ef625ac3a0a4d Mon Sep 17 00:00:00 2001
+From a5abc630e8bf65e4ca21bc1cf1b74db7df6cd6d1 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Tue, 4 Apr 2023 02:23:57 +0800
 Subject: [PATCH 1003/1034] wifi: mt76: mt7915: add support for muru_onoff via
 
 ---
- mt7915/init.c        |  2 ++
  mt7915/mcu.c         | 10 ++++++++--
  mt7915/mcu.h         |  6 ++++++
  mt7915/mt7915.h      |  2 ++
  mt7915/mtk_debugfs.c | 33 +++++++++++++++++++++++++++++++++
- 5 files changed, 51 insertions(+), 2 deletions(-)
+ 4 files changed, 49 insertions(+), 2 deletions(-)
 
-diff --git a/mt7915/init.c b/mt7915/init.c
-index 0ea2af17..361c3847 100644
---- a/mt7915/init.c
-+++ b/mt7915/init.c
-@@ -470,6 +470,8 @@ mt7915_init_wiphy(struct mt7915_phy *phy)
- 		mphy->leds.cdev.brightness_set = mt7915_led_set_brightness;
- 		mphy->leds.cdev.blink_set = mt7915_led_set_blink;
- 	}
-+
-+	phy->muru_onoff = OFDMA_DL | MUMIMO_UL | MUMIMO_DL;
- }
- 
- static void
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 8994ea6a..fdb6bc5f 100644
+index 8994ea6..fdb6bc5 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -873,6 +873,7 @@ mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
@@ -58,7 +44,7 @@
  		muru->mimo_dl.vht_mu_bfee =
  			!!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index 67eac5c9..84c52d9b 100644
+index 67eac5c..84c52d9 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -666,4 +666,10 @@ struct csi_data {
@@ -73,7 +59,7 @@
 +
  #endif
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index c733035b..bd5543b5 100644
+index c733035..bd5543b 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -252,6 +252,8 @@ struct mt7915_phy {
@@ -86,7 +72,7 @@
  	struct mt76_channel_state state_ts;
  
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index e6796bc3..e1758edb 100644
+index e6796bc..e1758ed 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2558,6 +2558,38 @@ static int mt7915_token_txd_read(struct seq_file *s, void *data)
@@ -137,5 +123,5 @@
  			    &fops_fw_debug_module);
  	debugfs_create_file("fw_debug_level", 0600, dir, dev,
 -- 
-2.39.2
+2.39.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-additional-supports.patch b/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-additional-supports.patch
index 182f6eb..cb5281c 100644
--- a/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-additional-supports.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1009-wifi-mt76-testmode-additional-supports.patch
@@ -1,7 +1,7 @@
-From 5048f24cdd1ddaa11edd91747a07ba3a7484e49c Mon Sep 17 00:00:00 2001
+From 333678e670a88fe48c859af6dbb357405f084a52 Mon Sep 17 00:00:00 2001
 From: Shayne Chen <shayne.chen@mediatek.com>
 Date: Thu, 21 Apr 2022 15:43:19 +0800
-Subject: [PATCH 1009/1034] wifi: mt76: testmode: additional supports
+Subject: [PATCH] wifi: mt76: testmode: additional supports
 
 Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
@@ -20,16 +20,16 @@
  mt7915/mmio.c     |    2 +
  mt7915/mt7915.h   |   16 +-
  mt7915/regs.h     |    3 +
- mt7915/testmode.c | 1221 ++++++++++++++++++++++++++++++++++++++++++---
+ mt7915/testmode.c | 1222 ++++++++++++++++++++++++++++++++++++++++++---
  mt7915/testmode.h |  278 +++++++++++
  testmode.c        |  282 +++++++++--
  testmode.h        |   75 +++
  tools/fields.c    |   84 +++-
  tx.c              |    3 +-
- 20 files changed, 2023 insertions(+), 166 deletions(-)
+ 20 files changed, 2024 insertions(+), 166 deletions(-)
 
 diff --git a/dma.c b/dma.c
-index c9d2671d..fc92e391 100644
+index c9d2671..fc92e39 100644
 --- a/dma.c
 +++ b/dma.c
 @@ -574,8 +574,7 @@ free:
@@ -43,7 +43,7 @@
  #endif
  
 diff --git a/mac80211.c b/mac80211.c
-index eac90b3e..ed1d659b 100644
+index eac90b3..ed1d659 100644
 --- a/mac80211.c
 +++ b/mac80211.c
 @@ -55,6 +55,13 @@ static const struct ieee80211_channel mt76_channels_5ghz[] = {
@@ -73,7 +73,7 @@
  
  static const struct ieee80211_channel mt76_channels_6ghz[] = {
 diff --git a/mt76.h b/mt76.h
-index c2ad06a2..a5908de4 100644
+index c2ad06a..a5908de 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -657,6 +657,21 @@ struct mt76_testmode_ops {
@@ -235,7 +235,7 @@
  static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
  {
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index a558c68c..74b63f0f 100644
+index a558c68..74b63f0 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -395,6 +395,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
@@ -257,7 +257,7 @@
  		return;
  
 diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
-index 92e0b912..e3980c37 100644
+index 92e0b91..e3980c3 100644
 --- a/mt76_connac_mcu.h
 +++ b/mt76_connac_mcu.h
 @@ -998,6 +998,7 @@ enum {
@@ -277,7 +277,7 @@
  	MCU_EXT_CMD_CSI_CTRL = 0xc2,
  };
 diff --git a/mt7915/eeprom.c b/mt7915/eeprom.c
-index 76be7308..f5ab3319 100644
+index 76be730..f5ab331 100644
 --- a/mt7915/eeprom.c
 +++ b/mt7915/eeprom.c
 @@ -131,7 +131,7 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev)
@@ -290,7 +290,7 @@
  				return ret;
  		}
 diff --git a/mt7915/init.c b/mt7915/init.c
-index 361c3847..6d1c0f71 100644
+index 361c384..6d1c0f7 100644
 --- a/mt7915/init.c
 +++ b/mt7915/init.c
 @@ -730,7 +730,7 @@ static void mt7915_init_work(struct work_struct *work)
@@ -303,7 +303,7 @@
  	mt7915_txbf_init(dev);
  }
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 7ae34b77..6c9401dd 100644
+index 7ae34b7..6c9401d 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -604,16 +604,38 @@ mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi,
@@ -385,7 +385,7 @@
  		goto out;
  
 diff --git a/mt7915/main.c b/mt7915/main.c
-index 9b54ba1c..d7bcd5f8 100644
+index 9b54ba1..d7bcd5f 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -238,7 +238,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
@@ -398,7 +398,7 @@
  		mvif->mt76.wmm_idx += 2;
  
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 1f5f938e..6a5f204c 100644
+index 1f5f938..6a5f204 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -389,6 +389,11 @@ mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
@@ -479,7 +479,7 @@
  
  	return 0;
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index c15b4b74..99827354 100644
+index c15b4b7..9982735 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -8,10 +8,15 @@
@@ -537,7 +537,7 @@
  
  enum {
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index b23679ac..6505d763 100644
+index b23679a..6505d76 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -134,6 +134,7 @@ static const u32 mt7915_offs[] = {
@@ -557,7 +557,7 @@
  	[AGG_PCR0]		= 0x040,
  	[AGG_ACR0]		= 0x054,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index bf0f7dee..c91415f7 100644
+index bf0f7de..c91415f 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -272,6 +272,9 @@ struct mt7915_phy {
@@ -605,7 +605,7 @@
  static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev)
  {
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 038596c3..428f4820 100644
+index 038596c..428f482 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -62,6 +62,7 @@ enum offs_rev {
@@ -626,7 +626,7 @@
  							  (_n) * 4))
  #define MT_AGG_PCR0(_band, _n)		MT_WF_AGG(_band, (__OFFS(AGG_PCR0) +	\
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 46939191..1e72bf2a 100644
+index 4693919..e5d0ef9 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -9,6 +9,9 @@
@@ -749,7 +749,7 @@
  	};
  
  	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
-@@ -182,12 +249,738 @@ mt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu)
+@@ -182,12 +249,739 @@ mt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu)
  	return mt7915_mcu_set_muru_ctrl(dev, MURU_SET_ARB_OP_MODE, op_mode);
  }
  
@@ -1354,6 +1354,7 @@
 +mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
 +{
 +	struct mt76_testmode_data *td = &phy->mt76->test;
++	u8 nss = hweight8(td->tx_antenna_mask);
 +	u16 pfmu_idx = val[0];
 +	u16 subc_id = val[1];
 +	u16 angle11 = val[2];
@@ -1366,9 +1367,9 @@
 +	if (subc_id > 63)
 +		return -EINVAL;
 +
-+	if (td->tx_antenna_mask == 2) {
++	if (nss == 2) {
 +		phi11 = (s16)(angle21 - angle11);
-+	} else if (td->tx_antenna_mask == 3) {
++	} else if (nss == 3) {
 +		phi11 = (s16)(angle31 - angle11);
 +		phi21 = (s16)(angle31 - angle21);
 +	} else {
@@ -1490,7 +1491,7 @@
  	struct edca *e = &req.edca[0];
  
  	e->queue = qid + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS;
-@@ -263,7 +1056,8 @@ done:
+@@ -263,7 +1057,8 @@ done:
  
  	return mt7915_tm_set_wmm_qid(phy,
  				     mt76_connac_lmac_mapping(IEEE80211_AC_BE),
@@ -1500,7 +1501,7 @@
  }
  
  static int
-@@ -339,7 +1133,7 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
+@@ -339,7 +1134,7 @@ mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time)
  	bitrate = cfg80211_calculate_bitrate(&rate);
  	tx_len = bitrate * tx_time / 10 / 8;
  
@@ -1509,7 +1510,7 @@
  	if (ret)
  		return ret;
  
-@@ -458,64 +1252,227 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
+@@ -458,64 +1253,227 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
  
  	phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
  
@@ -1768,7 +1769,7 @@
  
  	mt7915_tm_set_trx(phy, TM_MAC_TX, en);
  }
-@@ -544,10 +1501,6 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
+@@ -544,10 +1502,6 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
  		return ret;
  
  	rs_band = (struct mt7915_tm_rx_stat_band *)skb->data;
@@ -1779,7 +1780,7 @@
  
  	if (!clear) {
  		enum mt76_rxq_id q = req.band ? MT_RXQ_BAND1 : MT_RXQ_MAIN;
-@@ -562,13 +1515,61 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
+@@ -562,13 +1516,61 @@ mt7915_tm_get_rx_stats(struct mt7915_phy *phy, bool clear)
  	return 0;
  }
  
@@ -1842,7 +1843,7 @@
  
  		/* read-clear */
  		mt7915_tm_get_rx_stats(phy, true);
-@@ -576,9 +1577,12 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
+@@ -576,9 +1578,12 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
  		/* clear fw count */
  		mt7915_tm_set_phy_count(phy, 0);
  		mt7915_tm_set_phy_count(phy, 1);
@@ -1857,7 +1858,7 @@
  }
  
  static int
-@@ -617,34 +1621,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
+@@ -617,34 +1622,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
  	tx_cont->tx_ant = td->tx_antenna_mask;
  	tx_cont->band = band;
  
@@ -1893,7 +1894,7 @@
  
  	if (!en) {
  		req.op.rf.param.func_data = cpu_to_le32(band);
-@@ -728,6 +1705,12 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
+@@ -728,6 +1706,12 @@ mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed)
  		mt7915_tm_set_freq_offset(phy, en, en ? td->freq_offset : 0);
  	if (changed & BIT(TM_CHANGED_TXPOWER))
  		mt7915_tm_set_tx_power(phy);
@@ -1906,7 +1907,7 @@
  }
  
  static int
-@@ -737,6 +1720,11 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+@@ -737,6 +1721,11 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
  	struct mt7915_phy *phy = mphy->priv;
  	enum mt76_testmode_state prev_state = td->state;
  
@@ -1918,7 +1919,7 @@
  	mphy->test.state = state;
  
  	if (prev_state == MT76_TM_STATE_TX_FRAMES ||
-@@ -757,7 +1745,7 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+@@ -757,7 +1746,7 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
  	    (state == MT76_TM_STATE_OFF &&
  	     prev_state == MT76_TM_STATE_IDLE)) {
  		u32 changed = 0;
@@ -1927,7 +1928,7 @@
  
  		for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
  			u16 cur = tm_change_map[i];
-@@ -766,6 +1754,10 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
+@@ -766,6 +1755,10 @@ mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
  				changed |= BIT(i);
  		}
  
@@ -1938,7 +1939,7 @@
  		mt7915_tm_update_params(phy, changed);
  	}
  
-@@ -778,10 +1770,8 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
+@@ -778,10 +1771,8 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
  {
  	struct mt76_testmode_data *td = &mphy->test;
  	struct mt7915_phy *phy = mphy->priv;
@@ -1951,7 +1952,7 @@
  
  	BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
  
-@@ -789,9 +1779,9 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
+@@ -789,9 +1780,9 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
  	    td->state == MT76_TM_STATE_OFF)
  		return 0;
  
@@ -1964,7 +1965,7 @@
  
  	for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
  		if (tb[tm_change_map[i]])
-@@ -807,6 +1797,7 @@ static int
+@@ -807,6 +1798,7 @@ static int
  mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
  {
  	struct mt7915_phy *phy = mphy->priv;
@@ -1972,7 +1973,7 @@
  	void *rx, *rssi;
  	int i;
  
-@@ -852,11 +1843,75 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
+@@ -852,11 +1844,75 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
  
  	nla_nest_end(msg, rx);
  
@@ -2049,7 +2050,7 @@
 +	.set_eeprom = mt7915_tm_set_eeprom,
  };
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index a1c54c89..eb0e0432 100644
+index a1c54c8..eb0e043 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -4,6 +4,8 @@
@@ -2364,7 +2365,7 @@
 +
  #endif
 diff --git a/testmode.c b/testmode.c
-index 1b37392c..d2cef5b9 100644
+index 1b37392..d2cef5b 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -28,28 +28,16 @@ const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
@@ -2837,7 +2838,7 @@
  
  	if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
 diff --git a/testmode.h b/testmode.h
-index 89613266..8c55fa0f 100644
+index 8961326..8c55fa0 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -6,6 +6,8 @@
@@ -2942,7 +2943,7 @@
 +
  #endif
 diff --git a/tools/fields.c b/tools/fields.c
-index e3f69089..7e564a3b 100644
+index e3f6908..7e564a3 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
@@ -3083,7 +3084,7 @@
  };
  
 diff --git a/tx.c b/tx.c
-index 6cc26cc6..5d7bf340 100644
+index 6cc26cc..5d7bf34 100644
 --- a/tx.c
 +++ b/tx.c
 @@ -259,8 +259,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
@@ -3097,5 +3098,5 @@
  			wake_up(&dev->tx_wait);
  
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch b/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
index eac1cfe..f88823a 100644
--- a/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
+++ b/recipes-wifi/linux-mt76/files/patches/1013-wifi-mt76-testmode-add-iBF-eBF-cal-and-cert-commands.patch
@@ -1,8 +1,8 @@
-From 4bffcebdfcad62a1c6741c2bb7d4c023adf360d2 Mon Sep 17 00:00:00 2001
+From 7598ccc4626d9d41807a59fa8304649a7260b9ed Mon Sep 17 00:00:00 2001
 From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 Date: Thu, 15 Dec 2022 19:45:18 +0800
-Subject: [PATCH 1013/1034] wifi: mt76: testmode: add iBF/eBF cal and cert
- commands with golden
+Subject: [PATCH] wifi: mt76: testmode: add iBF/eBF cal and cert commands with
+ golden
 
 Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
 ---
@@ -17,15 +17,15 @@
  mt7915/mtk_debugfs.c |  35 ++++
  mt7915/mtk_mcu.c     | 246 ++++++++++++++++++++++-
  mt7915/regs.h        |   4 +
- mt7915/testmode.c    | 460 ++++++++++++++++++++++++++++---------------
+ mt7915/testmode.c    | 461 ++++++++++++++++++++++++++++---------------
  mt7915/testmode.h    | 134 +------------
  testmode.c           |   1 +
  testmode.h           |   9 +
  tools/fields.c       |   9 +
- 16 files changed, 856 insertions(+), 324 deletions(-)
+ 16 files changed, 857 insertions(+), 324 deletions(-)
 
 diff --git a/mt76.h b/mt76.h
-index 75b28748..af29bdeb 100644
+index 75b2874..af29bde 100644
 --- a/mt76.h
 +++ b/mt76.h
 @@ -703,6 +703,7 @@ struct mt76_testmode_data {
@@ -47,7 +47,7 @@
  	u32 tx_pending;
  	u32 tx_queued;
 diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
-index 74b63f0f..e6b468c4 100644
+index 74b63f0..e6b468c 100644
 --- a/mt76_connac_mcu.c
 +++ b/mt76_connac_mcu.c
 @@ -2767,6 +2767,7 @@ int mt76_connac_mcu_bss_basic_tlv(struct sk_buff *skb,
@@ -68,7 +68,7 @@
  		memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
  	}
 diff --git a/mt7915/mac.c b/mt7915/mac.c
-index 6c9401dd..1e0bee7b 100644
+index 6c9401d..1e0bee7 100644
 --- a/mt7915/mac.c
 +++ b/mt7915/mac.c
 @@ -729,8 +729,10 @@ mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi,
@@ -84,7 +84,7 @@
  	txwi[6] |= cpu_to_le32(val);
  #endif
 diff --git a/mt7915/main.c b/mt7915/main.c
-index d7bcd5f8..9694bd71 100644
+index d7bcd5f..9694bd7 100644
 --- a/mt7915/main.c
 +++ b/mt7915/main.c
 @@ -205,46 +205,37 @@ static void mt7915_init_bitrate_mask(struct ieee80211_vif *vif)
@@ -175,7 +175,7 @@
  
  	return ret;
 diff --git a/mt7915/mcu.c b/mt7915/mcu.c
-index 020780d3..c8b97e89 100644
+index 020780d..c8b97e8 100644
 --- a/mt7915/mcu.c
 +++ b/mt7915/mcu.c
 @@ -199,6 +199,7 @@ mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
@@ -254,7 +254,7 @@
  		return -EINVAL;
  	}
 diff --git a/mt7915/mcu.h b/mt7915/mcu.h
-index f4c3bf4e..c95d9900 100644
+index f4c3bf4..c95d990 100644
 --- a/mt7915/mcu.h
 +++ b/mt7915/mcu.h
 @@ -531,10 +531,12 @@ enum {
@@ -448,7 +448,7 @@
  	MURU_SET_ARB_OP_MODE = 14,
  	MURU_SET_PLATFORM_TYPE = 25,
 diff --git a/mt7915/mmio.c b/mt7915/mmio.c
-index 6505d763..25c3fe2b 100644
+index 6505d76..25c3fe2 100644
 --- a/mt7915/mmio.c
 +++ b/mt7915/mmio.c
 @@ -133,6 +133,7 @@ static const u32 mt7915_offs[] = {
@@ -468,7 +468,7 @@
  	[AGG_AALCR0]		= 0x028,
  	[AGG_AWSCR0]		= 0x030,
 diff --git a/mt7915/mt7915.h b/mt7915/mt7915.h
-index 88e45298..4572066b 100644
+index 88e4529..4572066 100644
 --- a/mt7915/mt7915.h
 +++ b/mt7915/mt7915.h
 @@ -274,7 +274,6 @@ struct mt7915_phy {
@@ -520,7 +520,7 @@
 +
  #endif
 diff --git a/mt7915/mtk_debugfs.c b/mt7915/mtk_debugfs.c
-index 9bb6dd05..6d9d009d 100644
+index 9bb6dd0..6d9d009 100644
 --- a/mt7915/mtk_debugfs.c
 +++ b/mt7915/mtk_debugfs.c
 @@ -2892,6 +2892,36 @@ mt7915_txpower_level_set(void *data, u64 val)
@@ -573,7 +573,7 @@
  
  	return 0;
 diff --git a/mt7915/mtk_mcu.c b/mt7915/mtk_mcu.c
-index 143dae26..906c8725 100644
+index 143dae2..906c872 100644
 --- a/mt7915/mtk_mcu.c
 +++ b/mt7915/mtk_mcu.c
 @@ -1,9 +1,10 @@
@@ -836,7 +836,7 @@
 +}
 +#endif
 diff --git a/mt7915/regs.h b/mt7915/regs.h
-index 482b3fee..44da7b82 100644
+index 482b3fe..44da7b8 100644
 --- a/mt7915/regs.h
 +++ b/mt7915/regs.h
 @@ -61,6 +61,7 @@ enum offs_rev {
@@ -858,7 +858,7 @@
  #define MT_WF_RMAC_BASE(_band)		((_band) ? 0x820f5000 : 0x820e5000)
  #define MT_WF_RMAC(_band, ofs)		(MT_WF_RMAC_BASE(_band) + (ofs))
 diff --git a/mt7915/testmode.c b/mt7915/testmode.c
-index 2e51dcd5..fafe9095 100644
+index e321848..d5c74df 100644
 --- a/mt7915/testmode.c
 +++ b/mt7915/testmode.c
 @@ -53,6 +53,8 @@ struct reg_band {
@@ -1262,7 +1262,7 @@
  	} else {
  		tag->t1.row_id1 = 4;
  		tag->t1.row_id2 = 5;
-@@ -780,6 +876,19 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
+@@ -780,6 +876,20 @@ mt7915_tm_txbf_profile_update(struct mt7915_phy *phy, u16 *val, bool ebf)
  	if (ret)
  		return ret;
  
@@ -1273,7 +1273,8 @@
 +			 mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx)));
 +	} else if (!td->ebf && ebf) {
 +		/* iBF's ebf profile update */
-+		mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_IBF_ENABLE);
++		if (!is_mt7915(&dev->mt76) || !dev->dbdc_support)
++			mt76_set(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx), MT_ARB_IBF_ENABLE);
 +		dev_info(dev->mt76.dev, "Set TX queue start CR for AX management (0x%x) = 0x%x\n",
 +			 MT_ARB_TQSAXM0(phy->mt76->band_idx),
 +			 mt76_rr(dev, MT_ARB_TQSAXM0(phy->mt76->band_idx)));
@@ -1282,7 +1283,7 @@
  	if (!ebf && is_atenl)
  		return mt7915_tm_txbf_apply_tx(phy, 1, false, true, true);
  
-@@ -797,7 +906,7 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
+@@ -797,7 +907,7 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
  		u8 category;
  		u8 group_l_m_n;
  		u8 group;
@@ -1291,7 +1292,7 @@
  		u8 cal_type;
  		u8 lna_gain_level;
  		u8 _rsv[2];
-@@ -805,12 +914,12 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
+@@ -805,12 +915,12 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
  		.category = MT_BF_PHASE_CAL,
  		.group = val[0],
  		.group_l_m_n = val[1],
@@ -1307,7 +1308,7 @@
  
  	phase[req.group].status = 0;
  
-@@ -818,53 +927,10 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
+@@ -818,53 +928,10 @@ mt7915_tm_txbf_phase_cal(struct mt7915_phy *phy, u16 *val)
  				 sizeof(req), true);
  }
  
@@ -1360,9 +1361,9 @@
  {
 +#define MT7915_TXBF_PFMU_DATA_LEN	(MT7915_TXBF_SUBCAR_NUM * sizeof(struct mt7915_pfmu_data))
  	struct mt76_testmode_data *td = &phy->mt76->test;
+ 	u8 nss = hweight8(td->tx_antenna_mask);
  	u16 pfmu_idx = val[0];
- 	u16 subc_id = val[1];
-@@ -873,9 +939,9 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
+@@ -874,9 +941,9 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
  	u16 angle31 = val[4];
  	u16 angle41 = val[5];
  	s16 phi11 = 0, phi21 = 0, phi31 = 0;
@@ -1373,8 +1374,8 @@
 +	if (subc_id > MT7915_TXBF_SUBCAR_NUM - 1)
  		return -EINVAL;
  
- 	if (td->tx_antenna_mask == 2) {
-@@ -889,7 +955,7 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
+ 	if (nss == 2) {
+@@ -890,7 +957,7 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
  		phi31 = (s16)(angle41 - angle31);
  	}
  
@@ -1383,7 +1384,7 @@
  	pfmu_data = &pfmu_data[subc_id];
  
  	if (subc_id < 32)
-@@ -899,21 +965,21 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
+@@ -900,21 +967,21 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
  	pfmu_data->phi11 = cpu_to_le16(phi11);
  	pfmu_data->phi21 = cpu_to_le16(phi21);
  	pfmu_data->phi31 = cpu_to_le16(phi31);
@@ -1408,7 +1409,7 @@
  
  		return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION),
  					 &req, sizeof(req), true);
-@@ -925,7 +991,7 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
+@@ -926,7 +993,7 @@ mt7915_tm_txbf_profile_update_all(struct mt7915_phy *phy, u16 *val)
  static int
  mt7915_tm_txbf_e2p_update(struct mt7915_phy *phy)
  {
@@ -1417,7 +1418,7 @@
  	struct mt7915_dev *dev = phy->dev;
  	u8 *eeprom = dev->mt76.eeprom.data;
  	u16 offset;
-@@ -935,7 +1001,7 @@ mt7915_tm_txbf_e2p_update(struct mt7915_phy *phy)
+@@ -936,7 +1003,7 @@ mt7915_tm_txbf_e2p_update(struct mt7915_phy *phy)
  	is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76);
  	offset = is_7976 ? 0x60a : 0x651;
  
@@ -1426,7 +1427,7 @@
  	for (i = 0; i < MAX_PHASE_GROUP_NUM; i++) {
  		p = &phase[i];
  
-@@ -950,17 +1016,75 @@ mt7915_tm_txbf_e2p_update(struct mt7915_phy *phy)
+@@ -951,17 +1018,75 @@ mt7915_tm_txbf_e2p_update(struct mt7915_phy *phy)
  	return 0;
  }
  
@@ -1504,7 +1505,7 @@
  		return mt7915_tm_txbf_init(phy, val);
  	case MT76_TM_TXBF_ACT_UPDATE_CH:
  		mt7915_tm_update_channel(phy);
-@@ -986,6 +1110,36 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
+@@ -987,6 +1112,36 @@ mt7915_tm_set_txbf(struct mt7915_phy *phy)
  
  		return mt7915_tm_txbf_apply_tx(phy, wlan_idx, ebf, ibf, phase_cal);
  	}
@@ -1541,7 +1542,7 @@
  	default:
  		break;
  	};
-@@ -1261,9 +1415,10 @@ mt7915_tm_set_ipi(struct mt7915_phy *phy)
+@@ -1262,9 +1417,10 @@ mt7915_tm_set_ipi(struct mt7915_phy *phy)
  
  static int
  mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
@@ -1554,7 +1555,7 @@
  	struct mt7915_mcu_tx req = {
  		.valid = true,
  		.mode = tx_cmd,
-@@ -1271,6 +1426,9 @@ mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
+@@ -1272,6 +1428,9 @@ mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
  	};
  	struct edca *e = &req.edca[0];
  
@@ -1564,7 +1565,7 @@
  	e->queue = qid + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS;
  	e->set = WMM_PARAM_SET;
  
-@@ -1283,17 +1441,19 @@ mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
+@@ -1284,17 +1443,19 @@ mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min,
  }
  
  static int
@@ -1585,7 +1586,7 @@
  	u8 band = phy->mt76->band_idx;
  	u32 i2t_time, tr2t_time, txv_time;
  	u16 cw = 0;
-@@ -1307,6 +1467,7 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode)
+@@ -1308,6 +1469,7 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode)
  	ipg -= sig_ext;
  
  	if (ipg <= (TM_MAX_SIFS + slot_time)) {
@@ -1593,7 +1594,7 @@
  		sifs = ipg - slot_time;
  	} else {
  		u32 val = (ipg + slot_time) / slot_time;
-@@ -1342,10 +1503,12 @@ done:
+@@ -1343,10 +1505,12 @@ done:
  
  	mt7915_tm_set_slot_time(phy, slot_time, sifs);
  
@@ -1608,7 +1609,7 @@
  }
  
  static int
-@@ -1544,7 +1707,7 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
+@@ -1545,7 +1709,7 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
  
  		phy->mt76->test.aid = 0;
  		phy->mt76->test.tx_mpdu_len = 0;
@@ -1617,7 +1618,7 @@
  		mt7915_tm_set_entry(phy);
  	} else {
  		INIT_DELAYED_WORK(&phy->ipi_work, mt7915_tm_ipi_work);
-@@ -1729,7 +1892,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+@@ -1730,7 +1894,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
  		u32 tx_time = td->tx_time, ipg = td->tx_ipg;
  		u8 duty_cycle = td->tx_duty_cycle;
  
@@ -1626,7 +1627,7 @@
  			mt7915_tm_update_channel(phy);
  
  		if (td->tx_spe_idx)
-@@ -1744,7 +1907,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+@@ -1745,7 +1909,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
  			if (duty_cycle < 100)
  				tx_time = duty_cycle * ipg / (100 - duty_cycle);
  		}
@@ -1635,7 +1636,7 @@
  		mt7915_tm_set_tx_len(phy, tx_time);
  
  		if (ipg)
-@@ -1763,6 +1926,9 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
+@@ -1764,6 +1928,9 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
  		mt7915_tm_tx_frames_mu(phy, en);
  
  	mt7915_tm_set_trx(phy, TM_MAC_TX, en);
@@ -1645,7 +1646,7 @@
  }
  
  static int
-@@ -1854,7 +2020,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
+@@ -1855,7 +2022,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
  	mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false);
  
  	if (en) {
@@ -1654,7 +1655,7 @@
  			mt7915_tm_update_channel(phy);
  		if (td->aid)
  			mt7915_tm_set_rx_user_idx(phy, td->aid);
-@@ -1871,6 +2037,9 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
+@@ -1872,6 +2039,9 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
  		mt7915_tm_set_muru_aid(phy, en ? td->aid : 0xf800);
  
  	mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en);
@@ -1664,7 +1665,7 @@
  }
  
  static int
-@@ -1930,34 +2099,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
+@@ -1931,34 +2101,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
  		rate_idx = sband->bitrates[idx].hw_value & 0xff;
  	}
  
@@ -1701,7 +1702,7 @@
  	rateval =  mode << 6 | rate_idx;
  	tx_cont->rateval = cpu_to_le16(rateval);
 diff --git a/mt7915/testmode.h b/mt7915/testmode.h
-index 75698261..5aba13cf 100644
+index 7569826..5aba13c 100644
 --- a/mt7915/testmode.h
 +++ b/mt7915/testmode.h
 @@ -311,137 +311,7 @@ struct mt7915_tm_muru {
@@ -1845,7 +1846,7 @@
  
  #endif
 diff --git a/testmode.c b/testmode.c
-index babae8d4..4ea58777 100644
+index babae8d..4ea5877 100644
 --- a/testmode.c
 +++ b/testmode.c
 @@ -195,6 +195,7 @@ mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len,
@@ -1857,7 +1858,7 @@
  	memcpy(hdr->addr2, addr[1], ETH_ALEN);
  	memcpy(hdr->addr3, addr[2], ETH_ALEN);
 diff --git a/testmode.h b/testmode.h
-index 97e75964..e13920ec 100644
+index 97e7596..e13920e 100644
 --- a/testmode.h
 +++ b/testmode.h
 @@ -299,7 +299,10 @@ enum mt76_testmode_cfg {
@@ -1885,7 +1886,7 @@
  	/* keep last */
  	NUM_MT76_TM_TXBF_ACT,
 diff --git a/tools/fields.c b/tools/fields.c
-index fc01043e..7caacb94 100644
+index fc01043..7caacb9 100644
 --- a/tools/fields.c
 +++ b/tools/fields.c
 @@ -33,7 +33,10 @@ static const char * const testmode_tx_mode[] = {
@@ -1913,5 +1914,5 @@
  
  static const char * const testmode_offchan_bw[] = {
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch b/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch
new file mode 100644
index 0000000..a6eb593
--- /dev/null
+++ b/recipes-wifi/linux-mt76/files/patches/1035-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch
@@ -0,0 +1,25 @@
+From faa6a52a5f0429d401b7615cc34734c6f5b8fe01 Mon Sep 17 00:00:00 2001
+From: MeiChia Chiu <meichia.chiu@mediatek.com>
+Date: Tue, 8 Aug 2023 11:20:58 +0800
+Subject: [PATCH] wifi: mt76: mt7915: Establish BA in VO queue
+
+---
+ mt76_connac_mac.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/mt76_connac_mac.c b/mt76_connac_mac.c
+index ff3cefa..b87b733 100644
+--- a/mt76_connac_mac.c
++++ b/mt76_connac_mac.c
+@@ -1126,8 +1126,6 @@ void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+ 		return;
+ 
+ 	tid = le32_get_bits(txwi[1], MT_TXD1_TID);
+-	if (tid >= 6) /* skip VO queue */
+-		return;
+ 
+ 	val = le32_to_cpu(txwi[2]);
+ 	fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 |
+-- 
+2.39.0
+
diff --git a/recipes-wifi/linux-mt76/files/patches/patches.inc b/recipes-wifi/linux-mt76/files/patches/patches.inc
index 693e0a0..a6fede3 100644
--- a/recipes-wifi/linux-mt76/files/patches/patches.inc
+++ b/recipes-wifi/linux-mt76/files/patches/patches.inc
@@ -8,6 +8,7 @@
     file://0005-wifi-mt76-mt7915-add-pc-stack-dump-for-WM-s-coredump.patch \
     file://0006-wifi-mt76-mt7915-update-mpdu-density-in-2-5g-capabil.patch \
     file://0007-wifi-mt76-mt7915-move-temperature-margin-check-to-mt.patch \
+    file://0008-wifi-mt76-mt7915-fix-tx-emission-issue-during-bootup.patch \
     file://0009-wifi-mt76-mt7915-fix-txpower-issues.patch \
     file://0010-wifi-mt76-mt7915-rework-init-txpower.patch \
     file://0999-wifi-mt76-mt7915-build-pass-for-Linux-Kernel-5.4-fix.patch \
@@ -46,6 +47,7 @@
     file://1032-wifi-mt76-mt7915-remove-BW160-support.patch \
     file://1033-wifi-mt76-mt7915-add-txpower-info-dump-support.patch \
     file://1034-wifi-mt76-mt7915-report-tx-and-rx-byte-to-tpt_led-wh.patch \
+    file://1035-wifi-mt76-mt7915-Establish-BA-in-VO-queue.patch \
     file://2000-wifi-mt76-mt7915-wed-add-wed-tx-support.patch \
     file://2001-wifi-mt76-mt7915-wed-add-wds-support-when-wed-is-ena.patch \
     file://2002-wifi-mt76-mt7915-wed-add-fill-receive-path-to-report.patch \
diff --git a/recipes-wifi/ubus/ubus_git.bb b/recipes-wifi/ubus/ubus_git.bb
index 3e99c98..3b4ce84 100644
--- a/recipes-wifi/ubus/ubus_git.bb
+++ b/recipes-wifi/ubus/ubus_git.bb
@@ -10,7 +10,7 @@
           file://0001-lua5.3-support.patch \
           "
 
-SRCREV = "9913aa61de739e3efe067a2d186021c20bcd65e2"
+SRCREV = "f787c97b34894a38b15599886cacbca01271684f"
 
 PV = "git${SRCPV}"
 
diff --git a/recipes-wifi/ucode/files/0001-change-cmakelist.patch b/recipes-wifi/ucode/files/0001-change-cmakelist.patch
new file mode 100644
index 0000000..2770833
--- /dev/null
+++ b/recipes-wifi/ucode/files/0001-change-cmakelist.patch
@@ -0,0 +1,15 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index d8dd34a..07e37a5 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -23,8 +23,8 @@ OPTION(FS_SUPPORT "Filesystem plugin support" ON)
+ OPTION(MATH_SUPPORT "Math plugin support" ON)
+ OPTION(UBUS_SUPPORT "Ubus plugin support" ON)
+ OPTION(UCI_SUPPORT "UCI plugin support" ON)
+-OPTION(RTNL_SUPPORT "Route Netlink plugin support" ${LINUX})
+-OPTION(NL80211_SUPPORT "Wireless Netlink plugin support" ${LINUX})
++OPTION(RTNL_SUPPORT "Route Netlink plugin support" ON)
++OPTION(NL80211_SUPPORT "Wireless Netlink plugin support" ON)
+ OPTION(RESOLV_SUPPORT "NS resolve plugin support" ON)
+ OPTION(STRUCT_SUPPORT "Struct plugin support" ON)
+ OPTION(ULOOP_SUPPORT "Uloop plugin support" ON)
diff --git a/recipes-wifi/ucode/ucode_git.bb b/recipes-wifi/ucode/ucode_git.bb
new file mode 100644
index 0000000..a2a3ce7
--- /dev/null
+++ b/recipes-wifi/ucode/ucode_git.bb
@@ -0,0 +1,40 @@
+DESCRIPTION = "Tiny scripting and templating language"
+SECTION = "lang"
+LICENSE_NAME = "ISC"
+LICENSE = "${LICENSE_NAME}"
+LIC_FILES_CHKSUM = "file://LICENSE;md5=b42eb47dc3802282b0d1be1bc8f5336c"
+
+SRC_URI = " \
+    git://git@github.com/jow-/ucode.git;protocol=https \
+    file://0001-change-cmakelist.patch \
+"
+
+SRCREV = "c7d84aae09691a99ae3db427c0b2463732ef84f4"
+PV = "git${SRCPV}"
+
+S = "${WORKDIR}/git"
+
+
+DEPENDS += "libnl-tiny libubox ubus uci"
+
+INSANE_SKIP_${PN} += "file-rdeps dev-deps dev-so"
+FILES_SOLIBSDEV = ""
+
+FILES_${PN}-dev = "${includedir}/*"
+FILES_${PN} = "${bindir}/ucode ${libdir}/libucode.so* ${libdir}/ucode/*"
+
+inherit cmake pkgconfig python3native  
+
+
+EXTRA_OECMAKE = "\
+	-DFS_SUPPORT=ON \
+	-DMATH_SUPPORT=ON \
+	-DNL80211_SUPPORT=ON \
+	-DRESOLV_SUPPORT=ON \
+	-DRTNL_SUPPORT=ON \
+	-DSTRUCT_SUPPORT=ON \
+	-DUBUS_SUPPORT=OFF \
+	-DUCI_SUPPORT=ON \
+	-DULOOP_SUPPORT=ON \
+	"
+
diff --git a/recipes-wifi/wpa-supplicant/files/002-rdkb-add-ucode-support.patch b/recipes-wifi/wpa-supplicant/files/002-rdkb-add-ucode-support.patch
new file mode 100644
index 0000000..16e099a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/002-rdkb-add-ucode-support.patch
@@ -0,0 +1,21 @@
+--- a/hostapd/Makefile	2023-08-14 14:07:55.808647300 +0800
++++ b/hostapd/Makefile	2023-08-14 14:09:14.222019605 +0800
+@@ -182,6 +182,7 @@
+ OBJS += ../src/utils/ucode.o
+ OBJS += ../src/ap/ucode.o
+ NEED_ULOOP:=y
++LIBS += -lblobmsg_json -lucode
+ endif
+ 
+ ifdef NEED_ULOOP
+
+--- a/wpa_supplicant/Makefile	2023-08-14 17:51:44.351944100 +0800
++++ b/wpa_supplicant/Makefile	2023-08-14 17:54:54.232111091 +0800
+@@ -206,6 +206,7 @@
+ OBJS += ../src/utils/ucode.o
+ OBJS += ucode.o
+ NEED_ULOOP:=y
++LIBS += -lblobmsg_json -lucode
+ endif
+ 
+ ifdef NEED_ULOOP
\ No newline at end of file
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
similarity index 66%
copy from recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch
copy to recipes-wifi/wpa-supplicant/files/patches-2.10.3/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
index 7196118..7b0435a 100644
--- a/recipes-wifi/hostapd/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/180-BSS-coloring-fix-CCA-with-multiple-BSS.patch
@@ -1,23 +1,32 @@
-From ce585467d784d1015b5a40ec09895d0949690b04 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 3 May 2023 14:55:18 +0800
-Subject: [PATCH 29/32] hostapd: mtk: Fix CCA issue
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 7 Aug 2023 21:55:57 +0200
+Subject: [PATCH] BSS coloring: fix CCA with multiple BSS
 
-When receiving CCA-related nl80211 commands, hostapd used to work on
-struct wpa_driver_nl80211_data, whose ctx always points to
-hostpad_iface->bss[0]. However, CCA commands are sent on a per-BSS based.
-This patch makes hostapd handle CCA-related commands on a per-BSS based.
+Pass bss->ctx instead of drv->ctx in order to avoid multiple reports for
+the first bss. The first report would otherwise clear hapd->cca_color and
+subsequent reports would cause the iface bss color to be set to 0.
+In order to avoid any issues with cancellations, only overwrite the color
+based on hapd->cca_color if it was actually set.
 
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
+Fixes: 33c4dd26cd11 ("BSS coloring: Handle the collision and CCA events coming from the kernel")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
 ---
- src/drivers/driver_nl80211_event.c | 24 ++++++++++++------------
- 1 file changed, 12 insertions(+), 12 deletions(-)
 
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 3d0d9b7bb..ab988fad7 100644
+--- a/src/ap/drv_callbacks.c
++++ b/src/ap/drv_callbacks.c
+@@ -2260,7 +2260,8 @@ void wpa_supplicant_event(void *ctx, enu
+ 	case EVENT_CCA_NOTIFY:
+ 		wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ 			   hapd->conf->iface);
+-		hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
++		if (hapd->cca_color)
++			hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+ 		hostapd_cleanup_cca_params(hapd);
+ 		break;
+ #endif /* CONFIG_IEEE80211AX */
 --- a/src/drivers/driver_nl80211_event.c
 +++ b/src/drivers/driver_nl80211_event.c
-@@ -3662,7 +3662,7 @@ static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv,
+@@ -3653,7 +3653,7 @@ static void nl80211_assoc_comeback(struc
  
  #ifdef CONFIG_IEEE80211AX
  
@@ -26,7 +35,7 @@
  					 struct nlattr *tb[])
  {
  	union wpa_event_data data;
-@@ -3676,37 +3676,37 @@ static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
+@@ -3667,37 +3667,37 @@ static void nl80211_obss_color_collision
  
  	wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
  		   (long long unsigned int) data.bss_color_collision.bitmap);
@@ -71,7 +80,7 @@
  }
  
  #endif /* CONFIG_IEEE80211AX */
-@@ -3968,16 +3968,16 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
+@@ -3957,16 +3957,16 @@ static void do_process_drv_event(struct
  		break;
  #ifdef CONFIG_IEEE80211AX
  	case NL80211_CMD_OBSS_COLOR_COLLISION:
@@ -92,6 +101,3 @@
  		break;
  #endif /* CONFIG_IEEE80211AX */
  	default:
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch
index f7e797a..8ebbed0 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/200-multicall.patch
@@ -189,7 +189,7 @@
  {
  	struct hostapd_data *hapd = ctx;
  #ifndef CONFIG_NO_STDOUT_DEBUG
-@@ -2271,7 +2271,7 @@ void wpa_supplicant_event(void *ctx, enu
+@@ -2272,7 +2272,7 @@ void wpa_supplicant_event(void *ctx, enu
  }
  
  
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch
index f393634..f420c18 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/600-ubus_support.patch
@@ -1,11 +1,12 @@
 --- a/hostapd/Makefile
 +++ b/hostapd/Makefile
-@@ -166,6 +166,11 @@ OBJS += ../src/common/hw_features_common
+@@ -166,6 +166,12 @@ OBJS += ../src/common/hw_features_common
  
  OBJS += ../src/eapol_auth/eapol_auth_sm.o
  
 +ifdef CONFIG_UBUS
 +CFLAGS += -DUBUS_SUPPORT
++OBJS += ../src/utils/uloop.o
 +OBJS += ../src/ap/ubus.o
 +LIBS += -lubox -lubus
 +endif
@@ -22,15 +23,6 @@
  
  #define OCE_STA_CFON_ENABLED(hapd) \
  	((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -92,7 +93,7 @@ struct hapd_interfaces {
- #ifdef CONFIG_CTRL_IFACE_UDP
-        unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
- #endif /* CONFIG_CTRL_IFACE_UDP */
--
-+	struct ubus_object ubus;
- };
- 
- enum hostapd_chan_status {
 @@ -184,6 +185,7 @@ struct hostapd_data {
  	struct hostapd_iface *iface;
  	struct hostapd_config *iconf;
@@ -330,20 +322,21 @@
  
 --- a/wpa_supplicant/Makefile
 +++ b/wpa_supplicant/Makefile
-@@ -194,6 +194,12 @@ ifdef CONFIG_EAPOL_TEST
+@@ -194,6 +194,13 @@ ifdef CONFIG_EAPOL_TEST
  CFLAGS += -Werror -DEAPOL_TEST
  endif
  
 +ifdef CONFIG_UBUS
 +CFLAGS += -DUBUS_SUPPORT
 +OBJS += ubus.o
++OBJS += ../src/utils/uloop.o
 +LIBS += -lubox -lubus
 +endif
 +
  ifdef CONFIG_CODE_COVERAGE
  CFLAGS += -O0 -fprofile-arcs -ftest-coverage
  LIBS += -lgcov
-@@ -989,6 +995,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
+@@ -989,6 +996,9 @@ ifdef CONFIG_CTRL_IFACE_MIB
  CFLAGS += -DCONFIG_CTRL_IFACE_MIB
  endif
  OBJS += ../src/ap/ctrl_iface_ap.o
@@ -432,24 +425,6 @@
  	if (wpa_s->conf->wps_cred_processing == 1)
  		return 0;
  
---- a/hostapd/main.c
-+++ b/hostapd/main.c
-@@ -991,6 +991,7 @@ int main(int argc, char *argv[])
- 	}
- 
- 	hostapd_global_ctrl_iface_init(&interfaces);
-+	hostapd_ubus_add(&interfaces);
- 
- 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
- 		wpa_printf(MSG_ERROR, "Failed to start eloop");
-@@ -1000,6 +1001,7 @@ int main(int argc, char *argv[])
- 	ret = 0;
- 
-  out:
-+	hostapd_ubus_free(&interfaces);
- 	hostapd_global_ctrl_iface_deinit(&interfaces);
- 	/* Deinitialize all interfaces */
- 	for (i = 0; i < interfaces.count; i++) {
 --- a/wpa_supplicant/main.c
 +++ b/wpa_supplicant/main.c
 @@ -204,7 +204,7 @@ int main(int argc, char *argv[])
@@ -623,3 +598,151 @@
  	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
  		    pos, end - pos);
  }
+--- a/src/utils/eloop.c
++++ b/src/utils/eloop.c
+@@ -77,6 +77,9 @@ struct eloop_sock_table {
+ struct eloop_data {
+ 	int max_sock;
+ 
++	eloop_timeout_poll_handler timeout_poll_cb;
++	eloop_poll_handler poll_cb;
++
+ 	size_t count; /* sum of all table counts */
+ #ifdef CONFIG_ELOOP_POLL
+ 	size_t max_pollfd_map; /* number of pollfds_map currently allocated */
+@@ -1121,6 +1124,12 @@ void eloop_run(void)
+ 				os_reltime_sub(&timeout->time, &now, &tv);
+ 			else
+ 				tv.sec = tv.usec = 0;
++		}
++
++		if (eloop.timeout_poll_cb && eloop.timeout_poll_cb(&tv, !!timeout))
++			timeout = (void *)1;
++
++		if (timeout) {
+ #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
+ 			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+ #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+@@ -1190,7 +1199,8 @@ void eloop_run(void)
+ 		eloop.exceptions.changed = 0;
+ 
+ 		eloop_process_pending_signals();
+-
++		if (eloop.poll_cb)
++			eloop.poll_cb();
+ 
+ 		/* check if some registered timeouts have occurred */
+ 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+@@ -1252,6 +1262,14 @@ out:
+ 	return;
+ }
+ 
++int eloop_register_cb(eloop_poll_handler poll_cb,
++		      eloop_timeout_poll_handler timeout_cb)
++{
++	eloop.poll_cb = poll_cb;
++	eloop.timeout_poll_cb = timeout_cb;
++
++	return 0;
++}
+ 
+ void eloop_terminate(void)
+ {
+--- a/src/utils/eloop.h
++++ b/src/utils/eloop.h
+@@ -65,6 +65,9 @@ typedef void (*eloop_timeout_handler)(vo
+  */
+ typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+ 
++typedef bool (*eloop_timeout_poll_handler)(struct os_reltime *tv, bool tv_set);
++typedef void (*eloop_poll_handler)(void);
++
+ /**
+  * eloop_init() - Initialize global event loop data
+  * Returns: 0 on success, -1 on failure
+@@ -73,6 +76,9 @@ typedef void (*eloop_signal_handler)(int
+  */
+ int eloop_init(void);
+ 
++int eloop_register_cb(eloop_poll_handler poll_cb,
++		      eloop_timeout_poll_handler timeout_cb);
++
+ /**
+  * eloop_register_read_sock - Register handler for read events
+  * @sock: File descriptor number for the socket
+@@ -320,6 +326,8 @@ int eloop_register_signal_reconfig(eloop
+  */
+ int eloop_sock_requeue(void);
+ 
++void eloop_add_uloop(void);
++
+ /**
+  * eloop_run - Start the event loop
+  *
+--- /dev/null
++++ b/src/utils/uloop.c
+@@ -0,0 +1,64 @@
++#include <libubox/uloop.h>
++#include "includes.h"
++#include "common.h"
++#include "eloop.h"
++
++static void eloop_uloop_event_cb(int sock, void *eloop_ctx, void *sock_ctx)
++{
++}
++
++static void eloop_uloop_fd_cb(struct uloop_fd *fd, unsigned int events)
++{
++	unsigned int changed = events ^ fd->flags;
++
++	if (changed & ULOOP_READ) {
++		if (events & ULOOP_READ)
++			eloop_register_sock(fd->fd, EVENT_TYPE_READ, eloop_uloop_event_cb, fd, fd);
++		else
++			eloop_unregister_sock(fd->fd, EVENT_TYPE_READ);
++	}
++
++	if (changed & ULOOP_WRITE) {
++		if (events & ULOOP_WRITE)
++			eloop_register_sock(fd->fd, EVENT_TYPE_WRITE, eloop_uloop_event_cb, fd, fd);
++		else
++			eloop_unregister_sock(fd->fd, EVENT_TYPE_WRITE);
++	}
++}
++
++static bool uloop_timeout_poll_handler(struct os_reltime *tv, bool tv_set)
++{
++	struct os_reltime tv_uloop;
++	int timeout_ms = uloop_get_next_timeout();
++
++	if (timeout_ms < 0)
++		return false;
++
++	tv_uloop.sec = timeout_ms / 1000;
++	tv_uloop.usec = (timeout_ms % 1000) * 1000;
++
++	if (!tv_set || os_reltime_before(&tv_uloop, tv)) {
++		*tv = tv_uloop;
++		return true;
++	}
++
++	return false;
++}
++
++static void uloop_poll_handler(void)
++{
++	uloop_run_timeout(0);
++}
++
++void eloop_add_uloop(void)
++{
++	static bool init_done = false;
++
++	if (!init_done) {
++		uloop_init();
++		uloop_fd_set_cb = eloop_uloop_fd_cb;
++		init_done = true;
++	}
++
++	eloop_register_cb(uloop_poll_handler, uloop_timeout_poll_handler);
++}
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/601-ucode_support.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/601-ucode_support.patch
new file mode 100644
index 0000000..ff1a9dd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/601-ucode_support.patch
@@ -0,0 +1,234 @@
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -168,9 +168,21 @@ OBJS += ../src/eapol_auth/eapol_auth_sm.
+ 
+ ifdef CONFIG_UBUS
+ CFLAGS += -DUBUS_SUPPORT
+-OBJS += ../src/utils/uloop.o
+ OBJS += ../src/ap/ubus.o
+-LIBS += -lubox -lubus
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ../src/ap/ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
++OBJS += ../src/utils/uloop.o
++LIBS += -lubox
+ endif
+ 
+ ifdef CONFIG_CODE_COVERAGE
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -991,6 +991,7 @@ int main(int argc, char *argv[])
+ 	}
+ 
+ 	hostapd_global_ctrl_iface_init(&interfaces);
++	hostapd_ucode_init(&interfaces);
+ 
+ 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
+ 		wpa_printf(MSG_ERROR, "Failed to start eloop");
+@@ -1000,6 +1001,7 @@ int main(int argc, char *argv[])
+ 	ret = 0;
+ 
+  out:
++	hostapd_ucode_free();
+ 	hostapd_global_ctrl_iface_deinit(&interfaces);
+ 	/* Deinitialize all interfaces */
+ 	for (i = 0; i < interfaces.count; i++) {
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -19,6 +19,7 @@
+ #include "ap_config.h"
+ #include "drivers/driver.h"
+ #include "ubus.h"
++#include "ucode.h"
+ 
+ #define OCE_STA_CFON_ENABLED(hapd) \
+ 	((hapd->conf->oce & OCE_STA_CFON) && \
+@@ -51,6 +52,10 @@ struct hapd_interfaces {
+ 	struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ 	int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ 	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
++	int (*ctrl_iface_recv)(struct hostapd_data *hapd,
++			       char *buf, char *reply, int reply_size,
++			       struct sockaddr_storage *from,
++			       socklen_t fromlen);
+ 	int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ 				  int (*cb)(struct hostapd_iface *iface,
+ 					    void *ctx), void *ctx);
+@@ -186,6 +191,7 @@ struct hostapd_data {
+ 	struct hostapd_config *iconf;
+ 	struct hostapd_bss_config *conf;
+ 	struct hostapd_ubus_bss ubus;
++	struct hostapd_ucode_bss ucode;
+ 	int interface_added; /* virtual interface added for this BSS */
+ 	unsigned int started:1;
+ 	unsigned int disabled:1;
+@@ -506,6 +512,7 @@ struct hostapd_sta_info {
+  */
+ struct hostapd_iface {
+ 	struct hapd_interfaces *interfaces;
++	struct hostapd_ucode_iface ucode;
+ 	void *owner;
+ 	char *config_fname;
+ 	struct hostapd_config *conf;
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -276,6 +276,8 @@ int hostapd_reload_config(struct hostapd
+ 	size_t j;
+ 	int i;
+ 
++	hostapd_ucode_reload_bss(hapd, reconf);
++
+ 	if (iface->config_fname == NULL) {
+ 		/* Only in-memory config in use - assume it has been updated */
+ 		hostapd_clear_old(iface);
+@@ -455,6 +457,7 @@ void hostapd_free_hapd_data(struct hosta
+ 	hapd->beacon_set_done = 0;
+ 
+ 	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
++	hostapd_ucode_free_bss(hapd);
+ 	hostapd_ubus_free_bss(hapd);
+ 	accounting_deinit(hapd);
+ 	hostapd_deinit_wpa(hapd);
+@@ -619,6 +622,7 @@ void hostapd_cleanup_iface_partial(struc
+ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+ {
+ 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
++	hostapd_ucode_free_iface(iface);
+ 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ 	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ 			     NULL);
+@@ -1209,6 +1213,7 @@ static int hostapd_start_beacon(struct h
+ 		hapd->driver->set_operstate(hapd->drv_priv, 1);
+ 
+ 	hostapd_ubus_add_bss(hapd);
++	hostapd_ucode_add_bss(hapd);
+ 
+ 	return 0;
+ }
+--- a/wpa_supplicant/Makefile
++++ b/wpa_supplicant/Makefile
+@@ -197,8 +197,20 @@ endif
+ ifdef CONFIG_UBUS
+ CFLAGS += -DUBUS_SUPPORT
+ OBJS += ubus.o
++LIBS += -lubus
++NEED_ULOOP:=y
++endif
++
++ifdef CONFIG_UCODE
++CFLAGS += -DUCODE_SUPPORT
++OBJS += ../src/utils/ucode.o
++OBJS += ucode.o
++NEED_ULOOP:=y
++endif
++
++ifdef NEED_ULOOP
+ OBJS += ../src/utils/uloop.o
+-LIBS += -lubox -lubus
++LIBS += -lubox
+ endif
+ 
+ ifdef CONFIG_CODE_COVERAGE
+@@ -999,6 +1011,9 @@ OBJS += ../src/ap/ctrl_iface_ap.o
+ ifdef CONFIG_UBUS
+ OBJS += ../src/ap/ubus.o
+ endif
++ifdef CONFIG_UCODE
++OBJS += ../src/ap/ucode.o
++endif
+ endif
+ 
+ CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -7636,6 +7636,7 @@ struct wpa_supplicant * wpa_supplicant_a
+ #endif /* CONFIG_P2P */
+ 
+ 	wpas_ubus_add_bss(wpa_s);
++	wpas_ucode_add_bss(wpa_s);
+ 
+ 	return wpa_s;
+ }
+@@ -7663,6 +7664,7 @@ int wpa_supplicant_remove_iface(struct w
+ 	struct wpa_supplicant *parent = wpa_s->parent;
+ #endif /* CONFIG_MESH */
+ 
++	wpas_ucode_free_bss(wpa_s);
+ 	wpas_ubus_free_bss(wpa_s);
+ 
+ 	/* Remove interface from the global list of interfaces */
+@@ -7973,6 +7975,7 @@ struct wpa_global * wpa_supplicant_init(
+ 
+ 	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ 			       wpas_periodic, global, NULL);
++	wpas_ucode_init(global);
+ 
+ 	return global;
+ }
+@@ -8011,12 +8014,8 @@ int wpa_supplicant_run(struct wpa_global
+ 	eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+ 	eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+ 
+-	wpas_ubus_add(global);
+-
+ 	eloop_run();
+ 
+-	wpas_ubus_free(global);
+-
+ 	return 0;
+ }
+ 
+@@ -8049,6 +8048,8 @@ void wpa_supplicant_deinit(struct wpa_gl
+ 
+ 	wpas_notify_supplicant_deinitialized(global);
+ 
++	wpas_ucode_free();
++
+ 	eap_peer_unregister_methods();
+ #ifdef CONFIG_AP
+ 	eap_server_unregister_methods();
+--- a/wpa_supplicant/wpa_supplicant_i.h
++++ b/wpa_supplicant/wpa_supplicant_i.h
+@@ -22,6 +22,7 @@
+ #include "wmm_ac.h"
+ #include "pasn/pasn_common.h"
+ #include "ubus.h"
++#include "ucode.h"
+ 
+ extern const char *const wpa_supplicant_version;
+ extern const char *const wpa_supplicant_license;
+@@ -659,6 +660,7 @@ struct wpa_supplicant {
+ 	unsigned char perm_addr[ETH_ALEN];
+ 	char ifname[100];
+ 	struct wpas_ubus_bss ubus;
++	struct wpas_ucode_bss ucode;
+ #ifdef CONFIG_MATCH_IFACE
+ 	int matched;
+ #endif /* CONFIG_MATCH_IFACE */
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -4921,6 +4921,7 @@ try_again:
+ 		return -1;
+ 	}
+ 
++	interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+ 
+ 	return 0;
+@@ -5022,6 +5023,7 @@ fail:
+ 	os_free(fname);
+ 
+ 	interface->global_ctrl_sock = s;
++	interface->ctrl_iface_recv = hostapd_ctrl_iface_receive_process;
+ 	eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+ 				 interface, NULL);
+ 
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/700-wifi-reload.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/700-wifi-reload.patch
index 0c76276..c0e7e4d 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/700-wifi-reload.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/700-wifi-reload.patch
@@ -40,6 +40,15 @@
  	int rts_threshold;
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
+@@ -127,7 +127,7 @@ void hostapd_reconfig_encryption(struct
+ }
+ 
+ 
+-static void hostapd_reload_bss(struct hostapd_data *hapd)
++void hostapd_reload_bss(struct hostapd_data *hapd)
+ {
+ 	struct hostapd_ssid *ssid;
+ 
 @@ -255,6 +255,10 @@ static int hostapd_iface_conf_changed(st
  {
  	size_t i;
@@ -60,7 +69,7 @@
  {
  	struct hapd_interfaces *interfaces = iface->interfaces;
  	struct hostapd_data *hapd = iface->bss[0];
-@@ -296,6 +300,9 @@ int hostapd_reload_config(struct hostapd
+@@ -298,6 +302,9 @@ int hostapd_reload_config(struct hostapd
  		char *fname;
  		int res;
  
@@ -70,7 +79,7 @@
  		hostapd_clear_old(iface);
  
  		wpa_printf(MSG_DEBUG,
-@@ -322,6 +329,24 @@ int hostapd_reload_config(struct hostapd
+@@ -324,6 +331,24 @@ int hostapd_reload_config(struct hostapd
  			wpa_printf(MSG_ERROR,
  				   "Failed to enable interface on config reload");
  		return res;
@@ -95,7 +104,7 @@
  	}
  	iface->conf = newconf;
  
-@@ -338,6 +363,12 @@ int hostapd_reload_config(struct hostapd
+@@ -340,6 +365,12 @@ int hostapd_reload_config(struct hostapd
  
  	for (j = 0; j < iface->num_bss; j++) {
  		hapd = iface->bss[j];
@@ -108,7 +117,17 @@
  		if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
  		    os_strcmp(hapd->conf->config_id,
  			      newconf->bss[j]->config_id) != 0)
-@@ -2700,6 +2731,10 @@ hostapd_alloc_bss_data(struct hostapd_if
+@@ -1236,8 +1267,7 @@ static int hostapd_start_beacon(struct h
+  * initialized. Most of the modules that are initialized here will be
+  * deinitialized in hostapd_cleanup().
+  */
+-static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+-			     bool start_beacon)
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
+ {
+ 	struct hostapd_bss_config *conf = hapd->conf;
+ 	u8 ssid[SSID_MAX_LEN + 1];
+@@ -2705,6 +2735,10 @@ hostapd_alloc_bss_data(struct hostapd_if
  	hapd->iconf = conf;
  	hapd->conf = bss;
  	hapd->iface = hapd_iface;
@@ -119,9 +138,18 @@
  	if (conf)
  		hapd->driver = conf->driver;
  	hapd->ctrl_sock = -1;
+@@ -2723,7 +2757,7 @@ hostapd_alloc_bss_data(struct hostapd_if
+ }
+ 
+ 
+-static void hostapd_bss_deinit(struct hostapd_data *hapd)
++void hostapd_bss_deinit(struct hostapd_data *hapd)
+ {
+ 	if (!hapd)
+ 		return;
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -47,7 +47,7 @@ struct mesh_conf;
+@@ -48,7 +48,7 @@ struct mesh_conf;
  struct hostapd_iface;
  
  struct hapd_interfaces {
@@ -130,23 +158,33 @@
  	struct hostapd_config * (*config_read_cb)(const char *config_fname);
  	int (*ctrl_iface_init)(struct hostapd_data *hapd);
  	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-@@ -186,6 +186,7 @@ struct hostapd_data {
- 	struct hostapd_config *iconf;
+@@ -192,6 +192,7 @@ struct hostapd_data {
  	struct hostapd_bss_config *conf;
  	struct hostapd_ubus_bss ubus;
+ 	struct hostapd_ucode_bss ucode;
 +	char *config_id;
  	int interface_added; /* virtual interface added for this BSS */
  	unsigned int started:1;
  	unsigned int disabled:1;
-@@ -689,7 +690,7 @@ struct hostapd_iface {
+@@ -696,7 +697,9 @@ struct hostapd_iface {
  int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
  			       int (*cb)(struct hostapd_iface *iface,
  					 void *ctx), void *ctx);
 -int hostapd_reload_config(struct hostapd_iface *iface);
 +int hostapd_reload_config(struct hostapd_iface *iface, int reconf);
++void hostapd_reload_bss(struct hostapd_data *hapd);
++void hostapd_bss_deinit(struct hostapd_data *hapd);
  void hostapd_reconfig_encryption(struct hostapd_data *hapd);
  struct hostapd_data *
  hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+@@ -713,6 +716,7 @@ struct hostapd_iface * hostapd_init(stru
+ struct hostapd_iface *
+ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ 			   const char *config_fname, int debug);
++int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
+ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ 			   int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
 --- a/src/drivers/driver_nl80211.c
 +++ b/src/drivers/driver_nl80211.c
 @@ -5322,6 +5322,9 @@ static int wpa_driver_nl80211_set_ap(voi
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/701-reload_config_inline.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/701-reload_config_inline.patch
new file mode 100644
index 0000000..071281e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/701-reload_config_inline.patch
@@ -0,0 +1,33 @@
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4814,7 +4814,12 @@ struct hostapd_config * hostapd_config_r
+ 	int errors = 0;
+ 	size_t i;
+ 
+-	f = fopen(fname, "r");
++	if (!strncmp(fname, "data:", 5)) {
++		f = fmemopen((void *)(fname + 5), strlen(fname + 5), "r");
++		fname = "<inline>";
++	} else {
++		f = fopen(fname, "r");
++	}
+ 	if (f == NULL) {
+ 		wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+ 			   "for reading.", fname);
+--- a/wpa_supplicant/config_file.c
++++ b/wpa_supplicant/config_file.c
+@@ -326,8 +326,13 @@ struct wpa_config * wpa_config_read(cons
+ 	while (cred_tail && cred_tail->next)
+ 		cred_tail = cred_tail->next;
+ 
++	if (!strncmp(name, "data:", 5)) {
++		f = fmemopen((void *)(name + 5), strlen(name + 5), "r");
++		name = "<inline>";
++	} else {
++		f = fopen(name, "r");
++	}
+ 	wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+-	f = fopen(name, "r");
+ 	if (f == NULL) {
+ 		wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ 			   "error: %s", name, strerror(errno));
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch
index 0bb00f9..5f40aab 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/720-iface_max_num_sta.patch
@@ -17,7 +17,7 @@
  	} else if (os_strcmp(buf, "extended_key_id") == 0) {
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -734,6 +734,7 @@ void hostapd_cleanup_cs_params(struct ho
+@@ -744,6 +744,7 @@ void hostapd_cleanup_cs_params(struct ho
  void hostapd_periodic_iface(struct hostapd_iface *iface);
  int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
  void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
index 124f5ea..3e282b4 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/750-qos_map_set_without_interworking.patch
@@ -32,7 +32,7 @@
  		os_free(bss->dump_msk_file);
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -1534,6 +1534,7 @@ static int hostapd_setup_bss(struct host
+@@ -1538,6 +1538,7 @@ int hostapd_setup_bss(struct hostapd_dat
  		wpa_printf(MSG_ERROR, "GAS server initialization failed");
  		return -1;
  	}
@@ -40,7 +40,7 @@
  
  	if (conf->qos_map_set_len &&
  	    hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
-@@ -1541,7 +1542,6 @@ static int hostapd_setup_bss(struct host
+@@ -1545,7 +1546,6 @@ int hostapd_setup_bss(struct hostapd_dat
  		wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
  		return -1;
  	}
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch
index dad7afd..d60764a 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/761-shared_das_port.patch
@@ -10,7 +10,7 @@
  	unsigned int time_window;
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -1471,6 +1471,7 @@ static int hostapd_setup_bss(struct host
+@@ -1475,6 +1475,7 @@ int hostapd_setup_bss(struct hostapd_dat
  
  			os_memset(&das_conf, 0, sizeof(das_conf));
  			das_conf.port = conf->radius_das_port;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/770-radius_server.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/770-radius_server.patch
new file mode 100644
index 0000000..6fca86d
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/770-radius_server.patch
@@ -0,0 +1,154 @@
+--- a/hostapd/Makefile
++++ b/hostapd/Makefile
+@@ -63,6 +63,10 @@ endif
+ OBJS += main.o
+ OBJS += config_file.o
+ 
++ifdef CONFIG_RADIUS_SERVER
++OBJS += radius.o
++endif
++
+ OBJS += ../src/ap/hostapd.o
+ OBJS += ../src/ap/wpa_auth_glue.o
+ OBJS += ../src/ap/drv_callbacks.o
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -42,6 +42,7 @@ static struct hapd_global global;
+ static int daemonize = 0;
+ static char *pid_file = NULL;
+ 
++extern int radius_main(int argc, char **argv);
+ 
+ #ifndef CONFIG_NO_HOSTAPD_LOGGER
+ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+@@ -755,6 +756,11 @@ int main(int argc, char *argv[])
+ 	if (os_program_init())
+ 		return -1;
+ 
++#ifdef RADIUS_SERVER
++	if (strstr(argv[0], "radius"))
++		return radius_main(argc, argv);
++#endif
++
+ 	os_memset(&interfaces, 0, sizeof(interfaces));
+ 	interfaces.reload_config = hostapd_reload_config;
+ 	interfaces.config_read_cb = hostapd_config_read;
+--- a/src/radius/radius_server.c
++++ b/src/radius/radius_server.c
+@@ -63,6 +63,12 @@ struct radius_server_counters {
+ 	u32 unknown_acct_types;
+ };
+ 
++struct radius_accept_attr {
++	u8 type;
++	u16 len;
++	void *data;
++};
++
+ /**
+  * struct radius_session - Internal RADIUS server data for a session
+  */
+@@ -90,7 +96,7 @@ struct radius_session {
+ 	unsigned int macacl:1;
+ 	unsigned int t_c_filtering:1;
+ 
+-	struct hostapd_radius_attr *accept_attr;
++	struct radius_accept_attr *accept_attr;
+ 
+ 	u32 t_c_timestamp; /* Last read T&C timestamp from user DB */
+ };
+@@ -394,6 +400,7 @@ static void radius_server_session_free(s
+ 	radius_msg_free(sess->last_reply);
+ 	os_free(sess->username);
+ 	os_free(sess->nas_ip);
++	os_free(sess->accept_attr);
+ 	os_free(sess);
+ 	data->num_sess--;
+ }
+@@ -554,6 +561,36 @@ radius_server_erp_find_key(struct radius
+ }
+ #endif /* CONFIG_ERP */
+ 
++static struct radius_accept_attr *
++radius_server_copy_attr(const struct hostapd_radius_attr *data)
++{
++	const struct hostapd_radius_attr *attr;
++	struct radius_accept_attr *attr_new;
++	size_t data_size = 0;
++	void *data_buf;
++	int n_attr = 1;
++
++	for (attr = data; attr; attr = attr->next) {
++		n_attr++;
++		data_size += wpabuf_len(attr->val);
++	}
++
++	attr_new = os_zalloc(n_attr * sizeof(*attr) + data_size);
++	if (!attr_new)
++		return NULL;
++
++	data_buf = &attr_new[n_attr];
++	for (n_attr = 0, attr = data; attr; attr = attr->next) {
++		struct radius_accept_attr *cur = &attr_new[n_attr++];
++
++		cur->type = attr->type;
++		cur->len = wpabuf_len(attr->val);
++		cur->data = memcpy(data_buf, wpabuf_head(attr->val), cur->len);
++		data_buf += cur->len;
++	}
++
++	return attr_new;
++}
+ 
+ static struct radius_session *
+ radius_server_get_new_session(struct radius_server_data *data,
+@@ -607,7 +644,7 @@ radius_server_get_new_session(struct rad
+ 		eap_user_free(tmp);
+ 		return NULL;
+ 	}
+-	sess->accept_attr = tmp->accept_attr;
++	sess->accept_attr = radius_server_copy_attr(tmp->accept_attr);
+ 	sess->macacl = tmp->macacl;
+ 	eap_user_free(tmp);
+ 
+@@ -1118,11 +1155,10 @@ radius_server_encapsulate_eap(struct rad
+ 	}
+ 
+ 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+-		struct hostapd_radius_attr *attr;
+-		for (attr = sess->accept_attr; attr; attr = attr->next) {
+-			if (!radius_msg_add_attr(msg, attr->type,
+-						 wpabuf_head(attr->val),
+-						 wpabuf_len(attr->val))) {
++		struct radius_accept_attr *attr;
++		for (attr = sess->accept_attr; attr->data; attr++) {
++			if (!radius_msg_add_attr(msg, attr->type, attr->data,
++						 attr->len)) {
+ 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ 				radius_msg_free(msg);
+ 				return NULL;
+@@ -1211,11 +1247,10 @@ radius_server_macacl(struct radius_serve
+ 	}
+ 
+ 	if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+-		struct hostapd_radius_attr *attr;
+-		for (attr = sess->accept_attr; attr; attr = attr->next) {
+-			if (!radius_msg_add_attr(msg, attr->type,
+-						 wpabuf_head(attr->val),
+-						 wpabuf_len(attr->val))) {
++		struct radius_accept_attr *attr;
++		for (attr = sess->accept_attr; attr->data; attr++) {
++			if (!radius_msg_add_attr(msg, attr->type, attr->data,
++						 attr->len)) {
+ 				wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+ 				radius_msg_free(msg);
+ 				return NULL;
+@@ -2512,7 +2547,7 @@ static int radius_server_get_eap_user(vo
+ 	ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+ 				 phase2, user);
+ 	if (ret == 0 && user) {
+-		sess->accept_attr = user->accept_attr;
++		sess->accept_attr = radius_server_copy_attr(user->accept_attr);
+ 		sess->remediation = user->remediation;
+ 		sess->macacl = user->macacl;
+ 		sess->t_c_timestamp = user->t_c_timestamp;
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch
index 4e51a1c..4fa2dfb 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch
@@ -1,8 +1,8 @@
-From df9a616286f2d33e5c580517238b93ee22359f95 Mon Sep 17 00:00:00 2001
+From bfca3c43597bf827e4d5faec91ac8f420d33c921 Mon Sep 17 00:00:00 2001
 From: "howard.hsu" <howard-yh.hsu@mediatek.com>
 Date: Wed, 19 Jan 2022 19:18:07 +0800
-Subject: [PATCH 01/32] hostapd: mtk: Add neighbor report and BSS Termination
- for MBO certification
+Subject: [PATCH] hostapd: mtk: Add neighbor report and BSS Termination for MBO
+ certification
 
 1. Add hostapd_neighbor_count() and hostapd_neighbor_insert_buffer ()
 The first function can count the number of neighbor report in neighbore report
@@ -13,12 +13,11 @@
 3. Support including neignbor report elements in BTM response
 4. Support configuring BSS Termination TSF by using hostapd_cli command
 5. Disable interface if BSS Termination TSF is set
-6. Add set_send_disassoc_frame_timer() to send disassociate frame
-Function set_disassoc_timer() may fail if key was deleted first. This new
-function will not ask to delete key as set_disassoc_timer() did.
-7. Support including neighbor report elements in BTM request
-8. Add hostapd_neighbor_set_own_report_pref()
-9. Add hostapd_neighbor_set_pref_by_non_pref_chan()
+6. Support including neighbor report elements in BTM request
+7. Add hostapd_neighbor_set_own_report_pref()
+8. Add hostapd_neighbor_set_pref_by_non_pref_chan()
+
+Revert set_send_disassoc_frame_timer
 ---
  hostapd/ctrl_iface.c   |   5 ++
  src/ap/ap_config.c     |   1 +
@@ -28,11 +27,11 @@
  src/ap/gas_serv.h      |   2 +
  src/ap/neighbor_db.c   | 119 +++++++++++++++++++++++++++++++++++++++++
  src/ap/neighbor_db.h   |   9 ++++
- src/ap/wnm_ap.c        |  72 +++++++++++++++++++++++--
- 9 files changed, 252 insertions(+), 5 deletions(-)
+ src/ap/wnm_ap.c        |  42 ++++++++++++++-
+ 9 files changed, 223 insertions(+), 4 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 55711ab10..fab1287cd 100644
+index 55711ab..fab1287 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
 @@ -1347,6 +1347,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
@@ -48,7 +47,7 @@
  		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
  		if (ret)
 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 41c70708d..7d0de122d 100644
+index 41c7070..7d0de12 100644
 --- a/src/ap/ap_config.c
 +++ b/src/ap/ap_config.c
 @@ -171,6 +171,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
@@ -60,7 +59,7 @@
  
  
 diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
-index 02fd4940c..dc6025279 100644
+index 02fd494..dc60252 100644
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
 @@ -558,6 +558,7 @@ struct hostapd_bss_config {
@@ -72,7 +71,7 @@
  	/* IEEE 802.11u - Interworking */
  	int interworking;
 diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index 50a4dc4d6..cf1cff447 100644
+index 50a4dc4..cf1cff4 100644
 --- a/src/ap/ctrl_iface_ap.c
 +++ b/src/ap/ctrl_iface_ap.c
 @@ -1280,6 +1280,10 @@ int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
@@ -115,7 +114,7 @@
  	if (pos) {
  		unsigned int mbo_reason, cell_pref, reassoc_delay;
 diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
-index 4642e4927..cce6df41c 100644
+index 4642e49..cce6df4 100644
 --- a/src/ap/gas_serv.c
 +++ b/src/ap/gas_serv.c
 @@ -19,6 +19,7 @@
@@ -183,7 +182,7 @@
  #ifdef CONFIG_FILS
  		if (info_id == ANQP_FILS_REALM_INFO &&
 diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
-index 7646a98a4..ce492b53f 100644
+index 7646a98..ce492b5 100644
 --- a/src/ap/gas_serv.h
 +++ b/src/ap/gas_serv.h
 @@ -40,6 +40,8 @@
@@ -196,7 +195,7 @@
   * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
   * optimized bitmap.
 diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
-index 5b276e8da..1c14b3201 100644
+index 5b276e8..1c14b32 100644
 --- a/src/ap/neighbor_db.c
 +++ b/src/ap/neighbor_db.c
 @@ -89,6 +89,38 @@ int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
@@ -330,7 +329,7 @@
 +}
 +#endif
 diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
-index 992671b62..a1ddc075b 100644
+index 992671b..a1ddc07 100644
 --- a/src/ap/neighbor_db.h
 +++ b/src/ap/neighbor_db.h
 @@ -24,4 +24,13 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
@@ -348,7 +347,7 @@
 +#endif
  #endif /* NEIGHBOR_DB_H */
 diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
-index ba1dd2ed1..32ccf14ae 100644
+index ba1dd2e..939d447 100644
 --- a/src/ap/wnm_ap.c
 +++ b/src/ap/wnm_ap.c
 @@ -20,6 +20,7 @@
@@ -402,38 +401,10 @@
  	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
  	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
  		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
-@@ -890,6 +911,50 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+@@ -890,6 +911,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
  }
  
  
-+static void set_send_disassoc_frame_timer(struct hostapd_data *hapd, struct sta_info *sta,
-+			       int disassoc_timer)
-+{
-+	int timeout, beacon_int;
-+
-+	/*
-+	 * Prevent STA from reconnecting using cached PMKSA to force
-+	 * full authentication with the authentication server (which may
-+	 * decide to reject the connection),
-+	 */
-+	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
-+
-+	beacon_int = hapd->iconf->beacon_int;
-+	if (beacon_int < 1)
-+		beacon_int = 100; /* best guess */
-+	/* Calculate timeout in ms based on beacon_int in TU */
-+	timeout = disassoc_timer * beacon_int * 128 / 125;
-+	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
-+		   " set to %d ms", MAC2STR(sta->addr), timeout);
-+
-+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
-+
-+	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
-+	if (sta)
-+		ap_sta_disassociate(hapd, sta, reason);
-+}
-+
-+
 +void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
 +{
 +	struct hostapd_data *hapd = eloop_ctx;
@@ -453,16 +424,7 @@
  int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
  				   struct sta_info *sta, const char *url,
  				   int disassoc_timer)
-@@ -934,7 +999,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
- 	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
- 	if (disassoc_timer) {
- 		/* send disassociation frame after time-out */
--		set_disassoc_timer(hapd, sta, disassoc_timer);
-+		set_send_disassoc_frame_timer(hapd, sta, disassoc_timer);
- 	}
- 
- 	return 0;
-@@ -979,6 +1044,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+@@ -979,6 +1016,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
  	    bss_term_dur) {
  		os_memcpy(pos, bss_term_dur, 12);
  		pos += 12;
@@ -471,5 +433,5 @@
  
  	if (url) {
 -- 
-2.39.2
+2.18.0
 
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch
similarity index 89%
rename from recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch
rename to recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch
index af79b24..dbd7163 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch
@@ -1,4 +1,4 @@
-From ff273381d463d82898df70cb7b43341405e3d943 Mon Sep 17 00:00:00 2001
+From 67e2363c4875dd918418dd84b43f86041db690c7 Mon Sep 17 00:00:00 2001
 From: Evelyn Tsai <evelyn.tsai@mediatek.com>
 Date: Fri, 12 May 2023 05:21:28 +0800
 Subject: [PATCH] hostapd: mtk: Avoid setting beacon after wpa_supplicant stop
@@ -19,7 +19,7 @@
  6 files changed, 22 insertions(+), 6 deletions(-)
 
 diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
-index 9989a04..4952bef 100644
+index e575c37d1..0e352c5c7 100644
 --- a/hostapd/ctrl_iface.c
 +++ b/hostapd/ctrl_iface.c
 @@ -194,11 +194,15 @@ static int hostapd_ctrl_iface_update(struct hostapd_data *hapd, char *txt)
@@ -39,7 +39,7 @@
  
  	iface->interfaces->config_read_cb = config_read_cb;
 diff --git a/src/ap/beacon.c b/src/ap/beacon.c
-index f26e525..1aaeaa8 100644
+index f26e5254c..1aaeaa8e4 100644
 --- a/src/ap/beacon.c
 +++ b/src/ap/beacon.c
 @@ -2246,7 +2246,8 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
@@ -69,7 +69,7 @@
  			ret = -1;
  	}
 diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
-index 725d3cd..f2a1a10 100644
+index 725d3cd34..78fd9d8ec 100644
 --- a/src/ap/bss_load.c
 +++ b/src/ap/bss_load.c
 @@ -49,6 +49,9 @@ static void update_channel_utilization(void *eloop_data, void *user_data)
@@ -104,7 +104,7 @@
  			       NULL);
  }
 diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
-index aab8a46..d52188b 100644
+index aab8a4665..d52188bb7 100644
 --- a/src/ap/ctrl_iface_ap.c
 +++ b/src/ap/ctrl_iface_ap.c
 @@ -1028,8 +1028,10 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
@@ -120,18 +120,18 @@
  	return 0;
  }
 diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
-index abadd1a..afe0611 100644
+index 28e549b61..be75613c8 100644
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -508,6 +508,7 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
+@@ -510,6 +510,7 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
  	}
  	hapd->started = 0;
  	hapd->beacon_set_done = 0;
 +	hapd->stopped_by_supplicant = 0;
  
  	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
- 	hostapd_ubus_free_bss(hapd);
-@@ -1316,6 +1317,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+ 	hostapd_ucode_free_bss(hapd);
+@@ -1320,6 +1321,7 @@ int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon)
  		return -1;
  	}
  	hapd->started = 1;
@@ -139,7 +139,7 @@
  
  	if (!first || first == -1) {
  		u8 *addr = hapd->own_addr;
-@@ -4285,7 +4287,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+@@ -4289,7 +4291,7 @@ void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
  {
  	struct os_reltime now;
  
@@ -149,10 +149,10 @@
  
  	if (os_get_reltime(&now))
 diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
-index 072205c..4f1f988 100644
+index 5bd1537fe..435dbd027 100644
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -191,6 +191,7 @@ struct hostapd_data {
+@@ -197,6 +197,7 @@ struct hostapd_data {
  	unsigned int started:1;
  	unsigned int disabled:1;
  	unsigned int reenable_beacon:1;
@@ -161,5 +161,5 @@
  	u8 own_addr[ETH_ALEN];
  	u8 mld_addr[ETH_ALEN];
 -- 
-2.25.1
+2.39.2
 
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch
deleted file mode 100644
index 7196118..0000000
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0029-hostapd-mtk-Fix-CCA-issue.patch
+++ /dev/null
@@ -1,97 +0,0 @@
-From ce585467d784d1015b5a40ec09895d0949690b04 Mon Sep 17 00:00:00 2001
-From: Michael Lee <michael-cy.lee@mediatek.com>
-Date: Wed, 3 May 2023 14:55:18 +0800
-Subject: [PATCH 29/32] hostapd: mtk: Fix CCA issue
-
-When receiving CCA-related nl80211 commands, hostapd used to work on
-struct wpa_driver_nl80211_data, whose ctx always points to
-hostpad_iface->bss[0]. However, CCA commands are sent on a per-BSS based.
-This patch makes hostapd handle CCA-related commands on a per-BSS based.
-
-Signed-off-by: Michael Lee <michael-cy.lee@mediatek.com>
----
- src/drivers/driver_nl80211_event.c | 24 ++++++++++++------------
- 1 file changed, 12 insertions(+), 12 deletions(-)
-
-diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
-index 3d0d9b7bb..ab988fad7 100644
---- a/src/drivers/driver_nl80211_event.c
-+++ b/src/drivers/driver_nl80211_event.c
-@@ -3662,7 +3662,7 @@ static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv,
- 
- #ifdef CONFIG_IEEE80211AX
- 
--static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
-+static void nl80211_obss_color_collision(struct i802_bss *bss,
- 					 struct nlattr *tb[])
- {
- 	union wpa_event_data data;
-@@ -3676,37 +3676,37 @@ static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
- 
- 	wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
- 		   (long long unsigned int) data.bss_color_collision.bitmap);
--	wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
-+	wpa_supplicant_event(bss->ctx, EVENT_BSS_COLOR_COLLISION, &data);
- }
- 
- 
- static void
--nl80211_color_change_announcement_started(struct wpa_driver_nl80211_data *drv)
-+nl80211_color_change_announcement_started(struct i802_bss *bss)
- {
- 	union wpa_event_data data = {};
- 
- 	wpa_printf(MSG_DEBUG, "nl80211: CCA started");
--	wpa_supplicant_event(drv->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
-+	wpa_supplicant_event(bss->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
- }
- 
- 
- static void
--nl80211_color_change_announcement_aborted(struct wpa_driver_nl80211_data *drv)
-+nl80211_color_change_announcement_aborted(struct i802_bss *bss)
- {
- 	union wpa_event_data data = {};
- 
- 	wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
--	wpa_supplicant_event(drv->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
-+	wpa_supplicant_event(bss->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
- }
- 
- 
- static void
--nl80211_color_change_announcement_completed(struct wpa_driver_nl80211_data *drv)
-+nl80211_color_change_announcement_completed(struct i802_bss *bss)
- {
- 	union wpa_event_data data = {};
- 
- 	wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
--	wpa_supplicant_event(drv->ctx, EVENT_CCA_NOTIFY, &data);
-+	wpa_supplicant_event(bss->ctx, EVENT_CCA_NOTIFY, &data);
- }
- 
- #endif /* CONFIG_IEEE80211AX */
-@@ -3968,16 +3968,16 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
- 		break;
- #ifdef CONFIG_IEEE80211AX
- 	case NL80211_CMD_OBSS_COLOR_COLLISION:
--		nl80211_obss_color_collision(drv, tb);
-+		nl80211_obss_color_collision(bss, tb);
- 		break;
- 	case NL80211_CMD_COLOR_CHANGE_STARTED:
--		nl80211_color_change_announcement_started(drv);
-+		nl80211_color_change_announcement_started(bss);
- 		break;
- 	case NL80211_CMD_COLOR_CHANGE_ABORTED:
--		nl80211_color_change_announcement_aborted(drv);
-+		nl80211_color_change_announcement_aborted(bss);
- 		break;
- 	case NL80211_CMD_COLOR_CHANGE_COMPLETED:
--		nl80211_color_change_announcement_completed(drv);
-+		nl80211_color_change_announcement_completed(bss);
- 		break;
- #endif /* CONFIG_IEEE80211AX */
- 	default:
--- 
-2.39.2
-
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch
new file mode 100644
index 0000000..dd121ca
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch
@@ -0,0 +1,64 @@
+From 76e54b095fdd8ab65a562c28fba330afcfb519ec Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Wed, 5 Jul 2023 10:44:15 +0800
+Subject: [PATCH] hostapd: mtk: Fix background channel overlapping operating
+ channel issue
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/dfs.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/ap/dfs.c b/src/ap/dfs.c
+index 2f5c86e..c9a9c6c 100644
+--- a/src/ap/dfs.c
++++ b/src/ap/dfs.c
+@@ -807,6 +807,20 @@ static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+ }
+ 
+ 
++static void dfs_check_background_overlapped(struct hostapd_iface *iface)
++{
++	int width = hostapd_get_oper_chwidth(iface->conf);
++
++	if (!dfs_use_radar_background(iface))
++		return;
++
++	if (dfs_are_channels_overlapped(iface, iface->radar_background.freq,
++					width, iface->radar_background.centr_freq_seg0_idx,
++					iface->radar_background.centr_freq_seg1_idx))
++		iface->radar_background.channel = -1;
++}
++
++
+ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ 				     int start_chan_idx, int n_chans)
+ {
+@@ -1127,6 +1141,8 @@ static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+ 						  &oper_centr_freq_seg1_idx,
+ 						  &channel_type);
+ 	if (!channel ||
++	    channel->chan == iface->conf->channel ||
++	    channel->chan == iface->radar_background.channel ||
+ 	    hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ 				  channel->freq, channel->chan,
+ 				  iface->conf->ieee80211n,
+@@ -1361,6 +1377,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+ 	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ 					     oper_centr_freq_seg1_idx);
+ 	err = 0;
++	dfs_check_background_overlapped(iface);
+ 
+ 	hostapd_setup_interface_complete(iface, err);
+ 	return err;
+@@ -1488,6 +1505,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+ 			hostapd_set_oper_centr_freq_seg1_idx(
+ 				iface->conf, oper_centr_freq_seg1_idx);
+ 
++			dfs_check_background_overlapped(iface);
+ 			hostapd_disable_iface(iface);
+ 			hostapd_enable_iface(iface);
+ 			return 0;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
new file mode 100644
index 0000000..7cd34cd
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
@@ -0,0 +1,30 @@
+From 1671a37b5990929bd11823158d496e7877d83d92 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Wed, 2 Aug 2023 18:33:31 +0800
+Subject: [PATCH 38/38] hostapd: mtk: Fix 11vmbss aid using wrong pool
+
+Fix 11vmbss aid using wrong pool.
+All STAs use the aid pool in transmitted bss.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index d36798e..f20073c 100755
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -3307,6 +3307,9 @@ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ 	int i, j = 32, aid;
+ 
++	if (hapd->iconf->mbssid)
++		hapd = hostapd_mbssid_get_tx_bss(hapd);
++
+ 	/* get a unique AID */
+ 	if (sta->aid > 0) {
+ 		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
new file mode 100644
index 0000000..9218f9b
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
@@ -0,0 +1,34 @@
+From ea3e20d6cc8d11750e509a701131297da81ef35d Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Mon, 7 Aug 2023 15:27:27 +0800
+Subject: [PATCH 39/39] hostapd: mtk: Fix rnr ie length when no need to report
+ bss
+
+Fix rnr ie length when no need to report bss. If we don't have content in
+TBTT then don't change the length of the ie (*size_offset).
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index f20073c..ef520c8 100755
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7494,8 +7494,10 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ 		}
+ 
+ 		start = i;
+-		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+-		*size_offset = (eid - size_offset) - 1;
++		if (tbtt_count != 0) {
++			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
++			*size_offset = (eid - size_offset) - 1;
++		}
+ 	}
+ 
+ 	if (tbtt_count == 0)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..0a27496
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From 2ea36366ea036e3063ff553a2939c9cac17c6ac8 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 8 Aug 2023 19:21:41 +0800
+Subject: [PATCH] hostapd: mtk: add back ht vht cap missing field before dfs
+ channel fallback
+
+hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
+on the bandwidth of switched channel.
+For example, vht bw 160 support field would be cleared if we switch to
+non bw 160 channel.
+This design works fine with NON-DFS channel switch.
+However, for those DFS channels who require CAC, channel switch command
+calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
+This is simply restarting the interface not CHANNEL SWITCHING, so
+hostapd will not receive any ch_switch event from kernel.
+Therefore, the cleared field in vht_capab will not be set back to 1,
+even if we channel switch to dfs channel bw 160.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index c2e0b13..4b7ebc5 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -4161,6 +4161,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++	if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++	    freq_params->bandwidth > 20)
++		iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++	if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
++	    freq_params->bandwidth == 160)
++		iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
index 78dae59..89585bc 100644
--- a/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
+++ b/recipes-wifi/wpa-supplicant/files/patches-2.10.3/patches.inc
@@ -17,6 +17,7 @@
     file://150-add-NULL-checks-encountered-during-tests-hwsim.patch \
     file://160-dpp_pkex-EC-point-mul-w-value-prime.patch \
     file://170-hostapd-update-cfs0-and-cfs1-for-160MHz.patch \
+    file://180-BSS-coloring-fix-CCA-with-multiple-BSS.patch \
     file://200-multicall.patch \
     file://300-noscan.patch \
     file://301-mesh-noscan.patch \
@@ -46,8 +47,10 @@
     file://590-rrm-wnm-statistics.patch \
     file://599-wpa_supplicant-fix-warnings.patch \
     file://600-ubus_support.patch \
+    file://601-ucode_support.patch \
     file://610-hostapd_cli_ujail_permission.patch \
     file://700-wifi-reload.patch \
+    file://701-reload_config_inline.patch \
     file://710-vlan_no_bridge.patch \
     file://711-wds_bridge_force.patch \
     file://720-iface_max_num_sta.patch \
@@ -57,6 +60,7 @@
     file://751-qos_map_ignore_when_unsupported.patch \
     file://760-dynamic_own_ip.patch \
     file://761-shared_das_port.patch \
+    file://770-radius_server.patch \
     file://990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch \
     file://mtk-0001-hostapd-mtk-Add-neighbor-report-and-BSS-Termination-.patch \
     file://mtk-0002-hostapd-mtk-print-sae-groups-by-hostapd-ctrl.patch \
@@ -83,17 +87,20 @@
     file://mtk-0023-hostapd-mtk-Add-vendor-for-CAPI-certification-comman.patch \
     file://mtk-0024-hostapd-mtk-Air-Monitor-support-in-hostapd-by-vendor.patch \
     file://mtk-0025-hostapd-mtk-Fix-scan-result-updating-issue.patch \
-    file://mtk-0026-hostapd-mtk-avoid-setting-beacon-after-wpa_supplican.patch \
+    file://mtk-0026-hostapd-mtk-Avoid-setting-beacon-after-wpa_supplican.patch \
     file://mtk-0027-hostapd-mtk-Fix-setting-wrong-seg0-index-for-5G-cent.patch \
     file://mtk-0028-hostapd-mtk-Add-muru-user-number-debug-command.patch \
-    file://mtk-0029-hostapd-mtk-Fix-CCA-issue.patch \
     file://mtk-0030-hostapd-mtk-Fix-unexpected-AP-beacon-state-transitio.patch \
     file://mtk-0031-hostapd-mtk-add-connac3-PHY-MURU-manual-mode-config-.patch \
     file://mtk-0032-hostapd-mtk-Add-HE-capabilities-check.patch \
+    file://mtk-0033-hostapd-mtk-Fix-background-channel-overlapping-opera.patch \
     file://mtk-0034-hostapd-mtk-Fix-hostapd_dfs_start_cac-log.patch \
     file://mtk-0035-hostapd-mtk-Check-the-bridge-after-ioctl-SIOCBRADDIF.patch \
     file://mtk-0036-hostapd-mtk-Update-parameter_set_count-in-MU-EDCA-IE.patch \
     file://mtk-0037-hostapd-mtk-Add-extension-IE-list-for-non-inherit-IE.patch \
+    file://mtk-0038-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch \
+    file://mtk-0039-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch \
+    file://mtk-0040-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch \
     file://mtk-1000-hostapd-mtk-update-eht-operation-element.patch \
     file://mtk-1001-hostapd-mtk-Add-support-for-gtk-rekeying-in-hostapd-.patch \
     "
diff --git a/recipes-wifi/wpa-supplicant/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch b/recipes-wifi/wpa-supplicant/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch
index f51b42f..b3f4138 100644
--- a/recipes-wifi/wpa-supplicant/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch
+++ b/recipes-wifi/wpa-supplicant/files/patches/mtk-0006-hostapd-mtk-Add-hostapd-MU-SET-GET-control.patch
@@ -1,7 +1,7 @@
-From e063e89785d74506976e34a0a3cbb14836d6b8e3 Mon Sep 17 00:00:00 2001
+From 53a7b7af61b75ea0e3f9d12d3d6302cf40941a8a Mon Sep 17 00:00:00 2001
 From: TomLiu <tomml.liu@mediatek.com>
 Date: Tue, 9 Aug 2022 10:23:44 -0700
-Subject: [PATCH 06/35] hostapd: mtk: Add hostapd MU SET/GET control
+Subject: [PATCH 06/37] hostapd: mtk: Add hostapd MU SET/GET control
 
 ---
  hostapd/config_file.c             |   9 +++
@@ -156,14 +156,14 @@
  	{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
  	  "report a scanned DPP URI from a QR Code" },
 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
-index 344585a..d4fc191 100644
+index 344585a..1b0e990 100644
 --- a/src/ap/ap_config.c
 +++ b/src/ap/ap_config.c
 @@ -280,6 +280,7 @@ struct hostapd_config * hostapd_config_defaults(void)
  	conf->he_6ghz_max_ampdu_len_exp = 7;
  	conf->he_6ghz_rx_ant_pat = 1;
  	conf->he_6ghz_tx_ant_pat = 1;
-+	conf->mu_onoff = 13;
++	conf->mu_onoff = 0xf;
  #endif /* CONFIG_IEEE80211AX */
  
  	/* The third octet of the country string uses an ASCII space character
@@ -446,5 +446,5 @@
  			}
  
 -- 
-2.18.0
+2.39.0
 
diff --git a/recipes-wifi/wpa-supplicant/files/patches/mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch b/recipes-wifi/wpa-supplicant/files/patches/mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
new file mode 100644
index 0000000..aff2234
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches/mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch
@@ -0,0 +1,30 @@
+From 845f192fe55b4774425752ecba8cb8f5c4e24c69 Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Wed, 2 Aug 2023 18:33:31 +0800
+Subject: [PATCH 37/38] hostapd: mtk: Fix 11vmbss aid using wrong pool
+
+Fix 11vmbss aid using wrong pool.
+All STAs use the aid pool in transmitted bss.
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index f866b8a..b73b821 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -4002,6 +4002,9 @@ int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+ {
+ 	int i, j = 32, aid;
+ 
++	if (hapd->iconf->mbssid)
++		hapd = hostapd_mbssid_get_tx_bss(hapd);
++
+ 	/* get a unique AID */
+ 	if (sta->aid > 0) {
+ 		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches/mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch b/recipes-wifi/wpa-supplicant/files/patches/mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
new file mode 100644
index 0000000..ec82302
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches/mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch
@@ -0,0 +1,44 @@
+From 87ed81b81030cb5567c04227d0fe65c6a15148f3 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Tue, 8 Aug 2023 19:21:41 +0800
+Subject: [PATCH] hostapd: mtk: add back ht vht cap missing field before dfs
+ channel fallback
+
+hostapd_event_ch_switch would set / clear ht_capab and vht_capab, based
+on the bandwidth of switched channel.
+For example, vht bw 160 support field would be cleared if we switch to
+non bw 160 channel.
+This design works fine with NON-DFS channel switch.
+However, for those DFS channels who require CAC, channel switch command
+calls hostapd_switch_channel_fallback instead of hostapd_switch_channel.
+This is simply restarting the interface not CHANNEL SWITCHING, so
+hostapd will not receive any ch_switch event from kernel.
+Therefore, the cleared field in vht_capab will not be set back to 1,
+even if we channel switch to dfs channel bw 160.
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ src/ap/hostapd.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 707e7d5..5d17cf8 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3888,6 +3888,13 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 		break;
+ 	}
+ 
++	if ((iface->current_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++	    freq_params->bandwidth > 20)
++		iface->conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
++	if ((iface->current_mode->vht_capab & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) &&
++	    freq_params->bandwidth == 160)
++		iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
++
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches/mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch b/recipes-wifi/wpa-supplicant/files/patches/mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
new file mode 100644
index 0000000..d6fa8b6
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/patches/mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch
@@ -0,0 +1,34 @@
+From 70c8f9355c4df3c083965ce652d33e01f8fe7e7c Mon Sep 17 00:00:00 2001
+From: "Allen.Ye" <allen.ye@mediatek.com>
+Date: Mon, 7 Aug 2023 15:27:27 +0800
+Subject: [PATCH 38/38] hostapd: mtk: Fix rnr ie length when no need to report
+ bss
+
+Fix rnr ie length when no need to report bss. If we don't have content in
+TBTT then don't change the length of the ie (*size_offset).
+
+Signed-off-by: Allen.Ye <allen.ye@mediatek.com>
+---
+ src/ap/ieee802_11.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
+index b73b821..ff39fad 100644
+--- a/src/ap/ieee802_11.c
++++ b/src/ap/ieee802_11.c
+@@ -7530,8 +7530,10 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ 		}
+ 
+ 		start = i;
+-		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+-		*size_offset = (eid - size_offset) - 1;
++		if (tbtt_count != 0) {
++			*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
++			*size_offset = (eid - size_offset) - 1;
++		}
+ 	}
+ 
+ 	if (tbtt_count == 0)
+-- 
+2.18.0
+
diff --git a/recipes-wifi/wpa-supplicant/files/patches/patches.inc b/recipes-wifi/wpa-supplicant/files/patches/patches.inc
index 3e8cc14..f783c48 100644
--- a/recipes-wifi/wpa-supplicant/files/patches/patches.inc
+++ b/recipes-wifi/wpa-supplicant/files/patches/patches.inc
@@ -99,4 +99,7 @@
     file://mtk-0035-hostapd-mtk-add-extension-IE-list-for-non-inherit-IE.patch \
     file://mtk-0036-hostapd-mtk-Fix-background-channel-overlapping-opera.patch \
     file://mtk-0036-hostapd-mtk-Fix-wpa_supplicant-configuration-parsing.patch \
+    file://mtk-0037-hostapd-mtk-Fix-11vmbss-aid-using-wrong-pool.patch \
+    file://mtk-0037-hostapd-mtk-add-back-ht-vht-cap-missing-field-before.patch \
+    file://mtk-0038-hostapd-mtk-Fix-rnr-ie-length-when-no-need-to-report.patch \
     "
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/hostapd/radius.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/hostapd/radius.c
new file mode 100644
index 0000000..362a22c
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/hostapd/radius.c
@@ -0,0 +1,715 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+
+#include "ap/ap_config.h"
+#include "eap_server/eap.h"
+#include "radius/radius.h"
+#include "radius/radius_server.h"
+#include "eap_register.h"
+
+#include <libubox/blobmsg_json.h>
+#include <libubox/blobmsg.h>
+#include <libubox/avl.h>
+#include <libubox/avl-cmp.h>
+#include <libubox/kvlist.h>
+
+#include <sys/stat.h>
+#include <fnmatch.h>
+
+#define VENDOR_ID_WISPR 14122
+#define VENDOR_ATTR_SIZE 6
+
+struct radius_parse_attr_data {
+	unsigned int vendor;
+	u8 type;
+	int size;
+	char format;
+	const char *data;
+};
+
+struct radius_parse_attr_state {
+	struct hostapd_radius_attr *prev;
+	struct hostapd_radius_attr *attr;
+	struct wpabuf *buf;
+	void *attrdata;
+};
+
+struct radius_user_state {
+	struct avl_node node;
+	struct eap_user data;
+};
+
+struct radius_user_data {
+	struct kvlist users;
+	struct avl_tree user_state;
+	struct blob_attr *wildcard;
+};
+
+struct radius_state {
+	struct radius_server_data *radius;
+	struct eap_config eap;
+
+	struct radius_user_data phase1, phase2;
+	const char *user_file;
+	time_t user_file_ts;
+
+	int n_attrs;
+	struct hostapd_radius_attr *attrs;
+};
+
+struct radius_config {
+	struct tls_connection_params tls;
+	struct radius_server_conf radius;
+};
+
+enum {
+	USER_ATTR_PASSWORD,
+	USER_ATTR_HASH,
+	USER_ATTR_SALT,
+	USER_ATTR_METHODS,
+	USER_ATTR_RADIUS,
+	USER_ATTR_VLAN,
+	USER_ATTR_MAX_RATE_UP,
+	USER_ATTR_MAX_RATE_DOWN,
+	__USER_ATTR_MAX
+};
+
+static void radius_tls_event(void *ctx, enum tls_event ev,
+			      union tls_event_data *data)
+{
+	switch (ev) {
+	case TLS_CERT_CHAIN_SUCCESS:
+		wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
+		break;
+	case TLS_CERT_CHAIN_FAILURE:
+		wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+			   data->cert_fail.reason,
+			   data->cert_fail.depth,
+			   data->cert_fail.subject,
+			   data->cert_fail.reason_txt);
+		break;
+	case TLS_PEER_CERTIFICATE:
+		wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
+			   data->peer_cert.depth,
+			   data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+			   data->peer_cert.subject);
+		break;
+	case TLS_ALERT:
+		if (data->alert.is_local)
+			wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
+				   data->alert.description);
+		else
+			wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
+				   data->alert.description);
+		break;
+	case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+		/* Not applicable to TLS server */
+		break;
+	}
+}
+
+static void radius_userdata_init(struct radius_user_data *u)
+{
+	kvlist_init(&u->users, kvlist_blob_len);
+	avl_init(&u->user_state, avl_strcmp, false, NULL);
+}
+
+static void radius_userdata_free(struct radius_user_data *u)
+{
+	struct radius_user_state *s, *tmp;
+
+	kvlist_free(&u->users);
+	free(u->wildcard);
+	u->wildcard = NULL;
+	avl_remove_all_elements(&u->user_state, s, node, tmp)
+		free(s);
+}
+
+static void
+radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
+{
+	enum {
+		USERSTATE_USERS,
+		USERSTATE_WILDCARD,
+		__USERSTATE_MAX,
+	};
+	static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
+		[USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
+		[USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
+	};
+	struct blob_attr *tb[__USERSTATE_MAX], *cur;
+	int rem;
+
+	if (!data)
+		return;
+
+	blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
+
+	blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
+		kvlist_set(&u->users, blobmsg_name(cur), cur);
+
+	if (tb[USERSTATE_WILDCARD])
+		u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
+}
+
+static void
+load_userfile(struct radius_state *s)
+{
+	enum {
+		USERDATA_PHASE1,
+		USERDATA_PHASE2,
+		__USERDATA_MAX
+	};
+	static const struct blobmsg_policy policy[__USERDATA_MAX] = {
+		[USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
+		[USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
+	};
+	struct blob_attr *tb[__USERDATA_MAX], *cur;
+	static struct blob_buf b;
+	struct stat st;
+	int rem;
+
+	if (stat(s->user_file, &st))
+		return;
+
+	if (s->user_file_ts == st.st_mtime)
+		return;
+
+	s->user_file_ts = st.st_mtime;
+	radius_userdata_free(&s->phase1);
+	radius_userdata_free(&s->phase2);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_json_from_file(&b, s->user_file);
+	blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
+	radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
+	radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
+
+	blob_buf_free(&b);
+}
+
+static struct blob_attr *
+radius_user_get(struct radius_user_data *s, const char *name)
+{
+	struct blob_attr *cur;
+	int rem;
+
+	cur = kvlist_get(&s->users, name);
+	if (cur)
+		return cur;
+
+	blobmsg_for_each_attr(cur, s->wildcard, rem) {
+		static const struct blobmsg_policy policy = {
+			"name", BLOBMSG_TYPE_STRING
+		};
+		struct blob_attr *pattern;
+
+		if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
+			continue;
+
+		blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
+		if (!name)
+			continue;
+
+		if (!fnmatch(blobmsg_get_string(pattern), name, 0))
+			return cur;
+	}
+
+	return NULL;
+}
+
+static struct radius_parse_attr_data *
+radius_parse_attr(struct blob_attr *attr)
+{
+	static const struct blobmsg_policy policy[4] = {
+		{ .type = BLOBMSG_TYPE_INT32 },
+		{ .type = BLOBMSG_TYPE_INT32 },
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+	};
+	static struct radius_parse_attr_data data;
+	struct blob_attr *tb[4];
+	const char *format;
+
+	blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
+
+	if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
+		return NULL;
+
+	format = blobmsg_get_string(tb[2]);
+	if (strlen(format) != 1)
+		return NULL;
+
+	data.vendor = blobmsg_get_u32(tb[0]);
+	data.type = blobmsg_get_u32(tb[1]);
+	data.format = format[0];
+	data.data = blobmsg_get_string(tb[3]);
+	data.size = strlen(data.data);
+
+	switch (data.format) {
+	case 's':
+		break;
+	case 'x':
+		if (data.size & 1)
+			return NULL;
+		data.size /= 2;
+		break;
+	case 'd':
+		data.size = 4;
+		break;
+	default:
+		return NULL;
+	}
+
+	return &data;
+}
+
+static void
+radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
+{
+	struct blob_attr *data = tb[USER_ATTR_RADIUS];
+	struct blob_attr *cur;
+	int rem;
+
+	blobmsg_for_each_attr(cur, data, rem) {
+		struct radius_parse_attr_data *data;
+		size_t prev = *attr_size;
+
+		data = radius_parse_attr(cur);
+		if (!data)
+			continue;
+
+		*attr_size += data->size;
+		if (data->vendor)
+			*attr_size += VENDOR_ATTR_SIZE;
+
+		(*n_attr)++;
+	}
+
+	*n_attr += !!tb[USER_ATTR_VLAN] * 3 +
+		   !!tb[USER_ATTR_MAX_RATE_UP] +
+		   !!tb[USER_ATTR_MAX_RATE_DOWN];
+	*attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
+		      !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
+		      !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
+}
+
+static void *
+radius_add_attr(struct radius_parse_attr_state *state,
+		u32 vendor, u8 type, u8 len)
+{
+	struct hostapd_radius_attr *attr;
+	struct wpabuf *buf;
+	void *val;
+
+	val = state->attrdata;
+
+	buf = state->buf++;
+	buf->buf = val;
+
+	attr = state->attr++;
+	attr->val = buf;
+	attr->type = type;
+
+	if (state->prev)
+		state->prev->next = attr;
+	state->prev = attr;
+
+	if (vendor) {
+		u8 *vendor_hdr = val + 4;
+
+		WPA_PUT_BE32(val, vendor);
+		vendor_hdr[0] = type;
+		vendor_hdr[1] = len + 2;
+
+		len += VENDOR_ATTR_SIZE;
+		val += VENDOR_ATTR_SIZE;
+		attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
+	}
+
+	buf->size = buf->used = len;
+	state->attrdata += len;
+
+	return val;
+}
+
+static void
+radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
+{
+	struct blob_attr *data = tb[USER_ATTR_RADIUS];
+	struct hostapd_radius_attr *prev = NULL;
+	struct blob_attr *cur;
+	int len, rem;
+	void *val;
+
+	if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
+		char buf[5];
+
+		val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
+		WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
+
+		val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
+		WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
+
+		len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
+		val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
+		memcpy(val, buf, len);
+	}
+
+	if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
+		val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
+		WPA_PUT_BE32(val, blobmsg_get_u32(cur));
+	}
+
+	if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
+		val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
+		WPA_PUT_BE32(val, blobmsg_get_u32(cur));
+	}
+
+	blobmsg_for_each_attr(cur, data, rem) {
+		struct radius_parse_attr_data *data;
+		void *val;
+		int size;
+
+		data = radius_parse_attr(cur);
+		if (!data)
+			continue;
+
+		val = radius_add_attr(state, data->vendor, data->type, data->size);
+		switch (data->format) {
+		case 's':
+			memcpy(val, data->data, data->size);
+			break;
+		case 'x':
+			hexstr2bin(data->data, val, data->size);
+			break;
+		case 'd':
+			WPA_PUT_BE32(val, atoi(data->data));
+			break;
+		}
+	}
+}
+
+static void
+radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
+{
+	struct blob_attr *cur;
+	int rem, n = 0;
+
+	if (!data)
+		return;
+
+	blobmsg_for_each_attr(cur, data, rem) {
+		const char *method;
+
+		if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
+			continue;
+
+		if (n == EAP_MAX_METHODS)
+			break;
+
+		method = blobmsg_get_string(cur);
+		eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
+		if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
+		    eap->methods[n].method == EAP_TYPE_NONE) {
+			if (!strcmp(method, "TTLS-PAP")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
+				continue;
+			}
+			if (!strcmp(method, "TTLS-CHAP")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+				continue;
+			}
+			if (!strcmp(method, "TTLS-MSCHAP")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+				continue;
+			}
+			if (!strcmp(method, "TTLS-MSCHAPV2")) {
+				eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+				continue;
+			}
+		}
+		n++;
+	}
+}
+
+static struct eap_user *
+radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
+		      const char *id)
+{
+	static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
+		[USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
+		[USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
+		[USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
+		[USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
+		[USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
+		[USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
+		[USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
+		[USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
+	};
+	struct blob_attr *tb[__USER_ATTR_MAX], *cur;
+	char *password_buf, *salt_buf, *name_buf;
+	struct radius_parse_attr_state astate = {};
+	struct hostapd_radius_attr *attr;
+	struct radius_user_state *state;
+	int pw_len = 0, salt_len = 0;
+	struct eap_user *eap;
+	struct wpabuf *val;
+	size_t attrsize = 0;
+	void *attrdata;
+	int n_attr = 0;
+
+	state = avl_find_element(&u->user_state, id, state, node);
+	if (state)
+		return &state->data;
+
+	blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
+
+	if ((cur = tb[USER_ATTR_SALT]) != NULL)
+		salt_len = strlen(blobmsg_get_string(cur)) / 2;
+	if ((cur = tb[USER_ATTR_HASH]) != NULL)
+		pw_len = strlen(blobmsg_get_string(cur)) / 2;
+	else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
+		pw_len = blobmsg_len(cur) - 1;
+	radius_count_attrs(tb, &n_attr, &attrsize);
+
+	state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
+			 &password_buf, pw_len,
+			 &salt_buf, salt_len,
+			 &astate.attr, n_attr * sizeof(*astate.attr),
+			 &astate.buf, n_attr * sizeof(*astate.buf),
+			 &astate.attrdata, attrsize);
+	eap = &state->data;
+	eap->salt = salt_len ? salt_buf : NULL;
+	eap->salt_len = salt_len;
+	eap->password = pw_len ? password_buf : NULL;
+	eap->password_len = pw_len;
+	eap->force_version = -1;
+
+	if ((cur = tb[USER_ATTR_SALT]) != NULL)
+		hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
+	if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
+		memcpy(password_buf, blobmsg_get_string(cur), pw_len);
+	else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
+		hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
+		eap->password_hash = 1;
+	}
+	radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
+
+	if (n_attr > 0) {
+		cur = tb[USER_ATTR_RADIUS];
+		eap->accept_attr = astate.attr;
+		radius_parse_attrs(tb, &astate);
+	}
+
+	state->node.key = strcpy(name_buf, id);
+	avl_insert(&u->user_state, &state->node);
+
+	return &state->data;
+
+free:
+	free(state);
+	return NULL;
+}
+
+static int radius_get_eap_user(void *ctx, const u8 *identity,
+			       size_t identity_len, int phase2,
+			       struct eap_user *user)
+{
+	struct radius_state *s = ctx;
+	struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
+	struct blob_attr *entry;
+	struct eap_user *data;
+	char *id;
+
+	if (identity_len > 512)
+		return -1;
+
+	load_userfile(s);
+
+	id = alloca(identity_len + 1);
+	memcpy(id, identity, identity_len);
+	id[identity_len] = 0;
+
+	entry = radius_user_get(u, id);
+	if (!entry)
+		return -1;
+
+	if (!user)
+		return 0;
+
+	data = radius_user_get_state(u, entry, id);
+	if (!data)
+		return -1;
+
+	*user = *data;
+	if (user->password_len > 0)
+		user->password = os_memdup(user->password, user->password_len);
+	if (user->salt_len > 0)
+		user->salt = os_memdup(user->salt, user->salt_len);
+	user->phase2 = phase2;
+
+	return 0;
+}
+
+static int radius_setup(struct radius_state *s, struct radius_config *c)
+{
+	struct eap_config *eap = &s->eap;
+	struct tls_config conf = {
+		.event_cb = radius_tls_event,
+		.tls_flags = TLS_CONN_DISABLE_TLSv1_3,
+		.cb_ctx = s,
+	};
+
+	eap->eap_server = 1;
+	eap->max_auth_rounds = 100;
+	eap->max_auth_rounds_short = 50;
+	eap->ssl_ctx = tls_init(&conf);
+	if (!eap->ssl_ctx) {
+		wpa_printf(MSG_INFO, "TLS init failed\n");
+		return 1;
+	}
+
+	if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
+		wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
+		return 1;
+	}
+
+	c->radius.eap_cfg = eap;
+	c->radius.conf_ctx = s;
+	c->radius.get_eap_user = radius_get_eap_user;
+	s->radius = radius_server_init(&c->radius);
+	if (!s->radius) {
+		wpa_printf(MSG_INFO, "failed to initialize radius server\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+static int radius_init(struct radius_state *s)
+{
+	memset(s, 0, sizeof(*s));
+	radius_userdata_init(&s->phase1);
+	radius_userdata_init(&s->phase2);
+}
+
+static void radius_deinit(struct radius_state *s)
+{
+	if (s->radius)
+		radius_server_deinit(s->radius);
+
+	if (s->eap.ssl_ctx)
+		tls_deinit(s->eap.ssl_ctx);
+
+	radius_userdata_free(&s->phase1);
+	radius_userdata_free(&s->phase2);
+}
+
+static int usage(const char *progname)
+{
+	fprintf(stderr, "Usage: %s <options>\n",
+		progname);
+}
+
+int radius_main(int argc, char **argv)
+{
+	static struct radius_state state = {};
+	static struct radius_config config = {};
+	const char *progname = argv[0];
+	int ret = 0;
+	int ch;
+
+	wpa_debug_setup_stdout();
+	wpa_debug_level = 0;
+
+	if (eloop_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+		return 1;
+	}
+
+	eap_server_register_methods();
+	radius_init(&state);
+
+	while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
+		switch (ch) {
+		case '6':
+			config.radius.ipv6 = 1;
+			break;
+		case 'C':
+			config.tls.ca_cert = optarg;
+			break;
+		case 'c':
+			if (config.tls.client_cert2)
+				return usage(progname);
+
+			if (config.tls.client_cert)
+				config.tls.client_cert2 = optarg;
+			else
+				config.tls.client_cert = optarg;
+			break;
+		case 'd':
+			config.tls.dh_file = optarg;
+			break;
+		case 'i':
+			state.eap.server_id = optarg;
+			state.eap.server_id_len = strlen(optarg);
+			break;
+		case 'k':
+			if (config.tls.private_key2)
+				return usage(progname);
+
+			if (config.tls.private_key)
+				config.tls.private_key2 = optarg;
+			else
+				config.tls.private_key = optarg;
+			break;
+		case 'K':
+			if (config.tls.private_key_passwd2)
+				return usage(progname);
+
+			if (config.tls.private_key_passwd)
+				config.tls.private_key_passwd2 = optarg;
+			else
+				config.tls.private_key_passwd = optarg;
+			break;
+		case 'p':
+			config.radius.auth_port = atoi(optarg);
+			break;
+		case 'P':
+			config.radius.acct_port = atoi(optarg);
+			break;
+		case 's':
+			config.radius.client_file = optarg;
+			break;
+		case 'u':
+			state.user_file = optarg;
+			break;
+		default:
+			return usage(progname);
+		}
+	}
+
+	if (!config.tls.client_cert || !config.tls.private_key ||
+	    !config.radius.client_file || !state.eap.server_id ||
+	    !state.user_file) {
+		wpa_printf(MSG_INFO, "missing options\n");
+		goto out;
+	}
+
+	ret = radius_setup(&state, &config);
+	if (ret)
+		goto out;
+
+	load_userfile(&state);
+	eloop_run();
+
+out:
+	radius_deinit(&state);
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c
new file mode 100644
index 0000000..a609eb1
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.c
@@ -0,0 +1,2002 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/wpabuf.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "hostapd.h"
+#include "neighbor_db.h"
+#include "wps_hostapd.h"
+#include "sta_info.h"
+#include "ubus.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "rrm.h"
+#include "wnm_ap.h"
+#include "taxonomy.h"
+#include "airtime_policy.h"
+#include "hw_features.h"
+
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static int ctx_ref;
+
+static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct hostapd_data, ubus.obj);
+}
+
+struct ubus_banned_client {
+	struct avl_node avl;
+	u8 addr[ETH_ALEN];
+};
+
+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
+{
+	if (ubus_reconnect(ctx, NULL)) {
+		eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+		return;
+	}
+
+	ubus_add_uloop(ctx);
+}
+
+static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
+{
+	uloop_fd_delete(&ctx->sock);
+	eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+}
+
+static bool hostapd_ubus_init(void)
+{
+	if (ctx)
+		return true;
+
+	eloop_add_uloop();
+	ctx = ubus_connect(NULL);
+	if (!ctx)
+		return false;
+
+	ctx->connection_lost = hostapd_ubus_connection_lost;
+	ubus_add_uloop(ctx);
+
+	return true;
+}
+
+static void hostapd_ubus_ref_inc(void)
+{
+	ctx_ref++;
+}
+
+static void hostapd_ubus_ref_dec(void)
+{
+	ctx_ref--;
+	if (!ctx)
+		return;
+
+	if (ctx_ref)
+		return;
+
+	uloop_fd_delete(&ctx->sock);
+	ubus_free(ctx);
+	ctx = NULL;
+}
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+	if (!hostapd_ubus_init())
+		return;
+}
+
+void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+	if (!ctx)
+		return;
+}
+
+static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
+{
+	char *event_type;
+
+	if (!ctx || !obj)
+		return;
+
+	if (asprintf(&event_type, "bss.%s", event) < 0)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "name", bssname);
+	ubus_notify(ctx, obj, event_type, b.head, -1);
+	free(event_type);
+}
+
+static void
+hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
+{
+	struct ubus_banned_client *ban = eloop_data;
+	struct hostapd_data *hapd = user_ctx;
+
+	avl_delete(&hapd->ubus.banned, &ban->avl);
+	free(ban);
+}
+
+static void
+hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
+{
+	struct ubus_banned_client *ban;
+
+	if (time < 0)
+		time = 0;
+
+	ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+	if (!ban) {
+		if (!time)
+			return;
+
+		ban = os_zalloc(sizeof(*ban));
+		memcpy(ban->addr, addr, sizeof(ban->addr));
+		ban->avl.key = ban->addr;
+		avl_insert(&hapd->ubus.banned, &ban->avl);
+	} else {
+		eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
+		if (!time) {
+			hostapd_bss_del_ban(ban, hapd);
+			return;
+		}
+	}
+
+	eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
+}
+
+static int
+hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	return hostapd_reload_config(hapd->iface, 1);
+}
+
+
+static void
+hostapd_parse_vht_map_blobmsg(uint16_t map)
+{
+	char label[4];
+	int16_t val;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		snprintf(label, 4, "%dss", i + 1);
+
+		val = (map & (BIT(1) | BIT(0))) + 7;
+		blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
+		map = map >> 2;
+	}
+}
+
+static void
+hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
+{
+	void *supported_mcs;
+	void *map;
+	int i;
+
+	static const struct {
+		const char *name;
+		uint32_t flag;
+	} vht_capas[] = {
+		{ "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
+		{ "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
+		blobmsg_add_u8(&b, vht_capas[i].name,
+				!!(vhtc->vht_capabilities_info & vht_capas[i].flag));
+
+	supported_mcs = blobmsg_open_table(&b, "mcs_map");
+
+	/* RX map */
+	map = blobmsg_open_table(&b, "rx");
+	hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
+	blobmsg_close_table(&b, map);
+
+	/* TX map */
+	map = blobmsg_open_table(&b, "tx");
+	hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
+	blobmsg_close_table(&b, map);
+
+	blobmsg_close_table(&b, supported_mcs);
+}
+
+static void
+hostapd_parse_capab_blobmsg(struct sta_info *sta)
+{
+	void *r, *v;
+
+	v = blobmsg_open_table(&b, "capabilities");
+
+	if (sta->vht_capabilities) {
+		r = blobmsg_open_table(&b, "vht");
+		hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
+		blobmsg_close_table(&b, r);
+	}
+
+	/* ToDo: Add HT / HE capability parsing */
+
+	blobmsg_close_table(&b, v);
+}
+
+static int
+hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct hostap_sta_driver_data sta_driver_data;
+	struct sta_info *sta;
+	void *list, *c;
+	char mac_buf[20];
+	static const struct {
+		const char *name;
+		uint32_t flag;
+	} sta_flags[] = {
+		{ "auth", WLAN_STA_AUTH },
+		{ "assoc", WLAN_STA_ASSOC },
+		{ "authorized", WLAN_STA_AUTHORIZED },
+		{ "preauth", WLAN_STA_PREAUTH },
+		{ "wds", WLAN_STA_WDS },
+		{ "wmm", WLAN_STA_WMM },
+		{ "ht", WLAN_STA_HT },
+		{ "vht", WLAN_STA_VHT },
+		{ "he", WLAN_STA_HE },
+		{ "wps", WLAN_STA_WPS },
+		{ "mfp", WLAN_STA_MFP },
+	};
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	list = blobmsg_open_table(&b, "clients");
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		void *r;
+		int i;
+
+		sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
+		c = blobmsg_open_table(&b, mac_buf);
+		for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
+			blobmsg_add_u8(&b, sta_flags[i].name,
+				       !!(sta->flags & sta_flags[i].flag));
+
+#ifdef CONFIG_MBO
+		blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
+#endif
+
+		r = blobmsg_open_array(&b, "rrm");
+		for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
+			blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
+		blobmsg_close_array(&b, r);
+
+		r = blobmsg_open_array(&b, "extended_capabilities");
+		/* Check if client advertises extended capabilities */
+		if (sta->ext_capability && sta->ext_capability[0] > 0) {
+			for (i = 0; i < sta->ext_capability[0]; i++) {
+				blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
+			}
+		}
+		blobmsg_close_array(&b, r);
+
+		blobmsg_add_u32(&b, "aid", sta->aid);
+#ifdef CONFIG_TAXONOMY
+		r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
+		if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
+			blobmsg_add_string_buffer(&b);
+#endif
+
+		/* Driver information */
+		if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
+			r = blobmsg_open_table(&b, "bytes");
+			blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
+			blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "airtime");
+			blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
+			blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "packets");
+			blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
+			blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "rate");
+			/* Rate in kbits */
+			blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
+			blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
+			blobmsg_close_table(&b, r);
+			blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
+		}
+
+		hostapd_parse_capab_blobmsg(sta);
+
+		blobmsg_close_table(&b, c);
+	}
+	blobmsg_close_array(&b, list);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
+	blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *req, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
+	struct os_reltime now;
+	char ssid[SSID_MAX_LEN + 1];
+	char phy_name[17];
+	size_t ssid_len = SSID_MAX_LEN;
+	u8 channel = 0, op_class = 0;
+
+	if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
+		ssid_len = hapd->conf->ssid.ssid_len;
+	
+	ieee80211_freq_to_channel_ext(hapd->iface->freq,
+				      hapd->iconf->secondary_channel,
+				      hostapd_get_oper_chwidth(hapd->iconf),
+				      &op_class, &channel);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
+	blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
+
+	memset(ssid, 0, SSID_MAX_LEN + 1);
+	memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
+	blobmsg_add_string(&b, "ssid", ssid);
+
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	blobmsg_add_u32(&b, "channel", channel);
+	blobmsg_add_u32(&b, "op_class", op_class);
+	blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
+#ifdef CONFIG_IEEE80211AX
+	blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
+					 hapd->iface->conf->he_op.he_bss_color);
+#else
+	blobmsg_add_u32(&b, "bss_color", -1);
+#endif
+
+	snprintf(phy_name, 17, "%s", hapd->iface->phy);
+	blobmsg_add_string(&b, "phy", phy_name);
+
+	/* RRM */
+	rrm_table = blobmsg_open_table(&b, "rrm");
+	blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
+	blobmsg_close_table(&b, rrm_table);
+
+	/* WNM */
+	wnm_table = blobmsg_open_table(&b, "wnm");
+	blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
+	blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
+	blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
+	blobmsg_close_table(&b, wnm_table);
+
+	/* Airtime */
+	airtime_table = blobmsg_open_table(&b, "airtime");
+	blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
+	blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
+	blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
+	blobmsg_close_table(&b, airtime_table);
+
+	/* DFS */
+	dfs_table = blobmsg_open_table(&b, "dfs");
+	blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
+	blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
+	os_reltime_age(&hapd->iface->dfs_cac_start, &now);
+	blobmsg_add_u32(&b, "cac_seconds_left",
+			hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
+	blobmsg_close_table(&b, dfs_table);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+enum {
+	NOTIFY_RESPONSE,
+	__NOTIFY_MAX
+};
+
+static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
+	[NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__NOTIFY_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct wpabuf *elems;
+	const char *pos;
+	size_t len;
+
+	blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
+		      blob_data(msg), blob_len(msg));
+
+	if (!tb[NOTIFY_RESPONSE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	DEL_CLIENT_ADDR,
+	DEL_CLIENT_REASON,
+	DEL_CLIENT_DEAUTH,
+	DEL_CLIENT_BAN_TIME,
+	__DEL_CLIENT_MAX
+};
+
+static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
+	[DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
+	[DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
+	[DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__DEL_CLIENT_MAX];
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct sta_info *sta;
+	bool deauth = false;
+	int reason;
+	u8 addr[ETH_ALEN];
+
+	blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[DEL_CLIENT_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[DEL_CLIENT_REASON])
+		reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
+
+	if (tb[DEL_CLIENT_DEAUTH])
+		deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		if (deauth) {
+			hostapd_drv_sta_deauth(hapd, addr, reason);
+			ap_sta_deauthenticate(hapd, sta, reason);
+		} else {
+			hostapd_drv_sta_disassoc(hapd, addr, reason);
+			ap_sta_disassociate(hapd, sta, reason);
+		}
+	}
+
+	if (tb[DEL_CLIENT_BAN_TIME])
+		hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
+
+	return 0;
+}
+
+static void
+blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
+{
+	char *s;
+
+	s = blobmsg_alloc_string_buffer(buf, name, 20);
+	sprintf(s, MACSTR, MAC2STR(addr));
+	blobmsg_add_string_buffer(buf);
+}
+
+static int
+hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct ubus_banned_client *ban;
+	void *c;
+
+	blob_buf_init(&b, 0);
+	c = blobmsg_open_array(&b, "clients");
+	avl_for_each_element(&hapd->ubus.banned, ban, avl)
+		blobmsg_add_macaddr(&b, NULL, ban->addr);
+	blobmsg_close_array(&b, c);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+#ifdef CONFIG_WPS
+static int
+hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = hostapd_wps_button_pushed(hapd, NULL);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+
+static const char * pbc_status_enum_str(enum pbc_status status)
+{
+	switch (status) {
+	case WPS_PBC_STATUS_DISABLE:
+		return "Disabled";
+	case WPS_PBC_STATUS_ACTIVE:
+		return "Active";
+	case WPS_PBC_STATUS_TIMEOUT:
+		return "Timed-out";
+	case WPS_PBC_STATUS_OVERLAP:
+		return "Overlap";
+	default:
+		return "Unknown";
+	}
+}
+
+static int
+hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	blob_buf_init(&b, 0);
+
+	blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
+	blobmsg_add_string(&b, "last_wps_result",
+			   (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
+			    "Success":
+			    (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
+			     "Failed" : "None")));
+
+	/* If status == Failure - Add possible Reasons */
+	if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
+	   hapd->wps_stats.failure_reason > 0)
+		blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
+
+	if (hapd->wps_stats.status)
+		blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = hostapd_wps_cancel(hapd);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+static int
+hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = ieee802_11_set_beacon(hapd);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+enum {
+	CONFIG_IFACE,
+	CONFIG_FILE,
+	__CONFIG_MAX
+};
+
+enum {
+	CSA_FREQ,
+	CSA_BCN_COUNT,
+	CSA_CENTER_FREQ1,
+	CSA_CENTER_FREQ2,
+	CSA_BANDWIDTH,
+	CSA_SEC_CHANNEL_OFFSET,
+	CSA_HT,
+	CSA_VHT,
+	CSA_HE,
+	CSA_BLOCK_TX,
+	CSA_FORCE,
+	__CSA_MAX
+};
+
+static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
+	[CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
+	[CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
+	[CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
+	[CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
+	[CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
+	[CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
+	[CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
+	[CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
+	[CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
+	[CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
+	[CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
+};
+
+
+static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_iface *iface = eloop_data;
+	struct hostapd_freq_params *freq_params = user_ctx;
+
+	hostapd_switch_channel_fallback(iface, freq_params);
+}
+
+#ifdef NEED_AP_MLME
+static int
+hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CSA_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_config *iconf = hapd->iface->conf;
+	struct hostapd_freq_params *freq_params;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	struct csa_settings css = {
+		.freq_params = {
+			.ht_enabled = iconf->ieee80211n,
+			.vht_enabled = iconf->ieee80211ac,
+			.he_enabled = iconf->ieee80211ax,
+			.sec_channel_offset = iconf->secondary_channel,
+		}
+	};
+	u8 chwidth = hostapd_get_oper_chwidth(iconf);
+	u8 seg0 = 0, seg1 = 0;
+	int ret = UBUS_STATUS_OK;
+	int i;
+
+	blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CSA_FREQ])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	switch (iconf->vht_oper_chwidth) {
+	case CHANWIDTH_USE_HT:
+		if (iconf->secondary_channel)
+			css.freq_params.bandwidth = 40;
+		else
+			css.freq_params.bandwidth = 20;
+		break;
+	case CHANWIDTH_160MHZ:
+		css.freq_params.bandwidth = 160;
+		break;
+	default:
+		css.freq_params.bandwidth = 80;
+		break;
+	}
+
+	css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
+
+#define SET_CSA_SETTING(name, field, type) \
+	do { \
+		if (tb[name]) \
+			css.field = blobmsg_get_ ## type(tb[name]); \
+	} while(0)
+
+	SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
+	SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
+	SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
+	SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
+	SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
+	SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
+	SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
+	SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
+	SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
+
+	css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
+	if (!css.freq_params.channel)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	switch (css.freq_params.bandwidth) {
+	case 160:
+		chwidth = CHANWIDTH_160MHZ;
+		break;
+	case 80:
+		chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
+		break;
+	default:
+		chwidth = CHANWIDTH_USE_HT;
+		break;
+	}
+
+	hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
+				css.freq_params.freq,
+				css.freq_params.channel, iconf->enable_edmg,
+				iconf->edmg_channel,
+				css.freq_params.ht_enabled,
+				css.freq_params.vht_enabled,
+				css.freq_params.he_enabled,
+				css.freq_params.eht_enabled,
+				css.freq_params.sec_channel_offset,
+				chwidth, seg0, seg1,
+				iconf->vht_capab,
+				mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				NULL,
+				mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+				NULL);
+
+	for (i = 0; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+
+		if (hostapd_switch_channel(bss, &css) != 0)
+			ret = UBUS_STATUS_NOT_SUPPORTED;
+	}
+
+	if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
+		return ret;
+
+	freq_params = malloc(sizeof(*freq_params));
+	memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
+	eloop_register_timeout(0, 1, switch_chan_fallback_cb,
+			       hapd->iface, freq_params);
+
+	return 0;
+#undef SET_CSA_SETTING
+}
+#endif
+
+enum {
+	VENDOR_ELEMENTS,
+	__VENDOR_ELEMENTS_MAX
+};
+
+static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
+	/* vendor elements are provided as hex-string */
+	[VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_bss_config *bss = hapd->conf;
+	struct wpabuf *elems;
+	const char *pos;
+	size_t len;
+
+	blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
+		      blob_data(msg), blob_len(msg));
+
+	if (!tb[VENDOR_ELEMENTS])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
+	len = os_strlen(pos);
+	if (len & 0x01)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+	len /= 2;
+	if (len == 0) {
+		wpabuf_free(bss->vendor_elements);
+		bss->vendor_elements = NULL;
+		return 0;
+	}
+
+	elems = wpabuf_alloc(len);
+	if (elems == NULL)
+		return 1;
+
+	if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+		wpabuf_free(elems);
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	wpabuf_free(bss->vendor_elements);
+	bss->vendor_elements = elems;
+
+	/* update beacons if vendor elements were set successfully */
+	if (ieee802_11_update_beacons(hapd->iface) != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+	return UBUS_STATUS_OK;
+}
+
+static void
+hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
+{
+	const u8 *data;
+	char *str;
+	int len;
+
+	blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
+
+	str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
+	memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
+	str[nr->ssid.ssid_len] = 0;
+	blobmsg_add_string_buffer(&b);
+
+	len = wpabuf_len(nr->nr);
+	str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
+	wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
+	blobmsg_add_string_buffer(&b);
+}
+
+enum {
+	BSS_MGMT_EN_NEIGHBOR,
+	BSS_MGMT_EN_BEACON,
+	BSS_MGMT_EN_LINK_MEASUREMENT,
+#ifdef CONFIG_WNM_AP
+	BSS_MGMT_EN_BSS_TRANSITION,
+#endif
+	__BSS_MGMT_EN_MAX
+};
+
+static bool
+__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+	uint32_t flags;
+
+	switch (flag) {
+	case BSS_MGMT_EN_NEIGHBOR:
+		if (bss->radio_measurements[0] &
+		    WLAN_RRM_CAPS_NEIGHBOR_REPORT)
+			return false;
+
+		bss->radio_measurements[0] |=
+			WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+		hostapd_neighbor_set_own_report(hapd);
+		return true;
+	case BSS_MGMT_EN_BEACON:
+		flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+			WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+			WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+		if (bss->radio_measurements[0] & flags == flags)
+			return false;
+
+		bss->radio_measurements[0] |= (u8) flags;
+		return true;
+	case BSS_MGMT_EN_LINK_MEASUREMENT:
+		flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+		if (bss->radio_measurements[0] & flags == flags)
+			return false;
+
+		bss->radio_measurements[0] |= (u8) flags;
+		return true;
+#ifdef CONFIG_WNM_AP
+	case BSS_MGMT_EN_BSS_TRANSITION:
+		if (bss->bss_transition)
+			return false;
+
+		bss->bss_transition = 1;
+		return true;
+#endif
+	}
+}
+
+static void
+__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
+{
+	bool update = false;
+	int i;
+
+	for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
+		if (!(flags & (1 << i)))
+			continue;
+
+		update |= __hostapd_bss_mgmt_enable_f(hapd, i);
+	}
+
+	if (update)
+		ieee802_11_update_beacons(hapd->iface);
+}
+
+
+static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
+	[BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
+	[BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
+	[BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
+#ifdef CONFIG_WNM_AP
+	[BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
+#endif
+};
+
+static int
+hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct blob_attr *tb[__BSS_MGMT_EN_MAX];
+	struct blob_attr *cur;
+	uint32_t flags = 0;
+	int i;
+	bool neigh = false, beacon = false;
+
+	blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
+
+	for (i = 0; i < ARRAY_SIZE(tb); i++) {
+		if (!tb[i] || !blobmsg_get_bool(tb[i]))
+			continue;
+
+		flags |= (1 << i);
+	}
+
+	__hostapd_bss_mgmt_enable(hapd, flags);
+
+	return 0;
+}
+
+
+static void
+hostapd_rrm_nr_enable(struct hostapd_data *hapd)
+{
+	__hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
+}
+
+static int
+hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *req, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_neighbor_entry *nr;
+	void *c;
+
+	hostapd_rrm_nr_enable(hapd);
+
+	nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
+	if (!nr)
+		return UBUS_STATUS_NOT_FOUND;
+
+	blob_buf_init(&b, 0);
+
+	c = blobmsg_open_array(&b, "value");
+	hostapd_rrm_print_nr(nr);
+	blobmsg_close_array(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_neighbor_entry *nr;
+	void *c;
+
+	hostapd_rrm_nr_enable(hapd);
+	blob_buf_init(&b, 0);
+
+	c = blobmsg_open_array(&b, "list");
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+		void *cur;
+
+		if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+			continue;
+
+		cur = blobmsg_open_array(&b, NULL);
+		hostapd_rrm_print_nr(nr);
+		blobmsg_close_array(&b, cur);
+	}
+	blobmsg_close_array(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+enum {
+	NR_SET_LIST,
+	__NR_SET_LIST_MAX
+};
+
+static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
+	[NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
+};
+
+
+static void
+hostapd_rrm_nr_clear(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr;
+
+restart:
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+		if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+			continue;
+
+		hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
+		goto restart;
+	}
+}
+
+static int
+hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	static const struct blobmsg_policy nr_e_policy[] = {
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+	};
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct blob_attr *tb_l[__NR_SET_LIST_MAX];
+	struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
+	struct blob_attr *cur;
+	int rem;
+
+	hostapd_rrm_nr_enable(hapd);
+
+	blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
+	if (!tb_l[NR_SET_LIST])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	hostapd_rrm_nr_clear(hapd);
+	blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
+		struct wpa_ssid_value ssid;
+		struct wpabuf *data;
+		u8 bssid[ETH_ALEN];
+		char *s, *nr_s;
+
+		blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
+		if (!tb[0] || !tb[1] || !tb[2])
+			goto invalid;
+
+		/* Neighbor Report binary */
+		nr_s = blobmsg_get_string(tb[2]);
+		data = wpabuf_parse_bin(nr_s);
+		if (!data)
+			goto invalid;
+
+		/* BSSID */
+		s = blobmsg_get_string(tb[0]);
+		if (strlen(s) == 0) {
+			/* Copy BSSID from neighbor report */
+			if (hwaddr_compact_aton(nr_s, bssid))
+				goto invalid;
+		} else if (hwaddr_aton(s, bssid)) {
+			goto invalid;
+		}
+
+		/* SSID */
+		s = blobmsg_get_string(tb[1]);
+		if (strlen(s) == 0) {
+			/* Copy SSID from hostapd BSS conf */
+			memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
+		} else {
+			ssid.ssid_len = strlen(s);
+			if (ssid.ssid_len > sizeof(ssid.ssid))
+				goto invalid;
+
+			memcpy(&ssid, s, ssid.ssid_len);
+		}
+
+		hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
+		wpabuf_free(data);
+		continue;
+
+invalid:
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	return 0;
+}
+
+enum {
+	BEACON_REQ_ADDR,
+	BEACON_REQ_MODE,
+	BEACON_REQ_OP_CLASS,
+	BEACON_REQ_CHANNEL,
+	BEACON_REQ_DURATION,
+	BEACON_REQ_BSSID,
+	BEACON_REQ_SSID,
+	__BEACON_REQ_MAX,
+};
+
+static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
+	[BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
+	[BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *ureq, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BEACON_REQ_MAX];
+	struct blob_attr *cur;
+	struct wpabuf *req;
+	u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+	u8 addr[ETH_ALEN];
+	int mode, rem, ret;
+	int buf_len = 13;
+
+	blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
+	    !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BEACON_REQ_SSID])
+		buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
+
+	mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
+	if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BEACON_REQ_BSSID] &&
+	    hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	req = wpabuf_alloc(buf_len);
+	if (!req)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	/* 1: regulatory class */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
+
+	/* 2: channel number */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
+
+	/* 3-4: randomization interval */
+	wpabuf_put_le16(req, 0);
+
+	/* 5-6: duration */
+	wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
+
+	/* 7: mode */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
+
+	/* 8-13: BSSID */
+	wpabuf_put_data(req, bssid, ETH_ALEN);
+
+	if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
+		wpabuf_put_u8(req, WLAN_EID_SSID);
+		wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
+		wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
+	}
+
+	ret = hostapd_send_beacon_req(hapd, addr, 0, req);
+	if (ret < 0)
+		return -ret;
+
+	return 0;
+}
+
+enum {
+	LM_REQ_ADDR,
+	LM_REQ_TX_POWER_USED,
+	LM_REQ_TX_POWER_MAX,
+	__LM_REQ_MAX,
+};
+
+static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
+	[LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
+	[LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *ureq, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__LM_REQ_MAX];
+	struct wpabuf *buf;
+	u8 addr[ETH_ALEN];
+	int ret;
+	int8_t txp_used, txp_max;
+
+	txp_used = 0;
+	txp_max = 0;
+
+	blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[LM_REQ_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[LM_REQ_TX_POWER_USED])
+		txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
+
+	if (tb[LM_REQ_TX_POWER_MAX])
+		txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
+
+	if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	buf = wpabuf_alloc(5);
+	if (!buf)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, 1);
+	/* TX-Power used */
+	wpabuf_put_u8(buf, txp_used);
+	/* Max TX Power */
+	wpabuf_put_u8(buf, txp_max);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+	if (ret < 0)
+		return -ret;
+
+	return 0;
+}
+
+
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
+	const u8 *pos, *end;
+	u8 token;
+
+	end = data + len;
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+
+	if (end - pos < 8)
+		return;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", mgmt->sa);
+	blobmsg_add_u16(&b, "dialog-token", token);
+	blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
+	blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
+	blobmsg_add_u16(&b, "rcpi", pos[6]);
+	blobmsg_add_u16(&b, "rsni", pos[7]);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
+}
+
+
+#ifdef CONFIG_WNM_AP
+
+static int
+hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
+		    u16 disassoc_timer, u8 validity_period, u8 dialog_token,
+		    struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
+{
+	struct blob_attr *cur;
+	struct sta_info *sta;
+	int nr_len = 0;
+	int rem;
+	u8 *nr = NULL;
+	u8 req_mode = 0;
+	u8 mbo[10];
+	size_t mbo_len = 0;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return UBUS_STATUS_NOT_FOUND;
+
+	if (neighbors) {
+		u8 *nr_cur;
+
+		if (blobmsg_check_array(neighbors,
+					BLOBMSG_TYPE_STRING) < 0)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+		blobmsg_for_each_attr(cur, neighbors, rem) {
+			int len = strlen(blobmsg_get_string(cur));
+
+			if (len % 2)
+				return UBUS_STATUS_INVALID_ARGUMENT;
+
+			nr_len += (len / 2) + 2;
+		}
+
+		if (nr_len) {
+			nr = os_zalloc(nr_len);
+			if (!nr)
+				return UBUS_STATUS_UNKNOWN_ERROR;
+		}
+
+		nr_cur = nr;
+		blobmsg_for_each_attr(cur, neighbors, rem) {
+			int len = strlen(blobmsg_get_string(cur)) / 2;
+
+			*nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
+			*nr_cur++ = (u8) len;
+			if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
+				free(nr);
+				return UBUS_STATUS_INVALID_ARGUMENT;
+			}
+
+			nr_cur += len;
+		}
+	}
+
+	if (nr)
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+
+	if (abridged)
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+
+	if (disassoc_imminent)
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+	u8 *mbo_pos = mbo;
+
+	if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+	*mbo_pos++ = 1;
+	*mbo_pos++ = mbo_reason;
+	*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+	*mbo_pos++ = 1;
+	*mbo_pos++ = cell_pref;
+
+	if (reassoc_delay) {
+		*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+		*mbo_pos++ = 2;
+		WPA_PUT_LE16(mbo_pos, reassoc_delay);
+		mbo_pos += 2;
+	}
+
+	mbo_len = mbo_pos - mbo;
+#endif
+
+	if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
+				dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	return 0;
+}
+
+enum {
+	BSS_TR_ADDR,
+	BSS_TR_DA_IMMINENT,
+	BSS_TR_DA_TIMER,
+	BSS_TR_VALID_PERIOD,
+	BSS_TR_NEIGHBORS,
+	BSS_TR_ABRIDGED,
+	BSS_TR_DIALOG_TOKEN,
+#ifdef CONFIG_MBO
+	BSS_TR_MBO_REASON,
+	BSS_TR_CELL_PREF,
+	BSS_TR_REASSOC_DELAY,
+#endif
+	__BSS_TR_DISASSOC_MAX
+};
+
+static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
+	[BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
+	[BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
+#ifdef CONFIG_MBO
+	[BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
+#endif
+};
+
+static int
+hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
+			       struct ubus_request_data *ureq, const char *method,
+			       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
+	struct sta_info *sta;
+	u32 da_timer = 0;
+	u32 valid_period = 0;
+	u8 addr[ETH_ALEN];
+	u32 dialog_token = 1;
+	bool abridged;
+	bool da_imminent;
+	u8 mbo_reason;
+	u8 cell_pref;
+	u8 reassoc_delay;
+
+	blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BSS_TR_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BSS_TR_DA_TIMER])
+		da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
+
+	if (tb[BSS_TR_VALID_PERIOD])
+		valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
+
+	if (tb[BSS_TR_DIALOG_TOKEN])
+		dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
+
+	da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
+	abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
+
+#ifdef CONFIG_MBO
+	if (tb[BSS_TR_MBO_REASON])
+		mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
+
+	if (tb[BSS_TR_CELL_PREF])
+		cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
+
+	if (tb[BSS_TR_REASSOC_DELAY])
+		reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
+#endif
+
+	return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
+				   dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
+}
+#endif
+
+#ifdef CONFIG_AIRTIME_POLICY
+enum {
+	UPDATE_AIRTIME_STA,
+	UPDATE_AIRTIME_WEIGHT,
+	__UPDATE_AIRTIME_MAX,
+};
+
+
+static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
+	[UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
+	[UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
+			   struct ubus_request_data *ureq, const char *method,
+			   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
+	struct sta_info *sta = NULL;
+	u8 addr[ETH_ALEN];
+	int weight;
+
+	blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[UPDATE_AIRTIME_WEIGHT])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
+
+	if (!tb[UPDATE_AIRTIME_STA]) {
+		if (!weight)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+		hapd->conf->airtime_weight = weight;
+		return 0;
+	}
+
+	if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return UBUS_STATUS_NOT_FOUND;
+
+	sta->dyn_airtime_weight = weight;
+	airtime_policy_new_sta(hapd, sta);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_TAXONOMY
+static const struct blobmsg_policy addr_policy[] = {
+	{ "address", BLOBMSG_TYPE_STRING }
+};
+
+static bool
+hostapd_add_b64_data(const char *name, const struct wpabuf *buf)
+{
+	char *str;
+
+	if (!buf)
+		return false;
+
+	str = blobmsg_alloc_string_buffer(&b, name, B64_ENCODE_LEN(wpabuf_len(buf)));
+	b64_encode(wpabuf_head(buf), wpabuf_len(buf), str, B64_ENCODE_LEN(wpabuf_len(buf)));
+	blobmsg_add_string_buffer(&b);
+
+	return true;
+}
+
+static int
+hostapd_bss_get_sta_ies(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb;
+	struct sta_info *sta;
+	u8 addr[ETH_ALEN];
+
+	blobmsg_parse(addr_policy, 1, &tb, blobmsg_data(msg), blobmsg_len(msg));
+
+	if (!tb || hwaddr_aton(blobmsg_data(tb), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || (!sta->probe_ie_taxonomy && !sta->assoc_ie_taxonomy))
+		return UBUS_STATUS_NOT_FOUND;
+
+	blob_buf_init(&b, 0);
+	hostapd_add_b64_data("probe_ie", sta->probe_ie_taxonomy);
+	hostapd_add_b64_data("assoc_ie", sta->assoc_ie_taxonomy);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+#endif
+
+
+static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
+	UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
+#ifdef CONFIG_TAXONOMY
+	UBUS_METHOD("get_sta_ies", hostapd_bss_get_sta_ies, addr_policy),
+#endif
+	UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
+	UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
+#ifdef CONFIG_AIRTIME_POLICY
+	UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
+#endif
+	UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
+#ifdef CONFIG_WPS
+	UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
+	UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
+	UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
+#endif
+	UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
+	UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
+#ifdef NEED_AP_MLME
+	UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
+#endif
+	UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
+	UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
+	UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
+	UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
+	UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
+	UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
+	UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
+	UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
+#ifdef CONFIG_WNM_AP
+	UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
+#endif
+};
+
+static struct ubus_object_type bss_object_type =
+	UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
+
+static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
+{
+	return memcmp(k1, k2, ETH_ALEN);
+}
+
+void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+	struct ubus_object *obj = &hapd->ubus.obj;
+	char *name;
+	int ret;
+
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED)
+		return;
+#endif
+
+	if (!hostapd_ubus_init())
+		return;
+
+	if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
+		return;
+
+	avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
+	obj->name = name;
+	obj->type = &bss_object_type;
+	obj->methods = bss_object_type.methods;
+	obj->n_methods = bss_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	hostapd_ubus_ref_inc();
+}
+
+void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+	struct ubus_object *obj = &hapd->ubus.obj;
+	char *name = (char *) obj->name;
+
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED)
+		return;
+#endif
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		hostapd_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+static void
+hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+			 const char *action)
+{
+	struct vlan_description *desc = &vlan->vlan_desc;
+	void *c;
+	int i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "ifname", vlan->ifname);
+	blobmsg_add_string(&b, "bridge", vlan->bridge);
+	blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
+
+	if (desc->notempty) {
+		blobmsg_add_u32(&b, "untagged", desc->untagged);
+		c = blobmsg_open_array(&b, "tagged");
+		for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
+			blobmsg_add_u32(&b, "", desc->tagged[i]);
+		blobmsg_close_array(&b, c);
+	}
+
+	ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
+}
+
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
+}
+
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
+}
+
+struct ubus_event_req {
+	struct ubus_notify_request nreq;
+	int resp;
+};
+
+static void
+ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
+{
+	struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
+
+	ureq->resp = ret;
+}
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+	struct ubus_banned_client *ban;
+	const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
+		[HOSTAPD_UBUS_PROBE_REQ] = "probe",
+		[HOSTAPD_UBUS_AUTH_REQ] = "auth",
+		[HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
+	};
+	const char *type = "mgmt";
+	struct ubus_event_req ureq = {};
+	const u8 *addr;
+
+	if (req->mgmt_frame)
+		addr = req->mgmt_frame->sa;
+	else
+		addr = req->addr;
+
+	ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+	if (ban)
+		return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return WLAN_STATUS_SUCCESS;
+
+	if (req->type < ARRAY_SIZE(types))
+		type = types[req->type];
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	if (req->mgmt_frame)
+		blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
+	if (req->ssi_signal)
+		blobmsg_add_u32(&b, "signal", req->ssi_signal);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+
+	if (req->elems) {
+		if(req->elems->ht_capabilities)
+		{
+			struct ieee80211_ht_capabilities *ht_capabilities;
+			void *ht_cap, *ht_cap_mcs_set, *mcs_set;
+
+
+			ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
+			ht_cap = blobmsg_open_table(&b, "ht_capabilities");
+			blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
+			ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
+			blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
+			blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
+			blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
+			blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
+			mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
+			for (int i = 0; i < 16; i++) {
+				blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
+			}
+			blobmsg_close_array(&b, mcs_set);
+			blobmsg_close_table(&b, ht_cap_mcs_set);
+			blobmsg_close_table(&b, ht_cap);
+		}
+		if(req->elems->vht_capabilities)
+		{
+			struct ieee80211_vht_capabilities *vht_capabilities;
+			void *vht_cap, *vht_cap_mcs_set;
+
+			vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
+			vht_cap = blobmsg_open_table(&b, "vht_capabilities");
+			blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
+			vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
+			blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
+			blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
+			blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
+			blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
+			blobmsg_close_table(&b, vht_cap_mcs_set);
+			blobmsg_close_table(&b, vht_cap);
+		}
+	}
+
+	if (!hapd->ubus.notify_response) {
+		ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
+		return WLAN_STATUS_SUCCESS;
+
+	ureq.nreq.status_cb = ubus_event_cb;
+	ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+	if (ureq.resp)
+		return ureq.resp;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+
+	ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+}
+
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+				    const char *auth_alg)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", sta->addr);
+	if (auth_alg)
+		blobmsg_add_string(&b, "auth-alg", auth_alg);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
+}
+
+void hostapd_ubus_notify_beacon_report(
+	struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
+	struct rrm_measurement_beacon_report *rep, size_t len)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr || !rep)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u16(&b, "op-class", rep->op_class);
+	blobmsg_add_u16(&b, "channel", rep->channel);
+	blobmsg_add_u64(&b, "start-time", rep->start_time);
+	blobmsg_add_u16(&b, "duration", rep->duration);
+	blobmsg_add_u16(&b, "report-info", rep->report_info);
+	blobmsg_add_u16(&b, "rcpi", rep->rcpi);
+	blobmsg_add_u16(&b, "rsni", rep->rsni);
+	blobmsg_add_macaddr(&b, "bssid", rep->bssid);
+	blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
+	blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
+	blobmsg_add_u16(&b, "rep-mode", rep_mode);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
+}
+
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+					int chan_width, int cf1, int cf2)
+{
+	struct hostapd_data *hapd;
+	int i;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u16(&b, "frequency", frequency);
+	blobmsg_add_u16(&b, "width", chan_width);
+	blobmsg_add_u16(&b, "center1", cf1);
+	blobmsg_add_u16(&b, "center2", cf2);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		hapd = iface->bss[i];
+		ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
+	}
+}
+
+#ifdef CONFIG_WNM_AP
+static void hostapd_ubus_notify_bss_transition_add_candidate_list(
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+	char *cl_str;
+	int i;
+
+	if (candidate_list_len == 0)
+		return;
+
+	cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
+	for (i = 0; i < candidate_list_len; i++)
+		snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
+	blobmsg_add_string_buffer(&b);
+
+}
+#endif
+
+void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+	u16 i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u8(&b, "dialog-token", dialog_token);
+	blobmsg_add_u8(&b, "status-code", status_code);
+	blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
+	if (target_bssid)
+		blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
+	
+	hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
+#endif
+}
+
+int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+	struct ubus_event_req ureq = {};
+	char *cl_str;
+	u16 i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return 0;
+
+	if (!addr)
+		return 0;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u8(&b, "dialog-token", dialog_token);
+	blobmsg_add_u8(&b, "reason", reason);
+	hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+	if (!hapd->ubus.notify_response) {
+		ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
+		return 0;
+	}
+
+	if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
+		return 0;
+
+	ureq.nreq.status_cb = ubus_event_cb;
+	ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+	return ureq.resp;
+#endif
+}
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h
new file mode 100644
index 0000000..b0f7c44
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ubus.h
@@ -0,0 +1,154 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef __HOSTAPD_UBUS_H
+#define __HOSTAPD_UBUS_H
+
+enum hostapd_ubus_event_type {
+	HOSTAPD_UBUS_PROBE_REQ,
+	HOSTAPD_UBUS_AUTH_REQ,
+	HOSTAPD_UBUS_ASSOC_REQ,
+	HOSTAPD_UBUS_TYPE_MAX
+};
+
+struct hostapd_ubus_request {
+	enum hostapd_ubus_event_type type;
+	const struct ieee80211_mgmt *mgmt_frame;
+	const struct ieee802_11_elems *elems;
+	int ssi_signal; /* dBm */
+	const u8 *addr;
+};
+
+struct hostapd_iface;
+struct hostapd_data;
+struct hapd_interfaces;
+struct rrm_measurement_beacon_report;
+
+#ifdef UBUS_SUPPORT
+
+#include <libubox/avl.h>
+#include <libubus.h>
+
+struct hostapd_ubus_bss {
+	struct ubus_object obj;
+	struct avl_tree banned;
+	int notify_response;
+};
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface);
+void hostapd_ubus_free_iface(struct hostapd_iface *iface);
+void hostapd_ubus_add_bss(struct hostapd_data *hapd);
+void hostapd_ubus_free_bss(struct hostapd_data *hapd);
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
+void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+				       const u8 *addr, u8 token, u8 rep_mode,
+				       struct rrm_measurement_beacon_report *rep,
+				       size_t len);
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+					int chan_width, int cf1, int cf2);
+
+void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_add(struct hapd_interfaces *interfaces);
+void hostapd_ubus_free(struct hapd_interfaces *interfaces);
+int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+				    const char *auth_alg);
+
+#else
+
+struct hostapd_ubus_bss {};
+
+static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+	return 0;
+}
+
+static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+}
+
+static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
+{
+}
+
+static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+						     const u8 *addr, u8 token,
+						     u8 rep_mode,
+						     struct rrm_measurement_beacon_report *rep,
+						     size_t len)
+{
+}
+static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+						      int chan_width, int cf1, int cf2)
+{
+}
+
+static inline void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+}
+
+static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+	return 0;
+}
+
+static inline void
+hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+			       const char *auth_alg)
+{
+}
+
+#endif
+
+#endif
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c
new file mode 100644
index 0000000..137cb81
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.c
@@ -0,0 +1,394 @@
+#include <sys/un.h>
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include <libubox/uloop.h>
+
+static uc_resource_type_t *global_type, *bss_type, *iface_type;
+static struct hapd_interfaces *interfaces;
+static uc_value_t *global, *bss_registry, *iface_registry;
+static uc_vm_t *vm;
+
+static uc_value_t *
+hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
+{
+	uc_value_t *val;
+
+	if (hapd->ucode.idx)
+		return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
+
+	val = uc_resource_new(bss_type, hapd);
+	wpa_ucode_registry_add(bss_registry, val, &hapd->ucode.idx);
+
+	return val;
+}
+
+static uc_value_t *
+hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
+{
+	uc_value_t *val;
+
+	if (hapd->ucode.idx)
+		return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
+
+	val = uc_resource_new(iface_type, hapd);
+	wpa_ucode_registry_add(iface_registry, val, &hapd->ucode.idx);
+
+	return val;
+}
+
+static void
+hostapd_ucode_update_bss_list(struct hostapd_iface *iface)
+{
+	uc_value_t *ifval, *list;
+	int i;
+
+	list = ucv_array_new(vm);
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+		uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
+		uc_value_t *proto = ucv_prototype_get(val);
+
+		ucv_object_add(proto, "name", ucv_get(ucv_string_new(hapd->conf->iface)));
+		ucv_object_add(proto, "index", ucv_int64_new(i));
+		ucv_array_set(list, i, ucv_get(val));
+	}
+
+	ifval = hostapd_ucode_iface_get_uval(iface);
+	ucv_object_add(ucv_prototype_get(ifval), "bss", ucv_get(list));
+}
+
+static void
+hostapd_ucode_update_interfaces(void)
+{
+	uc_value_t *ifs = ucv_object_new(vm);
+	int i;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+
+		ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
+		hostapd_ucode_update_bss_list(iface);
+	}
+
+	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+	ucv_gc(vm);
+}
+
+static uc_value_t *
+uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *iface = uc_fn_arg(0);
+	int ret;
+
+	if (ucv_type(iface) != UC_STRING)
+		return ucv_int64_new(-1);
+
+	ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
+	hostapd_ucode_update_interfaces();
+
+	return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *iface = uc_fn_arg(0);
+
+	if (ucv_type(iface) != UC_STRING)
+		return NULL;
+
+	hostapd_remove_iface(interfaces, ucv_string_get(iface));
+	hostapd_ucode_update_interfaces();
+
+	return NULL;
+}
+
+static uc_value_t *
+uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+	struct hostapd_bss_config *old_bss;
+	struct hostapd_iface *iface;
+	struct hostapd_config *conf;
+	uc_value_t *file = uc_fn_arg(0);
+	uc_value_t *index = uc_fn_arg(1);
+	unsigned int i, idx = 0;
+	int ret = -1;
+
+	if (!hapd || ucv_type(file) != UC_STRING)
+		goto out;
+
+	if (ucv_type(index) == UC_INTEGER)
+		idx = ucv_int64_get(index);
+
+	iface = hapd->iface;
+	conf = interfaces->config_read_cb(ucv_string_get(file));
+	if (!conf || idx > conf->num_bss || !conf->bss[idx])
+		goto out;
+
+	hostapd_bss_deinit_no_free(hapd);
+	hostapd_drv_stop_ap(hapd);
+	hostapd_free_hapd_data(hapd);
+
+	old_bss = hapd->conf;
+	for (i = 0; i < iface->conf->num_bss; i++)
+		if (iface->conf->bss[i] == hapd->conf)
+			iface->conf->bss[i] = conf->bss[idx];
+	hapd->conf = conf->bss[idx];
+	conf->bss[idx] = old_bss;
+	hostapd_config_free(conf);
+
+	hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
+
+	ret = 0;
+
+out:
+	return ucv_int64_new(ret);
+}
+
+static void
+hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
+			      struct hostapd_bss_config *conf)
+{
+	int i;
+
+	for (i = 0; i < iconf->num_bss; i++)
+		if (iconf->bss[i] == conf)
+			break;
+
+	if (i == iconf->num_bss)
+		return;
+
+	for (i++; i < iconf->num_bss; i++)
+		iconf->bss[i - 1] = iconf->bss[i];
+	iconf->num_bss--;
+}
+
+
+static uc_value_t *
+uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+	struct hostapd_iface *iface;
+	int i, idx;
+
+	if (!hapd || hapd == hapd->iface->bss[0])
+		return NULL;
+
+	iface = hapd->iface;
+	for (idx = 0; idx < iface->num_bss; idx++)
+		if (iface->bss[idx] == hapd)
+			break;
+
+	if (idx == iface->num_bss)
+		return NULL;
+
+	for (i = idx + 1; i < iface->num_bss; i++)
+		iface->bss[i - 1] = iface->bss[i];
+	iface->num_bss--;
+
+	hostapd_drv_stop_ap(hapd);
+	hostapd_bss_deinit(hapd);
+	hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
+	hostapd_config_free_bss(hapd->conf);
+	os_free(hapd);
+
+	hostapd_ucode_update_bss_list(iface);
+	ucv_gc(vm);
+
+	return NULL;
+}
+
+static uc_value_t *
+uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+	struct hostapd_bss_config *bss;
+	struct hostapd_config *conf;
+	struct hostapd_data *hapd;
+	uc_value_t *file = uc_fn_arg(0);
+	uc_value_t *index = uc_fn_arg(1);
+	unsigned int idx = 0;
+	uc_value_t *ret = NULL;
+
+	if (!iface || ucv_type(file) != UC_STRING)
+		goto out;
+
+	if (ucv_type(index) == UC_INTEGER)
+		idx = ucv_int64_get(index);
+
+	conf = interfaces->config_read_cb(ucv_string_get(file));
+	if (!conf || idx > conf->num_bss || !conf->bss[idx])
+		goto out;
+
+	bss = conf->bss[idx];
+	hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+	if (!hapd)
+		goto out;
+
+	hapd->driver = iface->bss[0]->driver;
+	hapd->drv_priv = iface->bss[0]->drv_priv;
+	if (interfaces->ctrl_iface_init &&
+	    interfaces->ctrl_iface_init(hapd) < 0)
+		goto free_hapd;
+
+	if (iface->state == HAPD_IFACE_ENABLED &&
+	    hostapd_setup_bss(hapd, -1, true))
+		goto deinit_ctrl;
+
+	iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+				      sizeof(*iface->bss));
+	iface->bss[iface->num_bss++] = hapd;
+
+	iface->conf->bss = os_realloc_array(iface->conf->bss,
+					    iface->conf->num_bss + 1,
+					    sizeof(*iface->conf->bss));
+	iface->conf->bss[iface->conf->num_bss] = bss;
+	conf->bss[idx] = NULL;
+	ret = hostapd_ucode_bss_get_uval(hapd);
+	hostapd_ucode_update_bss_list(iface);
+	goto out;
+
+deinit_ctrl:
+	if (interfaces->ctrl_iface_deinit)
+		interfaces->ctrl_iface_deinit(hapd);
+free_hapd:
+	hostapd_free_hapd_data(hapd);
+	os_free(hapd);
+out:
+	hostapd_config_free(conf);
+	return ret;
+}
+
+static uc_value_t *
+uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
+{
+	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+	uc_value_t *arg = uc_fn_arg(0);
+	struct sockaddr_storage from = {};
+	static char reply[4096];
+	int reply_len;
+
+	if (!hapd || !interfaces->ctrl_iface_recv ||
+	    ucv_type(arg) != UC_STRING)
+		return NULL;
+
+	reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
+						reply, sizeof(reply),
+						&from, sizeof(from));
+	if (reply_len < 0)
+		return NULL;
+
+	if (reply_len && reply[reply_len - 1] == '\n')
+		reply_len--;
+
+	return ucv_string_new_length(reply, reply_len);
+}
+
+int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+{
+	static const uc_function_list_t global_fns[] = {
+		{ "printf",	uc_wpa_printf },
+		{ "getpid", uc_wpa_getpid },
+		{ "sha1", uc_wpa_sha1 },
+		{ "add_iface", uc_hostapd_add_iface },
+		{ "remove_iface", uc_hostapd_remove_iface },
+	};
+	static const uc_function_list_t bss_fns[] = {
+		{ "ctrl", uc_hostapd_bss_ctrl },
+		{ "set_config", uc_hostapd_bss_set_config },
+		{ "delete", uc_hostapd_bss_delete },
+	};
+	static const uc_function_list_t iface_fns[] = {
+		{ "add_bss", uc_hostapd_iface_add_bss }
+	};
+	uc_value_t *data, *proto;
+
+	interfaces = ifaces;
+	vm = wpa_ucode_create_vm();
+
+	global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
+	bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
+	iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
+
+	bss_registry = ucv_array_new(vm);
+	uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
+
+	iface_registry = ucv_array_new(vm);
+	uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
+
+	global = wpa_ucode_global_init("hostapd", global_type);
+
+	if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
+		goto free_vm;
+	ucv_gc(vm);
+
+	return 0;
+
+free_vm:
+	wpa_ucode_free_vm();
+	return -1;
+}
+
+void hostapd_ucode_free(void)
+{
+	if (wpa_ucode_call_prepare("shutdown") == 0)
+		ucv_put(wpa_ucode_call(0));
+	wpa_ucode_free_vm();
+}
+
+void hostapd_ucode_free_iface(struct hostapd_iface *iface)
+{
+	wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
+}
+
+void hostapd_ucode_add_bss(struct hostapd_data *hapd)
+{
+	uc_value_t *val;
+
+	if (wpa_ucode_call_prepare("bss_add"))
+		return;
+
+	val = hostapd_ucode_bss_get_uval(hapd);
+	uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
+	uc_value_push(ucv_get(val));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
+
+void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf)
+{
+	uc_value_t *val;
+
+	if (wpa_ucode_call_prepare("bss_reload"))
+		return;
+
+	val = hostapd_ucode_bss_get_uval(hapd);
+	uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
+	uc_value_push(ucv_get(val));
+	uc_value_push(ucv_int64_new(reconf));
+	ucv_put(wpa_ucode_call(3));
+	ucv_gc(vm);
+}
+
+void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+{
+	uc_value_t *val;
+
+	val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
+	if (!val)
+		return;
+
+	hapd->ucode.idx = 0;
+	if (wpa_ucode_call_prepare("bss_remove"))
+		return;
+
+	uc_value_push(ucv_string_new(hapd->conf->iface));
+	uc_value_push(ucv_get(val));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.h
new file mode 100644
index 0000000..dbc49e6
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/ap/ucode.h
@@ -0,0 +1,54 @@
+#ifndef __HOSTAPD_AP_UCODE_H
+#define __HOSTAPD_AP_UCODE_H
+
+#include "utils/ucode.h"
+
+struct hostapd_data;
+
+struct hostapd_ucode_bss {
+#ifdef UCODE_SUPPORT
+	int idx;
+#endif
+};
+
+struct hostapd_ucode_iface {
+#ifdef UCODE_SUPPORT
+	int idx;
+#endif
+};
+
+#ifdef UCODE_SUPPORT
+
+int hostapd_ucode_init(struct hapd_interfaces *ifaces);
+
+void hostapd_ucode_free(void);
+void hostapd_ucode_free_iface(struct hostapd_iface *iface);
+void hostapd_ucode_add_bss(struct hostapd_data *hapd);
+void hostapd_ucode_free_bss(struct hostapd_data *hapd);
+void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf);
+
+#else
+
+static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
+{
+	return -EINVAL;
+}
+static inline void hostapd_ucode_free(void)
+{
+}
+static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface)
+{
+}
+static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf)
+{
+}
+static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd)
+{
+}
+static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
+{
+}
+
+#endif
+
+#endif
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/build_features.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/build_features.h
new file mode 100644
index 0000000..553769e
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/build_features.h
@@ -0,0 +1,65 @@
+#ifndef BUILD_FEATURES_H
+#define BUILD_FEATURES_H
+
+static inline int has_feature(const char *feat)
+{
+#if defined(IEEE8021X_EAPOL) || (defined(HOSTAPD) && !defined(CONFIG_NO_RADIUS))
+	if (!strcmp(feat, "eap"))
+		return 1;
+#endif
+#ifdef CONFIG_IEEE80211AC
+	if (!strcmp(feat, "11ac"))
+		return 1;
+#endif
+#ifdef CONFIG_IEEE80211AX
+	if (!strcmp(feat, "11ax"))
+		return 1;
+#endif
+#ifdef CONFIG_IEEE80211R
+	if (!strcmp(feat, "11r"))
+		return 1;
+#endif
+#ifdef CONFIG_ACS
+	if (!strcmp(feat, "acs"))
+		return 1;
+#endif
+#ifdef CONFIG_SAE
+	if (!strcmp(feat, "sae"))
+		return 1;
+#endif
+#ifdef CONFIG_OWE
+	if (!strcmp(feat, "owe"))
+		return 1;
+#endif
+#ifdef CONFIG_SUITEB192
+	if (!strcmp(feat, "suiteb192"))
+		return 1;
+#endif
+#ifdef CONFIG_WEP
+	if (!strcmp(feat, "wep"))
+		return 1;
+#endif
+#ifdef CONFIG_HS20
+	if (!strcmp(feat, "hs20"))
+		return 1;
+#endif
+#ifdef CONFIG_WPS
+	if (!strcmp(feat, "wps"))
+		return 1;
+#endif
+#ifdef CONFIG_FILS
+	if (!strcmp(feat, "fils"))
+		return 1;
+#endif
+#ifdef CONFIG_OCV
+	if (!strcmp(feat, "ocv"))
+		return 1;
+#endif
+#ifdef CONFIG_MESH
+	if (!strcmp(feat, "mesh"))
+		return 1;
+#endif
+	return 0;
+}
+
+#endif /* BUILD_FEATURES_H */
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/ucode.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/ucode.c
new file mode 100644
index 0000000..a92913a
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/ucode.c
@@ -0,0 +1,251 @@
+#include <unistd.h>
+#include "ucode.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include <libubox/uloop.h>
+#include <ucode/compiler.h>
+
+static uc_value_t *registry;
+static uc_vm_t vm;
+static struct uloop_timeout gc_timer;
+
+static void uc_gc_timer(struct uloop_timeout *timeout)
+{
+	ucv_gc(&vm);
+}
+
+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *level = uc_fn_arg(0);
+	uc_value_t *ret, **args;
+	uc_cfn_ptr_t _sprintf;
+	int l = MSG_INFO;
+	int i, start = 0;
+
+	_sprintf = uc_stdlib_function("sprintf");
+	if (!sprintf)
+		return NULL;
+
+	if (ucv_type(level) == UC_INTEGER) {
+		l = ucv_int64_get(level);
+		start++;
+	}
+
+	if (nargs <= start)
+		return NULL;
+
+	ret = _sprintf(vm, nargs - start);
+	if (ucv_type(ret) != UC_STRING)
+		return NULL;
+
+	wpa_printf(l, "%s", ucv_string_get(ret));
+	ucv_put(ret);
+
+	return NULL;
+}
+
+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
+{
+	return ucv_int64_new(getpid());
+}
+
+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
+{
+	u8 hash[SHA1_MAC_LEN];
+	char hash_hex[2 * ARRAY_SIZE(hash) + 1];
+	uc_value_t *val;
+	size_t *lens;
+	const u8 **args;
+	int i;
+
+	if (!nargs)
+		return NULL;
+
+	args = alloca(nargs * sizeof(*args));
+	lens = alloca(nargs * sizeof(*lens));
+	for (i = 0; i < nargs; i++) {
+		val = uc_fn_arg(i);
+		if (ucv_type(val) != UC_STRING)
+			return NULL;
+
+		args[i] = ucv_string_get(val);
+		lens[i] = ucv_string_length(val);
+	}
+
+	if (sha1_vector(nargs, args, lens, hash))
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(hash); i++)
+		sprintf(hash_hex + 2 * i, "%02x", hash[i]);
+
+	return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
+}
+
+uc_vm_t *wpa_ucode_create_vm(void)
+{
+	static uc_parse_config_t config = {
+		.strict_declarations = true,
+		.lstrip_blocks = true,
+		.trim_blocks = true,
+		.raw_mode = true
+	};
+
+	uc_search_path_init(&config.module_search_path);
+	uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
+	uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
+
+	uc_vm_init(&vm, &config);
+
+	uc_stdlib_load(uc_vm_scope_get(&vm));
+	eloop_add_uloop();
+	gc_timer.cb = uc_gc_timer;
+
+	return &vm;
+}
+
+int wpa_ucode_run(const char *script)
+{
+	uc_source_t *source;
+	uc_program_t *prog;
+	uc_value_t *ops;
+	char *err;
+	int ret;
+
+	source = uc_source_new_file(script);
+	if (!source)
+		return -1;
+
+	prog = uc_compile(vm.config, source, &err);
+	uc_source_put(source);
+	if (!prog) {
+		wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
+		return -1;
+	}
+
+	ret = uc_vm_execute(&vm, prog, &ops);
+	uc_program_put(prog);
+	if (ret || !ops)
+		return -1;
+
+	registry = ucv_array_new(&vm);
+	uc_vm_registry_set(&vm, "hostap.registry", registry);
+	ucv_array_set(registry, 0, ucv_get(ops));
+
+	return 0;
+}
+
+int wpa_ucode_call_prepare(const char *fname)
+{
+	uc_value_t *obj, *func;
+
+	if (!registry)
+		return -1;
+
+	obj = ucv_array_get(registry, 0);
+	if (!obj)
+		return -1;
+
+	func = ucv_object_get(obj, fname, NULL);
+	if (!ucv_is_callable(func))
+		return -1;
+
+	uc_vm_stack_push(&vm, ucv_get(obj));
+	uc_vm_stack_push(&vm, ucv_get(func));
+
+	return 0;
+}
+
+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
+{
+	uc_value_t *global = uc_resource_new(global_type, NULL);
+	uc_value_t *proto;
+
+	uc_vm_registry_set(&vm, "hostap.global", global);
+	proto = ucv_prototype_get(global);
+	ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
+
+#define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
+	ADD_CONST(MSG_EXCESSIVE);
+	ADD_CONST(MSG_MSGDUMP);
+	ADD_CONST(MSG_DEBUG);
+	ADD_CONST(MSG_INFO);
+	ADD_CONST(MSG_WARNING);
+	ADD_CONST(MSG_ERROR);
+#undef ADD_CONST
+
+	ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
+
+	return global;
+}
+
+static uc_value_t *wpa_ucode_prototype_clone(uc_value_t *uval)
+{
+	uc_value_t *proto, *proto_new;
+
+	proto = ucv_prototype_get(uval);
+	proto_new = ucv_object_new(&vm);
+
+	ucv_object_foreach(proto, key, val)
+		ucv_object_add(proto_new, key, ucv_get(val));
+	ucv_prototype_set(uval, ucv_get(proto));
+
+	return proto;
+}
+
+void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx)
+{
+	uc_value_t *data;
+	int i = 0;
+
+	while (ucv_array_get(reg, i))
+		i++;
+
+	ucv_array_set(reg, i, ucv_get(val));
+
+	data = ucv_object_new(&vm);
+	ucv_object_add(wpa_ucode_prototype_clone(val), "data", ucv_get(data));
+
+	*idx = i + 1;
+}
+
+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
+{
+	if (!idx)
+		return NULL;
+
+	return ucv_array_get(reg, idx - 1);
+}
+
+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
+{
+	uc_value_t *val = wpa_ucode_registry_get(reg, idx);
+
+	if (val)
+		ucv_array_set(reg, idx - 1, NULL);
+
+	return val;
+}
+
+
+uc_value_t *wpa_ucode_call(size_t nargs)
+{
+	if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
+		return NULL;
+
+	if (!gc_timer.pending)
+		uloop_timeout_set(&gc_timer, 10);
+
+	return uc_vm_stack_pop(&vm);
+}
+
+void wpa_ucode_free_vm(void)
+{
+	if (!vm.config)
+		return;
+
+	uc_search_path_free(&vm.config->module_search_path);
+	uc_vm_free(&vm);
+	registry = NULL;
+	vm = (uc_vm_t){};
+}
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/ucode.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/ucode.h
new file mode 100644
index 0000000..4caf8ad
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/src/utils/ucode.h
@@ -0,0 +1,28 @@
+#ifndef __HOSTAPD_UTILS_UCODE_H
+#define __HOSTAPD_UTILS_UCODE_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include <ucode/lib.h>
+#include <ucode/vm.h>
+
+#define HOSTAPD_UC_PATH	"/usr/share/hostap/"
+
+extern uc_value_t *uc_registry;
+uc_vm_t *wpa_ucode_create_vm(void);
+int wpa_ucode_run(const char *script);
+int wpa_ucode_call_prepare(const char *fname);
+uc_value_t *wpa_ucode_call(size_t nargs);
+void wpa_ucode_free_vm(void);
+
+uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type);
+
+void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx);
+uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx);
+uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx);
+
+uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
+
+#endif
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ubus.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ubus.c
new file mode 100644
index 0000000..1c477f0
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ubus.c
@@ -0,0 +1,280 @@
+/*
+ * wpa_supplicant / ubus support
+ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/wpabuf.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "ubus.h"
+
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static int ctx_ref;
+
+static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct wpa_global, ubus_global);
+}
+
+static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct wpa_supplicant, ubus.obj);
+}
+
+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
+{
+	if (ubus_reconnect(ctx, NULL)) {
+		eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+		return;
+	}
+
+	ubus_add_uloop(ctx);
+}
+
+static void wpas_ubus_connection_lost(struct ubus_context *ctx)
+{
+	uloop_fd_delete(&ctx->sock);
+	eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+}
+
+static bool wpas_ubus_init(void)
+{
+	if (ctx)
+		return true;
+
+	eloop_add_uloop();
+	ctx = ubus_connect(NULL);
+	if (!ctx)
+		return false;
+
+	ctx->connection_lost = wpas_ubus_connection_lost;
+	ubus_add_uloop(ctx);
+
+	return true;
+}
+
+static void wpas_ubus_ref_inc(void)
+{
+	ctx_ref++;
+}
+
+static void wpas_ubus_ref_dec(void)
+{
+	ctx_ref--;
+	if (!ctx)
+		return;
+
+	if (ctx_ref)
+		return;
+
+	uloop_fd_delete(&ctx->sock);
+	ubus_free(ctx);
+	ctx = NULL;
+}
+
+static int
+wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u8(&b, "ht_supported", ht_supported(wpa_s->hw.modes));
+	blobmsg_add_u8(&b, "vht_supported", vht_supported(wpa_s->hw.modes));
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg)
+{
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	if (wpa_supplicant_reload_configuration(wpa_s))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	else
+		return 0;
+}
+
+#ifdef CONFIG_WPS
+enum {
+	WPS_START_MULTI_AP,
+	__WPS_START_MAX
+};
+
+static const struct blobmsg_policy wps_start_policy[] = {
+	[WPS_START_MULTI_AP] = { "multi_ap", BLOBMSG_TYPE_BOOL },
+};
+
+static int
+wpas_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+	struct blob_attr *tb[__WPS_START_MAX], *cur;
+	int multi_ap = 0;
+
+	blobmsg_parse(wps_start_policy, __WPS_START_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
+
+	if (tb[WPS_START_MULTI_AP])
+		multi_ap = blobmsg_get_bool(tb[WPS_START_MULTI_AP]);
+
+	rc = wpas_wps_start_pbc(wpa_s, NULL, 0, multi_ap);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+static int
+wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	rc = wpas_wps_cancel(wpa_s);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+#endif
+
+static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", wpas_bss_reload),
+	UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
+#ifdef CONFIG_WPS
+	UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
+	UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
+#endif
+};
+
+static struct ubus_object_type bss_object_type =
+	UBUS_OBJECT_TYPE("wpas_bss", bss_methods);
+
+void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
+{
+	struct ubus_object *obj = &wpa_s->ubus.obj;
+	char *name;
+	int ret;
+
+	if (!wpas_ubus_init())
+		return;
+
+	if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
+		return;
+
+	obj->name = name;
+	obj->type = &bss_object_type;
+	obj->methods = bss_object_type.methods;
+	obj->n_methods = bss_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	wpas_ubus_ref_inc();
+}
+
+void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
+{
+	struct ubus_object *obj = &wpa_s->ubus.obj;
+	char *name = (char *) obj->name;
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		wpas_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+#ifdef CONFIG_WPS
+void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
+{
+	u16 auth_type;
+	char *ifname, *encryption, *ssid, *key;
+	size_t ifname_len;
+
+	if (!cred)
+		return;
+
+	auth_type = cred->auth_type;
+
+	if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))
+		auth_type = WPS_AUTH_WPA2PSK;
+
+	if (auth_type != WPS_AUTH_OPEN &&
+	    auth_type != WPS_AUTH_WPAPSK &&
+	    auth_type != WPS_AUTH_WPA2PSK) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
+			   "unsupported authentication type 0x%x",
+			   auth_type);
+		return;
+	}
+
+	if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
+		if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
+			wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
+				   "invalid Network Key length %lu",
+				   (unsigned long) cred->key_len);
+			return;
+		}
+	}
+
+	blob_buf_init(&b, 0);
+
+	ifname_len = strlen(wpa_s->ifname);
+	ifname = blobmsg_alloc_string_buffer(&b, "ifname", ifname_len + 1);
+	memcpy(ifname, wpa_s->ifname, ifname_len + 1);
+	ifname[ifname_len] = '\0';
+	blobmsg_add_string_buffer(&b);
+
+	switch (auth_type) {
+		case WPS_AUTH_WPA2PSK:
+			encryption = "psk2";
+			break;
+		case WPS_AUTH_WPAPSK:
+			encryption = "psk";
+			break;
+		default:
+			encryption = "none";
+			break;
+	}
+
+	blobmsg_add_string(&b, "encryption", encryption);
+
+	ssid = blobmsg_alloc_string_buffer(&b, "ssid", cred->ssid_len + 1);
+	memcpy(ssid, cred->ssid, cred->ssid_len);
+	ssid[cred->ssid_len] = '\0';
+	blobmsg_add_string_buffer(&b);
+
+	if (cred->key_len > 0) {
+		key = blobmsg_alloc_string_buffer(&b, "key", cred->key_len + 1);
+		memcpy(key, cred->key, cred->key_len);
+		key[cred->key_len] = '\0';
+		blobmsg_add_string_buffer(&b);
+	}
+
+//	ubus_notify(ctx, &wpa_s->ubus.obj, "wps_credentials", b.head, -1);
+	ubus_send_event(ctx, "wps_credentials", b.head);
+}
+#endif /* CONFIG_WPS */
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ubus.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ubus.h
new file mode 100644
index 0000000..f6681cb
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ubus.h
@@ -0,0 +1,55 @@
+/*
+ * wpa_supplicant / ubus support
+ * Copyright (c) 2018, Daniel Golle <daniel@makrotopia.org>
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef __WPAS_UBUS_H
+#define __WPAS_UBUS_H
+
+struct wpa_supplicant;
+struct wpa_global;
+
+#include "wps_supplicant.h"
+
+#ifdef UBUS_SUPPORT
+#include <libubus.h>
+
+struct wpas_ubus_bss {
+	struct ubus_object obj;
+};
+
+void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
+void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
+
+#ifdef CONFIG_WPS
+void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
+#endif
+
+#else
+struct wpas_ubus_bss {};
+
+static inline void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
+{
+}
+
+static inline void wpas_ubus_add(struct wpa_global *global)
+{
+}
+
+static inline void wpas_ubus_free(struct wpa_global *global)
+{
+}
+#endif
+
+#endif
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ucode.c b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ucode.c
new file mode 100644
index 0000000..eec3980
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ucode.c
@@ -0,0 +1,177 @@
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/ucode.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "ucode.h"
+
+static struct wpa_global *wpa_global;
+static uc_resource_type_t *global_type, *iface_type;
+static uc_value_t *global, *iface_registry;
+static uc_vm_t *vm;
+
+static uc_value_t *
+wpas_ucode_iface_get_uval(struct wpa_supplicant *wpa_s)
+{
+	uc_value_t *val;
+
+	if (wpa_s->ucode.idx)
+		return wpa_ucode_registry_get(iface_registry, wpa_s->ucode.idx);
+
+	val = uc_resource_new(iface_type, wpa_s);
+	wpa_ucode_registry_add(iface_registry, val, &wpa_s->ucode.idx);
+
+	return val;
+}
+
+static void
+wpas_ucode_update_interfaces(void)
+{
+	uc_value_t *ifs = ucv_object_new(vm);
+	struct wpa_supplicant *wpa_s;
+	int i;
+
+	for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
+		ucv_object_add(ifs, wpa_s->ifname, ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
+
+	ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
+	ucv_gc(vm);
+}
+
+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
+{
+	uc_value_t *val;
+
+	if (wpa_ucode_call_prepare("iface_add"))
+		return;
+
+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+	uc_value_push(ucv_get(wpas_ucode_iface_get_uval(wpa_s)));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
+
+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
+{
+	uc_value_t *val;
+
+	val = wpa_ucode_registry_remove(iface_registry, wpa_s->ucode.idx);
+	if (!val)
+		return;
+
+	wpa_s->ucode.idx = 0;
+	if (wpa_ucode_call_prepare("iface_remove"))
+		return;
+
+	uc_value_push(ucv_get(ucv_string_new(wpa_s->ifname)));
+	uc_value_push(ucv_get(val));
+	ucv_put(wpa_ucode_call(2));
+	ucv_gc(vm);
+}
+
+static const char *obj_stringval(uc_value_t *obj, const char *name)
+{
+	uc_value_t *val = ucv_object_get(obj, name, NULL);
+
+	return ucv_string_get(val);
+}
+
+static uc_value_t *
+uc_wpas_add_iface(uc_vm_t *vm, size_t nargs)
+{
+	uc_value_t *info = uc_fn_arg(0);
+	uc_value_t *ifname = ucv_object_get(info, "iface", NULL);
+	uc_value_t *bridge = ucv_object_get(info, "bridge", NULL);
+	uc_value_t *config = ucv_object_get(info, "config", NULL);
+	uc_value_t *ctrl = ucv_object_get(info, "ctrl", NULL);
+	uc_value_t *hapd_ctrl = ucv_object_get(info, "hostapd_ctrl", NULL);
+	struct wpa_interface iface;
+	int ret = -1;
+
+	if (ucv_type(info) != UC_OBJECT)
+		goto out;
+
+	iface = (struct wpa_interface){
+		.driver = "nl80211",
+		.ifname = ucv_string_get(ifname),
+		.bridge_ifname = ucv_string_get(bridge),
+		.confname = ucv_string_get(config),
+		.ctrl_interface = ucv_string_get(ctrl),
+		.hostapd_ctrl = ucv_string_get(hapd_ctrl),
+	};
+
+	if (!iface.ifname || !iface.confname)
+		goto out;
+
+	ret = wpa_supplicant_add_iface(wpa_global, &iface, 0) ? 0 : -1;
+	wpas_ucode_update_interfaces();
+
+out:
+	return ucv_int64_new(ret);
+}
+
+static uc_value_t *
+uc_wpas_remove_iface(uc_vm_t *vm, size_t nargs)
+{
+	struct wpa_supplicant *wpa_s = NULL;
+	uc_value_t *ifname_arg = uc_fn_arg(0);
+	const char *ifname = ucv_string_get(ifname_arg);
+	int ret = -1;
+
+	if (!ifname)
+		goto out;
+
+	for (wpa_s = wpa_global->ifaces; wpa_s; wpa_s = wpa_s->next)
+		if (!strcmp(wpa_s->ifname, ifname))
+			break;
+
+	if (!wpa_s)
+		goto out;
+
+	ret = wpa_supplicant_remove_iface(wpa_global, wpa_s, 0);
+	wpas_ucode_update_interfaces();
+
+out:
+	return ucv_int64_new(ret);
+}
+
+int wpas_ucode_init(struct wpa_global *gl)
+{
+	static const uc_function_list_t global_fns[] = {
+		{ "printf",	uc_wpa_printf },
+		{ "getpid", uc_wpa_getpid },
+		{ "add_iface", uc_wpas_add_iface },
+		{ "remove_iface", uc_wpas_remove_iface },
+	};
+	static const uc_function_list_t iface_fns[] = {
+	};
+	uc_value_t *data, *proto;
+
+	wpa_global = gl;
+	vm = wpa_ucode_create_vm();
+
+	global_type = uc_type_declare(vm, "wpas.global", global_fns, NULL);
+	iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
+
+	iface_registry = ucv_array_new(vm);
+	uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
+
+	global = wpa_ucode_global_init("wpas", global_type);
+
+	if (wpa_ucode_run(HOSTAPD_UC_PATH "wpa_supplicant.uc"))
+		goto free_vm;
+
+	ucv_gc(vm);
+	return 0;
+
+free_vm:
+	wpa_ucode_free_vm();
+	return -1;
+}
+
+void wpas_ucode_free(void)
+{
+	if (wpa_ucode_call_prepare("shutdown") == 0)
+		ucv_put(wpa_ucode_call(0));
+	wpa_ucode_free_vm();
+}
diff --git a/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ucode.h b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ucode.h
new file mode 100644
index 0000000..fcd2313
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/src-2.10.3/wpa_supplicant/ucode.h
@@ -0,0 +1,38 @@
+#ifndef __WPAS_UCODE_H
+#define __WPAS_UCODE_H
+
+#include "utils/ucode.h"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+struct wpas_ucode_bss {
+#ifdef UCODE_SUPPORT
+	unsigned int idx;
+#endif
+};
+
+#ifdef UCODE_SUPPORT
+int wpas_ucode_init(struct wpa_global *gl);
+void wpas_ucode_free(void);
+void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s);
+void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s);
+#else
+static inline int wpas_ucode_init(struct wpa_global *gl)
+{
+	return -EINVAL;
+}
+static inline void wpas_ucode_free(void)
+{
+}
+static inline void wpas_ucode_add_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_ucode_free_bss(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif
+
+#endif
diff --git a/recipes-wifi/wpa-supplicant/files/wpa_supplicant.uc b/recipes-wifi/wpa-supplicant/files/wpa_supplicant.uc
new file mode 100644
index 0000000..22cb130
--- /dev/null
+++ b/recipes-wifi/wpa-supplicant/files/wpa_supplicant.uc
@@ -0,0 +1,161 @@
+let libubus = require("ubus");
+import { open, readfile } from "fs";
+import { wdev_create, wdev_remove, is_equal, vlist_new } from "common";
+
+let ubus = libubus.connect();
+
+wpas.data.config = {};
+
+function iface_stop(iface)
+{
+	let ifname = iface.config.iface;
+
+	wpas.remove_iface(ifname);
+	wdev_remove(ifname);
+	iface.running = false;
+}
+
+function iface_start(phy, iface)
+{
+	if (iface.running)
+		return;
+
+	let ifname = iface.config.iface;
+
+	wdev_remove(ifname);
+	let ret = wdev_create(phy, ifname, iface.config);
+	if (ret)
+		wpas.printf(`Failed to create device ${ifname}: ${ret}`);
+	wpas.add_iface(iface.config);
+	iface.running = true;
+}
+
+function iface_cb(new_if, old_if)
+{
+	if (old_if && new_if && is_equal(old_if.config, new_if.config)) {
+		new_if.running = old_if.running;
+		return;
+	}
+
+	if (old_if && old_if.running)
+		iface_stop(old_if);
+}
+
+function prepare_config(config)
+{
+	config.config_data = readfile(config.config);
+
+	return { config: config };
+}
+
+function set_config(phy_name, config_list)
+{
+	let phy = wpas.data.config[phy_name];
+
+	if (!phy) {
+		phy = vlist_new(iface_cb, false);
+		wpas.data.config[phy_name] = phy;
+	}
+
+	let values = [];
+	for (let config in config_list)
+		push(values, [ config.iface, prepare_config(config) ]);
+
+	phy.update(values);
+}
+
+function start_pending(phy_name)
+{
+	let phy = wpas.data.config[phy_name];
+
+	for (let ifname in phy.data)
+		iface_start(phy_name, phy.data[ifname]);
+}
+
+let main_obj = {
+	config_set: {
+		args: {
+			phy: "",
+			config: [],
+			defer: true,
+		},
+		call: function(req) {
+			if (!req.args.phy)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			try {
+				if (req.args.config)
+					set_config(req.args.phy, req.args.config);
+
+				if (!req.args.defer)
+					start_pending(req.args.phy);
+			} catch (e) {
+				wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
+				return libubus.STATUS_INVALID_ARGUMENT;
+			}
+
+			return {
+				pid: wpas.getpid()
+			};
+		}
+	},
+	config_add: {
+		args: {
+			driver: "",
+			iface: "",
+			bridge: "",
+			hostapd_ctrl: "",
+			ctrl: "",
+			config: "",
+		},
+		call: function(req) {
+			if (!req.args.iface || !req.args.config)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			if (wpas.add_iface(req.args) < 0)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			return {
+				pid: wpas.getpid()
+			};
+		}
+	},
+	config_remove: {
+		args: {
+			iface: ""
+		},
+		call: function(req) {
+			if (!req.args.iface)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			wpas.remove_iface(req.args.iface);
+			return 0;
+		}
+	},
+};
+
+wpas.data.ubus = ubus;
+wpas.data.obj = ubus.publish("wpa_supplicant", main_obj);
+
+function iface_event(type, name, data) {
+	let ubus = wpas.data.ubus;
+
+	data ??= {};
+	data.name = name;
+	wpas.data.obj.notify(`iface.${type}`, data, null, null, null, -1);
+	ubus.call("service", "event", { type: `wpa_supplicant.${name}.${type}`, data: {} });
+}
+
+return {
+	shutdown: function() {
+		for (let phy in wpas.data.config)
+			set_config(phy, []);
+		wpas.ubus.disconnect();
+	},
+	iface_add: function(name, obj) {
+		iface_event("add", name);
+	},
+	iface_remove: function(name, obj) {
+		iface_event("remove", name);
+	}
+};
diff --git a/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb b/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
index 0f446aa..f3bb828 100644
--- a/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
+++ b/recipes-wifi/wpa-supplicant/wpa-supplicant_2.10.3.bb
@@ -6,7 +6,7 @@
 LICENSE = "BSD-3-Clause"
 LIC_FILES_CHKSUM = "file://hostapd/README;md5=c905478466c90f1cefc0df987c40e172"
 
-DEPENDS = "dbus libnl ubus"
+DEPENDS = "dbus libnl ubus ucode"
 FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
 FILESEXTRAPATHS_prepend := "${THISDIR}/files/patches-${PV}:"
 
@@ -17,8 +17,9 @@
            file://wpa_supplicant.conf-sane \
            file://99_wpa_supplicant \
            file://wpa_supplicant-full.config \
-           file://src \
-           file://001-rdkb-remove-ubus-support.patch;apply=no \
+	   file://wpa_supplicant.uc \
+           file://src-${PV} \
+           file://002-rdkb-add-ucode-support.patch;apply=no \
            "
 require files/patches-${PV}/patches.inc
 
@@ -38,13 +39,13 @@
 }
 
 do_copy_openwrt_src() {
-    cp -Rfp ${WORKDIR}/src/* ${S}/
+    cp -Rfp ${WORKDIR}/src-${PV}/* ${S}/
 }
 
 do_filogic_patches() {
     cd ${S}
         if [ ! -e patch_applied ]; then
-            patch -p1 < ${WORKDIR}/001-rdkb-remove-ubus-support.patch
+            patch -p1 < ${WORKDIR}/002-rdkb-add-ucode-support.patch
             touch patch_applied
         fi
 }
@@ -83,8 +84,10 @@
 	echo "CONFIG_AP=y" >> wpa_supplicant/.config
 	echo "CONFIG_MESH=y" >> wpa_supplicant/.config
 	echo "CONFIG_IEEE80211BE=y" >> wpa_supplicant/.config
+	echo "CONFIG_IEEE80211AC=y" >> wpa_supplicant/.config
 	echo "CONFIG_HE_OVERRIDES=y" >> wpa_supplicant/.config
 	echo "CONFIG_EHT_OVERRIDES=y" >> wpa_supplicant/.config
+	echo "CONFIG_UCODE=y" >> wpa_supplicant/.config
 }
 
 do_compile () {
@@ -125,6 +128,9 @@
 
 	install -d ${D}${libdir}
 	install -m 0644 ${S}/wpa_supplicant/libwpa_client.so ${D}${libdir}
+
+	install -d ${D}${datadir}/hostap
+	install -m 0755 ${WORKDIR}/wpa_supplicant.uc ${D}${datadir}/hostap
 }
 
 pkg_postinst:${PN} () {
@@ -144,7 +150,7 @@
 
 FILES:${PN}-passphrase = "${sbindir}/wpa_passphrase"
 FILES:${PN}-cli = "${sbindir}/wpa_cli"
-FILES:${PN} += "${datadir}/dbus-1/system-services/* ${systemd_system_unitdir}/*"
+FILES:${PN} += "${datadir}/dbus-1/system-services/* ${systemd_system_unitdir}/* ${datadir}/hostap/*"
 FILES:${PN}-dbg += "${sbindir}/.debug ${libdir}/.debug"
 
 CONFFILES:${PN} += "${sysconfdir}/wpa_supplicant.conf"